1 /* PASSDSS-3DES-1 SASL plugin
3 * $Id: passdss.c,v 1.4 2006/04/24 19:21:44 mel Exp $
6 * Copyright (c) 1998-2004 Carnegie Mellon University. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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
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
27 * Pittsburgh, PA 15213-3890
28 * (412) 268-4387, fax: (412) 268-7395
29 * tech-transfer@andrew.cmu.edu
31 * 4. Redistributions of any form whatsoever must retain the following
33 * "This product includes software developed by Computing Services
34 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
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.
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
61 /* for big number support */
62 #include <openssl/bn.h>
64 /* for Diffie-Hellman support */
65 #include <openssl/dh.h>
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>
75 #define MD5_H /* suppress internal MD5 */
78 #include "plugin_common.h"
81 #include <sasl_passdss_plugin_decl.h>
84 /***************************** Common Section *****************************/
86 static const char plugin_id[] = "$Id: passdss.c,v 1.4 2006/04/24 19:21:44 mel Exp $";
89 const char N[] = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF";
91 #define NO_LAYER_FLAG (1<<0)
92 #define INTEGRITY_LAYER_FLAG (1<<1)
93 #define PRIVACY_LAYER_FLAG (1<<2)
95 #define NO_LAYER_SSF 0
96 #define INTEGRITY_LAYER_SSF 1
97 #define PRIVACY_LAYER_SSF 112
99 typedef struct context {
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 */
107 DH *dh; /* Diffie-Hellman parameters */
109 /* copy of utils from the params structures */
110 const sasl_utils_t *utils;
112 /* per-step mem management */
114 unsigned out_buf_len;
116 /* security layer foo */
117 unsigned char secmask; /* bitmask of enabled security layers */
118 unsigned char padding[EVP_MAX_BLOCK_LENGTH]; /* block of NULs */
120 HMAC_CTX hmac_send_ctx;
121 HMAC_CTX hmac_recv_ctx;
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 */
128 EVP_CIPHER_CTX cipher_enc_ctx;
129 EVP_CIPHER_CTX cipher_dec_ctx;
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];
137 /* replay detection sequence numbers */
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;
145 /* layers buffering */
146 decode_context_t decode_context;
151 static int passdss_encode(void *context,
152 const struct iovec *invec,
157 context_t *text = (context_t *) context;
158 unsigned long inputlen;
159 unsigned char hmac[EVP_MAX_MD_SIZE];
164 if (!context || !invec || !numiov || !output || !outputlen) {
165 PARAMERROR( text->utils );
166 return SASL_BADPARAM;
169 /* calculate total size of input */
170 for (i = 0, inputlen = 0; i < numiov; i++)
171 inputlen += invec[i].iov_len;
173 /* allocate a buffer for the output */
174 ret = _plug_buf_alloc(text->utils, &text->encode_buf,
175 &text->encode_buf_len,
177 inputlen + /* content */
178 EVP_MAX_MD_SIZE + /* HMAC */
179 EVP_MAX_BLOCK_LENGTH - 1); /* padding */
180 if (ret != SASL_OK) return ret;
182 *outputlen = 4; /* skip length */
184 /* prepend packet number to integrity key */
185 tmpnum = htonl(text->pktnum_out++);
186 memcpy(text->send_integrity_key, &tmpnum, 4);
189 HMAC_Init_ex(&text->hmac_send_ctx, text->send_integrity_key,
190 4+SHA_DIGEST_LENGTH, EVP_sha1(), NULL);
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);
197 if (text->secmask & PRIVACY_LAYER_FLAG) {
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;
207 /* copy the raw input to the output */
208 memcpy(text->encode_buf + *outputlen, invec[i].iov_base,
210 *outputlen += invec[i].iov_len;
214 /* calculate the HMAC */
215 HMAC_Final(&text->hmac_send_ctx, hmac, &hmaclen);
217 if (text->secmask & PRIVACY_LAYER_FLAG) {
219 unsigned char padlen;
221 /* encrypt the HMAC into the output buffer */
222 EVP_EncryptUpdate(&text->cipher_enc_ctx,
223 text->encode_buf + *outputlen, &enclen,
225 *outputlen += enclen;
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,
237 *outputlen += enclen;
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;
245 /* copy the HMAC to the output */
246 memcpy(text->encode_buf + *outputlen, hmac, hmaclen);
247 *outputlen += hmaclen;
250 /* prepend the length of the output */
251 tmpnum = *outputlen - 4;
252 tmpnum = htonl(tmpnum);
253 memcpy(text->encode_buf, &tmpnum, 4);
255 *output = text->encode_buf;
260 /* Decode a single PASSDSS packet */
261 static int passdss_decode_packet(void *context,
267 context_t *text = (context_t *) context;
269 unsigned char hmac[EVP_MAX_MD_SIZE];
273 if (text->secmask & PRIVACY_LAYER_FLAG) {
274 unsigned declen, padlen;
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;
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);
286 EVP_DecryptFinal_ex(&text->cipher_dec_ctx, /* should be no output */
287 text->decode_pkt_buf + declen, &declen);
289 SETERROR(text->utils, "Error decrypting input");
292 input = text->decode_pkt_buf;
295 padlen = text->decode_pkt_buf[inputlen - 1] + 1;
300 inputlen -= SHA_DIGEST_LENGTH;
302 /* prepend packet number to integrity key */
303 tmpnum = htonl(text->pktnum_in++);
304 memcpy(text->recv_integrity_key, &tmpnum, 4);
306 /* calculate the HMAC */
307 HMAC(EVP_sha1(), text->recv_integrity_key, 4+SHA_DIGEST_LENGTH,
308 input, inputlen, hmac, &hmaclen);
311 if (memcmp(hmac, input+inputlen, hmaclen)) {
312 SETERROR(text->utils, "HMAC is incorrect\n");
316 *output = (char *) input;
317 *outputlen = inputlen;
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)
327 context_t *text = (context_t *) context;
330 ret = _plug_decode(&text->decode_context, input, inputlen,
331 &text->decode_buf, &text->decode_buf_len, outputlen,
332 passdss_decode_packet, text);
334 *output = text->decode_buf;
339 #define MAX_MPI_LEN 2147483643
340 #define MAX_UTF8_LEN 2147483643
343 * Create/append to a PASSDSS buffer from the data specified by the fmt string.
345 static int MakeBuffer(const sasl_utils_t *utils, char **buf, unsigned offset,
346 unsigned *buflen, unsigned *outlen, const char *fmt, ...)
349 char *p, *out = NULL, *lptr = NULL;
350 int r, alloclen, len = -1, argc = 0;
355 /* first pass to calculate size of buffer */
357 for (p = (char *) fmt, alloclen = offset; *p; p++) {
363 /* check for length prefix ('a', 'o', 'u', and 's' only) */
365 /* arg is length of next arg */
366 len = va_arg(ap, int);
369 else if (isdigit((int) *p)) {
371 while (isdigit((int) *p)) len = 10 * len + *p++ - '0';
376 /* insert total length of next N args */
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");
394 /* octet sequence (len given by prefix) */
396 os = va_arg(ap, char *);
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");
414 u = va_arg(ap, uint32_t);
415 if (len == -1) len = 4;
428 r = _plug_buf_alloc(utils, buf, buflen, alloclen);
429 if (r != SASL_OK) return r;
433 /* second pass to fill buffer */
435 for (p = (char *) fmt; *p; p++) {
442 /* check for length prefix ('a', 'o', 'u', and 's' only) */
444 /* arg is length of next arg */
445 len = va_arg(ap, int);
448 else if (isdigit((int) *p)) {
450 while (isdigit((int) *p)) len = 10 * len + *p++ - '0';
455 /* total length of next N args */
465 mpi = va_arg(ap, BIGNUM *);
466 len = BN_bn2bin(mpi, out+4);
468 memcpy(out, &nl, 4); /* add 4 byte len (network order) */
473 /* octet sequence (len given by prefix) */
474 os = va_arg(ap, char *);
475 memcpy(out, os, len); /* add data */
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);
485 memcpy(out, &nl, 4); /* add 4 byte len (network order) */
486 memcpy(out+4, str, len); /* add string */
492 u = va_arg(ap, uint32_t);
494 if (len == -1) len = 4;
495 memcpy(out, &nl + 4 - len, len);
505 /* see if we're done counting args */
506 if (lptr && !--argc) {
507 len = out - lptr - 4;
509 memcpy(lptr, &nl, 4); /* insert 4 byte len (network order) */
518 *outlen = out - *buf;
524 * Extract a PASSDSS buffer into the data specified by the fmt string.
526 static int UnBuffer(const sasl_utils_t *utils, const char *buf,
527 unsigned buflen, const char *fmt, ...)
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 */
542 for (p = (char *) fmt; *p; p++) {
554 /* check for octet flags */
555 octet_flag = OCTET_COPY;
557 octet_flag = OCTET_REFERENCE;
560 else if (*p == '+') {
561 octet_flag = OCTET_ALLOC;
565 /* check for length prefix ('o', 'u', and 'p' only) */
568 /* arg is length of next arg */
569 len = va_arg(ap, int);
572 else if (isdigit((int) *p)) {
574 while (isdigit((int) *p)) len = 10 * len + *p++ - '0';
580 mpi = va_arg(ap, BIGNUM **);
583 SETERROR(utils, "Buffer is not big enough to be PASSDSS MPI\n");
594 /* make sure it's right */
596 SETERROR(utils, "Not enough data for this PASSDSS MPI\n");
602 if (!*mpi) *mpi = BN_new();
604 BN_bin2bn(buf, len, *mpi);
609 /* octet sequence (len given by prefix) */
610 os = va_arg(ap, char **);
612 /* make sure it's right */
614 SETERROR(utils, "Not enough data for this PASSDSS os\n");
620 if (octet_flag == OCTET_REFERENCE)
623 if (octet_flag == OCTET_ALLOC &&
624 (*os = (char *) utils->malloc(len)) == NULL) {
629 memcpy(*os, buf, len);
635 /* padding (max len given by prefix) */
637 if (buflen < len) len = buflen;
642 str = va_arg(ap, char **);
643 if (str) *str = NULL;
646 SETERROR(utils, "Buffer is not big enough to be PASSDSS string\n");
657 /* make sure it's right */
659 SETERROR(utils, "Not enough data for this PASSDSS string\n");
665 *str = (char *) utils->malloc(len+1); /* +1 for NUL */
671 memcpy(*str, buf, len);
678 u = va_arg(ap, uint32_t*);
682 SETERROR(utils, "Buffer is not big enough to be PASSDSS uint32\n");
689 memcpy(u + 4 - len, buf, len);
708 SETERROR(utils, "Extra data in PASSDSS buffer\n");
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)
725 void CalcLayerParams(context_t *text, char *K, unsigned Klen,
726 char *hash, unsigned hashlen)
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);
743 * Dispose of a PASSDSS context (could be server or client)
745 static void passdss_common_mech_dispose(void *conn_context,
746 const sasl_utils_t *utils)
748 context_t *text = (context_t *) conn_context;
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));
756 if (text->dh) DH_free(text->dh);
758 HMAC_CTX_cleanup(&text->hmac_send_ctx);
759 HMAC_CTX_cleanup(&text->hmac_recv_ctx);
761 EVP_CIPHER_CTX_cleanup(&text->cipher_enc_ctx);
762 EVP_CIPHER_CTX_cleanup(&text->cipher_dec_ctx);
764 _plug_decode_free(&text->decode_context);
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);
774 /***************************** Server Section *****************************/
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)),
784 /* holds state are in */
785 text = sparams->utils->malloc(sizeof(context_t));
787 MEMERROR(sparams->utils);
791 memset(text, 0, sizeof(context_t));
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;
798 *conn_context = text;
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)))
814 unsigned char *K = NULL;
815 unsigned Klen, hashlen;
818 unsigned char hash[EVP_MAX_MD_SIZE];
824 * (1) string azname ; authorization name
825 * (2) string authname ; authentication name
826 * (3) mpint X ; Diffie-Hellman parameter X
829 result = UnBuffer(params->utils, clientin, clientinlen,
830 "%s%s%m", &text->userid, &text->authid, &X);
832 params->utils->seterror(params->utils->conn, 0,
833 "Error UnBuffering input in step 1");
837 /* Fetch DSA (XXX create one for now) */
838 dsa = DSA_generate_parameters(1024, NULL, 0, NULL, NULL, NULL, NULL);
843 DSA_generate_key(dsa);
845 /* Create Diffie-Hellman parameters */
847 BN_hex2bn(&text->dh->p, N);
848 BN_hex2bn(&text->dh->g, g);
849 DH_generate_key(text->dh);
851 /* Alloc space for shared secret K as mpint */
852 K = text->utils->malloc(DH_size(text->dh) + 4);
854 params->utils->log(NULL, SASL_LOG_ERR, "Error allocing K\n");
859 /* Calculate DH shared secret (leave space at head for length) */
860 Klen = DH_compute_key(K+4, X, text->dh);
862 /* Prepend length in network byte order (make it a mpint) */
863 *((uint32_t *) K) = htonl(Klen);
866 /* Which layers can we support? */
867 if (params->props.maxbufsize < 32) {
870 need = params->props.max_ssf - params->external_ssf;
871 musthave = params->props.min_ssf - params->external_ssf;
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;
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
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
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);
907 params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
911 /* Hash (1) - (7) and K */
912 EVP_DigestInit(&mdctx, EVP_sha1());
914 EVP_DigestUpdate(&mdctx, clientin, clientinlen);
916 EVP_DigestUpdate(&mdctx, text->out_buf, *serveroutlen);
918 EVP_DigestUpdate(&mdctx, K, Klen);
919 EVP_DigestFinal(&mdctx, hash, &hashlen);
921 /* Calculate security layer params */
922 CalcLayerParams(text, K, Klen, hash, hashlen);
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);
929 HMAC_Update(&text->hmac_recv_ctx, clientin, clientinlen);
931 HMAC_Update(&text->hmac_recv_ctx, text->out_buf, *serveroutlen);
934 sig = DSA_do_sign(hash, hashlen, dsa);
936 params->utils->log(NULL, SASL_LOG_ERR,
937 "Error calculating DSS signature\n");
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);
947 params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
950 *serverout = text->out_buf;
953 result = SASL_CONTINUE;
957 if (K) text->utils->free(K);
958 if (dsa) DSA_free(dsa);
959 if (sig) DSA_SIG_free(sig);
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)
973 char *password = NULL;
974 unsigned declen, hmaclen;
975 unsigned char *csecmask, *cli_hmac, hmac[EVP_MAX_MD_SIZE];
977 int r, result = SASL_OK;
979 /* Expect (3DES encrypted):
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
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);
991 params->utils->log(NULL, SASL_LOG_ERR,
992 "Error allocating decrypt buffer in step 2\n");
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);
1003 /* Decrypt the blob */
1004 r = EVP_DecryptUpdate(&text->cipher_dec_ctx, text->decode_pkt_buf, &declen,
1005 clientin, clientinlen);
1007 r = EVP_DecryptFinal_ex(&text->cipher_dec_ctx, /* should be no output */
1008 text->decode_pkt_buf + declen, &declen);
1010 params->utils->seterror(params->utils->conn, 0,
1011 "Error decrypting input in step 2");
1012 result = SASL_BADPROT;
1015 clientin = text->decode_pkt_buf;
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);
1021 params->utils->seterror(params->utils->conn, 0,
1022 "Error UnBuffering input in step 2");
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);
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;
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) {
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));
1053 if (result != SASL_OK) {
1054 params->utils->seterror(params->utils->conn, 0,
1055 "Password verification failed");
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;
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;
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;
1084 oparams->doneflag = 1;
1085 oparams->param_version = 0;
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 */
1092 HMAC_CTX_init(&text->hmac_send_ctx);
1094 if (oparams->mech_ssf > 1) {
1095 oparams->maxoutbuf -= text->blk_siz-1; /* padding */
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);
1104 _plug_decode_init(&text->decode_context, text->utils,
1105 (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1106 params->props.maxbufsize);
1109 oparams->encode = NULL;
1110 oparams->decode = NULL;
1111 oparams->maxoutbuf = 0;
1117 if (password) _plug_free_string(params->utils, &password);
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)
1130 context_t *text = (context_t *) conn_context;
1136 return SASL_BADPARAM;
1138 sparams->utils->log(NULL, SASL_LOG_DEBUG,
1139 "PASSDSS server step %d\n", text->state);
1144 switch (text->state) {
1147 return passdss_server_mech_step1(text, sparams, clientin, clientinlen,
1148 serverout, serveroutlen, oparams);
1151 return passdss_server_mech_step2(text, sparams, clientin, clientinlen,
1152 serverout, serveroutlen, oparams);
1155 sparams->utils->seterror(sparams->utils->conn, 0,
1156 "Invalid PASSDSS server step %d", text->state);
1160 return SASL_FAIL; /* should never get here */
1163 static sasl_server_plug_t passdss_server_plugins[] =
1166 "PASSDSS-3DES-1", /* mech_name */
1168 SASL_SEC_NOPLAINTEXT
1169 | SASL_SEC_NOANONYMOUS
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 */
1182 NULL, /* user_query */
1184 NULL, /* mech_avail */
1189 int passdss_server_plug_init(const sasl_utils_t *utils,
1192 sasl_server_plug_t **pluglist,
1195 if (maxversion < SASL_SERVER_PLUG_VERSION) {
1196 SETERROR(utils, "PASSDSS version mismatch");
1197 return SASL_BADVERS;
1200 *out_version = SASL_SERVER_PLUG_VERSION;
1201 *pluglist = passdss_server_plugins;
1207 /***************************** Client Section *****************************/
1209 static int passdss_client_mech_new(void *glob_context __attribute__((unused)),
1210 sasl_client_params_t *params,
1211 void **conn_context)
1215 /* holds state are in */
1216 text = params->utils->malloc(sizeof(context_t));
1218 MEMERROR(params->utils);
1222 memset(text, 0, sizeof(context_t));
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;
1229 *conn_context = text;
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)
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;
1250 /* Expect: absolutely nothing */
1251 if (serverinlen > 0) {
1252 SETERROR(params->utils, "Invalid input to first step of PASSDSS\n");
1253 return SASL_BADPROT;
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;
1263 /* try to get the authid */
1264 if (oparams->authid == NULL) {
1265 auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1267 if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
1271 /* try to get the userid */
1272 if (oparams->user == NULL) {
1273 user_result = _plug_get_userid(params->utils, &user, prompt_need);
1275 if ((user_result != SASL_OK) && (user_result != SASL_INTERACT))
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);
1284 if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT))
1288 /* free prompts we got */
1289 if (prompt_need && *prompt_need) {
1290 params->utils->free(*prompt_need);
1291 *prompt_need = NULL;
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 */
1299 _plug_make_prompts(params->utils, prompt_need,
1300 user_result == SASL_INTERACT ?
1301 "Please enter your authorization name" : NULL,
1303 auth_result == SASL_INTERACT ?
1304 "Please enter your authentication name" : NULL,
1306 pass_result == SASL_INTERACT ?
1307 "Please enter your password" : NULL, NULL,
1310 if (result != SASL_OK) goto cleanup;
1312 return SASL_INTERACT;
1315 if (!text->password) {
1316 PARAMERROR(params->utils);
1317 return SASL_BADPARAM;
1320 if (!user || !*user) {
1321 result = params->canon_user(params->utils->conn, authid, 0,
1322 SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
1325 result = params->canon_user(params->utils->conn, user, 0,
1326 SASL_CU_AUTHZID, oparams);
1327 if (result != SASL_OK) goto cleanup;
1329 result = params->canon_user(params->utils->conn, authid, 0,
1330 SASL_CU_AUTHID, oparams);
1332 if (result != SASL_OK) goto cleanup;
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);
1343 * (1) string azname ; authorization name
1344 * (2) string authname ; authentication name
1345 * (3) mpint X ; Diffie-Hellman parameter X
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);
1353 params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
1356 *clientout = text->out_buf;
1359 result = SASL_CONTINUE;
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)
1376 DSA *dsa = DSA_new();
1377 DSA_SIG *sig = DSA_SIG_new();
1380 unsigned char *K = NULL;
1381 unsigned Klen, hashlen, enclen;
1382 unsigned char *ssecmask;
1385 unsigned char hash[EVP_MAX_MD_SIZE];
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
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
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);
1411 params->utils->seterror(params->utils->conn, 0,
1412 "Error UnBuffering input in step 2");
1416 /* XXX Validate server DSA public key */
1418 /* Alloc space for shared secret K as mpint */
1419 K = text->utils->malloc(DH_size(text->dh) + 4);
1421 params->utils->log(NULL, SASL_LOG_ERR, "Error allocing K\n");
1422 result = SASL_NOMEM;
1426 /* Calculate DH shared secret (leave space at head for length) */
1427 Klen = DH_compute_key(K+4, Y, text->dh);
1429 /* Prepend length in network byte order (make it a mpint) */
1430 *((uint32_t *) K) = htonl(Klen);
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);
1438 EVP_DigestUpdate(&mdctx, serverin, serverinlen - siglen - 4);
1440 EVP_DigestUpdate(&mdctx, K, Klen);
1441 EVP_DigestFinal(&mdctx, hash, &hashlen);
1443 /* Verify signature on the hash */
1444 result = DSA_do_verify(hash, hashlen, sig, dsa);
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;
1453 /* Calculate security layer params */
1454 CalcLayerParams(text, K, Klen, hash, hashlen);
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);
1464 if (params->props.maxbufsize < 32) {
1465 need = musthave = 0;
1467 need = params->props.max_ssf - params->external_ssf;
1468 musthave = params->props.min_ssf - params->external_ssf;
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;
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;
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);
1498 HMAC_Update(&text->hmac_send_ctx, serverin, serverinlen - siglen - 4);
1501 /* Send out (3DES encrypted):
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
1509 result = MakeBuffer(text->utils, &text->out_buf, 0,
1510 &text->out_buf_len, clientoutlen, "%1o%3u%*s",
1512 (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1513 params->props.maxbufsize,
1514 text->password->len, text->password->data);
1516 params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
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);
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);
1530 params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n");
1534 /* Alloc space for the encrypted output */
1535 result = _plug_buf_alloc(text->utils, &text->encode_buf,
1536 &text->encode_buf_len, *clientoutlen);
1538 params->utils->log(NULL, SASL_LOG_ERR,
1539 "Error allocating encrypt buffer in step 2\n");
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));
1548 r = EVP_EncryptFinal_ex(&text->cipher_enc_ctx, /* should be no output */
1549 text->encode_buf + *clientoutlen, &enclen);
1551 params->utils->seterror(params->utils->conn, 0,
1552 "Error encrypting output in step 2");
1556 *clientout = text->encode_buf;
1559 oparams->doneflag = 1;
1560 oparams->param_version = 0;
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 */
1567 HMAC_CTX_init(&text->hmac_recv_ctx);
1569 if (oparams->mech_ssf > 1) {
1570 oparams->maxoutbuf -= text->blk_siz-1; /* padding */
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);
1579 _plug_decode_init(&text->decode_context, text->utils,
1580 (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1581 params->props.maxbufsize);
1584 oparams->encode = NULL;
1585 oparams->decode = NULL;
1586 oparams->maxoutbuf = 0;
1593 if (K) text->utils->free(K);
1594 if (dsa) DSA_free(dsa);
1595 if (sig) DSA_SIG_free(sig);
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)
1609 context_t *text = (context_t *) conn_context;
1611 params->utils->log(NULL, SASL_LOG_DEBUG,
1612 "PASSDSS client step %d\n", text->state);
1617 switch (text->state) {
1620 return passdss_client_mech_step1(text, params, serverin, serverinlen,
1621 prompt_need, clientout, clientoutlen,
1625 return passdss_client_mech_step2(text, params, serverin, serverinlen,
1626 prompt_need, clientout, clientoutlen,
1630 params->utils->log(NULL, SASL_LOG_ERR,
1631 "Invalid PASSDSS client step %d\n", text->state);
1635 return SASL_FAIL; /* should never get here */
1639 static sasl_client_plug_t passdss_client_plugins[] =
1642 "PASSDSS-3DES-1", /* mech_name */
1644 SASL_SEC_NOPLAINTEXT
1645 | SASL_SEC_NOANONYMOUS
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 */
1664 int passdss_client_plug_init(sasl_utils_t *utils,
1667 sasl_client_plug_t **pluglist,
1670 if (maxversion < SASL_CLIENT_PLUG_VERSION) {
1671 SETERROR(utils, "PASSDSS version mismatch");
1672 return SASL_BADVERS;
1675 *out_version = SASL_CLIENT_PLUG_VERSION;
1676 *pluglist = passdss_client_plugins;