GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / plugins / passdss.c
1 /* PASSDSS-3DES-1 SASL plugin
2  * Ken Murchison
3  * $Id: passdss.c,v 1.4 2006/04/24 19:21:44 mel Exp $
4  */
5 /* 
6  * Copyright (c) 1998-2004 Carnegie Mellon University.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer. 
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. The name "Carnegie Mellon University" must not be used to
21  *    endorse or promote products derived from this software without
22  *    prior written permission. For permission or any other legal
23  *    details, please contact  
24  *      Office of Technology Transfer
25  *      Carnegie Mellon University
26  *      5000 Forbes Avenue
27  *      Pittsburgh, PA  15213-3890
28  *      (412) 268-4387, fax: (412) 268-7395
29  *      tech-transfer@andrew.cmu.edu
30  *
31  * 4. Redistributions of any form whatsoever must retain the following
32  *    acknowledgment:
33  *    "This product includes software developed by Computing Services
34  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
35  *
36  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
37  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
38  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
39  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
41  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
42  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43  */
44
45 /*
46  * Notes:
47  *
48  */
49
50 #include <config.h>
51 #include <stdio.h>
52 #include <ctype.h>
53 #include <string.h>
54
55 /* check OpenSSL version */
56 #include <openssl/opensslv.h>
57 #if (OPENSSL_VERSION_NUMBER < 0x0090700f)
58 #error OpenSSL 0.9.7 or later is required
59 #endif
60
61 /* for big number support */
62 #include <openssl/bn.h>
63
64 /* for Diffie-Hellman support */
65 #include <openssl/dh.h>
66
67 /* for digest and cipher support */
68 #include <openssl/evp.h>
69 #include <openssl/hmac.h>
70 #include <openssl/md5.h>
71 #include <openssl/sha.h>
72 #include <openssl/dsa.h>
73
74 #include <sasl.h>
75 #define MD5_H  /* suppress internal MD5 */
76 #include <saslplug.h>
77
78 #include "plugin_common.h"
79
80 #ifdef macintosh 
81 #include <sasl_passdss_plugin_decl.h> 
82 #endif 
83
84 /*****************************  Common Section  *****************************/
85
86 static const char plugin_id[] = "$Id: passdss.c,v 1.4 2006/04/24 19:21:44 mel Exp $";
87
88 const char g[] = "2";
89 const char N[] = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF";
90
91 #define NO_LAYER_FLAG           (1<<0)
92 #define INTEGRITY_LAYER_FLAG    (1<<1)
93 #define PRIVACY_LAYER_FLAG      (1<<2)
94
95 #define NO_LAYER_SSF            0
96 #define INTEGRITY_LAYER_SSF     1
97 #define PRIVACY_LAYER_SSF       112
98
99 typedef struct context {
100     int state;
101
102     char *authid;               /* authentication id (server) */
103     char *userid;               /* authorization id (server) */
104     sasl_secret_t *password;    /* user secret (client) */
105     unsigned int free_password; /* set if we need to free password */
106
107     DH *dh;                     /* Diffie-Hellman parameters */
108
109     /* copy of utils from the params structures */
110     const sasl_utils_t *utils;
111     
112     /* per-step mem management */
113     char *out_buf;
114     unsigned out_buf_len;
115
116     /* security layer foo */
117     unsigned char secmask;      /* bitmask of enabled security layers */
118     unsigned char padding[EVP_MAX_BLOCK_LENGTH];  /* block of NULs */
119
120     HMAC_CTX hmac_send_ctx;
121     HMAC_CTX hmac_recv_ctx;
122
123     unsigned char send_integrity_key[4 + EVP_MAX_MD_SIZE]; /* +4 for pktnum */
124     unsigned char recv_integrity_key[4 + EVP_MAX_MD_SIZE]; /* +4 for pktnum */
125     unsigned char *cs_integrity_key;  /* ptr to bare key in send/recv key */
126     unsigned char *sc_integrity_key;  /* ptr to bare key in send/recv key */
127
128     EVP_CIPHER_CTX cipher_enc_ctx;
129     EVP_CIPHER_CTX cipher_dec_ctx;
130     unsigned blk_siz;
131     
132     unsigned char cs_encryption_iv[EVP_MAX_MD_SIZE];
133     unsigned char sc_encryption_iv[EVP_MAX_MD_SIZE];
134     unsigned char cs_encryption_key[2 * EVP_MAX_MD_SIZE];
135     unsigned char sc_encryption_key[2 * EVP_MAX_MD_SIZE];
136
137     /* replay detection sequence numbers */
138     uint32_t pktnum_out;
139     uint32_t pktnum_in;
140     
141     /* for encoding/decoding mem management */
142     char           *encode_buf, *decode_buf, *decode_pkt_buf;
143     unsigned       encode_buf_len, decode_buf_len, decode_pkt_buf_len;
144     
145     /* layers buffering */
146     decode_context_t decode_context;
147     
148 } context_t;
149
150
151 static int passdss_encode(void *context,
152                           const struct iovec *invec,
153                           unsigned numiov,
154                           const char **output,
155                           unsigned *outputlen)
156 {
157     context_t *text = (context_t *) context;
158     unsigned long inputlen;
159     unsigned char hmac[EVP_MAX_MD_SIZE];
160     unsigned i, hmaclen;
161     uint32_t tmpnum;
162     int ret;
163     
164     if (!context || !invec || !numiov || !output || !outputlen) {
165         PARAMERROR( text->utils );
166         return SASL_BADPARAM;
167     }
168
169     /* calculate total size of input */
170     for (i = 0, inputlen = 0; i < numiov; i++)
171         inputlen += invec[i].iov_len;
172
173     /* allocate a buffer for the output */
174     ret = _plug_buf_alloc(text->utils, &text->encode_buf,
175                           &text->encode_buf_len,
176                           4 +                           /* length */
177                           inputlen +                    /* content */
178                           EVP_MAX_MD_SIZE +             /* HMAC */
179                           EVP_MAX_BLOCK_LENGTH - 1);    /* padding */
180     if (ret != SASL_OK) return ret;
181
182     *outputlen = 4; /* skip length */
183
184     /* prepend packet number to integrity key */
185     tmpnum = htonl(text->pktnum_out++);
186     memcpy(text->send_integrity_key, &tmpnum, 4);
187
188     /* key the HMAC */
189     HMAC_Init_ex(&text->hmac_send_ctx, text->send_integrity_key,
190                  4+SHA_DIGEST_LENGTH, EVP_sha1(), NULL);
191
192     /* operate on each iovec */
193     for (i = 0; i < numiov; i++) {
194         /* hash the content */
195         HMAC_Update(&text->hmac_send_ctx, invec[i].iov_base, invec[i].iov_len);
196
197         if (text->secmask & PRIVACY_LAYER_FLAG) {
198             unsigned enclen;
199
200             /* encrypt the data into the output buffer */
201             EVP_EncryptUpdate(&text->cipher_enc_ctx,
202                               text->encode_buf + *outputlen, &enclen,
203                               invec[i].iov_base, invec[i].iov_len);
204             *outputlen += enclen;
205         }
206         else {
207             /* copy the raw input to the output */
208             memcpy(text->encode_buf + *outputlen, invec[i].iov_base,
209                    invec[i].iov_len);
210             *outputlen += invec[i].iov_len;
211         }
212     }
213
214     /* calculate the HMAC */
215     HMAC_Final(&text->hmac_send_ctx, hmac, &hmaclen);
216
217     if (text->secmask & PRIVACY_LAYER_FLAG) {
218         unsigned enclen;
219         unsigned char padlen;
220
221         /* encrypt the HMAC into the output buffer */
222         EVP_EncryptUpdate(&text->cipher_enc_ctx,
223                           text->encode_buf + *outputlen, &enclen,
224                           hmac, hmaclen);
225         *outputlen += enclen;
226
227         /* pad output buffer to multiple of blk_siz
228            with padlen-1 as last octet */
229         padlen = text->blk_siz - ((inputlen + hmaclen) % text->blk_siz) - 1;
230         EVP_EncryptUpdate(&text->cipher_enc_ctx,
231                           text->encode_buf + *outputlen, &enclen,
232                           text->padding, padlen);
233         *outputlen += enclen;
234         EVP_EncryptUpdate(&text->cipher_enc_ctx,
235                           text->encode_buf + *outputlen, &enclen,
236                           &padlen, 1);
237         *outputlen += enclen;
238
239         /* encrypt the last block of data into the output buffer */
240         EVP_EncryptFinal_ex(&text->cipher_enc_ctx,
241                             text->encode_buf + *outputlen, &enclen);
242         *outputlen += enclen;
243     }
244     else {
245         /* copy the HMAC to the output */
246         memcpy(text->encode_buf + *outputlen, hmac, hmaclen);
247         *outputlen += hmaclen;
248     }
249
250     /* prepend the length of the output */
251     tmpnum = *outputlen - 4;
252     tmpnum = htonl(tmpnum);
253     memcpy(text->encode_buf, &tmpnum, 4);
254
255     *output = text->encode_buf;
256     
257     return SASL_OK;
258 }
259
260 /* Decode a single PASSDSS packet */
261 static int passdss_decode_packet(void *context,
262                                  const char *input,
263                                  unsigned inputlen,
264                                  char **output,
265                                  unsigned *outputlen)
266 {
267     context_t *text = (context_t *) context;
268     uint32_t tmpnum;
269     unsigned char hmac[EVP_MAX_MD_SIZE];
270     unsigned hmaclen;
271     int ret;
272
273     if (text->secmask & PRIVACY_LAYER_FLAG) {
274         unsigned declen, padlen;
275
276         /* allocate a buffer for the output */
277         ret = _plug_buf_alloc(text->utils, &(text->decode_pkt_buf),
278                               &(text->decode_pkt_buf_len), inputlen);
279         if (ret != SASL_OK) return ret;
280
281         /* decrypt the data into the output buffer */
282         ret = EVP_DecryptUpdate(&text->cipher_dec_ctx,
283                                 text->decode_pkt_buf, &declen,
284                                 (char *) input, inputlen);
285         if (ret)
286             EVP_DecryptFinal_ex(&text->cipher_dec_ctx,  /* should be no output */
287                                 text->decode_pkt_buf + declen, &declen);
288         if (!ret) {
289             SETERROR(text->utils, "Error decrypting input");
290             return SASL_BADPROT;
291         }
292         input = text->decode_pkt_buf;
293
294         /* trim padding */
295         padlen = text->decode_pkt_buf[inputlen - 1] + 1;
296         inputlen -= padlen;
297     }
298
299     /* trim HMAC */
300     inputlen -= SHA_DIGEST_LENGTH;
301
302     /* prepend packet number to integrity key */
303     tmpnum = htonl(text->pktnum_in++);
304     memcpy(text->recv_integrity_key, &tmpnum, 4);
305
306     /* calculate the HMAC */
307     HMAC(EVP_sha1(), text->recv_integrity_key, 4+SHA_DIGEST_LENGTH,
308          input, inputlen, hmac, &hmaclen);
309
310     /* verify HMAC */
311     if (memcmp(hmac, input+inputlen, hmaclen)) {
312         SETERROR(text->utils, "HMAC is incorrect\n");
313         return SASL_BADMAC;
314     }
315
316     *output = (char *) input;
317     *outputlen = inputlen;
318
319     return SASL_OK;
320 }
321
322 /* Decode and concatenate multiple PASSDSS packets */
323 static int passdss_decode(void *context,
324                       const char *input, unsigned inputlen,
325                       const char **output, unsigned *outputlen)
326 {
327     context_t *text = (context_t *) context;
328     int ret;
329     
330     ret = _plug_decode(&text->decode_context, input, inputlen,
331                        &text->decode_buf, &text->decode_buf_len, outputlen,
332                        passdss_decode_packet, text);
333     
334     *output = text->decode_buf;
335     
336     return ret;
337 }
338
339 #define MAX_MPI_LEN 2147483643
340 #define MAX_UTF8_LEN 2147483643
341
342 /*
343  * Create/append to a PASSDSS buffer from the data specified by the fmt string.
344  */
345 static int MakeBuffer(const sasl_utils_t *utils, char **buf, unsigned offset,
346                       unsigned *buflen, unsigned *outlen, const char *fmt, ...)
347 {
348     va_list ap;
349     char *p, *out = NULL, *lptr = NULL;
350     int r, alloclen, len = -1, argc = 0;
351     BIGNUM *mpi;
352     char *os, *str;
353     uint32_t u, nl;
354
355     /* first pass to calculate size of buffer */
356     va_start(ap, fmt);
357     for (p = (char *) fmt, alloclen = offset; *p; p++) {
358         if (*p != '%') {
359             alloclen++;
360             continue;
361         }
362
363         /* check for length prefix ('a', 'o', 'u', and 's' only) */
364         if (*++p == '*') {
365             /* arg is length of next arg */
366             len = va_arg(ap, int);
367             p++;
368         }
369         else if (isdigit((int) *p)) {
370             len = 0;
371             while (isdigit((int) *p)) len = 10 * len + *p++ - '0';
372         }
373
374         switch (*p) {
375         case 'a':
376             /* insert total length of next N args */
377             alloclen += 4;
378             break;
379
380         case 'm':
381             /* MPI */
382             mpi = va_arg(ap, BIGNUM *);
383             len = BN_num_bytes(mpi);
384             if (len > MAX_MPI_LEN) {
385                 utils->log(NULL, SASL_LOG_ERR,
386                            "String too long to create mpi string\n");
387                 r = SASL_FAIL;
388                 goto done;
389             }
390             alloclen += len + 4;
391             break;
392
393         case 'o':
394             /* octet sequence (len given by prefix) */
395             alloclen += len;
396             os = va_arg(ap, char *);
397             break;
398
399         case 's':
400             /* string */
401             str = va_arg(ap, char *);
402             if (len == -1) len = strlen(str);
403             if (len > MAX_UTF8_LEN) {
404                 utils->log(NULL, SASL_LOG_ERR,
405                            "String too long to create utf8 string\n");
406                 r = SASL_FAIL;
407                 goto done;
408             }
409             alloclen += len + 4;
410             break;
411
412         case 'u':
413             /* unsigned int */
414             u = va_arg(ap, uint32_t);
415             if (len == -1) len = 4;
416             alloclen += len;
417             break;
418
419         default:
420             alloclen++;
421             break;
422         }
423
424         len = -1;
425     }
426     va_end(ap);
427
428     r = _plug_buf_alloc(utils, buf, buflen, alloclen);
429     if (r != SASL_OK) return r;
430
431     out = *buf + offset;
432
433     /* second pass to fill buffer */
434     va_start(ap, fmt);
435     for (p = (char *) fmt; *p; p++) {
436         if (*p != '%') {
437             *out = *p;
438             out++;
439             continue;
440         }
441
442         /* check for length prefix ('a', 'o', 'u', and 's' only) */
443         if (*++p == '*') {
444             /* arg is length of next arg */
445             len = va_arg(ap, int);
446             p++;
447         }
448         else if (isdigit((int) *p)) {
449             len = 0;
450             while (isdigit((int) *p)) len = 10 * len + *p++ - '0';
451         }
452
453         switch (*p) {
454         case 'a':
455             /* total length of next N args */
456             argc = len;
457             len = -1;
458             lptr = out;
459             out += 4;
460             continue;
461             break;
462
463         case 'm':
464             /* MPI */
465             mpi = va_arg(ap, BIGNUM *);
466             len = BN_bn2bin(mpi, out+4);
467             nl = htonl(len);
468             memcpy(out, &nl, 4);        /* add 4 byte len (network order) */
469             out += len + 4;
470             break;
471
472         case 'o':
473             /* octet sequence (len given by prefix) */
474             os = va_arg(ap, char *);
475             memcpy(out, os, len);       /* add data */
476             out += len;
477             break;
478
479         case 's':
480             /* string (len possibly given by prefix) */
481             str = va_arg(ap, char *);
482             /* xxx do actual utf8 conversion */
483             if (len == -1) len = strlen(str);
484             nl = htonl(len);
485             memcpy(out, &nl, 4);        /* add 4 byte len (network order) */
486             memcpy(out+4, str, len);    /* add string */
487             out += len + 4;
488             break;
489
490         case 'u':
491             /* unsigned int */
492             u = va_arg(ap, uint32_t);
493             nl = htonl(u);
494             if (len == -1) len = 4;
495             memcpy(out, &nl + 4 - len, len);
496             out += len;
497             break;
498
499         default:
500             *out = *p;
501             out++;
502             break;
503         }
504
505         /* see if we're done counting args */
506         if (lptr && !--argc) {
507             len = out - lptr - 4;
508             nl = htonl(len);
509             memcpy(lptr, &nl, 4);       /* insert 4 byte len (network order) */
510             lptr = NULL;
511         }
512
513         len = -1;
514     }
515   done:
516     va_end(ap);
517
518     *outlen = out - *buf;
519
520     return r;
521 }
522
523 /* 
524  * Extract a PASSDSS buffer into the data specified by the fmt string.
525  */
526 static int UnBuffer(const sasl_utils_t *utils, const char *buf,
527                     unsigned buflen, const char *fmt, ...)
528 {
529     va_list ap;
530     char *p;
531     BIGNUM **mpi;
532     char **os, **str;
533     uint32_t *u, nl;
534     unsigned len;
535     enum { OCTET_REFERENCE,     /* just point to the data (reference it) */
536            OCTET_COPY,          /* copy the data into the given buffer */
537            OCTET_ALLOC          /* alloc space for the data, then copy */
538     } octet_flag;
539     int r = SASL_OK;
540
541     va_start(ap, fmt);
542     for (p = (char *) fmt; *p; p++) {
543         if (*p != '%') {
544             if (*buf != *p) {
545                 r = SASL_BADPROT;
546                 goto done;
547             }
548             buf++;
549             buflen--;
550             continue;
551         }
552         p++;
553
554         /* check for octet flags */
555         octet_flag = OCTET_COPY;
556         if (*p == '-') {
557             octet_flag = OCTET_REFERENCE;
558             p++;
559         }
560         else if (*p == '+') {
561             octet_flag = OCTET_ALLOC;
562             p++;
563         }
564
565         /* check for length prefix ('o', 'u', and 'p' only) */
566         len = 0;
567         if (*p == '*') {
568             /* arg is length of next arg */
569             len = va_arg(ap, int);
570             p++;
571         }
572         else if (isdigit((int) *p)) {
573             len = 0;
574             while (isdigit((int) *p)) len = 10 * len + *p++ - '0';
575         }
576
577         switch (*p) {
578         case 'm':
579             /* MPI */
580             mpi = va_arg(ap, BIGNUM **);
581
582             if (buflen < 4) {
583                 SETERROR(utils, "Buffer is not big enough to be PASSDSS MPI\n");
584                 r = SASL_BADPROT;
585                 goto done;
586             }
587     
588             /* get the length */
589             memcpy(&nl, buf, 4);
590             len = ntohl(nl);
591             buf += 4;
592             buflen -= 4;
593     
594             /* make sure it's right */
595             if (len > buflen) {
596                 SETERROR(utils, "Not enough data for this PASSDSS MPI\n");
597                 r = SASL_BADPROT;
598                 goto done;
599             }
600             
601             if (mpi) {
602                 if (!*mpi) *mpi = BN_new();
603                 BN_init(*mpi);
604                 BN_bin2bn(buf, len, *mpi);
605             }
606             break;
607
608         case 'o':
609             /* octet sequence (len given by prefix) */
610             os = va_arg(ap, char **);
611
612             /* make sure it's right */
613             if (len > buflen) {
614                 SETERROR(utils, "Not enough data for this PASSDSS os\n");
615                 r = SASL_BADPROT;
616                 goto done;
617             }
618             
619             if (os) {
620                 if (octet_flag == OCTET_REFERENCE)
621                     *os = (char *) buf;
622                 else {
623                     if (octet_flag == OCTET_ALLOC &&
624                         (*os = (char *) utils->malloc(len)) == NULL) {
625                         r = SASL_NOMEM;
626                         goto done;
627                     }
628     
629                     memcpy(*os, buf, len);
630                 }
631             }
632             break;
633
634         case 'p':
635             /* padding (max len given by prefix) */
636
637             if (buflen < len) len = buflen;
638             break;
639
640         case 's':
641             /* string */
642             str = va_arg(ap, char **);
643             if (str) *str = NULL;
644
645             if (buflen < 4) {
646                 SETERROR(utils, "Buffer is not big enough to be PASSDSS string\n");
647                 r = SASL_BADPROT;
648                 goto done;
649             }
650     
651             /* get the length */
652             memcpy(&nl, buf, 4);
653             len = ntohl(nl);
654             buf += 4;
655             buflen -= 4;
656     
657             /* make sure it's right */
658             if (len > buflen) {
659                 SETERROR(utils, "Not enough data for this PASSDSS string\n");
660                 r = SASL_BADPROT;
661                 goto done;
662             }
663             
664             if (str) {
665                 *str = (char *) utils->malloc(len+1); /* +1 for NUL */
666                 if (!*str) {
667                     r = SASL_NOMEM;
668                     goto done;
669                 }
670     
671                 memcpy(*str, buf, len);
672                 (*str)[len] = '\0';
673             }
674             break;
675
676         case 'u':
677             /* unsigned int */
678             u = va_arg(ap, uint32_t*);
679
680             if (!len) len = 4;
681             if (buflen < len) {
682                 SETERROR(utils, "Buffer is not big enough to be PASSDSS uint32\n");
683                 r = SASL_BADPROT;
684                 goto done;
685             }
686
687             if (u) {
688                 memset(u, 0, 4);
689                 memcpy(u + 4 - len, buf, len);
690                 *u = ntohl(*u);
691             }
692             break;
693
694         default:
695             len = 1;
696             if (*buf != *p) {
697                 r = SASL_BADPROT;
698                 goto done;
699             }
700             break;
701         }
702
703         buf += len;
704         buflen -= len;
705     }
706
707     if (buflen != 0) {
708         SETERROR(utils, "Extra data in PASSDSS buffer\n");
709         r = SASL_BADPROT;
710     }
711
712   done:
713     va_end(ap);
714
715     return r;
716 }
717
718 #define DOHASH(out, in1, len1, in2, len2, in3, len3)    \
719     EVP_DigestInit(&mdctx, EVP_sha1());                 \
720     EVP_DigestUpdate(&mdctx, in1, len1);                \
721     EVP_DigestUpdate(&mdctx, in2, len2);                \
722     EVP_DigestUpdate(&mdctx, in3, len3);                \
723     EVP_DigestFinal(&mdctx, out, NULL)
724
725 void CalcLayerParams(context_t *text, char *K, unsigned Klen,
726                      char *hash, unsigned hashlen)
727 {
728     EVP_MD_CTX mdctx;
729
730     DOHASH(text->cs_encryption_iv, K, Klen, "A", 1, hash, hashlen);
731     DOHASH(text->sc_encryption_iv, K, Klen, "B", 1, hash, hashlen);
732     DOHASH(text->cs_encryption_key, K, Klen, "C", 1, hash, hashlen);
733     DOHASH(text->cs_encryption_key + hashlen, K, Klen, "", 0,
734            text->cs_encryption_key, hashlen);
735     DOHASH(text->sc_encryption_key, K, Klen, "D", 1, hash, hashlen);
736     DOHASH(text->sc_encryption_key + hashlen, K, Klen, "", 0,
737            text->sc_encryption_key, hashlen);
738     DOHASH(text->cs_integrity_key, K, Klen, "E", 1, hash, hashlen);
739     DOHASH(text->sc_integrity_key, K, Klen, "F", 1, hash, hashlen);
740 }
741
742 /*
743  * Dispose of a PASSDSS context (could be server or client)
744  */ 
745 static void passdss_common_mech_dispose(void *conn_context,
746                                         const sasl_utils_t *utils)
747 {
748     context_t *text = (context_t *) conn_context;
749     
750     if (!text) return;
751     
752     if (text->authid)           utils->free(text->authid);
753     if (text->userid)           utils->free(text->userid);
754     if (text->free_password)    _plug_free_secret(utils, &(text->password));
755
756     if (text->dh)               DH_free(text->dh);
757
758     HMAC_CTX_cleanup(&text->hmac_send_ctx);
759     HMAC_CTX_cleanup(&text->hmac_recv_ctx);
760
761     EVP_CIPHER_CTX_cleanup(&text->cipher_enc_ctx);
762     EVP_CIPHER_CTX_cleanup(&text->cipher_dec_ctx);
763     
764     _plug_decode_free(&text->decode_context);
765
766     if (text->encode_buf)       utils->free(text->encode_buf);
767     if (text->decode_buf)       utils->free(text->decode_buf);
768     if (text->decode_pkt_buf)   utils->free(text->decode_pkt_buf);
769     if (text->out_buf)          utils->free(text->out_buf);
770     
771     utils->free(text);
772 }
773
774 /*****************************  Server Section  *****************************/
775
776 static int passdss_server_mech_new(void *glob_context __attribute__((unused)), 
777                                  sasl_server_params_t *sparams,
778                                  const char *challenge __attribute__((unused)),
779                                  unsigned challen __attribute__((unused)),
780                                  void **conn_context)
781 {
782     context_t *text;
783
784     /* holds state are in */
785     text = sparams->utils->malloc(sizeof(context_t));
786     if (text == NULL) {
787         MEMERROR(sparams->utils);
788         return SASL_NOMEM;
789     }
790     
791     memset(text, 0, sizeof(context_t));
792     
793     text->state = 1;
794     text->utils = sparams->utils;
795     text->cs_integrity_key = text->recv_integrity_key + 4;
796     text->sc_integrity_key = text->send_integrity_key + 4;
797     
798     *conn_context = text;
799     
800     return SASL_OK;
801 }
802
803 static int
804 passdss_server_mech_step1(context_t *text,
805                           sasl_server_params_t *params,
806                           const char *clientin,
807                           unsigned clientinlen,
808                           const char **serverout,
809                           unsigned *serveroutlen,
810                           sasl_out_params_t *oparams __attribute__((unused)))
811 {
812     BIGNUM *X = NULL;
813     DSA *dsa = NULL;
814     unsigned char *K = NULL;
815     unsigned Klen, hashlen;
816     int need, musthave;
817     EVP_MD_CTX mdctx;
818     unsigned char hash[EVP_MAX_MD_SIZE];
819     DSA_SIG *sig = NULL;
820     int result;
821     
822     /* Expect:
823      *
824      * (1) string azname        ; authorization name
825      * (2) string authname      ; authentication name
826      * (3) mpint  X             ; Diffie-Hellman parameter X
827      */
828     
829     result = UnBuffer(params->utils, clientin, clientinlen,
830                       "%s%s%m", &text->userid, &text->authid, &X);
831     if (result) {
832         params->utils->seterror(params->utils->conn, 0, 
833                                 "Error UnBuffering input in step 1");
834         goto cleanup;
835     }
836
837     /* Fetch DSA (XXX create one for now) */
838     dsa = DSA_generate_parameters(1024, NULL, 0, NULL, NULL, NULL, NULL);
839     if (!dsa) {
840         result = SASL_FAIL;
841         goto cleanup;
842     }
843     DSA_generate_key(dsa);
844
845     /* Create Diffie-Hellman parameters */
846     text->dh = DH_new();
847     BN_hex2bn(&text->dh->p, N);
848     BN_hex2bn(&text->dh->g, g);
849     DH_generate_key(text->dh);
850
851     /* Alloc space for shared secret K as mpint */
852     K = text->utils->malloc(DH_size(text->dh) + 4);
853     if (!K) {
854         params->utils->log(NULL, SASL_LOG_ERR, "Error allocing K\n");
855         result = SASL_NOMEM;
856         goto cleanup;
857     }
858
859     /* Calculate DH shared secret (leave space at head for length) */
860     Klen = DH_compute_key(K+4, X, text->dh);
861
862     /* Prepend length in network byte order (make it a mpint) */
863     *((uint32_t *) K) = htonl(Klen);
864     Klen += 4;
865
866     /* Which layers can we support? */
867     if (params->props.maxbufsize < 32) {
868         need = musthave = 0;
869     } else {
870         need = params->props.max_ssf - params->external_ssf;
871         musthave = params->props.min_ssf - params->external_ssf;
872     }
873
874     if (musthave <= NO_LAYER_SSF)
875         text->secmask |= NO_LAYER_FLAG;
876     if ((musthave <= INTEGRITY_LAYER_SSF) && (INTEGRITY_LAYER_SSF <= need))
877         text->secmask |= INTEGRITY_LAYER_FLAG;
878     if ((musthave <= PRIVACY_LAYER_SSF) && (PRIVACY_LAYER_SSF <= need))
879         text->secmask |= PRIVACY_LAYER_FLAG;
880
881
882     /* Send out:
883      *
884      * (4) uint32   pklength    ; length of SSH-style DSA server public key
885      *       string "ssh-dss"   ; constant string "ssh-dss" (lower case)
886      *       mpint  p           ; DSA public key parameters
887      *       mpint  q
888      *       mpint  g
889      *       mpint  y
890      * (5) mpint    Y           ; Diffie-Hellman parameter Y
891      * (6) OCTET    ssecmask    ; SASL security layers offered
892      * (7) 3 OCTET  sbuflen     ; maximum server security layer block size
893      * (8) uint32   siglength   ; length of SSH-style dss signature
894      *       string "ssh-dss"   ; constant string "ssh-dss" (lower case)
895      *       mpint  r           ; DSA signature parameters
896      *       mpint  s
897      */
898
899     /* Items (4) - (7) */
900     result = MakeBuffer(text->utils, &text->out_buf, 0, &text->out_buf_len,
901                         serveroutlen, "%5a%s%m%m%m%m%m%1o%3u",
902                         "ssh-dss", dsa->p, dsa->q, dsa->g, dsa->pub_key,
903                         text->dh->pub_key, &text->secmask,
904                         (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
905                         params->props.maxbufsize);
906     if (result) {
907         params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
908         goto cleanup;
909     }
910
911     /* Hash (1) - (7) and K */
912     EVP_DigestInit(&mdctx, EVP_sha1());
913     /* (1) - (3) */
914     EVP_DigestUpdate(&mdctx, clientin, clientinlen);
915     /* (4) - (7) */
916     EVP_DigestUpdate(&mdctx, text->out_buf, *serveroutlen);
917     /* K */
918     EVP_DigestUpdate(&mdctx, K, Klen);
919     EVP_DigestFinal(&mdctx, hash, &hashlen);
920
921     /* Calculate security layer params */
922     CalcLayerParams(text, K, Klen, hash, hashlen);
923
924     /* Start cli-hmac */
925     HMAC_CTX_init(&text->hmac_recv_ctx);
926     HMAC_Init_ex(&text->hmac_recv_ctx, text->cs_integrity_key,
927                  SHA_DIGEST_LENGTH, EVP_sha1(), NULL);
928     /* (1) - (3) */
929     HMAC_Update(&text->hmac_recv_ctx, clientin, clientinlen);
930     /* (4) - (7) */
931     HMAC_Update(&text->hmac_recv_ctx, text->out_buf, *serveroutlen);
932
933     /* Sign the hash */
934     sig = DSA_do_sign(hash, hashlen, dsa);
935     if (!sig) {
936         params->utils->log(NULL, SASL_LOG_ERR,
937                            "Error calculating DSS signature\n");
938         result = SASL_FAIL;
939         goto cleanup;
940     }
941
942     /* Item (8) */
943     result = MakeBuffer(text->utils, &text->out_buf, *serveroutlen,
944                         &text->out_buf_len, serveroutlen,
945                         "%3a%s%m%m", "ssh-dss", sig->r, sig->s);
946     if (result) {
947         params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
948         goto cleanup;
949     }
950     *serverout = text->out_buf;
951
952     text->state = 2;
953     result = SASL_CONTINUE;
954
955   cleanup:
956     if (X) BN_free(X);
957     if (K) text->utils->free(K);
958     if (dsa) DSA_free(dsa);
959     if (sig) DSA_SIG_free(sig);
960
961     return result;
962 }
963
964 static int
965 passdss_server_mech_step2(context_t *text,
966                           sasl_server_params_t *params,
967                           const char *clientin,
968                           unsigned clientinlen,
969                           const char **serverout __attribute__((unused)),
970                           unsigned *serveroutlen __attribute__((unused)),
971                           sasl_out_params_t *oparams)
972 {
973     char *password = NULL;
974     unsigned declen, hmaclen;
975     unsigned char *csecmask, *cli_hmac, hmac[EVP_MAX_MD_SIZE];
976     uint32_t cbufsiz;
977     int r, result = SASL_OK;
978     
979     /* Expect (3DES encrypted):
980      *
981      * (9) OCTET    csecmask    ; SASL security layer selection
982      *     3 OCTET  cbuflen     ; maximum client block size
983      *     string   passphrase  ; the user's passphrase
984      *     20 OCTET cli-hmac    ; a client HMAC-SHA-1 signature
985      */
986
987     /* Alloc space for the decrypted input */
988     result = _plug_buf_alloc(text->utils, &text->decode_pkt_buf,
989                              &text->decode_pkt_buf_len, clientinlen);
990     if (result) {
991         params->utils->log(NULL, SASL_LOG_ERR,
992                            "Error allocating decrypt buffer in step 2\n");
993         goto cleanup;
994     }
995
996     /* Initialize decrypt cipher */
997     EVP_CIPHER_CTX_init(&text->cipher_dec_ctx);
998     EVP_DecryptInit_ex(&text->cipher_dec_ctx, EVP_des_ede3_cbc(), NULL,
999                        text->cs_encryption_key, text->cs_encryption_iv);
1000     EVP_CIPHER_CTX_set_padding(&text->cipher_dec_ctx, 0);
1001     text->blk_siz = EVP_CIPHER_CTX_block_size(&text->cipher_dec_ctx);
1002
1003     /* Decrypt the blob */
1004     r = EVP_DecryptUpdate(&text->cipher_dec_ctx, text->decode_pkt_buf, &declen,
1005                           clientin, clientinlen);
1006     if (r)
1007         r = EVP_DecryptFinal_ex(&text->cipher_dec_ctx,  /* should be no output */
1008                                 text->decode_pkt_buf + declen, &declen);
1009     if (!r) {
1010         params->utils->seterror(params->utils->conn, 0, 
1011                                 "Error decrypting input in step 2");
1012         result = SASL_BADPROT;
1013         goto cleanup;
1014     }
1015     clientin = text->decode_pkt_buf;
1016
1017     result = UnBuffer(params->utils, clientin, clientinlen,
1018                       "%-1o%3u%s%-*o%*p", &csecmask, &cbufsiz, &password,
1019                       SHA_DIGEST_LENGTH, &cli_hmac, text->blk_siz - 1);
1020     if (result) {
1021         params->utils->seterror(params->utils->conn, 0, 
1022                                 "Error UnBuffering input in step 2");
1023         goto cleanup;
1024     }
1025
1026     /* Finish cli-hmac */
1027     /* (1) - (7) hashed in step 1 */
1028     /* 1st 4 bytes of (9) */
1029     HMAC_Update(&text->hmac_recv_ctx, clientin, 4);
1030     HMAC_Final(&text->hmac_recv_ctx, hmac, &hmaclen);
1031
1032     /* Verify cli-hmac */
1033     if (memcmp(cli_hmac, hmac, hmaclen)) {
1034         params->utils->seterror(params->utils->conn, 0,
1035                                 "Client HMAC verification failed");
1036         result = SASL_BADMAC;
1037         goto cleanup;
1038     }
1039
1040     /* Canonicalize authentication ID first, so that password verification
1041      * is only against the canonical id */
1042     result = params->canon_user(params->utils->conn,
1043                                 text->authid, 0, SASL_CU_AUTHID, oparams);
1044     if (result != SASL_OK) {
1045         return result;
1046     }
1047     
1048     /* Verify password - return sasl_ok on success */
1049     result = params->utils->checkpass(params->utils->conn,
1050                                       oparams->authid, oparams->alen,
1051                                       password, strlen(password));
1052         
1053     if (result != SASL_OK) {
1054         params->utils->seterror(params->utils->conn, 0,
1055                                 "Password verification failed");
1056         goto cleanup;
1057     }
1058
1059     /* Canonicalize and store the authorization ID */
1060     /* We need to do this after calling verify_user just in case verify_user
1061      * needed to get auxprops itself */
1062     result = params->canon_user(params->utils->conn,
1063                                 *text->userid ? text->userid : text->authid, 0,
1064                                 SASL_CU_AUTHZID, oparams);
1065     if (result != SASL_OK) return result;
1066
1067     /* See which layer the client selected */
1068     text->secmask &= *csecmask;
1069     if (text->secmask & PRIVACY_LAYER_FLAG) {
1070         oparams->mech_ssf = PRIVACY_LAYER_SSF;
1071     } else if (text->secmask & INTEGRITY_LAYER_FLAG) {
1072         oparams->mech_ssf = INTEGRITY_LAYER_SSF;
1073     } else if (text->secmask & NO_LAYER_FLAG) {
1074         oparams->mech_ssf = NO_LAYER_SSF;
1075     } else {
1076         /* Mark that we tried */
1077         oparams->mech_ssf = 2;
1078         SETERROR(params->utils,
1079                  "unable to agree on layers with server");
1080         return SASL_BADPROT;
1081     }
1082
1083     /* Set oparams */
1084     oparams->doneflag = 1;
1085     oparams->param_version = 0;
1086
1087     if (oparams->mech_ssf > 0) {
1088         oparams->encode = &passdss_encode;
1089         oparams->decode = &passdss_decode;
1090         oparams->maxoutbuf = cbufsiz - 4 - SHA_DIGEST_LENGTH; /* -len -HMAC */
1091
1092         HMAC_CTX_init(&text->hmac_send_ctx);
1093
1094         if (oparams->mech_ssf > 1) {
1095             oparams->maxoutbuf -= text->blk_siz-1; /* padding */
1096
1097             /* Initialize encrypt cipher */
1098             EVP_CIPHER_CTX_init(&text->cipher_enc_ctx);
1099             EVP_EncryptInit_ex(&text->cipher_enc_ctx, EVP_des_ede3_cbc(), NULL,
1100                                text->sc_encryption_key, text->sc_encryption_iv);
1101             EVP_CIPHER_CTX_set_padding(&text->cipher_enc_ctx, 0);
1102         }
1103
1104         _plug_decode_init(&text->decode_context, text->utils,
1105                           (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1106                           params->props.maxbufsize);
1107     }
1108     else {
1109         oparams->encode = NULL;
1110         oparams->decode = NULL;
1111         oparams->maxoutbuf = 0;
1112     }
1113
1114     result = SASL_OK;
1115     
1116   cleanup:
1117     if (password) _plug_free_string(params->utils, &password);
1118
1119     return result;
1120 }
1121
1122 static int passdss_server_mech_step(void *conn_context,
1123                                     sasl_server_params_t *sparams,
1124                                     const char *clientin,
1125                                     unsigned clientinlen,
1126                                     const char **serverout,
1127                                     unsigned *serveroutlen,
1128                                     sasl_out_params_t *oparams)
1129 {
1130     context_t *text = (context_t *) conn_context;
1131     
1132     if (!sparams
1133         || !serverout
1134         || !serveroutlen
1135         || !oparams)
1136         return SASL_BADPARAM;
1137     
1138     sparams->utils->log(NULL, SASL_LOG_DEBUG,
1139                         "PASSDSS server step %d\n", text->state);
1140     
1141     *serverout = NULL;
1142     *serveroutlen = 0;
1143         
1144     switch (text->state) {
1145
1146     case 1:
1147         return passdss_server_mech_step1(text, sparams, clientin, clientinlen,
1148                                          serverout, serveroutlen, oparams);
1149
1150     case 2:
1151         return passdss_server_mech_step2(text, sparams, clientin, clientinlen,
1152                                          serverout, serveroutlen, oparams);
1153
1154     default:
1155         sparams->utils->seterror(sparams->utils->conn, 0,
1156                                  "Invalid PASSDSS server step %d", text->state);
1157         return SASL_FAIL;
1158     }
1159     
1160     return SASL_FAIL; /* should never get here */
1161 }
1162
1163 static sasl_server_plug_t passdss_server_plugins[] = 
1164 {
1165     {
1166         "PASSDSS-3DES-1",               /* mech_name */
1167         112,                            /* max_ssf */
1168         SASL_SEC_NOPLAINTEXT
1169         | SASL_SEC_NOANONYMOUS
1170         | SASL_SEC_NOACTIVE
1171         | SASL_SEC_NODICTIONARY
1172         | SASL_SEC_FORWARD_SECRECY
1173         | SASL_SEC_MUTUAL_AUTH,         /* security_flags */
1174         SASL_FEAT_WANT_CLIENT_FIRST
1175         | SASL_FEAT_ALLOWS_PROXY,       /* features */
1176         NULL,                           /* glob_context */
1177         &passdss_server_mech_new,       /* mech_new */
1178         &passdss_server_mech_step,      /* mech_step */
1179         &passdss_common_mech_dispose,   /* mech_dispose */
1180         NULL,                           /* mech_free */
1181         NULL,                           /* setpass */
1182         NULL,                           /* user_query */
1183         NULL,                           /* idle */
1184         NULL,                           /* mech_avail */
1185         NULL                            /* spare */
1186     }
1187 };
1188
1189 int passdss_server_plug_init(const sasl_utils_t *utils,
1190                            int maxversion,
1191                            int *out_version,
1192                            sasl_server_plug_t **pluglist,
1193                            int *plugcount)
1194 {
1195     if (maxversion < SASL_SERVER_PLUG_VERSION) {
1196         SETERROR(utils, "PASSDSS version mismatch");
1197         return SASL_BADVERS;
1198     }
1199     
1200     *out_version = SASL_SERVER_PLUG_VERSION;
1201     *pluglist = passdss_server_plugins;
1202     *plugcount = 1;  
1203     
1204     return SASL_OK;
1205 }
1206
1207 /*****************************  Client Section  *****************************/
1208
1209 static int passdss_client_mech_new(void *glob_context __attribute__((unused)),
1210                                  sasl_client_params_t *params,
1211                                  void **conn_context)
1212 {
1213     context_t *text;
1214
1215     /* holds state are in */
1216     text = params->utils->malloc(sizeof(context_t));
1217     if (text == NULL) {
1218         MEMERROR(params->utils);
1219         return SASL_NOMEM;
1220     }
1221     
1222     memset(text, 0, sizeof(context_t));
1223     
1224     text->state = 1;
1225     text->utils = params->utils;
1226     text->cs_integrity_key = text->send_integrity_key + 4;
1227     text->sc_integrity_key = text->recv_integrity_key + 4;
1228     
1229     *conn_context = text;
1230     
1231     return SASL_OK;
1232 }
1233
1234 static int
1235 passdss_client_mech_step1(context_t *text,
1236                           sasl_client_params_t *params,
1237                           const char *serverin __attribute__((unused)),
1238                           unsigned serverinlen __attribute__((unused)),
1239                           sasl_interact_t **prompt_need,
1240                           const char **clientout,
1241                           unsigned *clientoutlen,
1242                           sasl_out_params_t *oparams)
1243 {
1244     const char *user = NULL, *authid = NULL;
1245     int user_result = SASL_OK;
1246     int auth_result = SASL_OK;
1247     int pass_result = SASL_OK;
1248     int result;
1249
1250     /* Expect: absolutely nothing */
1251     if (serverinlen > 0) {
1252         SETERROR(params->utils, "Invalid input to first step of PASSDSS\n");
1253         return SASL_BADPROT;
1254     }
1255
1256     /* check if security layer is strong enough */
1257     if (params->props.min_ssf > PRIVACY_LAYER_SSF + params->external_ssf) {
1258         SETERROR(params->utils,
1259                  "minimum ssf too strong for PASSDSS");
1260         return SASL_TOOWEAK;
1261     }
1262
1263     /* try to get the authid */    
1264     if (oparams->authid == NULL) {
1265         auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1266         
1267         if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
1268             return auth_result;
1269     }           
1270     
1271     /* try to get the userid */
1272     if (oparams->user == NULL) {
1273         user_result = _plug_get_userid(params->utils, &user, prompt_need);
1274         
1275         if ((user_result != SASL_OK) && (user_result != SASL_INTERACT))
1276             return user_result;
1277     }
1278     
1279     /* try to get the password */
1280     if (text->password == NULL) {
1281         pass_result = _plug_get_password(params->utils, &text->password,
1282                                          &text->free_password, prompt_need);
1283         
1284         if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT))
1285             return pass_result;
1286     }
1287     
1288     /* free prompts we got */
1289     if (prompt_need && *prompt_need) {
1290         params->utils->free(*prompt_need);
1291         *prompt_need = NULL;
1292     }
1293     
1294     /* if there are prompts not filled in */
1295     if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) ||
1296         (pass_result == SASL_INTERACT)) {
1297         /* make the prompt list */
1298         result =
1299             _plug_make_prompts(params->utils, prompt_need,
1300                                user_result == SASL_INTERACT ?
1301                                "Please enter your authorization name" : NULL,
1302                                NULL,
1303                                auth_result == SASL_INTERACT ?
1304                                "Please enter your authentication name" : NULL,
1305                                NULL,
1306                                pass_result == SASL_INTERACT ?
1307                                "Please enter your password" : NULL, NULL,
1308                                NULL, NULL, NULL,
1309                                NULL, NULL, NULL);
1310         if (result != SASL_OK) goto cleanup;
1311         
1312         return SASL_INTERACT;
1313     }
1314     
1315     if (!text->password) {
1316         PARAMERROR(params->utils);
1317         return SASL_BADPARAM;
1318     }
1319
1320     if (!user || !*user) {
1321         result = params->canon_user(params->utils->conn, authid, 0,
1322                                     SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
1323     }
1324     else {
1325         result = params->canon_user(params->utils->conn, user, 0,
1326                                     SASL_CU_AUTHZID, oparams);
1327         if (result != SASL_OK) goto cleanup;
1328         
1329         result = params->canon_user(params->utils->conn, authid, 0,
1330                                     SASL_CU_AUTHID, oparams);
1331     }
1332     if (result != SASL_OK) goto cleanup;
1333
1334     /* create Diffie-Hellman parameters */
1335     text->dh = DH_new();
1336     BN_hex2bn(&text->dh->p, N);
1337     BN_hex2bn(&text->dh->g, g);
1338     DH_generate_key(text->dh);
1339
1340
1341     /* Send out:
1342      *
1343      * (1) string azname        ; authorization name
1344      * (2) string authname      ; authentication name
1345      * (3) mpint  X             ; Diffie-Hellman parameter X
1346      */
1347     
1348     result = MakeBuffer(text->utils, &text->out_buf, 0, &text->out_buf_len,
1349                         clientoutlen, "%s%s%m",
1350                         (user && *user) ? (char *) oparams->user : "",
1351                         (char *) oparams->authid, text->dh->pub_key);
1352     if (result) {
1353         params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
1354         goto cleanup;
1355     }
1356     *clientout = text->out_buf;
1357     
1358     text->state = 2;
1359     result = SASL_CONTINUE;
1360
1361   cleanup:
1362     
1363     return result;
1364 }
1365
1366 static int
1367 passdss_client_mech_step2(context_t *text,
1368                           sasl_client_params_t *params,
1369                           const char *serverin,
1370                           unsigned serverinlen,
1371                           sasl_interact_t **prompt_need __attribute__((unused)),
1372                           const char **clientout,
1373                           unsigned *clientoutlen,
1374                           sasl_out_params_t *oparams)
1375 {
1376     DSA *dsa = DSA_new();
1377     DSA_SIG *sig = DSA_SIG_new();
1378     BIGNUM *Y = NULL;
1379     uint32_t siglen;
1380     unsigned char *K = NULL;
1381     unsigned Klen, hashlen, enclen;
1382     unsigned char *ssecmask;
1383     uint32_t sbufsiz;
1384     EVP_MD_CTX mdctx;
1385     unsigned char hash[EVP_MAX_MD_SIZE];
1386     int need, musthave;
1387     int result, r;
1388     
1389     /* Expect:
1390      *
1391      * (4) uint32   pklength    ; length of SSH-style DSA server public key
1392      *       string "ssh-dss"   ; constant string "ssh-dss" (lower case)
1393      *       mpint  p           ; DSA public key parameters
1394      *       mpint  q
1395      *       mpint  g
1396      *       mpint  y
1397      * (5) mpint    Y           ; Diffie-Hellman parameter Y
1398      * (6) OCTET    ssecmask    ; SASL security layers offered
1399      * (7) 3 OCTET  sbuflen     ; maximum server security layer block size
1400      * (8) uint32   siglength   ; length of SSH-style dss signature
1401      *       string "ssh-dss"   ; constant string "ssh-dss" (lower case)
1402      *       mpint  r           ; DSA signature parameters
1403      *       mpint  s
1404      */
1405
1406     result = UnBuffer(params->utils, serverin, serverinlen,
1407                       "%u%3p\7ssh-dss%m%m%m%m%m%-1o%3u%u%3p\7ssh-dss%m%m",
1408                       NULL, &dsa->p, &dsa->q, &dsa->g, &dsa->pub_key,
1409                       &Y, &ssecmask, &sbufsiz, &siglen, &sig->r, &sig->s);
1410     if (result) {
1411         params->utils->seterror(params->utils->conn, 0, 
1412                                 "Error UnBuffering input in step 2");
1413         goto cleanup;
1414     }
1415
1416     /* XXX  Validate server DSA public key */
1417
1418     /* Alloc space for shared secret K as mpint */
1419     K = text->utils->malloc(DH_size(text->dh) + 4);
1420     if (!K) {
1421         params->utils->log(NULL, SASL_LOG_ERR, "Error allocing K\n");
1422         result = SASL_NOMEM;
1423         goto cleanup;
1424     }
1425
1426     /* Calculate DH shared secret (leave space at head for length) */
1427     Klen = DH_compute_key(K+4, Y, text->dh);
1428
1429     /* Prepend length in network byte order (make it a mpint) */
1430     *((uint32_t *) K) = htonl(Klen);
1431     Klen += 4;
1432
1433     /* Hash (1) - (7) and K */
1434     EVP_DigestInit(&mdctx, EVP_sha1());
1435     /* (1) - (3) (output from step 1 still in buffer) */
1436     EVP_DigestUpdate(&mdctx, text->out_buf, text->out_buf_len);
1437     /* (4) - (7) */
1438     EVP_DigestUpdate(&mdctx, serverin, serverinlen - siglen - 4);
1439     /* K */
1440     EVP_DigestUpdate(&mdctx, K, Klen);
1441     EVP_DigestFinal(&mdctx, hash, &hashlen);
1442
1443     /* Verify signature on the hash */
1444     result = DSA_do_verify(hash, hashlen, sig, dsa);
1445     if (result != 1) {
1446         params->utils->log(NULL, SASL_LOG_ERR,
1447                            (result == 0) ? "Incorrect DSS signature\n" :
1448                            "Error verifying DSS signature\n");
1449         result = (result == 0) ? SASL_BADPROT : SASL_FAIL;
1450         goto cleanup;
1451     }
1452
1453     /* Calculate security layer params */
1454     CalcLayerParams(text, K, Klen, hash, hashlen);
1455
1456     /* Initialize encrypt cipher */
1457     EVP_CIPHER_CTX_init(&text->cipher_enc_ctx);
1458     EVP_EncryptInit_ex(&text->cipher_enc_ctx, EVP_des_ede3_cbc(), NULL,
1459                        text->cs_encryption_key, text->cs_encryption_iv);
1460     EVP_CIPHER_CTX_set_padding(&text->cipher_enc_ctx, 0);
1461     text->blk_siz = EVP_CIPHER_CTX_block_size(&text->cipher_enc_ctx);
1462
1463     /* pick a layer */
1464     if (params->props.maxbufsize < 32) {
1465         need = musthave = 0;
1466     } else {
1467         need = params->props.max_ssf - params->external_ssf;
1468         musthave = params->props.min_ssf - params->external_ssf;
1469     }
1470
1471     if ((*ssecmask & PRIVACY_LAYER_FLAG) &&
1472         (need >= PRIVACY_LAYER_SSF) && (musthave <= PRIVACY_LAYER_SSF)) {
1473         text->secmask = PRIVACY_LAYER_FLAG;
1474         oparams->mech_ssf = PRIVACY_LAYER_SSF;
1475     } else if ((*ssecmask & INTEGRITY_LAYER_FLAG) &&
1476                (need >= INTEGRITY_LAYER_SSF) &&
1477                (musthave <= INTEGRITY_LAYER_SSF)) {
1478         text->secmask =INTEGRITY_LAYER_FLAG;
1479         oparams->mech_ssf = INTEGRITY_LAYER_SSF;
1480     } else if ((*ssecmask & NO_LAYER_FLAG) && (musthave <= NO_LAYER_SSF)) {
1481         text->secmask = NO_LAYER_FLAG;
1482         oparams->mech_ssf = NO_LAYER_SSF;
1483     } else {
1484         /* Mark that we tried */
1485         oparams->mech_ssf = 2;
1486         SETERROR(params->utils,
1487                  "unable to agree on layers with server");
1488         return SASL_BADPROT;
1489     }
1490
1491     /* Start cli-hmac */
1492     HMAC_CTX_init(&text->hmac_send_ctx);
1493     HMAC_Init_ex(&text->hmac_send_ctx, text->cs_integrity_key,
1494                  SHA_DIGEST_LENGTH, EVP_sha1(), NULL);
1495     /* (1) - (3) (output from step 1 still in buffer) */
1496     HMAC_Update(&text->hmac_send_ctx, text->out_buf, text->out_buf_len);
1497     /* (4) - (7) */
1498     HMAC_Update(&text->hmac_send_ctx, serverin, serverinlen - siglen - 4);
1499
1500
1501     /* Send out (3DES encrypted):
1502      *
1503      * (9) OCTET    csecmask    ; SASL security layer selection
1504      *     3 OCTET  cbuflen     ; maximum client block size
1505      *     string   passphrase  ; the user's passphrase
1506      *     20 OCTET cli-hmac    ; a client HMAC-SHA-1 signature
1507      */
1508
1509     result = MakeBuffer(text->utils, &text->out_buf, 0,
1510                         &text->out_buf_len, clientoutlen, "%1o%3u%*s",
1511                         &text->secmask,
1512                         (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1513                         params->props.maxbufsize,
1514                         text->password->len, text->password->data);
1515     if (result) {
1516         params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
1517         goto cleanup;
1518     }
1519
1520     /* Finish cli-hmac */
1521     /* 1st 4 bytes of (9) */
1522     HMAC_Update(&text->hmac_send_ctx, text->out_buf, 4);
1523     HMAC_Final(&text->hmac_send_ctx, hash, &hashlen);
1524
1525     /* Add HMAC and pad to fill no more than current block */
1526     result = MakeBuffer(text->utils, &text->out_buf, *clientoutlen,
1527                         &text->out_buf_len, clientoutlen, "%*o%*o",
1528                         hashlen, hash, text->blk_siz - 1, text->padding);
1529     if (result) {
1530         params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
1531         goto cleanup;
1532     }
1533
1534     /* Alloc space for the encrypted output */
1535     result = _plug_buf_alloc(text->utils, &text->encode_buf,
1536                              &text->encode_buf_len, *clientoutlen);
1537     if (result) {
1538         params->utils->log(NULL, SASL_LOG_ERR,
1539                            "Error allocating encrypt buffer in step 2\n");
1540         goto cleanup;
1541     }
1542
1543     /* Encrypt (9) (here we calculate the exact number of full blocks) */
1544     r = EVP_EncryptUpdate(&text->cipher_enc_ctx, text->encode_buf,
1545                           clientoutlen, text->out_buf,
1546                           text->blk_siz * (*clientoutlen / text->blk_siz));
1547     if (r)
1548         r = EVP_EncryptFinal_ex(&text->cipher_enc_ctx,  /* should be no output */
1549                                 text->encode_buf + *clientoutlen, &enclen);
1550     if (!r) {
1551         params->utils->seterror(params->utils->conn, 0, 
1552                                 "Error encrypting output in step 2");
1553         result = SASL_FAIL;
1554         goto cleanup;
1555     }
1556     *clientout = text->encode_buf;
1557
1558     /* Set oparams */
1559     oparams->doneflag = 1;
1560     oparams->param_version = 0;
1561
1562     if (oparams->mech_ssf > 0) {
1563         oparams->encode = &passdss_encode;
1564         oparams->decode = &passdss_decode;
1565         oparams->maxoutbuf = sbufsiz - 4 - SHA_DIGEST_LENGTH; /* -len -HMAC */
1566
1567         HMAC_CTX_init(&text->hmac_recv_ctx);
1568
1569         if (oparams->mech_ssf > 1) {
1570             oparams->maxoutbuf -= text->blk_siz-1; /* padding */
1571
1572             /* Initialize decrypt cipher */
1573             EVP_CIPHER_CTX_init(&text->cipher_dec_ctx);
1574             EVP_DecryptInit_ex(&text->cipher_dec_ctx, EVP_des_ede3_cbc(), NULL,
1575                                text->sc_encryption_key, text->sc_encryption_iv);
1576             EVP_CIPHER_CTX_set_padding(&text->cipher_dec_ctx, 0);
1577         }
1578
1579         _plug_decode_init(&text->decode_context, text->utils,
1580                           (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1581                           params->props.maxbufsize);
1582     }
1583     else {
1584         oparams->encode = NULL;
1585         oparams->decode = NULL;
1586         oparams->maxoutbuf = 0;
1587     }
1588
1589     result = SASL_OK;
1590  
1591  cleanup:
1592     if (Y) BN_free(Y);
1593     if (K) text->utils->free(K);
1594     if (dsa) DSA_free(dsa);
1595     if (sig) DSA_SIG_free(sig);
1596     
1597     return result;
1598 }
1599
1600 static int passdss_client_mech_step(void *conn_context,
1601                                     sasl_client_params_t *params,
1602                                     const char *serverin,
1603                                     unsigned serverinlen,
1604                                     sasl_interact_t **prompt_need,
1605                                     const char **clientout,
1606                                     unsigned *clientoutlen,
1607                                     sasl_out_params_t *oparams)
1608 {
1609     context_t *text = (context_t *) conn_context;
1610     
1611     params->utils->log(NULL, SASL_LOG_DEBUG,
1612                        "PASSDSS client step %d\n", text->state);
1613     
1614     *clientout = NULL;
1615     *clientoutlen = 0;
1616     
1617     switch (text->state) {
1618
1619     case 1:
1620         return passdss_client_mech_step1(text, params, serverin, serverinlen, 
1621                                          prompt_need, clientout, clientoutlen,
1622                                          oparams);
1623
1624     case 2:
1625         return passdss_client_mech_step2(text, params, serverin, serverinlen, 
1626                                          prompt_need, clientout, clientoutlen,
1627                                          oparams);
1628
1629     default:
1630         params->utils->log(NULL, SASL_LOG_ERR,
1631                            "Invalid PASSDSS client step %d\n", text->state);
1632         return SASL_FAIL;
1633     }
1634     
1635     return SASL_FAIL; /* should never get here */
1636 }
1637
1638
1639 static sasl_client_plug_t passdss_client_plugins[] = 
1640 {
1641     {
1642         "PASSDSS-3DES-1",               /* mech_name */
1643         112,                            /* max_ssf */
1644         SASL_SEC_NOPLAINTEXT
1645         | SASL_SEC_NOANONYMOUS
1646         | SASL_SEC_NOACTIVE
1647         | SASL_SEC_NODICTIONARY
1648         | SASL_SEC_FORWARD_SECRECY
1649         | SASL_SEC_MUTUAL_AUTH,         /* security_flags */
1650         SASL_FEAT_WANT_CLIENT_FIRST
1651         | SASL_FEAT_ALLOWS_PROXY,       /* features */
1652         NULL,                           /* required_prompts */
1653         NULL,                           /* glob_context */
1654         &passdss_client_mech_new,       /* mech_new */
1655         &passdss_client_mech_step,      /* mech_step */
1656         &passdss_common_mech_dispose,   /* mech_dispose */
1657         NULL,                           /* mech_free */
1658         NULL,                           /* idle */
1659         NULL,                           /* spare */
1660         NULL                            /* spare */
1661     }
1662 };
1663
1664 int passdss_client_plug_init(sasl_utils_t *utils,
1665                            int maxversion,
1666                            int *out_version,
1667                            sasl_client_plug_t **pluglist,
1668                            int *plugcount)
1669 {
1670     if (maxversion < SASL_CLIENT_PLUG_VERSION) {
1671         SETERROR(utils, "PASSDSS version mismatch");
1672         return SASL_BADVERS;
1673     }
1674     
1675     *out_version = SASL_CLIENT_PLUG_VERSION;
1676     *pluglist = passdss_client_plugins;
1677     *plugcount = 1;
1678     
1679     return SASL_OK;
1680 }