1/* PASSDSS-3DES-1 SASL plugin
2 * Ken Murchison
3 * $Id: passdss.c,v 1.5 2008/10/29 17:59:41 murch 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
86static const char plugin_id[] = "$Id: passdss.c,v 1.5 2008/10/29 17:59:41 murch Exp $";
87
88const char g[] = "2";
89const 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
99typedef 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
151static 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 */
261static 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 */
323static 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 */
345static 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 */
526static 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
725void 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 */
745static 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
776static 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
803static int
804passdss_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
964static int
965passdss_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
1122static 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
1163static 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_PASS_CREDENTIALS
1174	| SASL_SEC_MUTUAL_AUTH,		/* security_flags */
1175	SASL_FEAT_WANT_CLIENT_FIRST
1176	| SASL_FEAT_ALLOWS_PROXY,	/* features */
1177	NULL,				/* glob_context */
1178	&passdss_server_mech_new,	/* mech_new */
1179	&passdss_server_mech_step,	/* mech_step */
1180	&passdss_common_mech_dispose,	/* mech_dispose */
1181	NULL,				/* mech_free */
1182	NULL,				/* setpass */
1183	NULL,				/* user_query */
1184	NULL,				/* idle */
1185	NULL,				/* mech_avail */
1186	NULL				/* spare */
1187    }
1188};
1189
1190int passdss_server_plug_init(const sasl_utils_t *utils,
1191			   int maxversion,
1192			   int *out_version,
1193			   sasl_server_plug_t **pluglist,
1194			   int *plugcount)
1195{
1196    if (maxversion < SASL_SERVER_PLUG_VERSION) {
1197	SETERROR(utils, "PASSDSS version mismatch");
1198	return SASL_BADVERS;
1199    }
1200
1201    *out_version = SASL_SERVER_PLUG_VERSION;
1202    *pluglist = passdss_server_plugins;
1203    *plugcount = 1;
1204
1205    return SASL_OK;
1206}
1207
1208/*****************************  Client Section  *****************************/
1209
1210static int passdss_client_mech_new(void *glob_context __attribute__((unused)),
1211				 sasl_client_params_t *params,
1212				 void **conn_context)
1213{
1214    context_t *text;
1215
1216    /* holds state are in */
1217    text = params->utils->malloc(sizeof(context_t));
1218    if (text == NULL) {
1219	MEMERROR(params->utils);
1220	return SASL_NOMEM;
1221    }
1222
1223    memset(text, 0, sizeof(context_t));
1224
1225    text->state = 1;
1226    text->utils = params->utils;
1227    text->cs_integrity_key = text->send_integrity_key + 4;
1228    text->sc_integrity_key = text->recv_integrity_key + 4;
1229
1230    *conn_context = text;
1231
1232    return SASL_OK;
1233}
1234
1235static int
1236passdss_client_mech_step1(context_t *text,
1237			  sasl_client_params_t *params,
1238			  const char *serverin __attribute__((unused)),
1239			  unsigned serverinlen __attribute__((unused)),
1240			  sasl_interact_t **prompt_need,
1241			  const char **clientout,
1242			  unsigned *clientoutlen,
1243			  sasl_out_params_t *oparams)
1244{
1245    const char *user = NULL, *authid = NULL;
1246    int user_result = SASL_OK;
1247    int auth_result = SASL_OK;
1248    int pass_result = SASL_OK;
1249    int result;
1250
1251    /* Expect: absolutely nothing */
1252    if (serverinlen > 0) {
1253	SETERROR(params->utils, "Invalid input to first step of PASSDSS\n");
1254	return SASL_BADPROT;
1255    }
1256
1257    /* check if security layer is strong enough */
1258    if (params->props.min_ssf > PRIVACY_LAYER_SSF + params->external_ssf) {
1259	SETERROR(params->utils,
1260		 "minimum ssf too strong for PASSDSS");
1261	return SASL_TOOWEAK;
1262    }
1263
1264    /* try to get the authid */
1265    if (oparams->authid == NULL) {
1266	auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1267
1268	if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
1269	    return auth_result;
1270    }
1271
1272    /* try to get the userid */
1273    if (oparams->user == NULL) {
1274	user_result = _plug_get_userid(params->utils, &user, prompt_need);
1275
1276	if ((user_result != SASL_OK) && (user_result != SASL_INTERACT))
1277	    return user_result;
1278    }
1279
1280    /* try to get the password */
1281    if (text->password == NULL) {
1282	pass_result = _plug_get_password(params->utils, &text->password,
1283					 &text->free_password, prompt_need);
1284
1285	if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT))
1286	    return pass_result;
1287    }
1288
1289    /* free prompts we got */
1290    if (prompt_need && *prompt_need) {
1291	params->utils->free(*prompt_need);
1292	*prompt_need = NULL;
1293    }
1294
1295    /* if there are prompts not filled in */
1296    if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) ||
1297	(pass_result == SASL_INTERACT)) {
1298	/* make the prompt list */
1299	result =
1300	    _plug_make_prompts(params->utils, prompt_need,
1301			       user_result == SASL_INTERACT ?
1302			       "Please enter your authorization name" : NULL,
1303			       NULL,
1304			       auth_result == SASL_INTERACT ?
1305			       "Please enter your authentication name" : NULL,
1306			       NULL,
1307			       pass_result == SASL_INTERACT ?
1308			       "Please enter your password" : NULL, NULL,
1309			       NULL, NULL, NULL,
1310			       NULL, NULL, NULL);
1311	if (result != SASL_OK) goto cleanup;
1312
1313	return SASL_INTERACT;
1314    }
1315
1316    if (!text->password) {
1317	PARAMERROR(params->utils);
1318	return SASL_BADPARAM;
1319    }
1320
1321    if (!user || !*user) {
1322	result = params->canon_user(params->utils->conn, authid, 0,
1323				    SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
1324    }
1325    else {
1326	result = params->canon_user(params->utils->conn, user, 0,
1327				    SASL_CU_AUTHZID, oparams);
1328	if (result != SASL_OK) goto cleanup;
1329
1330	result = params->canon_user(params->utils->conn, authid, 0,
1331				    SASL_CU_AUTHID, oparams);
1332    }
1333    if (result != SASL_OK) goto cleanup;
1334
1335    /* create Diffie-Hellman parameters */
1336    text->dh = DH_new();
1337    BN_hex2bn(&text->dh->p, N);
1338    BN_hex2bn(&text->dh->g, g);
1339    DH_generate_key(text->dh);
1340
1341
1342    /* Send out:
1343     *
1344     * (1) string azname	; authorization name
1345     * (2) string authname	; authentication name
1346     * (3) mpint  X 		; Diffie-Hellman parameter X
1347     */
1348
1349    result = MakeBuffer(text->utils, &text->out_buf, 0, &text->out_buf_len,
1350			clientoutlen, "%s%s%m",
1351			(user && *user) ? (char *) oparams->user : "",
1352			(char *) oparams->authid, text->dh->pub_key);
1353    if (result) {
1354	params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
1355	goto cleanup;
1356    }
1357    *clientout = text->out_buf;
1358
1359    text->state = 2;
1360    result = SASL_CONTINUE;
1361
1362  cleanup:
1363
1364    return result;
1365}
1366
1367static int
1368passdss_client_mech_step2(context_t *text,
1369			  sasl_client_params_t *params,
1370			  const char *serverin,
1371			  unsigned serverinlen,
1372			  sasl_interact_t **prompt_need __attribute__((unused)),
1373			  const char **clientout,
1374			  unsigned *clientoutlen,
1375			  sasl_out_params_t *oparams)
1376{
1377    DSA *dsa = DSA_new();
1378    DSA_SIG *sig = DSA_SIG_new();
1379    BIGNUM *Y = NULL;
1380    uint32_t siglen;
1381    unsigned char *K = NULL;
1382    unsigned Klen, hashlen, enclen;
1383    unsigned char *ssecmask;
1384    uint32_t sbufsiz;
1385    EVP_MD_CTX mdctx;
1386    unsigned char hash[EVP_MAX_MD_SIZE];
1387    int need, musthave;
1388    int result, r;
1389
1390    /* Expect:
1391     *
1392     * (4) uint32   pklength	; length of SSH-style DSA server public key
1393     *       string "ssh-dss"	; constant string "ssh-dss" (lower case)
1394     *       mpint  p		; DSA public key parameters
1395     *       mpint  q
1396     *       mpint  g
1397     *       mpint  y
1398     * (5) mpint    Y		; Diffie-Hellman parameter Y
1399     * (6) OCTET    ssecmask	; SASL security layers offered
1400     * (7) 3 OCTET  sbuflen	; maximum server security layer block size
1401     * (8) uint32   siglength	; length of SSH-style dss signature
1402     *       string "ssh-dss"	; constant string "ssh-dss" (lower case)
1403     *       mpint  r		; DSA signature parameters
1404     *       mpint  s
1405     */
1406
1407    result = UnBuffer(params->utils, serverin, serverinlen,
1408		      "%u%3p\7ssh-dss%m%m%m%m%m%-1o%3u%u%3p\7ssh-dss%m%m",
1409		      NULL, &dsa->p, &dsa->q, &dsa->g, &dsa->pub_key,
1410		      &Y, &ssecmask, &sbufsiz, &siglen, &sig->r, &sig->s);
1411    if (result) {
1412	params->utils->seterror(params->utils->conn, 0,
1413				"Error UnBuffering input in step 2");
1414	goto cleanup;
1415    }
1416
1417    /* XXX  Validate server DSA public key */
1418
1419    /* Alloc space for shared secret K as mpint */
1420    K = text->utils->malloc(DH_size(text->dh) + 4);
1421    if (!K) {
1422	params->utils->log(NULL, SASL_LOG_ERR, "Error allocing K\n");
1423	result = SASL_NOMEM;
1424	goto cleanup;
1425    }
1426
1427    /* Calculate DH shared secret (leave space at head for length) */
1428    Klen = DH_compute_key(K+4, Y, text->dh);
1429
1430    /* Prepend length in network byte order (make it a mpint) */
1431    *((uint32_t *) K) = htonl(Klen);
1432    Klen += 4;
1433
1434    /* Hash (1) - (7) and K */
1435    EVP_DigestInit(&mdctx, EVP_sha1());
1436    /* (1) - (3) (output from step 1 still in buffer) */
1437    EVP_DigestUpdate(&mdctx, text->out_buf, text->out_buf_len);
1438    /* (4) - (7) */
1439    EVP_DigestUpdate(&mdctx, serverin, serverinlen - siglen - 4);
1440    /* K */
1441    EVP_DigestUpdate(&mdctx, K, Klen);
1442    EVP_DigestFinal(&mdctx, hash, &hashlen);
1443
1444    /* Verify signature on the hash */
1445    result = DSA_do_verify(hash, hashlen, sig, dsa);
1446    if (result != 1) {
1447	params->utils->log(NULL, SASL_LOG_ERR,
1448			   (result == 0) ? "Incorrect DSS signature\n" :
1449			   "Error verifying DSS signature\n");
1450	result = (result == 0) ? SASL_BADPROT : SASL_FAIL;
1451	goto cleanup;
1452    }
1453
1454    /* Calculate security layer params */
1455    CalcLayerParams(text, K, Klen, hash, hashlen);
1456
1457    /* Initialize encrypt cipher */
1458    EVP_CIPHER_CTX_init(&text->cipher_enc_ctx);
1459    EVP_EncryptInit_ex(&text->cipher_enc_ctx, EVP_des_ede3_cbc(), NULL,
1460		       text->cs_encryption_key, text->cs_encryption_iv);
1461    EVP_CIPHER_CTX_set_padding(&text->cipher_enc_ctx, 0);
1462    text->blk_siz = EVP_CIPHER_CTX_block_size(&text->cipher_enc_ctx);
1463
1464    /* pick a layer */
1465    if (params->props.maxbufsize < 32) {
1466	need = musthave = 0;
1467    } else {
1468	need = params->props.max_ssf - params->external_ssf;
1469	musthave = params->props.min_ssf - params->external_ssf;
1470    }
1471
1472    if ((*ssecmask & PRIVACY_LAYER_FLAG) &&
1473	(need >= PRIVACY_LAYER_SSF) && (musthave <= PRIVACY_LAYER_SSF)) {
1474	text->secmask = PRIVACY_LAYER_FLAG;
1475	oparams->mech_ssf = PRIVACY_LAYER_SSF;
1476    } else if ((*ssecmask & INTEGRITY_LAYER_FLAG) &&
1477	       (need >= INTEGRITY_LAYER_SSF) &&
1478	       (musthave <= INTEGRITY_LAYER_SSF)) {
1479	text->secmask =INTEGRITY_LAYER_FLAG;
1480	oparams->mech_ssf = INTEGRITY_LAYER_SSF;
1481    } else if ((*ssecmask & NO_LAYER_FLAG) && (musthave <= NO_LAYER_SSF)) {
1482	text->secmask = NO_LAYER_FLAG;
1483	oparams->mech_ssf = NO_LAYER_SSF;
1484    } else {
1485	/* Mark that we tried */
1486	oparams->mech_ssf = 2;
1487	SETERROR(params->utils,
1488		 "unable to agree on layers with server");
1489	return SASL_BADPROT;
1490    }
1491
1492    /* Start cli-hmac */
1493    HMAC_CTX_init(&text->hmac_send_ctx);
1494    HMAC_Init_ex(&text->hmac_send_ctx, text->cs_integrity_key,
1495		 SHA_DIGEST_LENGTH, EVP_sha1(), NULL);
1496    /* (1) - (3) (output from step 1 still in buffer) */
1497    HMAC_Update(&text->hmac_send_ctx, text->out_buf, text->out_buf_len);
1498    /* (4) - (7) */
1499    HMAC_Update(&text->hmac_send_ctx, serverin, serverinlen - siglen - 4);
1500
1501
1502    /* Send out (3DES encrypted):
1503     *
1504     * (9) OCTET    csecmask	; SASL security layer selection
1505     *     3 OCTET  cbuflen	; maximum client block size
1506     *     string   passphrase	; the user's passphrase
1507     *     20 OCTET cli-hmac	; a client HMAC-SHA-1 signature
1508     */
1509
1510    result = MakeBuffer(text->utils, &text->out_buf, 0,
1511			&text->out_buf_len, clientoutlen, "%1o%3u%*s",
1512			&text->secmask,
1513			(params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1514			params->props.maxbufsize,
1515			text->password->len, text->password->data);
1516    if (result) {
1517	params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
1518	goto cleanup;
1519    }
1520
1521    /* Finish cli-hmac */
1522    /* 1st 4 bytes of (9) */
1523    HMAC_Update(&text->hmac_send_ctx, text->out_buf, 4);
1524    HMAC_Final(&text->hmac_send_ctx, hash, &hashlen);
1525
1526    /* Add HMAC and pad to fill no more than current block */
1527    result = MakeBuffer(text->utils, &text->out_buf, *clientoutlen,
1528			&text->out_buf_len, clientoutlen, "%*o%*o",
1529			hashlen, hash, text->blk_siz - 1, text->padding);
1530    if (result) {
1531	params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
1532	goto cleanup;
1533    }
1534
1535    /* Alloc space for the encrypted output */
1536    result = _plug_buf_alloc(text->utils, &text->encode_buf,
1537			     &text->encode_buf_len, *clientoutlen);
1538    if (result) {
1539	params->utils->log(NULL, SASL_LOG_ERR,
1540			   "Error allocating encrypt buffer in step 2\n");
1541	goto cleanup;
1542    }
1543
1544    /* Encrypt (9) (here we calculate the exact number of full blocks) */
1545    r = EVP_EncryptUpdate(&text->cipher_enc_ctx, text->encode_buf,
1546			  clientoutlen, text->out_buf,
1547			  text->blk_siz * (*clientoutlen / text->blk_siz));
1548    if (r)
1549	r = EVP_EncryptFinal_ex(&text->cipher_enc_ctx,  /* should be no output */
1550				text->encode_buf + *clientoutlen, &enclen);
1551    if (!r) {
1552	params->utils->seterror(params->utils->conn, 0,
1553				"Error encrypting output in step 2");
1554	result = SASL_FAIL;
1555	goto cleanup;
1556    }
1557    *clientout = text->encode_buf;
1558
1559    /* Set oparams */
1560    oparams->doneflag = 1;
1561    oparams->param_version = 0;
1562
1563    if (oparams->mech_ssf > 0) {
1564	oparams->encode = &passdss_encode;
1565	oparams->decode = &passdss_decode;
1566	oparams->maxoutbuf = sbufsiz - 4 - SHA_DIGEST_LENGTH; /* -len -HMAC */
1567
1568	HMAC_CTX_init(&text->hmac_recv_ctx);
1569
1570	if (oparams->mech_ssf > 1) {
1571	    oparams->maxoutbuf -= text->blk_siz-1; /* padding */
1572
1573	    /* Initialize decrypt cipher */
1574	    EVP_CIPHER_CTX_init(&text->cipher_dec_ctx);
1575	    EVP_DecryptInit_ex(&text->cipher_dec_ctx, EVP_des_ede3_cbc(), NULL,
1576			       text->sc_encryption_key, text->sc_encryption_iv);
1577	    EVP_CIPHER_CTX_set_padding(&text->cipher_dec_ctx, 0);
1578	}
1579
1580	_plug_decode_init(&text->decode_context, text->utils,
1581			  (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1582			  params->props.maxbufsize);
1583    }
1584    else {
1585	oparams->encode = NULL;
1586	oparams->decode = NULL;
1587	oparams->maxoutbuf = 0;
1588    }
1589
1590    result = SASL_OK;
1591
1592 cleanup:
1593    if (Y) BN_free(Y);
1594    if (K) text->utils->free(K);
1595    if (dsa) DSA_free(dsa);
1596    if (sig) DSA_SIG_free(sig);
1597
1598    return result;
1599}
1600
1601static int passdss_client_mech_step(void *conn_context,
1602				    sasl_client_params_t *params,
1603				    const char *serverin,
1604				    unsigned serverinlen,
1605				    sasl_interact_t **prompt_need,
1606				    const char **clientout,
1607				    unsigned *clientoutlen,
1608				    sasl_out_params_t *oparams)
1609{
1610    context_t *text = (context_t *) conn_context;
1611
1612    params->utils->log(NULL, SASL_LOG_DEBUG,
1613		       "PASSDSS client step %d\n", text->state);
1614
1615    *clientout = NULL;
1616    *clientoutlen = 0;
1617
1618    switch (text->state) {
1619
1620    case 1:
1621	return passdss_client_mech_step1(text, params, serverin, serverinlen,
1622					 prompt_need, clientout, clientoutlen,
1623					 oparams);
1624
1625    case 2:
1626	return passdss_client_mech_step2(text, params, serverin, serverinlen,
1627					 prompt_need, clientout, clientoutlen,
1628					 oparams);
1629
1630    default:
1631	params->utils->log(NULL, SASL_LOG_ERR,
1632			   "Invalid PASSDSS client step %d\n", text->state);
1633	return SASL_FAIL;
1634    }
1635
1636    return SASL_FAIL; /* should never get here */
1637}
1638
1639
1640static sasl_client_plug_t passdss_client_plugins[] =
1641{
1642    {
1643	"PASSDSS-3DES-1",		/* mech_name */
1644	112,				/* max_ssf */
1645	SASL_SEC_NOPLAINTEXT
1646	| SASL_SEC_NOANONYMOUS
1647	| SASL_SEC_NOACTIVE
1648	| SASL_SEC_NODICTIONARY
1649	| SASL_SEC_FORWARD_SECRECY
1650	| SASL_SEC_PASS_CREDENTIALS
1651	| SASL_SEC_MUTUAL_AUTH,		/* security_flags */
1652	SASL_FEAT_WANT_CLIENT_FIRST
1653	| SASL_FEAT_ALLOWS_PROXY,	/* features */
1654	NULL,				/* required_prompts */
1655	NULL,				/* glob_context */
1656	&passdss_client_mech_new,	/* mech_new */
1657	&passdss_client_mech_step,	/* mech_step */
1658	&passdss_common_mech_dispose,	/* mech_dispose */
1659	NULL,				/* mech_free */
1660	NULL,				/* idle */
1661	NULL,				/* spare */
1662	NULL				/* spare */
1663    }
1664};
1665
1666int passdss_client_plug_init(sasl_utils_t *utils,
1667			   int maxversion,
1668			   int *out_version,
1669			   sasl_client_plug_t **pluglist,
1670			   int *plugcount)
1671{
1672    if (maxversion < SASL_CLIENT_PLUG_VERSION) {
1673	SETERROR(utils, "PASSDSS version mismatch");
1674	return SASL_BADVERS;
1675    }
1676
1677    *out_version = SASL_CLIENT_PLUG_VERSION;
1678    *pluglist = passdss_client_plugins;
1679    *plugcount = 1;
1680
1681    return SASL_OK;
1682}
1683