9178 sasl_plugins: this statement may fall through
[unleashed.git] / usr / src / lib / sasl_plugins / digestmd5 / digestmd5.c
blob4bac87c1825a4e3f283a620a98ee4a0c60a2dace
1 /*
2 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 * Copyright (c) 2016 by Delphix. All rights reserved.
5 */
7 /* DIGEST-MD5 SASL plugin
8 * Rob Siemborski
9 * Tim Martin
10 * Alexey Melnikov
11 * $Id: digestmd5.c,v 1.153 2003/03/30 22:17:06 leg Exp $
14 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in
25 * the documentation and/or other materials provided with the
26 * distribution.
28 * 3. The name "Carnegie Mellon University" must not be used to
29 * endorse or promote products derived from this software without
30 * prior written permission. For permission or any other legal
31 * details, please contact
32 * Office of Technology Transfer
33 * Carnegie Mellon University
34 * 5000 Forbes Avenue
35 * Pittsburgh, PA 15213-3890
36 * (412) 268-4387, fax: (412) 268-7395
37 * tech-transfer@andrew.cmu.edu
39 * 4. Redistributions of any form whatsoever must retain the following
40 * acknowledgment:
41 * "This product includes software developed by Computing Services
42 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
44 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
45 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
46 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
47 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
48 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
49 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
50 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
53 #include <config.h>
55 #include <stdlib.h>
56 #include <stdio.h>
57 #include <string.h>
58 #ifndef macintosh
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #endif
62 #include <fcntl.h>
63 #include <ctype.h>
65 /* DES support */
66 #ifdef WITH_DES
67 # ifdef WITH_SSL_DES
68 # include <openssl/des.h>
69 # else /* system DES library */
70 # include <des.h>
71 # endif
72 #endif /* WITH_DES */
74 #ifdef WIN32
75 # include <winsock.h>
76 #else /* Unix */
77 # include <netinet/in.h>
78 #endif /* WIN32 */
80 #ifdef _SUN_SDK_
81 #include <unistd.h>
82 #endif /* _SUN_SDK_ */
84 #include <sasl.h>
85 #include <saslplug.h>
87 #include "plugin_common.h"
89 #if defined _SUN_SDK_ && defined USE_UEF
90 #include <security/cryptoki.h>
91 static int uef_init(const sasl_utils_t *utils);
92 #endif /* _SUN_SDK_ && USE_UEF */
94 #ifndef WIN32
95 extern int strcasecmp(const char *s1, const char *s2);
96 #endif /* end WIN32 */
98 #ifdef macintosh
99 #include <sasl_md5_plugin_decl.h>
100 #endif
102 /* external definitions */
104 #ifndef _SUN_SDK_
105 #ifdef sun
106 /* gotta define gethostname ourselves on suns */
107 extern int gethostname(char *, int);
108 #endif
109 #endif /* !_SUN_SDK_ */
111 #define bool int
113 #ifndef TRUE
114 #define TRUE (1)
115 #define FALSE (0)
116 #endif
118 #define DEFAULT_BUFSIZE 0xFFFF
120 /***************************** Common Section *****************************/
122 #ifndef _SUN_SDK_
123 static const char plugin_id[] = "$Id: digestmd5.c,v 1.153 2003/03/30 22:17:06 leg Exp $";
124 #endif /* !_SUN_SDK_ */
126 /* Definitions */
127 #define NONCE_SIZE (32) /* arbitrary */
129 /* Layer Flags */
130 #define DIGEST_NOLAYER (1)
131 #define DIGEST_INTEGRITY (2)
132 #define DIGEST_PRIVACY (4)
134 /* defines */
135 #define HASHLEN 16
136 typedef unsigned char HASH[HASHLEN + 1];
137 #define HASHHEXLEN 32
138 typedef unsigned char HASHHEX[HASHHEXLEN + 1];
140 #define MAC_SIZE 10
141 #define MAC_OFFS 2
143 const char *SEALING_CLIENT_SERVER="Digest H(A1) to client-to-server sealing key magic constant";
144 const char *SEALING_SERVER_CLIENT="Digest H(A1) to server-to-client sealing key magic constant";
146 const char *SIGNING_CLIENT_SERVER="Digest session key to client-to-server signing key magic constant";
147 const char *SIGNING_SERVER_CLIENT="Digest session key to server-to-client signing key magic constant";
149 #define HT (9)
150 #define CR (13)
151 #define LF (10)
152 #define SP (32)
153 #define DEL (127)
155 struct context;
157 /* function definitions for cipher encode/decode */
158 typedef int cipher_function_t(struct context *,
159 const char *,
160 unsigned,
161 unsigned char[],
162 char *,
163 unsigned *);
165 #ifdef _SUN_SDK_
166 typedef int cipher_init_t(struct context *, char [16],
167 char [16]);
168 #else
169 typedef int cipher_init_t(struct context *, unsigned char [16],
170 unsigned char [16]);
171 #endif /* _SUN_SDK_ */
173 typedef void cipher_free_t(struct context *);
175 enum Context_type { SERVER = 0, CLIENT = 1 };
177 typedef struct cipher_context cipher_context_t;
179 /* cached auth info used for fast reauth */
180 typedef struct reauth_entry {
181 char *authid;
182 char *realm;
183 unsigned char *nonce;
184 unsigned int nonce_count;
185 unsigned char *cnonce;
187 union {
188 struct {
189 time_t timestamp;
190 } s; /* server stuff */
192 struct {
193 char *serverFQDN;
194 int protection;
195 struct digest_cipher *cipher;
196 unsigned int server_maxbuf;
197 } c; /* client stuff */
198 } u;
199 } reauth_entry_t;
201 typedef struct reauth_cache {
202 /* static stuff */
203 enum Context_type i_am; /* are we the client or server? */
204 time_t timeout;
205 void *mutex;
206 size_t size;
208 reauth_entry_t *e; /* fixed-size hash table of entries */
209 } reauth_cache_t;
211 /* context that stores info */
212 typedef struct context {
213 int state; /* state in the authentication we are in */
214 enum Context_type i_am; /* are we the client or server? */
216 reauth_cache_t *reauth;
218 char *authid;
219 char *realm;
220 unsigned char *nonce;
221 unsigned int nonce_count;
222 unsigned char *cnonce;
224 char *response_value;
226 unsigned int seqnum;
227 unsigned int rec_seqnum; /* for checking integrity */
229 HASH Ki_send;
230 HASH Ki_receive;
232 HASH HA1; /* Kcc or Kcs */
234 /* copy of utils from the params structures */
235 const sasl_utils_t *utils;
237 /* For general use */
238 char *out_buf;
239 unsigned out_buf_len;
241 /* for encoding/decoding */
242 buffer_info_t *enc_in_buf;
243 char *encode_buf, *decode_buf, *decode_once_buf;
244 unsigned encode_buf_len, decode_buf_len, decode_once_buf_len;
245 char *decode_tmp_buf;
246 unsigned decode_tmp_buf_len;
247 char *MAC_buf;
248 unsigned MAC_buf_len;
250 char *buffer;
251 char sizebuf[4];
252 int cursize;
254 /* Layer info */
255 unsigned int size; /* Absolute size of buffer */
256 unsigned int needsize; /* How much of the size of the buffer is left */
258 /* Server MaxBuf for Client or Client MaxBuf For Server */
259 /* INCOMING */
260 unsigned int in_maxbuf;
262 /* if privacy mode is used use these functions for encode and decode */
263 cipher_function_t *cipher_enc;
264 cipher_function_t *cipher_dec;
265 cipher_init_t *cipher_init;
266 cipher_free_t *cipher_free;
267 struct cipher_context *cipher_enc_context;
268 struct cipher_context *cipher_dec_context;
269 } context_t;
271 struct digest_cipher {
272 char *name;
273 sasl_ssf_t ssf;
274 int n; /* bits to make privacy key */
275 int flag; /* a bitmask to make things easier for us */
277 cipher_function_t *cipher_enc;
278 cipher_function_t *cipher_dec;
279 cipher_init_t *cipher_init;
280 cipher_free_t *cipher_free;
283 #ifdef _SUN_SDK_
284 static const unsigned char *COLON = (unsigned char *)":";
285 #else
286 static const unsigned char *COLON = ":";
287 #endif /* _SUN_SDK_ */
289 /* Hashes a string to produce an unsigned short */
290 static unsigned hash(const char *str)
292 unsigned val = 0;
293 int i;
295 while (str && *str) {
296 i = (int) *str;
297 val ^= i;
298 val <<= 1;
299 str++;
302 return val;
305 static void CvtHex(HASH Bin, HASHHEX Hex)
307 unsigned short i;
308 unsigned char j;
310 for (i = 0; i < HASHLEN; i++) {
311 j = (Bin[i] >> 4) & 0xf;
312 if (j <= 9)
313 Hex[i * 2] = (j + '0');
314 else
315 Hex[i * 2] = (j + 'a' - 10);
316 j = Bin[i] & 0xf;
317 if (j <= 9)
318 Hex[i * 2 + 1] = (j + '0');
319 else
320 Hex[i * 2 + 1] = (j + 'a' - 10);
322 Hex[HASHHEXLEN] = '\0';
326 * calculate request-digest/response-digest as per HTTP Digest spec
328 void
329 DigestCalcResponse(const sasl_utils_t * utils,
330 HASHHEX HA1, /* H(A1) */
331 unsigned char *pszNonce, /* nonce from server */
332 unsigned int pszNonceCount, /* 8 hex digits */
333 unsigned char *pszCNonce, /* client nonce */
334 unsigned char *pszQop, /* qop-value: "", "auth",
335 * "auth-int" */
336 unsigned char *pszDigestUri, /* requested URL */
337 unsigned char *pszMethod,
338 HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
339 HASHHEX Response /* request-digest or response-digest */
342 MD5_CTX Md5Ctx;
343 HASH HA2;
344 HASH RespHash;
345 HASHHEX HA2Hex;
346 char ncvalue[10];
348 /* calculate H(A2) */
349 utils->MD5Init(&Md5Ctx);
351 if (pszMethod != NULL) {
352 utils->MD5Update(&Md5Ctx, pszMethod, strlen((char *) pszMethod));
354 utils->MD5Update(&Md5Ctx, (unsigned char *) COLON, 1);
356 /* utils->MD5Update(&Md5Ctx, (unsigned char *) "AUTHENTICATE:", 13); */
357 utils->MD5Update(&Md5Ctx, pszDigestUri, strlen((char *) pszDigestUri));
358 if (strcasecmp((char *) pszQop, "auth") != 0) {
359 /* append ":00000000000000000000000000000000" */
360 utils->MD5Update(&Md5Ctx, COLON, 1);
361 utils->MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
363 utils->MD5Final(HA2, &Md5Ctx);
364 CvtHex(HA2, HA2Hex);
366 /* calculate response */
367 utils->MD5Init(&Md5Ctx);
368 utils->MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
369 utils->MD5Update(&Md5Ctx, COLON, 1);
370 utils->MD5Update(&Md5Ctx, pszNonce, strlen((char *) pszNonce));
371 utils->MD5Update(&Md5Ctx, COLON, 1);
372 if (*pszQop) {
373 sprintf(ncvalue, "%08x", pszNonceCount);
374 #ifdef _SUN_SDK_
375 utils->MD5Update(&Md5Ctx, (unsigned char *)ncvalue, strlen(ncvalue));
376 #else
377 utils->MD5Update(&Md5Ctx, ncvalue, strlen(ncvalue));
378 #endif /* _SUN_SDK_ */
379 utils->MD5Update(&Md5Ctx, COLON, 1);
380 utils->MD5Update(&Md5Ctx, pszCNonce, strlen((char *) pszCNonce));
381 utils->MD5Update(&Md5Ctx, COLON, 1);
382 utils->MD5Update(&Md5Ctx, pszQop, strlen((char *) pszQop));
383 utils->MD5Update(&Md5Ctx, COLON, 1);
385 utils->MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
386 utils->MD5Final(RespHash, &Md5Ctx);
387 CvtHex(RespHash, Response);
390 static bool UTF8_In_8859_1(const unsigned char *base, int len)
392 const unsigned char *scan, *end;
394 end = base + len;
395 for (scan = base; scan < end; ++scan) {
396 if (*scan > 0xC3)
397 break; /* abort if outside 8859-1 */
398 if (*scan >= 0xC0 && *scan <= 0xC3) {
399 if (++scan == end || *scan < 0x80 || *scan > 0xBF)
400 break;
404 /* if scan >= end, then this is a 8859-1 string. */
405 return (scan >= end);
409 * if the string is entirely in the 8859-1 subset of UTF-8, then translate to
410 * 8859-1 prior to MD5
412 void MD5_UTF8_8859_1(const sasl_utils_t * utils,
413 MD5_CTX * ctx,
414 bool In_ISO_8859_1,
415 const unsigned char *base,
416 int len)
418 const unsigned char *scan, *end;
419 unsigned char cbuf;
421 end = base + len;
423 /* if we found a character outside 8859-1, don't alter string */
424 if (!In_ISO_8859_1) {
425 utils->MD5Update(ctx, base, len);
426 return;
428 /* convert to 8859-1 prior to applying hash */
429 do {
430 for (scan = base; scan < end && *scan < 0xC0; ++scan);
431 if (scan != base)
432 utils->MD5Update(ctx, base, scan - base);
433 if (scan + 1 >= end)
434 break;
435 cbuf = ((scan[0] & 0x3) << 6) | (scan[1] & 0x3f);
436 utils->MD5Update(ctx, &cbuf, 1);
437 base = scan + 2;
439 while (base < end);
442 static void DigestCalcSecret(const sasl_utils_t * utils,
443 unsigned char *pszUserName,
444 unsigned char *pszRealm,
445 unsigned char *Password,
446 int PasswordLen,
447 HASH HA1)
449 bool In_8859_1;
451 MD5_CTX Md5Ctx;
453 /* Chris Newman clarified that the following text in DIGEST-MD5 spec
454 is bogus: "if name and password are both in ISO 8859-1 charset"
455 We shoud use code example instead */
457 utils->MD5Init(&Md5Ctx);
459 /* We have to convert UTF-8 to ISO-8859-1 if possible */
460 In_8859_1 = UTF8_In_8859_1(pszUserName, strlen((char *) pszUserName));
461 MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1,
462 pszUserName, strlen((char *) pszUserName));
464 utils->MD5Update(&Md5Ctx, COLON, 1);
466 if (pszRealm != NULL && pszRealm[0] != '\0') {
467 /* a NULL realm is equivalent to the empty string */
468 utils->MD5Update(&Md5Ctx, pszRealm, strlen((char *) pszRealm));
471 utils->MD5Update(&Md5Ctx, COLON, 1);
473 /* We have to convert UTF-8 to ISO-8859-1 if possible */
474 In_8859_1 = UTF8_In_8859_1(Password, PasswordLen);
475 MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1,
476 Password, PasswordLen);
478 utils->MD5Final(HA1, &Md5Ctx);
481 static unsigned char *create_nonce(const sasl_utils_t * utils)
483 unsigned char *base64buf;
484 int base64len;
486 char *ret = (char *) utils->malloc(NONCE_SIZE);
487 if (ret == NULL)
488 return NULL;
490 #if defined _DEV_URANDOM && defined _SUN_SDK_
492 int fd = open(_DEV_URANDOM, O_RDONLY);
493 int nread = 0;
495 if (fd != -1) {
496 nread = read(fd, ret, NONCE_SIZE);
497 close(fd);
499 if (nread != NONCE_SIZE)
500 utils->rand(utils->rpool, (char *) ret, NONCE_SIZE);
502 #else
503 utils->rand(utils->rpool, (char *) ret, NONCE_SIZE);
504 #endif /* _DEV_URANDOM && _SUN_SDK_ */
506 /* base 64 encode it so it has valid chars */
507 base64len = (NONCE_SIZE * 4 / 3) + (NONCE_SIZE % 3 ? 4 : 0);
509 base64buf = (unsigned char *) utils->malloc(base64len + 1);
510 if (base64buf == NULL) {
511 #ifdef _SUN_SDK_
512 utils->log(utils->conn, SASL_LOG_ERR,
513 "Unable to allocate final buffer");
514 #else
515 utils->seterror(utils->conn, 0, "Unable to allocate final buffer");
516 #endif /* _SUN_SDK_ */
517 return NULL;
521 * Returns SASL_OK on success, SASL_BUFOVER if result won't fit
523 if (utils->encode64(ret, NONCE_SIZE,
524 (char *) base64buf, base64len, NULL) != SASL_OK) {
525 utils->free(ret);
526 return NULL;
528 utils->free(ret);
530 return base64buf;
533 static int add_to_challenge(const sasl_utils_t *utils,
534 char **str, unsigned *buflen, unsigned *curlen,
535 char *name,
536 unsigned char *value,
537 bool need_quotes)
539 int namesize = strlen(name);
540 int valuesize = strlen((char *) value);
541 int ret;
543 ret = _plug_buf_alloc(utils, str, buflen,
544 *curlen + 1 + namesize + 2 + valuesize + 2);
545 if(ret != SASL_OK) return ret;
547 *curlen = *curlen + 1 + namesize + 2 + valuesize + 2;
549 strcat(*str, ",");
550 strcat(*str, name);
552 if (need_quotes) {
553 strcat(*str, "=\"");
554 strcat(*str, (char *) value); /* XXX. What about quoting??? */
555 strcat(*str, "\"");
556 } else {
557 strcat(*str, "=");
558 strcat(*str, (char *) value);
561 return SASL_OK;
564 static char *skip_lws (char *s)
566 if(!s) return NULL;
568 /* skipping spaces: */
569 while (s[0] == ' ' || s[0] == HT || s[0] == CR || s[0] == LF) {
570 if (s[0]=='\0') break;
571 s++;
574 return s;
577 #ifdef __SUN_SDK_
578 static char *skip_token (char *s, int caseinsensitive __attribute__((unused)))
579 #else
580 static char *skip_token (char *s, int caseinsensitive)
581 #endif /* _SUN_SDK_ */
583 if(!s) return NULL;
585 #ifdef __SUN_SDK_
586 while (((unsigned char *)s)[0]>SP) {
587 #else
588 while (s[0]>SP) {
589 #endif /* _SUN_SDK_ */
590 if (s[0]==DEL || s[0]=='(' || s[0]==')' || s[0]=='<' || s[0]=='>' ||
591 s[0]=='@' || s[0]==',' || s[0]==';' || s[0]==':' || s[0]=='\\' ||
592 s[0]=='\'' || s[0]=='/' || s[0]=='[' || s[0]==']' || s[0]== '?' ||
593 s[0]=='=' || s[0]== '{' || s[0]== '}') {
594 #ifdef __SUN_SDK_
595 /* the above chars are never uppercase */
596 break;
597 #else
598 if (caseinsensitive == 1) {
599 if (!isupper((unsigned char) s[0]))
600 break;
601 } else {
602 break;
604 #endif /* _SUN_SDK_ */
606 s++;
608 return s;
611 /* NULL - error (unbalanced quotes),
612 otherwise pointer to the first character after value */
613 static char *unquote (char *qstr)
615 char *endvalue;
616 int escaped = 0;
617 char *outptr;
619 if(!qstr) return NULL;
621 if (qstr[0] == '"') {
622 qstr++;
623 outptr = qstr;
625 for (endvalue = qstr; endvalue[0] != '\0'; endvalue++, outptr++) {
626 if (escaped) {
627 outptr[0] = endvalue[0];
628 escaped = 0;
630 else if (endvalue[0] == '\\') {
631 escaped = 1;
632 outptr--; /* Will be incremented at the end of the loop */
634 else if (endvalue[0] == '"') {
635 break;
637 else {
638 outptr[0] = endvalue[0];
642 if (endvalue[0] != '"') {
643 return NULL;
646 while (outptr <= endvalue) {
647 outptr[0] = '\0';
648 outptr++;
650 endvalue++;
652 else { /* not qouted value (token) */
653 endvalue = skip_token(qstr,0);
656 return endvalue;
659 static void get_pair(char **in, char **name, char **value)
661 char *endpair;
662 /* int inQuotes; */
663 char *curp = *in;
664 *name = NULL;
665 *value = NULL;
667 if (curp == NULL) return;
668 if (curp[0] == '\0') return;
670 /* skipping spaces: */
671 curp = skip_lws(curp);
673 *name = curp;
675 curp = skip_token(curp,1);
677 /* strip wierd chars */
678 if (curp[0] != '=' && curp[0] != '\0') {
679 *curp++ = '\0';
682 curp = skip_lws(curp);
684 if (curp[0] != '=') { /* No '=' sign */
685 *name = NULL;
686 return;
689 curp[0] = '\0';
690 curp++;
692 curp = skip_lws(curp);
694 *value = (curp[0] == '"') ? curp+1 : curp;
696 endpair = unquote (curp);
697 if (endpair == NULL) { /* Unbalanced quotes */
698 *name = NULL;
699 return;
701 if (endpair[0] != ',') {
702 if (endpair[0]!='\0') {
703 *endpair++ = '\0';
707 endpair = skip_lws(endpair);
709 /* syntax check: MUST be '\0' or ',' */
710 if (endpair[0] == ',') {
711 endpair[0] = '\0';
712 endpair++; /* skipping <,> */
713 } else if (endpair[0] != '\0') {
714 *name = NULL;
715 return;
718 *in = endpair;
721 #ifdef WITH_DES
722 struct des_context_s {
723 des_key_schedule keysched; /* key schedule for des initialization */
724 des_cblock ivec; /* initial vector for encoding */
725 des_key_schedule keysched2; /* key schedule for 3des initialization */
728 typedef struct des_context_s des_context_t;
730 /* slide the first 7 bytes of 'inbuf' into the high seven bits of the
731 first 8 bytes of 'keybuf'. 'keybuf' better be 8 bytes long or longer. */
732 static void slidebits(unsigned char *keybuf, unsigned char *inbuf)
734 keybuf[0] = inbuf[0];
735 keybuf[1] = (inbuf[0]<<7) | (inbuf[1]>>1);
736 keybuf[2] = (inbuf[1]<<6) | (inbuf[2]>>2);
737 keybuf[3] = (inbuf[2]<<5) | (inbuf[3]>>3);
738 keybuf[4] = (inbuf[3]<<4) | (inbuf[4]>>4);
739 keybuf[5] = (inbuf[4]<<3) | (inbuf[5]>>5);
740 keybuf[6] = (inbuf[5]<<2) | (inbuf[6]>>6);
741 keybuf[7] = (inbuf[6]<<1);
744 /******************************
746 * 3DES functions
748 *****************************/
750 static int dec_3des(context_t *text,
751 const char *input,
752 unsigned inputlen,
753 unsigned char digest[16],
754 char *output,
755 unsigned *outputlen)
757 des_context_t *c = (des_context_t *) text->cipher_dec_context;
758 int padding, p;
760 des_ede2_cbc_encrypt((void *) input,
761 (void *) output,
762 inputlen,
763 c->keysched,
764 c->keysched2,
765 &c->ivec,
766 DES_DECRYPT);
768 /* now chop off the padding */
769 padding = output[inputlen - 11];
770 if (padding < 1 || padding > 8) {
771 /* invalid padding length */
772 return SASL_FAIL;
774 /* verify all padding is correct */
775 for (p = 1; p <= padding; p++) {
776 if (output[inputlen - 10 - p] != padding) {
777 return SASL_FAIL;
781 /* chop off the padding */
782 *outputlen = inputlen - padding - 10;
784 /* copy in the HMAC to digest */
785 memcpy(digest, output + inputlen - 10, 10);
787 return SASL_OK;
790 static int enc_3des(context_t *text,
791 const char *input,
792 unsigned inputlen,
793 unsigned char digest[16],
794 char *output,
795 unsigned *outputlen)
797 des_context_t *c = (des_context_t *) text->cipher_enc_context;
798 int len;
799 int paddinglen;
801 /* determine padding length */
802 paddinglen = 8 - ((inputlen + 10) % 8);
804 /* now construct the full stuff to be ciphered */
805 memcpy(output, input, inputlen); /* text */
806 memset(output+inputlen, paddinglen, paddinglen);/* pad */
807 memcpy(output+inputlen+paddinglen, digest, 10); /* hmac */
809 len=inputlen+paddinglen+10;
811 des_ede2_cbc_encrypt((void *) output,
812 (void *) output,
813 len,
814 c->keysched,
815 c->keysched2,
816 &c->ivec,
817 DES_ENCRYPT);
819 *outputlen=len;
821 return SASL_OK;
824 static int init_3des(context_t *text,
825 unsigned char enckey[16],
826 unsigned char deckey[16])
828 des_context_t *c;
829 unsigned char keybuf[8];
831 /* allocate enc & dec context */
832 c = (des_context_t *) text->utils->malloc(2 * sizeof(des_context_t));
833 if (c == NULL) return SASL_NOMEM;
835 /* setup enc context */
836 slidebits(keybuf, enckey);
837 if (des_key_sched((des_cblock *) keybuf, c->keysched) < 0)
838 return SASL_FAIL;
840 slidebits(keybuf, enckey + 7);
841 if (des_key_sched((des_cblock *) keybuf, c->keysched2) < 0)
842 return SASL_FAIL;
843 memcpy(c->ivec, ((char *) enckey) + 8, 8);
845 text->cipher_enc_context = (cipher_context_t *) c;
847 /* setup dec context */
848 c++;
849 slidebits(keybuf, deckey);
850 if (des_key_sched((des_cblock *) keybuf, c->keysched) < 0)
851 return SASL_FAIL;
853 slidebits(keybuf, deckey + 7);
854 if (des_key_sched((des_cblock *) keybuf, c->keysched2) < 0)
855 return SASL_FAIL;
857 memcpy(c->ivec, ((char *) deckey) + 8, 8);
859 text->cipher_dec_context = (cipher_context_t *) c;
861 return SASL_OK;
865 /******************************
867 * DES functions
869 *****************************/
871 static int dec_des(context_t *text,
872 const char *input,
873 unsigned inputlen,
874 unsigned char digest[16],
875 char *output,
876 unsigned *outputlen)
878 des_context_t *c = (des_context_t *) text->cipher_dec_context;
879 int p, padding = 0;
881 des_cbc_encrypt((void *) input,
882 (void *) output,
883 inputlen,
884 c->keysched,
885 &c->ivec,
886 DES_DECRYPT);
888 /* Update the ivec (des_cbc_encrypt implementations tend to be broken in
889 this way) */
890 memcpy(c->ivec, input + (inputlen - 8), 8);
892 /* now chop off the padding */
893 padding = output[inputlen - 11];
894 if (padding < 1 || padding > 8) {
895 /* invalid padding length */
896 return SASL_FAIL;
898 /* verify all padding is correct */
899 for (p = 1; p <= padding; p++) {
900 if (output[inputlen - 10 - p] != padding) {
901 return SASL_FAIL;
905 /* chop off the padding */
906 *outputlen = inputlen - padding - 10;
908 /* copy in the HMAC to digest */
909 memcpy(digest, output + inputlen - 10, 10);
911 return SASL_OK;
914 static int enc_des(context_t *text,
915 const char *input,
916 unsigned inputlen,
917 unsigned char digest[16],
918 char *output,
919 unsigned *outputlen)
921 des_context_t *c = (des_context_t *) text->cipher_enc_context;
922 int len;
923 int paddinglen;
925 /* determine padding length */
926 paddinglen = 8 - ((inputlen+10) % 8);
928 /* now construct the full stuff to be ciphered */
929 memcpy(output, input, inputlen); /* text */
930 memset(output+inputlen, paddinglen, paddinglen);/* pad */
931 memcpy(output+inputlen+paddinglen, digest, 10); /* hmac */
933 len = inputlen + paddinglen + 10;
935 des_cbc_encrypt((void *) output,
936 (void *) output,
937 len,
938 c->keysched,
939 &c->ivec,
940 DES_ENCRYPT);
942 /* Update the ivec (des_cbc_encrypt implementations tend to be broken in
943 this way) */
944 memcpy(c->ivec, output + (len - 8), 8);
946 *outputlen = len;
948 return SASL_OK;
951 static int init_des(context_t *text,
952 unsigned char enckey[16],
953 unsigned char deckey[16])
955 des_context_t *c;
956 unsigned char keybuf[8];
958 /* allocate enc context */
959 c = (des_context_t *) text->utils->malloc(2 * sizeof(des_context_t));
960 if (c == NULL) return SASL_NOMEM;
962 /* setup enc context */
963 slidebits(keybuf, enckey);
964 des_key_sched((des_cblock *) keybuf, c->keysched);
966 memcpy(c->ivec, ((char *) enckey) + 8, 8);
968 text->cipher_enc_context = (cipher_context_t *) c;
970 /* setup dec context */
971 c++;
972 slidebits(keybuf, deckey);
973 des_key_sched((des_cblock *) keybuf, c->keysched);
975 memcpy(c->ivec, ((char *) deckey) + 8, 8);
977 text->cipher_dec_context = (cipher_context_t *) c;
979 return SASL_OK;
982 static void free_des(context_t *text)
984 /* free des contextss. only cipher_enc_context needs to be free'd,
985 since cipher_dec_context was allocated at the same time. */
986 if (text->cipher_enc_context) text->utils->free(text->cipher_enc_context);
989 #endif /* WITH_DES */
991 #ifdef WITH_RC4
992 /* quick generic implementation of RC4 */
993 struct rc4_context_s {
994 unsigned char sbox[256];
995 int i, j;
998 typedef struct rc4_context_s rc4_context_t;
1000 static void rc4_init(rc4_context_t *text,
1001 const unsigned char *key,
1002 unsigned keylen)
1004 int i, j;
1006 /* fill in linearly s0=0 s1=1... */
1007 for (i=0;i<256;i++)
1008 text->sbox[i]=i;
1010 j=0;
1011 for (i = 0; i < 256; i++) {
1012 unsigned char tmp;
1013 /* j = (j + Si + Ki) mod 256 */
1014 j = (j + text->sbox[i] + key[i % keylen]) % 256;
1016 /* swap Si and Sj */
1017 tmp = text->sbox[i];
1018 text->sbox[i] = text->sbox[j];
1019 text->sbox[j] = tmp;
1022 /* counters initialized to 0 */
1023 text->i = 0;
1024 text->j = 0;
1027 static void rc4_encrypt(rc4_context_t *text,
1028 const char *input,
1029 char *output,
1030 unsigned len)
1032 int tmp;
1033 int i = text->i;
1034 int j = text->j;
1035 int t;
1036 int K;
1037 const char *input_end = input + len;
1039 while (input < input_end) {
1040 i = (i + 1) % 256;
1042 j = (j + text->sbox[i]) % 256;
1044 /* swap Si and Sj */
1045 tmp = text->sbox[i];
1046 text->sbox[i] = text->sbox[j];
1047 text->sbox[j] = tmp;
1049 t = (text->sbox[i] + text->sbox[j]) % 256;
1051 K = text->sbox[t];
1053 /* byte K is Xor'ed with plaintext */
1054 *output++ = *input++ ^ K;
1057 text->i = i;
1058 text->j = j;
1061 static void rc4_decrypt(rc4_context_t *text,
1062 const char *input,
1063 char *output,
1064 unsigned len)
1066 int tmp;
1067 int i = text->i;
1068 int j = text->j;
1069 int t;
1070 int K;
1071 const char *input_end = input + len;
1073 while (input < input_end) {
1074 i = (i + 1) % 256;
1076 j = (j + text->sbox[i]) % 256;
1078 /* swap Si and Sj */
1079 tmp = text->sbox[i];
1080 text->sbox[i] = text->sbox[j];
1081 text->sbox[j] = tmp;
1083 t = (text->sbox[i] + text->sbox[j]) % 256;
1085 K = text->sbox[t];
1087 /* byte K is Xor'ed with plaintext */
1088 *output++ = *input++ ^ K;
1091 text->i = i;
1092 text->j = j;
1095 static void free_rc4(context_t *text)
1097 /* free rc4 context structures */
1099 if(text->cipher_enc_context) text->utils->free(text->cipher_enc_context);
1100 if(text->cipher_dec_context) text->utils->free(text->cipher_dec_context);
1101 #ifdef _SUN_SDK_
1102 text->cipher_enc_context = NULL;
1103 text->cipher_dec_context = NULL;
1104 #endif /* _SUN_SDK_ */
1107 static int init_rc4(context_t *text,
1108 #ifdef _SUN_SDK_
1109 char enckey[16],
1110 char deckey[16])
1111 #else
1112 unsigned char enckey[16],
1113 unsigned char deckey[16])
1114 #endif /* _SUN_SDK_ */
1116 /* allocate rc4 context structures */
1117 text->cipher_enc_context=
1118 (cipher_context_t *) text->utils->malloc(sizeof(rc4_context_t));
1119 if (text->cipher_enc_context == NULL) return SASL_NOMEM;
1121 text->cipher_dec_context=
1122 (cipher_context_t *) text->utils->malloc(sizeof(rc4_context_t));
1123 #ifdef _SUN_SDK_
1124 if (text->cipher_dec_context == NULL) {
1125 text->utils->free(text->cipher_enc_context);
1126 text->cipher_enc_context = NULL;
1127 return SASL_NOMEM;
1129 #else
1130 if (text->cipher_dec_context == NULL) return SASL_NOMEM;
1131 #endif /* _SUN_SDK_ */
1133 /* initialize them */
1134 rc4_init((rc4_context_t *) text->cipher_enc_context,
1135 (const unsigned char *) enckey, 16);
1136 rc4_init((rc4_context_t *) text->cipher_dec_context,
1137 (const unsigned char *) deckey, 16);
1139 return SASL_OK;
1142 static int dec_rc4(context_t *text,
1143 const char *input,
1144 unsigned inputlen,
1145 unsigned char digest[16],
1146 char *output,
1147 unsigned *outputlen)
1149 /* decrypt the text part */
1150 rc4_decrypt((rc4_context_t *) text->cipher_dec_context,
1151 input, output, inputlen-10);
1153 /* decrypt the HMAC part */
1154 rc4_decrypt((rc4_context_t *) text->cipher_dec_context,
1155 input+(inputlen-10), (char *) digest, 10);
1157 /* no padding so we just subtract the HMAC to get the text length */
1158 *outputlen = inputlen - 10;
1160 return SASL_OK;
1163 static int enc_rc4(context_t *text,
1164 const char *input,
1165 unsigned inputlen,
1166 unsigned char digest[16],
1167 char *output,
1168 unsigned *outputlen)
1170 /* pad is zero */
1171 *outputlen = inputlen+10;
1173 /* encrypt the text part */
1174 rc4_encrypt((rc4_context_t *) text->cipher_enc_context,
1175 input,
1176 output,
1177 inputlen);
1179 /* encrypt the HMAC part */
1180 rc4_encrypt((rc4_context_t *) text->cipher_enc_context,
1181 (const char *) digest,
1182 (output)+inputlen, 10);
1184 return SASL_OK;
1187 #endif /* WITH_RC4 */
1189 struct digest_cipher available_ciphers[] =
1191 #ifdef WITH_RC4
1192 { "rc4-40", 40, 5, 0x01, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 },
1193 { "rc4-56", 56, 7, 0x02, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 },
1194 { "rc4", 128, 16, 0x04, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 },
1195 #endif
1196 #ifdef WITH_DES
1197 { "des", 55, 16, 0x08, &enc_des, &dec_des, &init_des, &free_des },
1198 { "3des", 112, 16, 0x10, &enc_3des, &dec_3des, &init_3des, &free_des },
1199 #endif
1200 { NULL, 0, 0, 0, NULL, NULL, NULL, NULL }
1204 #ifdef USE_UEF
1205 DEFINE_STATIC_MUTEX(uef_init_mutex);
1206 #define DES_CIPHER_INDEX 3
1207 #define DES3_CIPHER_INDEX 4
1209 static int got_uef_slot = FALSE;
1210 static sasl_ssf_t uef_max_ssf = 0;
1211 static CK_SLOT_ID rc4_slot_id;
1212 static CK_SLOT_ID des_slot_id;
1213 static CK_SLOT_ID des3_slot_id;
1215 struct uef_context_s {
1216 CK_SESSION_HANDLE hSession;
1217 CK_OBJECT_HANDLE hKey;
1220 typedef struct uef_context_s uef_context_t;
1223 * slide the first 7 bytes of 'inbuf' into the high seven bits of the
1224 * first 8 bytes of 'keybuf'. 'inbuf' better be 8 bytes long or longer.
1226 * This is used to compute the IV for "des" and "3des" as described in
1227 * draft-ietf-sasl-rfc2831bis-00.txt - The IV for "des"
1228 * and "3des" is the last 8 bytes of Kcc or Kcs - the encryption keys.
1231 static void slidebits(unsigned char *keybuf, unsigned char *inbuf)
1233 keybuf[0] = inbuf[0];
1234 keybuf[1] = (inbuf[0]<<7) | (inbuf[1]>>1);
1235 keybuf[2] = (inbuf[1]<<6) | (inbuf[2]>>2);
1236 keybuf[3] = (inbuf[2]<<5) | (inbuf[3]>>3);
1237 keybuf[4] = (inbuf[3]<<4) | (inbuf[4]>>4);
1238 keybuf[5] = (inbuf[4]<<3) | (inbuf[5]>>5);
1239 keybuf[6] = (inbuf[5]<<2) | (inbuf[6]>>6);
1240 keybuf[7] = (inbuf[6]<<1);
1244 * Create encryption and decryption session handle handles for later use.
1245 * Returns SASL_OK on success - any other return indicates failure.
1247 * free_uef is called to release associated resources by
1248 * digestmd5_common_mech_dispose
1251 static int init_uef(context_t *text,
1252 CK_KEY_TYPE keyType,
1253 CK_MECHANISM_TYPE mech_type,
1254 CK_SLOT_ID slot_id,
1255 char enckey[16],
1256 char deckey[16])
1258 CK_RV rv;
1259 uef_context_t *enc_context;
1260 uef_context_t *dec_context;
1261 CK_OBJECT_CLASS class = CKO_SECRET_KEY;
1262 CK_BBOOL true = TRUE;
1263 static CK_MECHANISM mechanism = {CKM_RC4, NULL, 0};
1264 unsigned char keybuf[24];
1265 CK_ATTRIBUTE template[] = {
1266 {CKA_CLASS, NULL, sizeof (class)},
1267 {CKA_KEY_TYPE, NULL, sizeof (keyType)},
1268 {CKA_ENCRYPT, NULL, sizeof (true)},
1269 {CKA_VALUE, NULL, 16}};
1271 template[0].pValue = &class;
1272 template[1].pValue = &keyType;
1273 template[2].pValue = &true;
1274 if (keyType == CKK_DES || keyType == CKK_DES3) {
1275 slidebits(keybuf, (unsigned char *)enckey);
1276 if (keyType == CKK_DES3) {
1277 slidebits(keybuf + 8, (unsigned char *)enckey + 7);
1278 (void) memcpy(keybuf + 16, keybuf, 8);
1279 template[3].ulValueLen = 24;
1280 } else {
1281 template[3].ulValueLen = 8;
1283 template[3].pValue = keybuf;
1284 mechanism.pParameter = enckey + 8;
1285 mechanism.ulParameterLen = 8;
1286 } else {
1287 template[3].pValue = enckey;
1289 mechanism.mechanism = mech_type;
1291 /* allocate rc4 context structures */
1292 enc_context = text->utils->malloc(sizeof (uef_context_t));
1293 if (enc_context == NULL)
1294 return SASL_NOMEM;
1296 rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR,
1297 &enc_context->hSession);
1298 if (rv != CKR_OK) {
1299 text->utils->free(enc_context);
1300 #ifdef DEBUG
1301 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1302 "enc C_OpenSession Failed:0x%.8X\n", rv);
1303 #endif
1304 return SASL_FAIL;
1307 rv = C_CreateObject(enc_context->hSession, template,
1308 sizeof (template)/sizeof (template[0]), &enc_context->hKey);
1309 if (rv != CKR_OK) {
1310 text->utils->free(enc_context);
1311 (void) C_CloseSession(enc_context->hSession);
1312 #ifdef DEBUG
1313 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1314 "enc C_CreateObject: rv = 0x%.8X\n", rv);
1315 #endif
1316 return SASL_FAIL;
1319 text->cipher_enc_context = (cipher_context_t *)enc_context;
1321 /* Initialize the encryption operation in the session */
1322 rv = C_EncryptInit(enc_context->hSession, &mechanism, enc_context->hKey);
1323 if (rv != CKR_OK) {
1324 #ifdef DEBUG
1325 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1326 "C_EncryptInit: rv = 0x%.8X\n", rv);
1327 #endif
1328 return SASL_FAIL;
1331 dec_context = text->utils->malloc(sizeof(uef_context_t));
1332 if (dec_context == NULL)
1333 return SASL_NOMEM;
1335 rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR,
1336 &dec_context->hSession);
1337 if (rv != CKR_OK) {
1338 #ifdef DEBUG
1339 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1340 "dec C_OpenSession Failed:0x%.8X\n", rv);
1341 #endif
1342 text->utils->free(dec_context);
1343 return SASL_FAIL;
1346 template[2].type = CKA_DECRYPT;
1347 if (keyType == CKK_DES || keyType == CKK_DES3) {
1348 slidebits(keybuf, (unsigned char *)deckey);
1349 if (keyType == CKK_DES3) {
1350 slidebits(keybuf + 8, (unsigned char *)deckey + 7);
1351 (void) memcpy(keybuf + 16, keybuf, 8);
1353 mechanism.pParameter = deckey + 8;
1354 } else {
1355 template[3].pValue = deckey;
1358 rv = C_CreateObject(dec_context->hSession, template,
1359 sizeof (template)/sizeof (template[0]), &dec_context->hKey);
1360 if (rv != CKR_OK) {
1361 #ifdef DEBUG
1362 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1363 "dec C_CreateObject: rv = 0x%.8X\n", rv);
1364 #endif
1365 (void) C_CloseSession(dec_context->hSession);
1366 text->utils->free(dec_context);
1367 return SASL_FAIL;
1369 text->cipher_dec_context = (cipher_context_t *)dec_context;
1371 /* Initialize the decryption operation in the session */
1372 rv = C_DecryptInit(dec_context->hSession, &mechanism, dec_context->hKey);
1373 if (rv != CKR_OK) {
1374 #ifdef DEBUG
1375 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1376 "C_DecryptInit: rv = 0x%.8X\n", rv);
1377 #endif
1378 return SASL_FAIL;
1381 return SASL_OK;
1384 static int init_rc4_uef(context_t *text,
1385 char enckey[16],
1386 char deckey[16])
1388 return init_uef(text, CKK_RC4, CKM_RC4, rc4_slot_id, enckey, deckey);
1391 static int init_des_uef(context_t *text,
1392 char enckey[16],
1393 char deckey[16])
1395 return init_uef(text, CKK_DES, CKM_DES_CBC, des_slot_id, enckey, deckey);
1398 static int init_3des_uef(context_t *text,
1399 char enckey[16],
1400 char deckey[16])
1402 return init_uef(text, CKK_DES3, CKM_DES3_CBC, des3_slot_id, enckey, deckey);
1405 static void
1406 free_uef(context_t *text)
1408 uef_context_t *enc_context =
1409 (uef_context_t *)text->cipher_enc_context;
1410 uef_context_t *dec_context =
1411 (uef_context_t *)text->cipher_dec_context;
1412 CK_RV rv;
1413 unsigned char buf[1];
1414 CK_ULONG ulLen = 0;
1417 if (enc_context != NULL) {
1418 rv = C_EncryptFinal(enc_context->hSession, buf, &ulLen);
1419 if (rv != CKR_OK) {
1420 #ifdef DEBUG
1421 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1422 "C_EncryptFinal failed:0x%.8X\n", rv);
1423 #endif
1425 rv = C_DestroyObject(enc_context->hSession, enc_context->hKey);
1426 if (rv != CKR_OK) {
1427 #ifdef DEBUG
1428 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1429 "C_DestroyObject failed:0x%.8X\n", rv);
1430 #endif
1432 rv = C_CloseSession(enc_context->hSession);
1433 if (rv != CKR_OK) {
1434 #ifdef DEBUG
1435 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1436 "C_CloseSession failed:0x%.8X\n", rv);
1437 #endif
1439 text->utils->free(enc_context);
1441 if (dec_context != NULL) {
1442 rv = C_DecryptFinal(dec_context->hSession, buf, &ulLen);
1443 if (rv != CKR_OK) {
1444 #ifdef DEBUG
1445 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1446 "C_DecryptFinal failed:0x%.8X\n", rv);
1447 #endif
1449 rv = C_DestroyObject(dec_context->hSession, dec_context->hKey);
1450 if (rv != CKR_OK) {
1451 #ifdef DEBUG
1452 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1453 "C_DestroyObject failed:0x%.8X\n", rv);
1454 #endif
1457 rv = C_CloseSession(dec_context->hSession);
1458 if (rv != CKR_OK) {
1459 #ifdef DEBUG
1460 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1461 "C_CloseSession failed:0x%.8X\n", rv);
1462 #endif
1464 text->utils->free(dec_context);
1466 text->cipher_enc_context = NULL;
1467 text->cipher_dec_context = NULL;
1470 static int
1471 dec_rc4_uef(context_t *text,
1472 const char *input,
1473 unsigned inputlen,
1474 unsigned char digest[16],
1475 char *output,
1476 unsigned *outputlen)
1478 CK_RV rv;
1479 uef_context_t *dec_context =
1480 (uef_context_t *)text->cipher_dec_context;
1481 CK_ULONG ulDataLen = *outputlen - MAC_SIZE;
1482 CK_ULONG ulDigestLen = MAC_SIZE;
1484 rv = C_DecryptUpdate(dec_context->hSession, (CK_BYTE_PTR)input,
1485 inputlen - MAC_SIZE, (CK_BYTE_PTR)output, &ulDataLen);
1486 if (rv != CKR_OK) {
1487 #ifdef DEBUG
1488 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1489 "C_DecryptUpdate failed:0x%.8X\n", rv);
1490 #endif
1491 return SASL_FAIL;
1493 *outputlen = (unsigned)ulDataLen;
1495 rv = C_DecryptUpdate(dec_context->hSession,
1496 (CK_BYTE_PTR)input+(inputlen-MAC_SIZE), MAC_SIZE, (CK_BYTE_PTR)digest,
1497 &ulDigestLen);
1498 if (rv != CKR_OK || ulDigestLen != MAC_SIZE) {
1499 #ifdef DEBUG
1500 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1501 "C_DecryptUpdate:0x%.8X, digestLen:%d\n",
1502 rv, ulDigestLen);
1503 #endif
1504 return SASL_FAIL;
1507 return SASL_OK;
1510 static int
1511 enc_rc4_uef(context_t *text,
1512 const char *input,
1513 unsigned inputlen,
1514 unsigned char digest[16],
1515 char *output,
1516 unsigned *outputlen)
1518 CK_RV rv;
1519 uef_context_t *enc_context =
1520 (uef_context_t *)text->cipher_enc_context;
1521 CK_ULONG ulDataLen = inputlen;
1522 CK_ULONG ulDigestLen = MAC_SIZE;
1524 rv = C_EncryptUpdate(enc_context->hSession, (CK_BYTE_PTR)input, inputlen,
1525 (CK_BYTE_PTR)output, &ulDataLen);
1526 if (rv != CKR_OK) {
1527 #ifdef DEBUG
1528 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1529 "C_EncryptUpdate failed: 0x%.8X "
1530 "inputlen:%d outputlen:%d\n",
1531 rv, inputlen, ulDataLen);
1532 #endif
1533 return SASL_FAIL;
1535 rv = C_EncryptUpdate(enc_context->hSession, (CK_BYTE_PTR)digest, MAC_SIZE,
1536 (CK_BYTE_PTR)output + inputlen, &ulDigestLen);
1537 if (rv != CKR_OK) {
1538 #ifdef DEBUG
1539 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1540 "C_EncryptUpdate failed: 0x%.8X ulDigestLen:%d\n",
1541 rv, ulDigestLen);
1542 #endif
1543 return SASL_FAIL;
1546 *outputlen = ulDataLen + ulDigestLen;
1548 return SASL_OK;
1551 static int
1552 dec_des_uef(context_t *text,
1553 const char *input,
1554 unsigned inputlen,
1555 unsigned char digest[16],
1556 char *output,
1557 unsigned *outputlen)
1559 CK_RV rv;
1560 uef_context_t *dec_context =
1561 (uef_context_t *)text->cipher_dec_context;
1562 CK_ULONG ulDataLen = inputlen;
1563 int padding, p;
1565 rv = C_DecryptUpdate(dec_context->hSession, (CK_BYTE_PTR)input,
1566 inputlen, (CK_BYTE_PTR)output, &ulDataLen);
1567 if (rv != CKR_OK) {
1568 #ifdef DEBUG
1569 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1570 "C_DecryptUpdate failed:0x%.8X\n", rv);
1571 #endif
1572 return SASL_FAIL;
1574 if (ulDataLen != inputlen) {
1575 #ifdef DEBUG
1576 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1577 "C_DecryptUpdate unexpected data len:%d !=%d\n",
1578 inputlen, ulDataLen);
1579 #endif
1580 return SASL_BUFOVER;
1583 /* now chop off the padding */
1584 padding = output[inputlen - 11];
1585 if (padding < 1 || padding > 8) {
1586 /* invalid padding length */
1587 return SASL_BADMAC;
1589 /* verify all padding is correct */
1590 for (p = 1; p <= padding; p++) {
1591 if (output[inputlen - MAC_SIZE - p] != padding) {
1592 return SASL_BADMAC;
1596 /* chop off the padding */
1597 *outputlen = inputlen - padding - MAC_SIZE;
1599 /* copy in the HMAC to digest */
1600 memcpy(digest, output + inputlen - MAC_SIZE, MAC_SIZE);
1602 return SASL_OK;
1605 static int
1606 enc_des_uef(context_t *text,
1607 const char *input,
1608 unsigned inputlen,
1609 unsigned char digest[16],
1610 char *output,
1611 unsigned *outputlen)
1613 CK_RV rv;
1614 uef_context_t *enc_context =
1615 (uef_context_t *)text->cipher_enc_context;
1616 CK_ULONG ulDataLen;
1617 int paddinglen;
1619 /* determine padding length */
1620 paddinglen = 8 - ((inputlen + MAC_SIZE) % 8);
1622 /* now construct the full stuff to be ciphered */
1623 memcpy(output, input, inputlen); /* text */
1624 memset(output+inputlen, paddinglen, paddinglen);/* pad */
1625 memcpy(output+inputlen+paddinglen, digest, MAC_SIZE); /* hmac */
1627 ulDataLen=inputlen+paddinglen+MAC_SIZE;
1629 rv = C_EncryptUpdate(enc_context->hSession, (CK_BYTE_PTR)output, ulDataLen,
1630 (CK_BYTE_PTR)output, &ulDataLen);
1631 if (rv != CKR_OK) {
1632 #ifdef DEBUG
1633 text->utils->log(text->utils->conn, SASL_LOG_DEBUG,
1634 "C_EncryptUpdate failed: 0x%.8X "
1635 "inputlen:%d outputlen:%d\n",
1636 rv, ulDataLen, ulDataLen);
1637 #endif
1638 return SASL_FAIL;
1640 *outputlen = (unsigned)ulDataLen;
1642 return SASL_OK;
1645 struct digest_cipher uef_ciphers[] =
1647 { "rc4-40", 40, 5, 0x01, &enc_rc4_uef, &dec_rc4_uef, &init_rc4_uef,
1648 &free_uef },
1649 { "rc4-56", 56, 7, 0x02, &enc_rc4_uef, &dec_rc4_uef, &init_rc4_uef,
1650 &free_uef },
1651 { "rc4", 128, 16, 0x04, &enc_rc4_uef, &dec_rc4_uef, &init_rc4_uef,
1652 &free_uef },
1653 { "des", 55, 16, 0x08, &enc_des_uef, &dec_des_uef, &init_des_uef,
1654 &free_uef },
1655 { "3des", 112, 16, 0x10, &enc_des_uef, &dec_des_uef, &init_3des_uef,
1656 &free_uef },
1657 { NULL, 0, 0, 0, NULL, NULL, NULL, NULL }
1660 struct digest_cipher *available_ciphers1 = uef_ciphers;
1661 #endif /* USE_UEF */
1663 static int create_layer_keys(context_t *text,
1664 const sasl_utils_t *utils,
1665 HASH key, int keylen,
1666 char enckey[16], char deckey[16])
1668 MD5_CTX Md5Ctx;
1670 utils->MD5Init(&Md5Ctx);
1671 utils->MD5Update(&Md5Ctx, key, keylen);
1672 if (text->i_am == SERVER) {
1673 utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_SERVER_CLIENT,
1674 strlen(SEALING_SERVER_CLIENT));
1675 } else {
1676 utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_CLIENT_SERVER,
1677 strlen(SEALING_CLIENT_SERVER));
1679 utils->MD5Final((unsigned char *) enckey, &Md5Ctx);
1681 utils->MD5Init(&Md5Ctx);
1682 utils->MD5Update(&Md5Ctx, key, keylen);
1683 if (text->i_am != SERVER) {
1684 utils->MD5Update(&Md5Ctx, (const unsigned char *)SEALING_SERVER_CLIENT,
1685 strlen(SEALING_SERVER_CLIENT));
1686 } else {
1687 utils->MD5Update(&Md5Ctx, (const unsigned char *)SEALING_CLIENT_SERVER,
1688 strlen(SEALING_CLIENT_SERVER));
1690 utils->MD5Final((unsigned char *) deckey, &Md5Ctx);
1692 /* create integrity keys */
1693 /* sending */
1694 utils->MD5Init(&Md5Ctx);
1695 utils->MD5Update(&Md5Ctx, text->HA1, HASHLEN);
1696 if (text->i_am == SERVER) {
1697 utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_SERVER_CLIENT,
1698 strlen(SIGNING_SERVER_CLIENT));
1699 } else {
1700 utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_CLIENT_SERVER,
1701 strlen(SIGNING_CLIENT_SERVER));
1703 utils->MD5Final(text->Ki_send, &Md5Ctx);
1705 /* receiving */
1706 utils->MD5Init(&Md5Ctx);
1707 utils->MD5Update(&Md5Ctx, text->HA1, HASHLEN);
1708 if (text->i_am != SERVER) {
1709 utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_SERVER_CLIENT,
1710 strlen(SIGNING_SERVER_CLIENT));
1711 } else {
1712 utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_CLIENT_SERVER,
1713 strlen(SIGNING_CLIENT_SERVER));
1715 utils->MD5Final(text->Ki_receive, &Md5Ctx);
1717 return SASL_OK;
1720 static const unsigned short version = 1;
1722 /* len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum */
1724 static int
1725 digestmd5_privacy_encode(void *context,
1726 const struct iovec *invec,
1727 unsigned numiov,
1728 const char **output,
1729 unsigned *outputlen)
1731 context_t *text = (context_t *) context;
1732 int tmp;
1733 unsigned int tmpnum;
1734 unsigned short int tmpshort;
1735 int ret;
1736 char *out;
1737 unsigned char digest[16];
1738 struct buffer_info *inblob, bufinfo;
1740 if(!context || !invec || !numiov || !output || !outputlen) {
1741 PARAMERROR(text->utils);
1742 return SASL_BADPARAM;
1745 if (numiov > 1) {
1746 ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf);
1747 if (ret != SASL_OK) return ret;
1748 inblob = text->enc_in_buf;
1749 } else {
1750 /* avoid the data copy */
1751 bufinfo.data = invec[0].iov_base;
1752 bufinfo.curlen = invec[0].iov_len;
1753 inblob = &bufinfo;
1756 /* make sure the output buffer is big enough for this blob */
1757 ret = _plug_buf_alloc(text->utils, &(text->encode_buf),
1758 &(text->encode_buf_len),
1759 (4 + /* for length */
1760 inblob->curlen + /* for content */
1761 10 + /* for MAC */
1762 8 + /* maximum pad */
1763 6 + /* for padding */
1764 1)); /* trailing null */
1765 if(ret != SASL_OK) return ret;
1767 /* skip by the length for now */
1768 out = (text->encode_buf)+4;
1770 /* construct (seqnum, msg) */
1771 /* We can just use the output buffer because it's big enough */
1772 tmpnum = htonl(text->seqnum);
1773 memcpy(text->encode_buf, &tmpnum, 4);
1774 memcpy(text->encode_buf + 4, inblob->data, inblob->curlen);
1776 /* HMAC(ki, (seqnum, msg) ) */
1777 text->utils->hmac_md5((const unsigned char *) text->encode_buf,
1778 inblob->curlen + 4,
1779 text->Ki_send, HASHLEN, digest);
1781 /* calculate the encrypted part */
1782 text->cipher_enc(text, inblob->data, inblob->curlen,
1783 digest, out, outputlen);
1784 out+=(*outputlen);
1786 /* copy in version */
1787 tmpshort = htons(version);
1788 memcpy(out, &tmpshort, 2); /* 2 bytes = version */
1790 out+=2;
1791 (*outputlen)+=2; /* for version */
1793 /* put in seqnum */
1794 tmpnum = htonl(text->seqnum);
1795 memcpy(out, &tmpnum, 4); /* 4 bytes = seq # */
1797 (*outputlen)+=4; /* for seqnum */
1799 /* put the 1st 4 bytes in */
1800 tmp=htonl(*outputlen);
1801 memcpy(text->encode_buf, &tmp, 4);
1803 (*outputlen)+=4;
1805 *output = text->encode_buf;
1806 text->seqnum++;
1808 return SASL_OK;
1811 static int
1812 digestmd5_privacy_decode_once(void *context,
1813 const char **input,
1814 unsigned *inputlen,
1815 char **output,
1816 unsigned *outputlen)
1818 context_t *text = (context_t *) context;
1819 unsigned int tocopy;
1820 unsigned diff;
1821 int result;
1822 unsigned char digest[16];
1823 int tmpnum;
1824 int lup;
1826 if (text->needsize>0) /* 4 bytes for how long message is */
1828 /* if less than 4 bytes just copy those we have into text->size */
1829 if (*inputlen<4)
1830 tocopy=*inputlen;
1831 else
1832 tocopy=4;
1834 if (tocopy>text->needsize)
1835 tocopy=text->needsize;
1837 memcpy(text->sizebuf+4-text->needsize, *input, tocopy);
1838 text->needsize-=tocopy;
1840 *input+=tocopy;
1841 *inputlen-=tocopy;
1843 if (text->needsize==0) /* got all of size */
1845 memcpy(&(text->size), text->sizebuf, 4);
1846 text->cursize=0;
1847 text->size=ntohl(text->size);
1849 if (text->size > text->in_maxbuf) {
1850 return SASL_FAIL; /* too big probably error */
1853 if(!text->buffer)
1854 text->buffer=text->utils->malloc(text->size+5);
1855 else
1856 text->buffer=text->utils->realloc(text->buffer,
1857 text->size+5);
1858 if (text->buffer == NULL) return SASL_NOMEM;
1861 *outputlen=0;
1862 *output=NULL;
1863 if (*inputlen==0) /* have to wait until next time for data */
1864 return SASL_OK;
1866 if (text->size==0) /* should never happen */
1867 return SASL_FAIL;
1870 diff=text->size - text->cursize; /* bytes need for full message */
1872 if (! text->buffer)
1873 return SASL_FAIL;
1875 if (*inputlen < diff) /* not enough for a decode */
1877 memcpy(text->buffer+text->cursize, *input, *inputlen);
1878 text->cursize+=*inputlen;
1879 *inputlen=0;
1880 *outputlen=0;
1881 *output=NULL;
1882 return SASL_OK;
1883 } else {
1884 memcpy(text->buffer+text->cursize, *input, diff);
1885 *input+=diff;
1886 *inputlen-=diff;
1890 unsigned short ver;
1891 unsigned int seqnum;
1892 unsigned char checkdigest[16];
1894 result = _plug_buf_alloc(text->utils, &text->decode_once_buf,
1895 &text->decode_once_buf_len,
1896 text->size-6);
1897 if (result != SASL_OK)
1898 return result;
1900 *output = text->decode_once_buf;
1901 *outputlen = *inputlen;
1903 result=text->cipher_dec(text,text->buffer,text->size-6,digest,
1904 *output, outputlen);
1906 if (result!=SASL_OK)
1907 return result;
1910 int i;
1911 for(i=10; i; i--) {
1912 memcpy(&ver, text->buffer+text->size-i,2);
1913 ver=ntohs(ver);
1917 /* check the version number */
1918 memcpy(&ver, text->buffer+text->size-6, 2);
1919 ver=ntohs(ver);
1920 if (ver != version)
1922 #ifdef _INTEGRATED_SOLARIS_
1923 text->utils->seterror(text->utils->conn, 0,
1924 gettext("Wrong Version"));
1925 #else
1926 text->utils->seterror(text->utils->conn, 0, "Wrong Version");
1927 #endif /* _INTEGRATED_SOLARIS_ */
1928 return SASL_FAIL;
1931 /* check the CMAC */
1933 /* construct (seqnum, msg) */
1934 result = _plug_buf_alloc(text->utils, &text->decode_tmp_buf,
1935 &text->decode_tmp_buf_len, *outputlen + 4);
1936 if(result != SASL_OK) return result;
1938 tmpnum = htonl(text->rec_seqnum);
1939 memcpy(text->decode_tmp_buf, &tmpnum, 4);
1940 memcpy(text->decode_tmp_buf + 4, *output, *outputlen);
1942 /* HMAC(ki, (seqnum, msg) ) */
1943 text->utils->hmac_md5((const unsigned char *) text->decode_tmp_buf,
1944 (*outputlen) + 4,
1945 text->Ki_receive, HASHLEN, checkdigest);
1947 /* now check it */
1948 for (lup=0;lup<10;lup++)
1949 if (checkdigest[lup]!=digest[lup])
1951 #ifdef _SUN_SDK_
1952 text->utils->log(text->utils->conn, SASL_LOG_ERR,
1953 "CMAC doesn't match at byte %d!", lup);
1954 return SASL_BADMAC;
1955 #else
1956 text->utils->seterror(text->utils->conn, 0,
1957 "CMAC doesn't match at byte %d!", lup);
1958 return SASL_FAIL;
1959 #endif /* _SUN_SDK_ */
1962 /* check the sequence number */
1963 memcpy(&seqnum, text->buffer+text->size-4,4);
1964 seqnum=ntohl(seqnum);
1966 if (seqnum!=text->rec_seqnum)
1968 #ifdef _SUN_SDK_
1969 text->utils->log(text->utils->conn, SASL_LOG_ERR,
1970 "Incorrect Sequence Number");
1971 #else
1972 text->utils->seterror(text->utils->conn, 0,
1973 "Incorrect Sequence Number");
1974 #endif /* _SUN_SDK_ */
1975 return SASL_FAIL;
1978 text->rec_seqnum++; /* now increment it */
1981 text->needsize=4;
1983 return SASL_OK;
1986 static int digestmd5_privacy_decode(void *context,
1987 const char *input, unsigned inputlen,
1988 const char **output, unsigned *outputlen)
1990 context_t *text = (context_t *) context;
1991 int ret;
1993 ret = _plug_decode(text->utils, context, input, inputlen,
1994 &text->decode_buf, &text->decode_buf_len, outputlen,
1995 digestmd5_privacy_decode_once);
1997 *output = text->decode_buf;
1999 return ret;
2002 static int
2003 digestmd5_integrity_encode(void *context,
2004 const struct iovec *invec,
2005 unsigned numiov,
2006 const char **output,
2007 unsigned *outputlen)
2009 context_t *text = (context_t *) context;
2010 unsigned char MAC[16];
2011 unsigned int tmpnum;
2012 unsigned short int tmpshort;
2013 struct buffer_info *inblob, bufinfo;
2014 int ret;
2016 if(!context || !invec || !numiov || !output || !outputlen) {
2017 PARAMERROR( text->utils );
2018 return SASL_BADPARAM;
2021 if (numiov > 1) {
2022 ret = _plug_iovec_to_buf(text->utils, invec, numiov,
2023 &text->enc_in_buf);
2024 if (ret != SASL_OK) return ret;
2025 inblob = text->enc_in_buf;
2026 } else {
2027 /* avoid the data copy */
2028 bufinfo.data = invec[0].iov_base;
2029 bufinfo.curlen = invec[0].iov_len;
2030 inblob = &bufinfo;
2033 /* construct output */
2034 *outputlen = 4 + inblob->curlen + 16;
2036 ret = _plug_buf_alloc(text->utils, &(text->encode_buf),
2037 &(text->encode_buf_len), *outputlen);
2038 if(ret != SASL_OK) return ret;
2040 /* construct (seqnum, msg) */
2041 /* we can just use the output buffer */
2042 tmpnum = htonl(text->seqnum);
2043 memcpy(text->encode_buf, &tmpnum, 4);
2044 memcpy(text->encode_buf + 4, inblob->data, inblob->curlen);
2046 /* HMAC(ki, (seqnum, msg) ) */
2047 #ifdef _SUN_SDK_
2048 text->utils->hmac_md5((unsigned char *)text->encode_buf,
2049 inblob->curlen + 4,
2050 text->Ki_send, HASHLEN, MAC);
2051 #else
2052 text->utils->hmac_md5(text->encode_buf, inblob->curlen + 4,
2053 text->Ki_send, HASHLEN, MAC);
2054 #endif /* _SUN_SDK_ */
2056 /* create MAC */
2057 tmpshort = htons(version);
2058 memcpy(MAC + 10, &tmpshort, MAC_OFFS); /* 2 bytes = version */
2060 tmpnum = htonl(text->seqnum);
2061 memcpy(MAC + 12, &tmpnum, 4); /* 4 bytes = sequence number */
2063 /* copy into output */
2064 tmpnum = htonl((*outputlen) - 4);
2066 /* length of message in network byte order */
2067 memcpy(text->encode_buf, &tmpnum, 4);
2068 /* the message text */
2069 memcpy(text->encode_buf + 4, inblob->data, inblob->curlen);
2070 /* the MAC */
2071 memcpy(text->encode_buf + 4 + inblob->curlen, MAC, 16);
2073 text->seqnum++; /* add one to sequence number */
2075 *output = text->encode_buf;
2077 return SASL_OK;
2080 static int
2081 create_MAC(context_t * text,
2082 char *input,
2083 int inputlen,
2084 int seqnum,
2085 unsigned char MAC[16])
2087 unsigned int tmpnum;
2088 unsigned short int tmpshort;
2089 int ret;
2091 if (inputlen < 0)
2092 return SASL_FAIL;
2094 ret = _plug_buf_alloc(text->utils, &(text->MAC_buf),
2095 &(text->MAC_buf_len), inputlen + 4);
2096 if(ret != SASL_OK) return ret;
2098 /* construct (seqnum, msg) */
2099 tmpnum = htonl(seqnum);
2100 memcpy(text->MAC_buf, &tmpnum, 4);
2101 memcpy(text->MAC_buf + 4, input, inputlen);
2103 /* HMAC(ki, (seqnum, msg) ) */
2104 #ifdef _SUN_SDK_
2105 text->utils->hmac_md5((unsigned char *)text->MAC_buf, inputlen + 4,
2106 text->Ki_receive, HASHLEN,
2107 MAC);
2108 #else
2109 text->utils->hmac_md5(text->MAC_buf, inputlen + 4,
2110 text->Ki_receive, HASHLEN,
2111 MAC);
2112 #endif /* _SUN_SDK_ */
2114 /* create MAC */
2115 tmpshort = htons(version);
2116 memcpy(MAC + 10, &tmpshort, 2); /* 2 bytes = version */
2118 tmpnum = htonl(seqnum);
2119 memcpy(MAC + 12, &tmpnum, 4); /* 4 bytes = sequence number */
2121 return SASL_OK;
2124 static int
2125 check_integrity(context_t * text,
2126 char *buf, int bufsize,
2127 char **output, unsigned *outputlen)
2129 unsigned char MAC[16];
2130 int result;
2132 result = create_MAC(text, buf, bufsize - 16, text->rec_seqnum, MAC);
2133 if (result != SASL_OK)
2134 return result;
2136 /* make sure the MAC is right */
2137 if (strncmp((char *) MAC, buf + bufsize - 16, 16) != 0)
2139 #ifdef _SUN_SDK_
2140 text->utils->log(text->utils->conn, SASL_LOG_ERR,
2141 "MAC doesn't match");
2142 return SASL_BADMAC;
2143 #else
2144 text->utils->seterror(text->utils->conn, 0, "MAC doesn't match");
2145 return SASL_FAIL;
2146 #endif /* _SUN_SDK_ */
2149 text->rec_seqnum++;
2151 /* ok make output message */
2152 result = _plug_buf_alloc(text->utils, &text->decode_once_buf,
2153 &text->decode_once_buf_len,
2154 bufsize - 15);
2155 if (result != SASL_OK)
2156 return result;
2158 *output = text->decode_once_buf;
2159 memcpy(*output, buf, bufsize - 16);
2160 *outputlen = bufsize - 16;
2161 (*output)[*outputlen] = 0;
2163 return SASL_OK;
2166 static int
2167 digestmd5_integrity_decode_once(void *context,
2168 const char **input,
2169 unsigned *inputlen,
2170 char **output,
2171 unsigned *outputlen)
2173 context_t *text = (context_t *) context;
2174 unsigned int tocopy;
2175 unsigned diff;
2176 int result;
2178 if (text->needsize > 0) { /* 4 bytes for how long message is */
2180 * if less than 4 bytes just copy those we have into text->size
2182 if (*inputlen < 4)
2183 tocopy = *inputlen;
2184 else
2185 tocopy = 4;
2187 if (tocopy > text->needsize)
2188 tocopy = text->needsize;
2190 memcpy(text->sizebuf + 4 - text->needsize, *input, tocopy);
2191 text->needsize -= tocopy;
2193 *input += tocopy;
2194 *inputlen -= tocopy;
2196 if (text->needsize == 0) { /* got all of size */
2197 memcpy(&(text->size), text->sizebuf, 4);
2198 text->cursize = 0;
2199 text->size = ntohl(text->size);
2201 if (text->size > text->in_maxbuf)
2202 return SASL_FAIL; /* too big probably error */
2204 if(!text->buffer)
2205 text->buffer=text->utils->malloc(text->size+5);
2206 else
2207 text->buffer=text->utils->realloc(text->buffer,text->size+5);
2208 if (text->buffer == NULL) return SASL_NOMEM;
2210 *outputlen = 0;
2211 *output = NULL;
2212 if (*inputlen == 0) /* have to wait until next time for data */
2213 return SASL_OK;
2215 if (text->size == 0) /* should never happen */
2216 return SASL_FAIL;
2218 diff = text->size - text->cursize; /* bytes need for full message */
2220 if(! text->buffer)
2221 return SASL_FAIL;
2223 if (*inputlen < diff) { /* not enough for a decode */
2224 memcpy(text->buffer + text->cursize, *input, *inputlen);
2225 text->cursize += *inputlen;
2226 *inputlen = 0;
2227 *outputlen = 0;
2228 *output = NULL;
2229 return SASL_OK;
2230 } else {
2231 memcpy(text->buffer + text->cursize, *input, diff);
2232 *input += diff;
2233 *inputlen -= diff;
2236 result = check_integrity(text, text->buffer, text->size,
2237 output, outputlen);
2238 if (result != SASL_OK)
2239 return result;
2241 /* Reset State */
2242 text->needsize = 4;
2244 return SASL_OK;
2247 static int digestmd5_integrity_decode(void *context,
2248 const char *input, unsigned inputlen,
2249 const char **output, unsigned *outputlen)
2251 context_t *text = (context_t *) context;
2252 int ret;
2254 ret = _plug_decode(text->utils, context, input, inputlen,
2255 &text->decode_buf, &text->decode_buf_len, outputlen,
2256 digestmd5_integrity_decode_once);
2258 *output = text->decode_buf;
2260 return ret;
2263 static void
2264 digestmd5_common_mech_dispose(void *conn_context, const sasl_utils_t *utils)
2266 context_t *text = (context_t *) conn_context;
2268 if (!text || !utils) return;
2270 if (text->authid) utils->free(text->authid);
2271 if (text->realm) utils->free(text->realm);
2272 if (text->nonce) utils->free(text->nonce);
2273 if (text->cnonce) utils->free(text->cnonce);
2275 if (text->cipher_free) text->cipher_free(text);
2277 /* free the stuff in the context */
2278 if (text->response_value) utils->free(text->response_value);
2280 if (text->buffer) utils->free(text->buffer);
2281 if (text->encode_buf) utils->free(text->encode_buf);
2282 if (text->decode_buf) utils->free(text->decode_buf);
2283 if (text->decode_once_buf) utils->free(text->decode_once_buf);
2284 if (text->decode_tmp_buf) utils->free(text->decode_tmp_buf);
2285 if (text->out_buf) utils->free(text->out_buf);
2286 if (text->MAC_buf) utils->free(text->MAC_buf);
2288 if (text->enc_in_buf) {
2289 if (text->enc_in_buf->data) utils->free(text->enc_in_buf->data);
2290 utils->free(text->enc_in_buf);
2293 utils->free(conn_context);
2296 static void
2297 clear_reauth_entry(reauth_entry_t *reauth, enum Context_type type,
2298 const sasl_utils_t *utils)
2300 if (!reauth) return;
2302 if (reauth->authid) utils->free(reauth->authid);
2303 if (reauth->realm) utils->free(reauth->realm);
2304 if (reauth->nonce) utils->free(reauth->nonce);
2305 if (reauth->cnonce) utils->free(reauth->cnonce);
2307 if (type == CLIENT) {
2308 if (reauth->u.c.serverFQDN) utils->free(reauth->u.c.serverFQDN);
2311 memset(reauth, 0, sizeof(reauth_entry_t));
2314 static void
2315 digestmd5_common_mech_free(void *glob_context, const sasl_utils_t *utils)
2317 reauth_cache_t *reauth_cache = (reauth_cache_t *) glob_context;
2318 size_t n;
2320 if (!reauth_cache) return;
2322 for (n = 0; n < reauth_cache->size; n++)
2323 clear_reauth_entry(&reauth_cache->e[n], reauth_cache->i_am, utils);
2324 if (reauth_cache->e) utils->free(reauth_cache->e);
2326 if (reauth_cache->mutex) utils->mutex_free(reauth_cache->mutex);
2328 utils->free(reauth_cache);
2331 /***************************** Server Section *****************************/
2333 typedef struct server_context {
2334 context_t common;
2336 time_t timestamp;
2337 int stale; /* last nonce is stale */
2338 sasl_ssf_t limitssf, requiressf; /* application defined bounds */
2339 } server_context_t;
2341 static void
2342 DigestCalcHA1FromSecret(context_t * text,
2343 const sasl_utils_t * utils,
2344 HASH HA1,
2345 unsigned char *authorization_id,
2346 unsigned char *pszNonce,
2347 unsigned char *pszCNonce,
2348 HASHHEX SessionKey)
2350 MD5_CTX Md5Ctx;
2352 /* calculate session key */
2353 utils->MD5Init(&Md5Ctx);
2354 utils->MD5Update(&Md5Ctx, HA1, HASHLEN);
2355 utils->MD5Update(&Md5Ctx, COLON, 1);
2356 utils->MD5Update(&Md5Ctx, pszNonce, strlen((char *) pszNonce));
2357 utils->MD5Update(&Md5Ctx, COLON, 1);
2358 utils->MD5Update(&Md5Ctx, pszCNonce, strlen((char *) pszCNonce));
2359 if (authorization_id != NULL) {
2360 utils->MD5Update(&Md5Ctx, COLON, 1);
2361 utils->MD5Update(&Md5Ctx, authorization_id, strlen((char *) authorization_id));
2363 utils->MD5Final(HA1, &Md5Ctx);
2365 CvtHex(HA1, SessionKey);
2368 /* save HA1 because we need it to make the privacy and integrity keys */
2369 memcpy(text->HA1, HA1, sizeof(HASH));
2372 static char *create_response(context_t * text,
2373 const sasl_utils_t * utils,
2374 unsigned char *nonce,
2375 unsigned int ncvalue,
2376 unsigned char *cnonce,
2377 char *qop,
2378 char *digesturi,
2379 HASH Secret,
2380 char *authorization_id,
2381 char **response_value)
2383 HASHHEX SessionKey;
2384 HASHHEX HEntity = "00000000000000000000000000000000";
2385 HASHHEX Response;
2386 char *result;
2388 if (qop == NULL)
2389 qop = "auth";
2391 DigestCalcHA1FromSecret(text,
2392 utils,
2393 Secret,
2394 (unsigned char *) authorization_id,
2395 nonce,
2396 cnonce,
2397 SessionKey);
2399 DigestCalcResponse(utils,
2400 SessionKey,/* H(A1) */
2401 nonce, /* nonce from server */
2402 ncvalue, /* 8 hex digits */
2403 cnonce, /* client nonce */
2404 (unsigned char *) qop, /* qop-value: "", "auth",
2405 * "auth-int" */
2406 (unsigned char *) digesturi, /* requested URL */
2407 (unsigned char *) "AUTHENTICATE",
2408 HEntity, /* H(entity body) if qop="auth-int" */
2409 Response /* request-digest or response-digest */
2412 result = utils->malloc(HASHHEXLEN + 1);
2413 #ifdef _SUN_SDK_
2414 if (result == NULL)
2415 return NULL;
2416 #endif /* _SUN_SDK_ */
2417 /* TODO */
2418 memcpy(result, Response, HASHHEXLEN);
2419 result[HASHHEXLEN] = 0;
2421 /* response_value (used for reauth i think */
2422 if (response_value != NULL) {
2423 DigestCalcResponse(utils,
2424 SessionKey, /* H(A1) */
2425 nonce, /* nonce from server */
2426 ncvalue, /* 8 hex digits */
2427 cnonce, /* client nonce */
2428 (unsigned char *) qop, /* qop-value: "", "auth",
2429 * "auth-int" */
2430 (unsigned char *) digesturi, /* requested URL */
2431 NULL,
2432 HEntity, /* H(entity body) if qop="auth-int" */
2433 Response /* request-digest or response-digest */
2436 *response_value = utils->malloc(HASHHEXLEN + 1);
2437 if (*response_value == NULL)
2438 return NULL;
2439 memcpy(*response_value, Response, HASHHEXLEN);
2440 (*response_value)[HASHHEXLEN] = 0;
2442 return result;
2445 static int
2446 get_server_realm(sasl_server_params_t * params,
2447 char **realm)
2449 /* look at user realm first */
2450 if (params->user_realm != NULL) {
2451 if(params->user_realm[0] != '\0') {
2452 *realm = (char *) params->user_realm;
2453 } else {
2454 /* Catch improperly converted apps */
2455 #ifdef _SUN_SDK_
2456 params->utils->log(params->utils->conn, SASL_LOG_ERR,
2457 "user_realm is an empty string!");
2458 #else
2459 params->utils->seterror(params->utils->conn, 0,
2460 "user_realm is an empty string!");
2461 #endif /* _SUN_SDK_ */
2462 return SASL_BADPARAM;
2464 } else if (params->serverFQDN != NULL) {
2465 *realm = (char *) params->serverFQDN;
2466 } else {
2467 #ifdef _SUN_SDK_
2468 params->utils->log(params->utils->conn, SASL_LOG_ERR,
2469 "no way to obtain domain");
2470 #else
2471 params->utils->seterror(params->utils->conn, 0,
2472 "no way to obtain domain");
2473 #endif /* _SUN_SDK_ */
2474 return SASL_FAIL;
2477 return SASL_OK;
2481 * Convert hex string to int
2483 static int htoi(unsigned char *hexin, unsigned int *res)
2485 int lup, inlen;
2486 inlen = strlen((char *) hexin);
2488 *res = 0;
2489 for (lup = 0; lup < inlen; lup++) {
2490 switch (hexin[lup]) {
2491 case '0':
2492 case '1':
2493 case '2':
2494 case '3':
2495 case '4':
2496 case '5':
2497 case '6':
2498 case '7':
2499 case '8':
2500 case '9':
2501 *res = (*res << 4) + (hexin[lup] - '0');
2502 break;
2504 case 'a':
2505 case 'b':
2506 case 'c':
2507 case 'd':
2508 case 'e':
2509 case 'f':
2510 *res = (*res << 4) + (hexin[lup] - 'a' + 10);
2511 break;
2513 case 'A':
2514 case 'B':
2515 case 'C':
2516 case 'D':
2517 case 'E':
2518 case 'F':
2519 *res = (*res << 4) + (hexin[lup] - 'A' + 10);
2520 break;
2522 default:
2523 return SASL_BADPARAM;
2528 return SASL_OK;
2531 static int digestmd5_server_mech_new(void *glob_context,
2532 sasl_server_params_t * sparams,
2533 const char *challenge __attribute__((unused)),
2534 unsigned challen __attribute__((unused)),
2535 void **conn_context)
2537 context_t *text;
2539 /* holds state are in -- allocate server size */
2540 text = sparams->utils->malloc(sizeof(server_context_t));
2541 if (text == NULL)
2542 return SASL_NOMEM;
2543 memset(text, 0, sizeof(server_context_t));
2545 text->state = 1;
2546 text->i_am = SERVER;
2547 text->reauth = glob_context;
2549 *conn_context = text;
2550 return SASL_OK;
2553 static int
2554 digestmd5_server_mech_step1(server_context_t *stext,
2555 sasl_server_params_t *sparams,
2556 const char *clientin __attribute__((unused)),
2557 unsigned clientinlen __attribute__((unused)),
2558 const char **serverout,
2559 unsigned *serveroutlen,
2560 sasl_out_params_t * oparams __attribute__((unused)))
2562 context_t *text = (context_t *) stext;
2563 int result;
2564 char *realm;
2565 unsigned char *nonce;
2566 char *charset = "utf-8";
2567 char qop[1024], cipheropts[1024];
2568 struct digest_cipher *cipher;
2569 unsigned resplen;
2570 int added_conf = 0;
2571 char maxbufstr[64];
2573 sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
2574 "DIGEST-MD5 server step 1");
2576 /* get realm */
2577 result = get_server_realm(sparams, &realm);
2578 if(result != SASL_OK) return result;
2580 /* what options should we offer the client? */
2581 qop[0] = '\0';
2582 cipheropts[0] = '\0';
2583 if (stext->requiressf == 0) {
2584 if (*qop) strcat(qop, ",");
2585 strcat(qop, "auth");
2587 if (stext->requiressf <= 1 && stext->limitssf >= 1) {
2588 if (*qop) strcat(qop, ",");
2589 strcat(qop, "auth-int");
2592 #ifdef USE_UEF_SERVER
2593 cipher = available_ciphers1;
2594 #else
2595 cipher = available_ciphers;
2596 #endif
2597 while (cipher->name) {
2598 /* do we allow this particular cipher? */
2599 if (stext->requiressf <= cipher->ssf &&
2600 stext->limitssf >= cipher->ssf) {
2601 if (!added_conf) {
2602 if (*qop) strcat(qop, ",");
2603 strcat(qop, "auth-conf");
2604 added_conf = 1;
2606 #ifdef _SUN_SDK_
2607 if(strlen(cipheropts) + strlen(cipher->name) + 1 >=
2608 sizeof (cipheropts)) {
2609 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2610 "internal error: cipheropts too big");
2611 return SASL_FAIL;
2613 #endif /* _SUN_SDK_ */
2614 if (*cipheropts) strcat(cipheropts, ",");
2615 strcat(cipheropts, cipher->name);
2617 cipher++;
2620 if (*qop == '\0') {
2621 /* we didn't allow anything?!? we'll return SASL_TOOWEAK, since
2622 that's close enough */
2623 return SASL_TOOWEAK;
2627 * digest-challenge = 1#( realm | nonce | qop-options | stale | maxbuf |
2628 * charset | cipher-opts | auth-param )
2631 #ifndef _SUN_SDK_
2632 /* FIXME: get nonce XXX have to clean up after self if fail */
2633 #endif /* !_SUN_SDK_ */
2634 nonce = create_nonce(sparams->utils);
2635 if (nonce == NULL) {
2636 #ifdef _SUN_SDK_
2637 /* Note typo below */
2638 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2639 "internal error: failed creating a nonce");
2640 #else
2641 SETERROR(sparams->utils, "internal erorr: failed creating a nonce");
2642 #endif /* _SUN_SDK_ */
2643 return SASL_FAIL;
2646 #ifdef _SUN_SDK_
2647 resplen = strlen((char *)nonce) + strlen("nonce") + 5;
2648 #else
2649 resplen = strlen(nonce) + strlen("nonce") + 5;
2650 #endif /* _SUN_SDK_ */
2651 result = _plug_buf_alloc(sparams->utils, &(text->out_buf),
2652 &(text->out_buf_len), resplen);
2653 #ifdef _SUN_SDK_
2654 if(result != SASL_OK) {
2655 sparams->utils->free(nonce);
2656 return result;
2658 #else
2659 if(result != SASL_OK) return result;
2660 #endif /* _SUN_SDK_ */
2662 sprintf(text->out_buf, "nonce=\"%s\"", nonce);
2664 /* add to challenge; if we chose not to specify a realm, we won't
2665 * send one to the client */
2666 if (realm && add_to_challenge(sparams->utils,
2667 &text->out_buf, &text->out_buf_len, &resplen,
2668 "realm", (unsigned char *) realm,
2669 TRUE) != SASL_OK) {
2670 #ifdef _SUN_SDK_
2671 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2672 "internal error: add_to_challenge failed");
2673 sparams->utils->free(nonce);
2674 #else
2675 SETERROR(sparams->utils, "internal error: add_to_challenge failed");
2676 #endif /* _SUN_SDK_ */
2677 return SASL_FAIL;
2680 * qop-options A quoted string of one or more tokens indicating the
2681 * "quality of protection" values supported by the server. The value
2682 * "auth" indicates authentication; the value "auth-int" indicates
2683 * authentication with integrity protection; the value "auth-conf"
2684 * indicates authentication with integrity protection and encryption.
2687 /* add qop to challenge */
2688 if (add_to_challenge(sparams->utils,
2689 &text->out_buf, &text->out_buf_len, &resplen,
2690 "qop",
2691 (unsigned char *) qop, TRUE) != SASL_OK) {
2692 #ifdef _SUN_SDK_
2693 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2694 "internal error: add_to_challenge 3 failed");
2695 sparams->utils->free(nonce);
2696 #else
2697 SETERROR(sparams->utils, "internal error: add_to_challenge 3 failed");
2698 #endif /* _SUN_SDK_ */
2699 return SASL_FAIL;
2703 * Cipheropts - list of ciphers server supports
2705 /* add cipher-opts to challenge; only add if there are some */
2706 if (strcmp(cipheropts,"")!=0)
2708 if (add_to_challenge(sparams->utils,
2709 &text->out_buf, &text->out_buf_len, &resplen,
2710 "cipher", (unsigned char *) cipheropts,
2711 TRUE) != SASL_OK) {
2712 #ifdef _SUN_SDK_
2713 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2714 "internal error: add_to_challenge 4 failed");
2715 sparams->utils->free(nonce);
2716 #else
2717 SETERROR(sparams->utils,
2718 "internal error: add_to_challenge 4 failed");
2719 #endif /* _SUN_SDK_ */
2720 return SASL_FAIL;
2724 /* "stale" is true if a reauth failed because of a nonce timeout */
2725 if (stext->stale &&
2726 add_to_challenge(sparams->utils,
2727 &text->out_buf, &text->out_buf_len, &resplen,
2728 #ifdef _SUN_SDK_
2729 "stale", (unsigned char *)"true", FALSE) != SASL_OK) {
2730 sparams->utils->free(nonce);
2731 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2732 "internal error: add_to_challenge failed");
2733 #else
2734 "stale", "true", FALSE) != SASL_OK) {
2735 SETERROR(sparams->utils, "internal error: add_to_challenge failed");
2736 #endif /* _SUN_SDK_ */
2737 return SASL_FAIL;
2741 * maxbuf A number indicating the size of the largest buffer the server
2742 * is able to receive when using "auth-int". If this directive is
2743 * missing, the default value is 65536. This directive may appear at most
2744 * once; if multiple instances are present, the client should abort the
2745 * authentication exchange.
2747 if(sparams->props.maxbufsize) {
2748 snprintf(maxbufstr, sizeof(maxbufstr), "%d",
2749 sparams->props.maxbufsize);
2750 if (add_to_challenge(sparams->utils,
2751 &text->out_buf, &text->out_buf_len, &resplen,
2752 "maxbuf",
2753 (unsigned char *) maxbufstr, FALSE) != SASL_OK) {
2754 #ifdef _SUN_SDK_
2755 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2756 "internal error: add_to_challenge 5 failed");
2757 #else
2758 SETERROR(sparams->utils,
2759 "internal error: add_to_challenge 5 failed");
2760 #endif /* _SUN_SDK_ */
2761 return SASL_FAIL;
2766 if (add_to_challenge(sparams->utils,
2767 &text->out_buf, &text->out_buf_len, &resplen,
2768 "charset",
2769 (unsigned char *) charset, FALSE) != SASL_OK) {
2770 #ifdef _SUN_SDK_
2771 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2772 "internal error: add_to_challenge 6 failed");
2773 sparams->utils->free(nonce);
2774 #else
2775 SETERROR(sparams->utils, "internal error: add_to_challenge 6 failed");
2776 #endif /* _SUN_SDK_ */
2777 return SASL_FAIL;
2782 * algorithm
2783 * This directive is required for backwards compatibility with HTTP
2784 * Digest., which supports other algorithms. . This directive is
2785 * required and MUST appear exactly once; if not present, or if multiple
2786 * instances are present, the client should abort the authentication
2787 * exchange.
2789 * algorithm = "algorithm" "=" "md5-sess"
2792 if (add_to_challenge(sparams->utils,
2793 &text->out_buf, &text->out_buf_len, &resplen,
2794 "algorithm",
2795 (unsigned char *) "md5-sess", FALSE)!=SASL_OK) {
2796 #ifdef _SUN_SDK_
2797 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2798 "internal error: add_to_challenge 7 failed");
2799 sparams->utils->free(nonce);
2800 #else
2801 SETERROR(sparams->utils, "internal error: add_to_challenge 7 failed");
2802 #endif /* _SUN_SDK_ */
2803 return SASL_FAIL;
2807 * The size of a digest-challenge MUST be less than 2048 bytes!!!
2809 if (*serveroutlen > 2048) {
2810 #ifdef _SUN_SDK_
2811 sparams->utils->free(nonce);
2812 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2813 "internal error: challenge larger than 2048 bytes");
2814 #else
2815 SETERROR(sparams->utils,
2816 "internal error: challenge larger than 2048 bytes");
2817 #endif /* _SUN_SDK_ */
2818 return SASL_FAIL;
2821 text->authid = NULL;
2822 _plug_strdup(sparams->utils, realm, &text->realm, NULL);
2823 text->nonce = nonce;
2824 text->nonce_count = 1;
2825 text->cnonce = NULL;
2826 stext->timestamp = time(0);
2828 *serveroutlen = strlen(text->out_buf);
2829 *serverout = text->out_buf;
2831 text->state = 2;
2833 return SASL_CONTINUE;
2836 static int
2837 digestmd5_server_mech_step2(server_context_t *stext,
2838 sasl_server_params_t *sparams,
2839 const char *clientin,
2840 unsigned clientinlen,
2841 const char **serverout,
2842 unsigned *serveroutlen,
2843 sasl_out_params_t * oparams)
2845 context_t *text = (context_t *) stext;
2846 /* verify digest */
2847 sasl_secret_t *sec = NULL;
2848 int result;
2849 char *serverresponse = NULL;
2850 char *username = NULL;
2851 char *authorization_id = NULL;
2852 char *realm = NULL;
2853 unsigned char *nonce = NULL, *cnonce = NULL;
2854 unsigned int noncecount = 0;
2855 char *qop = NULL;
2856 char *digesturi = NULL;
2857 char *response = NULL;
2859 /* setting the default value (65536) */
2860 unsigned int client_maxbuf = 65536;
2861 int maxbuf_count = 0; /* How many maxbuf instaces was found */
2863 char *charset = NULL;
2864 char *cipher = NULL;
2865 unsigned int n=0;
2867 HASH A1;
2869 /* password prop_request */
2870 const char *password_request[] = { SASL_AUX_PASSWORD,
2871 "*cmusaslsecretDIGEST-MD5",
2872 NULL };
2873 unsigned len;
2874 struct propval auxprop_values[2];
2876 /* can we mess with clientin? copy it to be safe */
2877 char *in_start = NULL;
2878 char *in = NULL;
2880 sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
2881 "DIGEST-MD5 server step 2");
2883 in = sparams->utils->malloc(clientinlen + 1);
2884 #ifdef _SUN_SDK_
2885 if (!in) return SASL_NOMEM;
2886 #endif /* _SUN_SDK_ */
2888 memcpy(in, clientin, clientinlen);
2889 in[clientinlen] = 0;
2891 in_start = in;
2894 /* parse what we got */
2895 while (in[0] != '\0') {
2896 char *name = NULL, *value = NULL;
2897 get_pair(&in, &name, &value);
2899 if (name == NULL)
2900 break;
2902 /* Extracting parameters */
2905 * digest-response = 1#( username | realm | nonce | cnonce |
2906 * nonce-count | qop | digest-uri | response | maxbuf | charset |
2907 * cipher | auth-param )
2910 if (strcasecmp(name, "username") == 0) {
2911 _plug_strdup(sparams->utils, value, &username, NULL);
2912 } else if (strcasecmp(name, "authzid") == 0) {
2913 _plug_strdup(sparams->utils, value, &authorization_id, NULL);
2914 } else if (strcasecmp(name, "cnonce") == 0) {
2915 _plug_strdup(sparams->utils, value, (char **) &cnonce, NULL);
2916 } else if (strcasecmp(name, "nc") == 0) {
2917 if (htoi((unsigned char *) value, &noncecount) != SASL_OK) {
2918 #ifdef _SUN_SDK_
2919 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2920 "error converting hex to int");
2921 #else
2922 SETERROR(sparams->utils,
2923 "error converting hex to int");
2924 #endif /* _SUN_SDK_ */
2925 result = SASL_BADAUTH;
2926 goto FreeAllMem;
2928 } else if (strcasecmp(name, "realm") == 0) {
2929 if (realm) {
2930 #ifdef _SUN_SDK_
2931 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2932 "duplicate realm: authentication aborted");
2933 #else
2934 SETERROR(sparams->utils,
2935 "duplicate realm: authentication aborted");
2936 #endif /* _SUN_SDK_ */
2937 result = SASL_FAIL;
2938 goto FreeAllMem;
2940 _plug_strdup(sparams->utils, value, &realm, NULL);
2941 } else if (strcasecmp(name, "nonce") == 0) {
2942 _plug_strdup(sparams->utils, value, (char **) &nonce, NULL);
2943 } else if (strcasecmp(name, "qop") == 0) {
2944 _plug_strdup(sparams->utils, value, &qop, NULL);
2945 } else if (strcasecmp(name, "digest-uri") == 0) {
2946 size_t service_len;
2949 * digest-uri-value = serv-type "/" host [ "/" serv-name ]
2952 _plug_strdup(sparams->utils, value, &digesturi, NULL);
2954 /* verify digest-uri format */
2956 /* make sure it's the service that we're expecting */
2957 service_len = strlen(sparams->service);
2958 if (strncasecmp(digesturi, sparams->service, service_len) ||
2959 digesturi[service_len] != '/') {
2960 result = SASL_BADAUTH;
2961 #ifdef _SUN_SDK_
2962 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2963 "bad digest-uri: doesn't match service");
2964 #else
2965 SETERROR(sparams->utils,
2966 "bad digest-uri: doesn't match service");
2967 #endif /* _SUN_SDK_ */
2968 goto FreeAllMem;
2971 /* xxx we don't verify the hostname component */
2973 } else if (strcasecmp(name, "response") == 0) {
2974 _plug_strdup(sparams->utils, value, &response, NULL);
2975 } else if (strcasecmp(name, "cipher") == 0) {
2976 _plug_strdup(sparams->utils, value, &cipher, NULL);
2977 } else if (strcasecmp(name, "maxbuf") == 0) {
2978 maxbuf_count++;
2979 if (maxbuf_count != 1) {
2980 result = SASL_BADAUTH;
2981 #ifdef _SUN_SDK_
2982 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2983 "duplicate maxbuf: authentication aborted");
2984 #else
2985 SETERROR(sparams->utils,
2986 "duplicate maxbuf: authentication aborted");
2987 #endif /* _SUN_SDK_ */
2988 goto FreeAllMem;
2989 } else if (sscanf(value, "%u", &client_maxbuf) != 1) {
2990 result = SASL_BADAUTH;
2991 #ifdef _SUN_SDK_
2992 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
2993 "invalid maxbuf parameter");
2994 #else
2995 SETERROR(sparams->utils, "invalid maxbuf parameter");
2996 #endif /* _SUN_SDK_ */
2997 goto FreeAllMem;
2998 } else {
2999 if (client_maxbuf <= 16) {
3000 result = SASL_BADAUTH;
3001 #ifdef _SUN_SDK_
3002 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3003 "maxbuf parameter too small");
3004 #else
3005 SETERROR(sparams->utils,
3006 "maxbuf parameter too small");
3007 #endif /* _SUN_SDK_ */
3008 goto FreeAllMem;
3011 } else if (strcasecmp(name, "charset") == 0) {
3012 if (strcasecmp(value, "utf-8") != 0) {
3013 #ifdef _SUN_SDK_
3014 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3015 "client doesn't support UTF-8");
3016 #else
3017 SETERROR(sparams->utils, "client doesn't support UTF-8");
3018 #endif /* _SUN_SDK_ */
3019 result = SASL_FAIL;
3020 goto FreeAllMem;
3022 _plug_strdup(sparams->utils, value, &charset, NULL);
3023 } else {
3024 sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
3025 "DIGEST-MD5 unrecognized pair %s/%s: ignoring",
3026 name, value);
3031 * username = "username" "=" <"> username-value <">
3032 * username-value = qdstr-val cnonce = "cnonce" "=" <">
3033 * cnonce-value <"> cnonce-value = qdstr-val nonce-count = "nc"
3034 * "=" nc-value nc-value = 8LHEX qop = "qop" "="
3035 * qop-value digest-uri = "digest-uri" "=" digest-uri-value
3036 * digest-uri-value = serv-type "/" host [ "/" serv-name ] serv-type
3037 * = 1*ALPHA host = 1*( ALPHA | DIGIT | "-" | "." ) service
3038 * = host response = "response" "=" <"> response-value <">
3039 * response-value = 32LHEX LHEX = "0" | "1" | "2" | "3" | "4" | "5" |
3040 * "6" | "7" | "8" | "9" | "a" | "b" | "c" | "d" | "e" | "f" cipher =
3041 * "cipher" "=" cipher-value
3043 /* Verifing that all parameters was defined */
3044 if ((username == NULL) ||
3045 (nonce == NULL) ||
3046 (noncecount == 0) ||
3047 (cnonce == NULL) ||
3048 (digesturi == NULL) ||
3049 (response == NULL)) {
3050 #ifdef _SUN_SDK_
3051 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3052 "required parameters missing");
3053 #else
3054 SETERROR(sparams->utils, "required parameters missing");
3055 #endif /* _SUN_SDK_ */
3056 result = SASL_BADAUTH;
3057 goto FreeAllMem;
3060 if (text->state == 1) {
3061 unsigned val = hash(username) % text->reauth->size;
3063 /* reauth attempt, see if we have any info for this user */
3064 if (sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
3065 if (text->reauth->e[val].authid &&
3066 !strcmp(username, text->reauth->e[val].authid)) {
3068 _plug_strdup(sparams->utils, text->reauth->e[val].realm,
3069 &text->realm, NULL);
3070 #ifdef _SUN_SDK_
3071 _plug_strdup(sparams->utils, (char *)text->reauth->e[val].nonce,
3072 (char **) &text->nonce, NULL);
3073 #else
3074 _plug_strdup(sparams->utils, text->reauth->e[val].nonce,
3075 (char **) &text->nonce, NULL);
3076 #endif /* _SUN_SDK_ */
3077 text->nonce_count = ++text->reauth->e[val].nonce_count;
3078 #ifdef _SUN_SDK_
3079 _plug_strdup(sparams->utils, (char *)text->reauth->e[val].cnonce,
3080 (char **) &text->cnonce, NULL);
3081 #else
3082 _plug_strdup(sparams->utils, text->reauth->e[val].cnonce,
3083 (char **) &text->cnonce, NULL);
3084 #endif /* _SUN_SDK_ */
3085 stext->timestamp = text->reauth->e[val].u.s.timestamp;
3087 sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
3090 if (!text->nonce) {
3091 /* we don't have any reauth info, so bail */
3092 result = SASL_FAIL;
3093 goto FreeAllMem;
3097 /* Sanity check the parameters */
3098 #ifdef _SUN_SDK_
3099 if ((realm != NULL && text->realm != NULL &&
3100 strcmp(realm, text->realm) != 0) ||
3101 (realm == NULL && text->realm != NULL) ||
3102 (realm != NULL && text->realm == NULL)) {
3103 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3104 "realm changed: authentication aborted");
3105 #else
3106 if (strcmp(realm, text->realm) != 0) {
3107 SETERROR(sparams->utils,
3108 "realm changed: authentication aborted");
3109 #endif /* _SUN_SDK_ */
3110 result = SASL_BADAUTH;
3111 goto FreeAllMem;
3113 #ifdef _SUN_SDK_
3114 if (strcmp((char *)nonce, (char *) text->nonce) != 0) {
3115 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3116 "nonce changed: authentication aborted");
3117 #else
3118 if (strcmp(nonce, (char *) text->nonce) != 0) {
3119 SETERROR(sparams->utils,
3120 "nonce changed: authentication aborted");
3121 #endif /* _SUN_SKD_ */
3122 result = SASL_BADAUTH;
3123 goto FreeAllMem;
3125 if (noncecount != text->nonce_count) {
3126 #ifdef _SUN_SDK_
3127 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3128 "incorrect nonce-count: authentication aborted");
3129 #else
3130 SETERROR(sparams->utils,
3131 "incorrect nonce-count: authentication aborted");
3132 #endif /* _SUN_SDK_ */
3133 result = SASL_BADAUTH;
3134 goto FreeAllMem;
3136 #ifdef _SUN_SDK_
3137 if (text->cnonce && strcmp((char *)cnonce, (char *)text->cnonce) != 0) {
3138 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3139 "cnonce changed: authentication aborted");
3140 #else
3141 if (text->cnonce && strcmp(cnonce, text->cnonce) != 0) {
3142 SETERROR(sparams->utils,
3143 "cnonce changed: authentication aborted");
3144 #endif /* _SUN_SDK_ */
3145 result = SASL_BADAUTH;
3146 goto FreeAllMem;
3149 result = sparams->utils->prop_request(sparams->propctx, password_request);
3150 if(result != SASL_OK) {
3151 #ifdef _SUN_SDK_
3152 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3153 "unable to request user password");
3154 #else
3155 SETERROR(sparams->utils, "unable to resquest user password");
3156 #endif /* _SUN_SDK_ */
3157 goto FreeAllMem;
3160 /* this will trigger the getting of the aux properties */
3161 /* Note that if we don't have an authorization id, we don't use it... */
3162 result = sparams->canon_user(sparams->utils->conn,
3163 username, 0, SASL_CU_AUTHID, oparams);
3164 if (result != SASL_OK) {
3165 #ifdef _SUN_SDK_
3166 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3167 "unable canonify user and get auxprops");
3168 #else
3169 SETERROR(sparams->utils, "unable canonify user and get auxprops");
3170 #endif /* _SUN_SDK_ */
3171 goto FreeAllMem;
3174 if (!authorization_id || !*authorization_id) {
3175 result = sparams->canon_user(sparams->utils->conn,
3176 username, 0, SASL_CU_AUTHZID, oparams);
3177 } else {
3178 result = sparams->canon_user(sparams->utils->conn,
3179 authorization_id, 0, SASL_CU_AUTHZID,
3180 oparams);
3183 if (result != SASL_OK) {
3184 #ifdef _SUN_SDK_
3185 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3186 "unable to canonicalize authorization ID");
3187 #else
3188 SETERROR(sparams->utils, "unable authorization ID");
3189 #endif /* _SUN_SDK_ */
3190 goto FreeAllMem;
3193 result = sparams->utils->prop_getnames(sparams->propctx, password_request,
3194 auxprop_values);
3195 if (result < 0 ||
3196 ((!auxprop_values[0].name || !auxprop_values[0].values) &&
3197 (!auxprop_values[1].name || !auxprop_values[1].values))) {
3198 /* We didn't find this username */
3199 #ifdef _INTEGRATED_SOLARIS_
3200 sparams->utils->seterror(sparams->utils->conn, 0,
3201 gettext("no secret in database"));
3202 #else
3203 sparams->utils->seterror(sparams->utils->conn, 0,
3204 "no secret in database");
3205 #endif /* _INTEGRATED_SOLARIS_ */
3206 result = SASL_NOUSER;
3207 goto FreeAllMem;
3210 if (auxprop_values[0].name && auxprop_values[0].values) {
3211 len = strlen(auxprop_values[0].values[0]);
3212 if (len == 0) {
3213 #ifdef _INTEGRATED_SOLARIS_
3214 sparams->utils->seterror(sparams->utils->conn,0,
3215 gettext("empty secret"));
3216 #else
3217 sparams->utils->seterror(sparams->utils->conn,0,
3218 "empty secret");
3219 #endif /* _INTEGRATED_SOLARIS_ */
3220 result = SASL_FAIL;
3221 goto FreeAllMem;
3224 sec = sparams->utils->malloc(sizeof(sasl_secret_t) + len);
3225 if (!sec) {
3226 #ifdef _SUN_SDK_
3227 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3228 "unable to allocate secret");
3229 #else
3230 SETERROR(sparams->utils, "unable to allocate secret");
3231 #endif /* _SUN_SDK_ */
3232 result = SASL_FAIL;
3233 goto FreeAllMem;
3236 sec->len = len;
3237 #ifdef _SUN_SDK_
3238 strncpy((char *)sec->data, auxprop_values[0].values[0], len + 1);
3239 #else
3240 strncpy(sec->data, auxprop_values[0].values[0], len + 1);
3241 #endif /* _SUN_SDK_ */
3244 * Verifying response obtained from client
3246 * H_URP = H({ username-value,":",realm-value,":",passwd}) sec->data
3247 * contains H_URP
3250 /* Calculate the secret from the plaintext password */
3252 HASH HA1;
3254 #ifdef _SUN_SDK_
3255 DigestCalcSecret(sparams->utils, (unsigned char *)username,
3256 (unsigned char *)text->realm, sec->data,
3257 sec->len, HA1);
3258 #else
3259 DigestCalcSecret(sparams->utils, username,
3260 text->realm, sec->data, sec->len, HA1);
3261 #endif /* _SUN_SDK_ */
3264 * A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
3265 * ":", nonce-value, ":", cnonce-value }
3268 memcpy(A1, HA1, HASHLEN);
3269 A1[HASHLEN] = '\0';
3272 /* We're done with sec now. Let's get rid of it */
3273 _plug_free_secret(sparams->utils, &sec);
3274 } else if (auxprop_values[1].name && auxprop_values[1].values) {
3275 memcpy(A1, auxprop_values[1].values[0], HASHLEN);
3276 A1[HASHLEN] = '\0';
3277 } else {
3278 #ifdef _SUN_SDK_
3279 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3280 "Have neither type of secret");
3281 #else
3282 sparams->utils->seterror(sparams->utils->conn, 0,
3283 "Have neither type of secret");
3284 #endif /* _SUN_SDK_ */
3285 #ifdef _SUN_SDK_
3286 result = SASL_FAIL;
3287 goto FreeAllMem;
3288 #else
3289 return SASL_FAIL;
3290 #endif /* _SUN_SDK_ */
3293 /* defaulting qop to "auth" if not specified */
3294 if (qop == NULL) {
3295 _plug_strdup(sparams->utils, "auth", &qop, NULL);
3298 /* check which layer/cipher to use */
3299 if ((!strcasecmp(qop, "auth-conf")) && (cipher != NULL)) {
3300 /* see what cipher was requested */
3301 struct digest_cipher *cptr;
3303 #ifdef USE_UEF_SERVER
3304 cptr = available_ciphers1;
3305 #else
3306 cptr = available_ciphers;
3307 #endif
3308 while (cptr->name) {
3309 /* find the cipher requested & make sure it's one we're happy
3310 with by policy */
3311 if (!strcasecmp(cipher, cptr->name) &&
3312 stext->requiressf <= cptr->ssf &&
3313 stext->limitssf >= cptr->ssf) {
3314 /* found it! */
3315 break;
3317 cptr++;
3320 if (cptr->name) {
3321 text->cipher_enc = cptr->cipher_enc;
3322 text->cipher_dec = cptr->cipher_dec;
3323 text->cipher_init = cptr->cipher_init;
3324 text->cipher_free = cptr->cipher_free;
3325 oparams->mech_ssf = cptr->ssf;
3326 n = cptr->n;
3327 } else {
3328 /* erg? client requested something we didn't advertise! */
3329 sparams->utils->log(sparams->utils->conn, SASL_LOG_WARN,
3330 "protocol violation: client requested invalid cipher");
3331 #ifndef _SUN_SDK_
3332 SETERROR(sparams->utils, "client requested invalid cipher");
3333 #endif /* !_SUN_SDK_ */
3334 /* Mark that we attempted security layer negotiation */
3335 oparams->mech_ssf = 2;
3336 result = SASL_FAIL;
3337 goto FreeAllMem;
3340 oparams->encode=&digestmd5_privacy_encode;
3341 oparams->decode=&digestmd5_privacy_decode;
3342 } else if (!strcasecmp(qop, "auth-int") &&
3343 stext->requiressf <= 1 && stext->limitssf >= 1) {
3344 oparams->encode = &digestmd5_integrity_encode;
3345 oparams->decode = &digestmd5_integrity_decode;
3346 oparams->mech_ssf = 1;
3347 } else if (!strcasecmp(qop, "auth") && stext->requiressf == 0) {
3348 oparams->encode = NULL;
3349 oparams->decode = NULL;
3350 oparams->mech_ssf = 0;
3351 } else {
3352 #ifdef _SUN_SDK_
3353 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3354 "protocol violation: client requested invalid qop");
3355 #else
3356 SETERROR(sparams->utils,
3357 "protocol violation: client requested invalid qop");
3358 #endif /* _SUN_SDK_ */
3359 result = SASL_FAIL;
3360 goto FreeAllMem;
3363 serverresponse = create_response(text,
3364 sparams->utils,
3365 text->nonce,
3366 text->nonce_count,
3367 cnonce,
3368 qop,
3369 digesturi,
3371 authorization_id,
3372 &text->response_value);
3374 if (serverresponse == NULL) {
3375 #ifndef _SUN_SDK_
3376 SETERROR(sparams->utils, "internal error: unable to create response");
3377 #endif /* !_SUN_SDK_ */
3378 result = SASL_NOMEM;
3379 goto FreeAllMem;
3382 /* if ok verified */
3383 if (strcmp(serverresponse, response) != 0) {
3384 #ifdef _INTEGRATED_SOLARIS_
3385 SETERROR(sparams->utils,
3386 gettext("client response doesn't match what we generated"));
3387 #else
3388 SETERROR(sparams->utils,
3389 "client response doesn't match what we generated");
3390 #endif /* _INTEGRATED_SOLARIS_ */
3391 result = SASL_BADAUTH;
3393 goto FreeAllMem;
3396 /* see if our nonce expired */
3397 if (text->reauth->timeout &&
3398 time(0) - stext->timestamp > text->reauth->timeout) {
3399 #ifdef _INTEGRATED_SOLARIS_
3400 SETERROR(sparams->utils, gettext("server nonce expired"));
3401 #else
3402 SETERROR(sparams->utils, "server nonce expired");
3403 #endif /* _INTEGRATED_SOLARIS_ */
3404 stext->stale = 1;
3405 result = SASL_BADAUTH;
3407 goto FreeAllMem;
3411 * nothing more to do; authenticated set oparams information
3413 oparams->doneflag = 1;
3414 oparams->maxoutbuf = client_maxbuf - 4;
3415 if (oparams->mech_ssf > 1) {
3416 #ifdef _SUN_SDK_
3417 if (oparams->maxoutbuf <= 25) {
3418 result = SASL_BADPARAM;
3419 goto FreeAllMem;
3421 #endif
3422 /* MAC block (privacy) */
3423 oparams->maxoutbuf -= 25;
3424 } else if(oparams->mech_ssf == 1) {
3425 #ifdef _SUN_SDK_
3426 if (oparams->maxoutbuf <= 16) {
3427 result = SASL_BADPARAM;
3428 goto FreeAllMem;
3430 #endif
3431 /* MAC block (integrity) */
3432 oparams->maxoutbuf -= 16;
3435 oparams->param_version = 0;
3437 text->seqnum = 0; /* for integrity/privacy */
3438 text->rec_seqnum = 0; /* for integrity/privacy */
3439 text->in_maxbuf =
3440 sparams->props.maxbufsize ? sparams->props.maxbufsize : DEFAULT_BUFSIZE;
3441 text->utils = sparams->utils;
3443 /* used by layers */
3444 text->needsize = 4;
3445 text->buffer = NULL;
3447 if (oparams->mech_ssf > 0) {
3448 char enckey[16];
3449 char deckey[16];
3451 create_layer_keys(text, sparams->utils,text->HA1,n,enckey,deckey);
3453 /* initialize cipher if need be */
3454 #ifdef _SUN_SDK_
3455 if (text->cipher_init) {
3456 if (text->cipher_free)
3457 text->cipher_free(text);
3458 if ((result = text->cipher_init(text, enckey, deckey)) != SASL_OK) {
3459 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3460 "couldn't init cipher");
3461 goto FreeAllMem;
3464 #else
3465 if (text->cipher_init)
3466 if (text->cipher_init(text, enckey, deckey) != SASL_OK) {
3467 sparams->utils->seterror(sparams->utils->conn, 0,
3468 "couldn't init cipher");
3470 #endif /* _SUN_SDK_ */
3474 * The server receives and validates the "digest-response". The server
3475 * checks that the nonce-count is "00000001". If it supports subsequent
3476 * authentication, it saves the value of the nonce and the nonce-count.
3480 * The "username-value", "realm-value" and "passwd" are encoded according
3481 * to the value of the "charset" directive. If "charset=UTF-8" is
3482 * present, and all the characters of either "username-value" or "passwd"
3483 * are in the ISO 8859-1 character set, then it must be converted to
3484 * UTF-8 before being hashed. A sample implementation of this conversion
3485 * is in section 8.
3488 /* add to challenge */
3490 unsigned resplen =
3491 strlen(text->response_value) + strlen("rspauth") + 3;
3493 result = _plug_buf_alloc(sparams->utils, &(text->out_buf),
3494 &(text->out_buf_len), resplen);
3495 if(result != SASL_OK) {
3496 goto FreeAllMem;
3499 sprintf(text->out_buf, "rspauth=%s", text->response_value);
3501 /* self check */
3502 if (strlen(text->out_buf) > 2048) {
3503 result = SASL_FAIL;
3504 goto FreeAllMem;
3508 *serveroutlen = strlen(text->out_buf);
3509 *serverout = text->out_buf;
3511 result = SASL_OK;
3513 FreeAllMem:
3514 if (text->reauth->timeout &&
3515 sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
3516 unsigned val = hash(username) % text->reauth->size;
3518 switch (result) {
3519 case SASL_OK:
3520 /* successful auth, setup for future reauth */
3521 if (text->nonce_count == 1) {
3522 /* successful initial auth, create new entry */
3523 clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils);
3524 text->reauth->e[val].authid = username; username = NULL;
3525 text->reauth->e[val].realm = text->realm; text->realm = NULL;
3526 text->reauth->e[val].nonce = text->nonce; text->nonce = NULL;
3527 text->reauth->e[val].cnonce = cnonce; cnonce = NULL;
3529 if (text->nonce_count <= text->reauth->e[val].nonce_count) {
3530 /* paranoia. prevent replay attacks */
3531 clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils);
3533 else {
3534 text->reauth->e[val].nonce_count = text->nonce_count;
3535 text->reauth->e[val].u.s.timestamp = time(0);
3537 break;
3538 default:
3539 if (text->nonce_count > 1) {
3540 /* failed reauth, clear entry */
3541 clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils);
3543 else {
3544 /* failed initial auth, leave existing cache */
3547 sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
3550 /* free everything */
3551 if (in_start) sparams->utils->free (in_start);
3553 if (username != NULL)
3554 sparams->utils->free (username);
3555 #ifdef _SUN_SDK_
3556 if (authorization_id != NULL)
3557 sparams->utils->free (authorization_id);
3558 #endif /* _SUN_SDK_ */
3559 if (realm != NULL)
3560 sparams->utils->free (realm);
3561 if (nonce != NULL)
3562 sparams->utils->free (nonce);
3563 if (cnonce != NULL)
3564 sparams->utils->free (cnonce);
3565 if (response != NULL)
3566 sparams->utils->free (response);
3567 if (cipher != NULL)
3568 sparams->utils->free (cipher);
3569 if (serverresponse != NULL)
3570 sparams->utils->free(serverresponse);
3571 if (charset != NULL)
3572 sparams->utils->free (charset);
3573 if (digesturi != NULL)
3574 sparams->utils->free (digesturi);
3575 if (qop!=NULL)
3576 sparams->utils->free (qop);
3577 if (sec)
3578 _plug_free_secret(sparams->utils, &sec);
3580 return result;
3583 static int
3584 digestmd5_server_mech_step(void *conn_context,
3585 sasl_server_params_t *sparams,
3586 const char *clientin,
3587 unsigned clientinlen,
3588 const char **serverout,
3589 unsigned *serveroutlen,
3590 sasl_out_params_t *oparams)
3592 context_t *text = (context_t *) conn_context;
3593 server_context_t *stext = (server_context_t *) conn_context;
3595 if (clientinlen > 4096) return SASL_BADPROT;
3597 *serverout = NULL;
3598 *serveroutlen = 0;
3600 switch (text->state) {
3602 case 1:
3603 /* setup SSF limits */
3604 if (!sparams->props.maxbufsize) {
3605 stext->limitssf = 0;
3606 stext->requiressf = 0;
3607 } else {
3608 if (sparams->props.max_ssf < sparams->external_ssf) {
3609 stext->limitssf = 0;
3610 } else {
3611 stext->limitssf =
3612 sparams->props.max_ssf - sparams->external_ssf;
3614 if (sparams->props.min_ssf < sparams->external_ssf) {
3615 stext->requiressf = 0;
3616 } else {
3617 stext->requiressf =
3618 sparams->props.min_ssf - sparams->external_ssf;
3622 if (clientin && text->reauth->timeout) {
3623 /* here's where we attempt fast reauth if possible */
3624 if (digestmd5_server_mech_step2(stext, sparams,
3625 clientin, clientinlen,
3626 serverout, serveroutlen,
3627 oparams) == SASL_OK) {
3628 return SASL_OK;
3631 #ifdef _SUN_SDK_
3632 sparams->utils->log(sparams->utils->conn, SASL_LOG_WARN,
3633 "DIGEST-MD5 reauth failed");
3634 #else
3635 sparams->utils->log(NULL, SASL_LOG_WARN,
3636 "DIGEST-MD5 reauth failed\n");
3637 #endif /* _SUN_SDK_ */
3639 /* re-initialize everything for a fresh start */
3640 memset(oparams, 0, sizeof(sasl_out_params_t));
3642 /* fall through and issue challenge */
3645 return digestmd5_server_mech_step1(stext, sparams,
3646 clientin, clientinlen,
3647 serverout, serveroutlen, oparams);
3649 case 2:
3650 return digestmd5_server_mech_step2(stext, sparams,
3651 clientin, clientinlen,
3652 serverout, serveroutlen, oparams);
3654 default:
3655 #ifdef _SUN_SDK_
3656 sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
3657 "Invalid DIGEST-MD5 server step %d", text->state);
3658 #else
3659 sparams->utils->log(NULL, SASL_LOG_ERR,
3660 "Invalid DIGEST-MD5 server step %d\n", text->state);
3661 #endif /* _SUN_SDK_ */
3662 return SASL_FAIL;
3665 #ifndef _SUN_SDK_
3666 return SASL_FAIL; /* should never get here */
3667 #endif /* !_SUN_SDK_ */
3670 static void
3671 digestmd5_server_mech_dispose(void *conn_context, const sasl_utils_t *utils)
3673 server_context_t *stext = (server_context_t *) conn_context;
3675 if (!stext || !utils) return;
3677 digestmd5_common_mech_dispose(conn_context, utils);
3680 static sasl_server_plug_t digestmd5_server_plugins[] =
3683 "DIGEST-MD5", /* mech_name */
3684 #ifdef WITH_RC4
3685 128, /* max_ssf */
3686 #elif WITH_DES
3687 112,
3688 #else
3690 #endif
3691 SASL_SEC_NOPLAINTEXT
3692 | SASL_SEC_NOANONYMOUS
3693 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
3694 SASL_FEAT_ALLOWS_PROXY, /* features */
3695 NULL, /* glob_context */
3696 &digestmd5_server_mech_new, /* mech_new */
3697 &digestmd5_server_mech_step, /* mech_step */
3698 &digestmd5_server_mech_dispose, /* mech_dispose */
3699 &digestmd5_common_mech_free, /* mech_free */
3700 NULL, /* setpass */
3701 NULL, /* user_query */
3702 NULL, /* idle */
3703 NULL, /* mech avail */
3704 NULL /* spare */
3708 int digestmd5_server_plug_init(sasl_utils_t *utils,
3709 int maxversion,
3710 int *out_version,
3711 sasl_server_plug_t **pluglist,
3712 int *plugcount)
3714 reauth_cache_t *reauth_cache;
3715 const char *timeout = NULL;
3716 unsigned int len;
3717 #if defined _SUN_SDK_ && defined USE_UEF
3718 int ret;
3719 #endif /* _SUN_SDK_ && USE_UEF */
3721 if (maxversion < SASL_SERVER_PLUG_VERSION)
3722 return SASL_BADVERS;
3724 #if defined _SUN_SDK_ && defined USE_UEF
3725 if ((ret = uef_init(utils)) != SASL_OK)
3726 return ret;
3727 #endif /* _SUN_SDK_ && USE_UEF */
3729 /* reauth cache */
3730 reauth_cache = utils->malloc(sizeof(reauth_cache_t));
3731 if (reauth_cache == NULL)
3732 return SASL_NOMEM;
3733 memset(reauth_cache, 0, sizeof(reauth_cache_t));
3734 reauth_cache->i_am = SERVER;
3736 /* fetch and canonify the reauth_timeout */
3737 utils->getopt(utils->getopt_context, "DIGEST-MD5", "reauth_timeout",
3738 &timeout, &len);
3739 if (timeout)
3740 reauth_cache->timeout = (time_t) 60 * strtol(timeout, NULL, 10);
3741 #ifdef _SUN_SDK_
3742 else
3743 reauth_cache->timeout = 0;
3744 #endif /* _SUN_SDK_ */
3745 if (reauth_cache->timeout < 0)
3746 reauth_cache->timeout = 0;
3748 if (reauth_cache->timeout) {
3749 /* mutex */
3750 reauth_cache->mutex = utils->mutex_alloc();
3751 if (!reauth_cache->mutex)
3752 return SASL_FAIL;
3754 /* entries */
3755 reauth_cache->size = 100;
3756 reauth_cache->e = utils->malloc(reauth_cache->size *
3757 sizeof(reauth_entry_t));
3758 if (reauth_cache->e == NULL)
3759 return SASL_NOMEM;
3760 memset(reauth_cache->e, 0, reauth_cache->size * sizeof(reauth_entry_t));
3763 digestmd5_server_plugins[0].glob_context = reauth_cache;
3765 #ifdef _SUN_SDK_
3766 #ifdef USE_UEF_CLIENT
3767 digestmd5_server_plugins[0].max_ssf = uef_max_ssf;
3768 #endif /* USE_UEF_CLIENT */
3769 #endif /* _SUN_SDK_ */
3771 #ifdef _INTEGRATED_SOLARIS_
3773 * Let libsasl know that we are a "Sun" plugin so that privacy
3774 * and integrity will be allowed.
3776 REG_PLUG("DIGEST-MD5", digestmd5_server_plugins);
3777 #endif /* _INTEGRATED_SOLARIS_ */
3779 *out_version = SASL_SERVER_PLUG_VERSION;
3780 *pluglist = digestmd5_server_plugins;
3781 *plugcount = 1;
3783 return SASL_OK;
3786 /***************************** Client Section *****************************/
3788 typedef struct client_context {
3789 context_t common;
3791 sasl_secret_t *password; /* user password */
3792 unsigned int free_password; /* set if we need to free password */
3794 int protection;
3795 struct digest_cipher *cipher;
3796 unsigned int server_maxbuf;
3797 #ifdef _INTEGRATED_SOLARIS_
3798 void *h;
3799 #endif /* _INTEGRATED_SOLARIS_ */
3800 } client_context_t;
3802 /* calculate H(A1) as per spec */
3803 static void
3804 DigestCalcHA1(context_t * text,
3805 const sasl_utils_t * utils,
3806 unsigned char *pszUserName,
3807 unsigned char *pszRealm,
3808 sasl_secret_t * pszPassword,
3809 unsigned char *pszAuthorization_id,
3810 unsigned char *pszNonce,
3811 unsigned char *pszCNonce,
3812 HASHHEX SessionKey)
3814 MD5_CTX Md5Ctx;
3815 HASH HA1;
3817 DigestCalcSecret(utils,
3818 pszUserName,
3819 pszRealm,
3820 (unsigned char *) pszPassword->data,
3821 pszPassword->len,
3822 HA1);
3824 /* calculate the session key */
3825 utils->MD5Init(&Md5Ctx);
3826 utils->MD5Update(&Md5Ctx, HA1, HASHLEN);
3827 utils->MD5Update(&Md5Ctx, COLON, 1);
3828 utils->MD5Update(&Md5Ctx, pszNonce, strlen((char *) pszNonce));
3829 utils->MD5Update(&Md5Ctx, COLON, 1);
3830 utils->MD5Update(&Md5Ctx, pszCNonce, strlen((char *) pszCNonce));
3831 if (pszAuthorization_id != NULL) {
3832 utils->MD5Update(&Md5Ctx, COLON, 1);
3833 utils->MD5Update(&Md5Ctx, pszAuthorization_id,
3834 strlen((char *) pszAuthorization_id));
3836 utils->MD5Final(HA1, &Md5Ctx);
3838 CvtHex(HA1, SessionKey);
3840 /* xxx rc-* use different n */
3842 /* save HA1 because we'll need it for the privacy and integrity keys */
3843 memcpy(text->HA1, HA1, sizeof(HASH));
3847 static char *calculate_response(context_t * text,
3848 const sasl_utils_t * utils,
3849 unsigned char *username,
3850 unsigned char *realm,
3851 unsigned char *nonce,
3852 unsigned int ncvalue,
3853 unsigned char *cnonce,
3854 char *qop,
3855 unsigned char *digesturi,
3856 sasl_secret_t * passwd,
3857 unsigned char *authorization_id,
3858 char **response_value)
3860 HASHHEX SessionKey;
3861 HASHHEX HEntity = "00000000000000000000000000000000";
3862 HASHHEX Response;
3863 char *result;
3865 /* Verifing that all parameters was defined */
3866 if(!username || !cnonce || !nonce || !ncvalue || !digesturi || !passwd) {
3867 PARAMERROR( utils );
3868 return NULL;
3871 if (realm == NULL) {
3872 /* a NULL realm is equivalent to the empty string */
3873 realm = (unsigned char *) "";
3876 if (qop == NULL) {
3877 /* default to a qop of just authentication */
3878 qop = "auth";
3881 DigestCalcHA1(text,
3882 utils,
3883 username,
3884 realm,
3885 passwd,
3886 authorization_id,
3887 nonce,
3888 cnonce,
3889 SessionKey);
3891 DigestCalcResponse(utils,
3892 SessionKey,/* H(A1) */
3893 nonce, /* nonce from server */
3894 ncvalue, /* 8 hex digits */
3895 cnonce, /* client nonce */
3896 (unsigned char *) qop, /* qop-value: "", "auth",
3897 * "auth-int" */
3898 digesturi, /* requested URL */
3899 (unsigned char *) "AUTHENTICATE",
3900 HEntity, /* H(entity body) if qop="auth-int" */
3901 Response /* request-digest or response-digest */
3904 result = utils->malloc(HASHHEXLEN + 1);
3905 #ifdef _SUN_SDK_
3906 if (result == NULL)
3907 return NULL;
3908 #endif /* _SUN_SDK_ */
3909 memcpy(result, Response, HASHHEXLEN);
3910 result[HASHHEXLEN] = 0;
3912 if (response_value != NULL) {
3913 DigestCalcResponse(utils,
3914 SessionKey, /* H(A1) */
3915 nonce, /* nonce from server */
3916 ncvalue, /* 8 hex digits */
3917 cnonce, /* client nonce */
3918 (unsigned char *) qop, /* qop-value: "", "auth",
3919 * "auth-int" */
3920 (unsigned char *) digesturi, /* requested URL */
3921 NULL,
3922 HEntity, /* H(entity body) if qop="auth-int" */
3923 Response /* request-digest or response-digest */
3926 #ifdef _SUN_SDK_
3927 if (*response_value != NULL)
3928 utils->free(*response_value);
3929 #endif /* _SUN_SDK_ */
3930 *response_value = utils->malloc(HASHHEXLEN + 1);
3931 if (*response_value == NULL)
3932 return NULL;
3934 memcpy(*response_value, Response, HASHHEXLEN);
3935 (*response_value)[HASHHEXLEN] = 0;
3939 return result;
3942 static int
3943 make_client_response(context_t *text,
3944 sasl_client_params_t *params,
3945 sasl_out_params_t *oparams)
3947 client_context_t *ctext = (client_context_t *) text;
3948 char *qop = NULL;
3949 unsigned nbits = 0;
3950 unsigned char *digesturi = NULL;
3951 bool IsUTF8 = FALSE;
3952 char ncvalue[10];
3953 char maxbufstr[64];
3954 char *response = NULL;
3955 unsigned resplen = 0;
3956 int result;
3958 switch (ctext->protection) {
3959 case DIGEST_PRIVACY:
3960 qop = "auth-conf";
3961 oparams->encode = &digestmd5_privacy_encode;
3962 oparams->decode = &digestmd5_privacy_decode;
3963 oparams->mech_ssf = ctext->cipher->ssf;
3965 nbits = ctext->cipher->n;
3966 text->cipher_enc = ctext->cipher->cipher_enc;
3967 text->cipher_dec = ctext->cipher->cipher_dec;
3968 text->cipher_free = ctext->cipher->cipher_free;
3969 text->cipher_init = ctext->cipher->cipher_init;
3970 break;
3971 case DIGEST_INTEGRITY:
3972 qop = "auth-int";
3973 oparams->encode = &digestmd5_integrity_encode;
3974 oparams->decode = &digestmd5_integrity_decode;
3975 oparams->mech_ssf = 1;
3976 break;
3977 case DIGEST_NOLAYER:
3978 default:
3979 qop = "auth";
3980 oparams->encode = NULL;
3981 oparams->decode = NULL;
3982 oparams->mech_ssf = 0;
3985 digesturi = params->utils->malloc(strlen(params->service) + 1 +
3986 strlen(params->serverFQDN) + 1 +
3988 if (digesturi == NULL) {
3989 result = SASL_NOMEM;
3990 goto FreeAllocatedMem;
3993 /* allocated exactly this. safe */
3994 strcpy((char *) digesturi, params->service);
3995 strcat((char *) digesturi, "/");
3996 strcat((char *) digesturi, params->serverFQDN);
3998 * strcat (digesturi, "/"); strcat (digesturi, params->serverFQDN);
4001 /* response */
4002 response =
4003 calculate_response(text,
4004 params->utils,
4005 #ifdef _SUN_SDK_
4006 (unsigned char *) oparams->authid,
4007 #else
4008 (char *) oparams->authid,
4009 #endif /* _SUN_SDK_ */
4010 (unsigned char *) text->realm,
4011 text->nonce,
4012 text->nonce_count,
4013 text->cnonce,
4014 qop,
4015 digesturi,
4016 ctext->password,
4017 strcmp(oparams->user, oparams->authid) ?
4018 #ifdef _SUN_SDK_
4019 (unsigned char *) oparams->user : NULL,
4020 #else
4021 (char *) oparams->user : NULL,
4022 #endif /* _SUN_SDK_ */
4023 &text->response_value);
4025 #ifdef _SUN_SDK_
4026 if (response == NULL) {
4027 result = SASL_NOMEM;
4028 goto FreeAllocatedMem;
4030 #endif /* _SUN_SDK_ */
4032 resplen = strlen(oparams->authid) + strlen("username") + 5;
4033 result =_plug_buf_alloc(params->utils, &(text->out_buf),
4034 &(text->out_buf_len),
4035 resplen);
4036 if (result != SASL_OK) goto FreeAllocatedMem;
4038 sprintf(text->out_buf, "username=\"%s\"", oparams->authid);
4040 if (add_to_challenge(params->utils,
4041 &text->out_buf, &text->out_buf_len, &resplen,
4042 "realm", (unsigned char *) text->realm,
4043 TRUE) != SASL_OK) {
4044 result = SASL_FAIL;
4045 goto FreeAllocatedMem;
4047 if (strcmp(oparams->user, oparams->authid)) {
4048 if (add_to_challenge(params->utils,
4049 &text->out_buf, &text->out_buf_len, &resplen,
4050 #ifdef _SUN_SDK_
4051 "authzid", (unsigned char *) oparams->user,
4052 TRUE) != SASL_OK) {
4053 #else
4054 "authzid", (char *) oparams->user, TRUE) != SASL_OK) {
4055 #endif /* _SUN_SDK_ */
4056 result = SASL_FAIL;
4057 goto FreeAllocatedMem;
4060 if (add_to_challenge(params->utils,
4061 &text->out_buf, &text->out_buf_len, &resplen,
4062 "nonce", text->nonce, TRUE) != SASL_OK) {
4063 result = SASL_FAIL;
4064 goto FreeAllocatedMem;
4066 if (add_to_challenge(params->utils,
4067 &text->out_buf, &text->out_buf_len, &resplen,
4068 "cnonce", text->cnonce, TRUE) != SASL_OK) {
4069 result = SASL_FAIL;
4070 goto FreeAllocatedMem;
4072 snprintf(ncvalue, sizeof(ncvalue), "%08x", text->nonce_count);
4073 if (add_to_challenge(params->utils,
4074 &text->out_buf, &text->out_buf_len, &resplen,
4075 "nc", (unsigned char *) ncvalue, FALSE) != SASL_OK) {
4076 result = SASL_FAIL;
4077 goto FreeAllocatedMem;
4079 if (add_to_challenge(params->utils,
4080 &text->out_buf, &text->out_buf_len, &resplen,
4081 "qop", (unsigned char *) qop, FALSE) != SASL_OK) {
4082 result = SASL_FAIL;
4083 goto FreeAllocatedMem;
4085 if (ctext->cipher != NULL) {
4086 if (add_to_challenge(params->utils,
4087 &text->out_buf, &text->out_buf_len, &resplen,
4088 "cipher",
4089 (unsigned char *) ctext->cipher->name,
4090 TRUE) != SASL_OK) {
4091 result = SASL_FAIL;
4092 goto FreeAllocatedMem;
4096 if (params->props.maxbufsize) {
4097 snprintf(maxbufstr, sizeof(maxbufstr), "%d", params->props.maxbufsize);
4098 if (add_to_challenge(params->utils,
4099 &text->out_buf, &text->out_buf_len, &resplen,
4100 "maxbuf", (unsigned char *) maxbufstr,
4101 FALSE) != SASL_OK) {
4102 #ifdef _SUN_SDK_
4103 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4104 "internal error: add_to_challenge maxbuf failed");
4105 #else
4106 SETERROR(params->utils,
4107 "internal error: add_to_challenge maxbuf failed");
4108 #endif /* _SUN_SDK_ */
4109 goto FreeAllocatedMem;
4113 if (IsUTF8) {
4114 if (add_to_challenge(params->utils,
4115 &text->out_buf, &text->out_buf_len, &resplen,
4116 "charset", (unsigned char *) "utf-8",
4117 FALSE) != SASL_OK) {
4118 result = SASL_FAIL;
4119 goto FreeAllocatedMem;
4122 if (add_to_challenge(params->utils,
4123 &text->out_buf, &text->out_buf_len, &resplen,
4124 "digest-uri", digesturi, TRUE) != SASL_OK) {
4125 result = SASL_FAIL;
4126 goto FreeAllocatedMem;
4128 if (add_to_challenge(params->utils,
4129 &text->out_buf, &text->out_buf_len, &resplen,
4130 "response", (unsigned char *) response,
4131 FALSE) != SASL_OK) {
4133 result = SASL_FAIL;
4134 goto FreeAllocatedMem;
4137 /* self check */
4138 if (strlen(text->out_buf) > 2048) {
4139 result = SASL_FAIL;
4140 goto FreeAllocatedMem;
4143 /* set oparams */
4144 #ifdef _SUN_SDK_
4145 oparams->maxoutbuf = ctext->server_maxbuf - 4;
4146 #else
4147 oparams->maxoutbuf = ctext->server_maxbuf;
4148 #endif /* _SUN_SDK_ */
4149 if(oparams->mech_ssf > 1) {
4150 #ifdef _SUN_SDK_
4151 if (oparams->maxoutbuf <= 25)
4152 return (SASL_BADPARAM);
4153 #endif
4154 /* MAC block (privacy) */
4155 oparams->maxoutbuf -= 25;
4156 } else if(oparams->mech_ssf == 1) {
4157 #ifdef _SUN_SDK_
4158 if (oparams->maxoutbuf <= 16)
4159 return (SASL_BADPARAM);
4160 #endif
4161 /* MAC block (integrity) */
4162 oparams->maxoutbuf -= 16;
4165 text->seqnum = 0; /* for integrity/privacy */
4166 text->rec_seqnum = 0; /* for integrity/privacy */
4167 text->utils = params->utils;
4169 text->in_maxbuf =
4170 params->props.maxbufsize ? params->props.maxbufsize : DEFAULT_BUFSIZE;
4172 /* used by layers */
4173 text->needsize = 4;
4174 text->buffer = NULL;
4176 if (oparams->mech_ssf > 0) {
4177 char enckey[16];
4178 char deckey[16];
4180 create_layer_keys(text, params->utils, text->HA1, nbits,
4181 enckey, deckey);
4183 /* initialize cipher if need be */
4184 #ifdef _SUN_SDK_
4185 if (text->cipher_init) {
4186 if (text->cipher_free)
4187 text->cipher_free(text);
4188 if((result = text->cipher_init(text, enckey, deckey)) != SASL_OK) {
4189 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4190 "couldn't init cipher");
4191 goto FreeAllocatedMem;
4194 #else
4195 if (text->cipher_init)
4196 text->cipher_init(text, enckey, deckey);
4197 #endif /* _SUN_SDK_ */
4200 result = SASL_OK;
4202 FreeAllocatedMem:
4203 if (digesturi) params->utils->free(digesturi);
4204 if (response) params->utils->free(response);
4206 return result;
4209 static int parse_server_challenge(client_context_t *ctext,
4210 sasl_client_params_t *params,
4211 const char *serverin, unsigned serverinlen,
4212 char ***outrealms, int *noutrealm)
4214 context_t *text = (context_t *) ctext;
4215 int result = SASL_OK;
4216 char *in_start = NULL;
4217 char *in = NULL;
4218 char **realms = NULL;
4219 int nrealm = 0;
4220 sasl_ssf_t limit, musthave = 0;
4221 sasl_ssf_t external;
4222 int protection = 0;
4223 int ciphers = 0;
4224 int maxbuf_count = 0;
4225 #ifndef _SUN_SDK_
4226 bool IsUTF8 = FALSE;
4227 #endif /* !_SUN_SDK_ */
4228 int algorithm_count = 0;
4230 if (!serverin || !serverinlen) {
4231 #ifndef _SUN_SDK_
4232 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4233 "no server challenge");
4234 #else
4235 params->utils->seterror(params->utils->conn, 0,
4236 "no server challenge");
4237 #endif /* _SUN_SDK_ */
4238 return SASL_FAIL;
4241 in_start = in = params->utils->malloc(serverinlen + 1);
4242 if (in == NULL) return SASL_NOMEM;
4244 memcpy(in, serverin, serverinlen);
4245 in[serverinlen] = 0;
4247 ctext->server_maxbuf = 65536; /* Default value for maxbuf */
4249 /* create a new cnonce */
4250 text->cnonce = create_nonce(params->utils);
4251 if (text->cnonce == NULL) {
4252 #ifdef _SUN_SDK_
4253 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4254 "failed to create cnonce");
4255 #else
4256 params->utils->seterror(params->utils->conn, 0,
4257 "failed to create cnonce");
4258 #endif /* _SUN_SDK_ */
4259 result = SASL_FAIL;
4260 goto FreeAllocatedMem;
4263 /* parse the challenge */
4264 while (in[0] != '\0') {
4265 char *name, *value;
4267 get_pair(&in, &name, &value);
4269 /* if parse error */
4270 if (name == NULL) {
4271 #ifdef _SUN_SDK_
4272 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4273 "Parse error");
4274 #else
4275 params->utils->seterror(params->utils->conn, 0, "Parse error");
4276 #endif /* _SUN_SDK_ */
4277 result = SASL_FAIL;
4278 goto FreeAllocatedMem;
4281 if (strcasecmp(name, "realm") == 0) {
4282 nrealm++;
4284 if(!realms)
4285 realms = params->utils->malloc(sizeof(char *) * (nrealm + 1));
4286 else
4287 realms = params->utils->realloc(realms,
4288 sizeof(char *) * (nrealm + 1));
4290 if (realms == NULL) {
4291 result = SASL_NOMEM;
4292 goto FreeAllocatedMem;
4295 _plug_strdup(params->utils, value, &realms[nrealm-1], NULL);
4296 realms[nrealm] = NULL;
4297 } else if (strcasecmp(name, "nonce") == 0) {
4298 _plug_strdup(params->utils, value, (char **) &text->nonce,
4299 NULL);
4300 text->nonce_count = 1;
4301 } else if (strcasecmp(name, "qop") == 0) {
4302 while (value && *value) {
4303 char *comma = strchr(value, ',');
4304 if (comma != NULL) {
4305 *comma++ = '\0';
4308 if (strcasecmp(value, "auth-conf") == 0) {
4309 protection |= DIGEST_PRIVACY;
4310 } else if (strcasecmp(value, "auth-int") == 0) {
4311 protection |= DIGEST_INTEGRITY;
4312 } else if (strcasecmp(value, "auth") == 0) {
4313 protection |= DIGEST_NOLAYER;
4314 } else {
4315 params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4316 "Server supports unknown layer: %s\n",
4317 value);
4320 value = comma;
4323 if (protection == 0) {
4324 result = SASL_BADAUTH;
4325 #ifdef _INTEGRATED_SOLARIS_
4326 params->utils->seterror(params->utils->conn, 0,
4327 gettext("Server doesn't support known qop level"));
4328 #else
4329 params->utils->seterror(params->utils->conn, 0,
4330 "Server doesn't support known qop level");
4331 #endif /* _INTEGRATED_SOLARIS_ */
4332 goto FreeAllocatedMem;
4334 } else if (strcasecmp(name, "cipher") == 0) {
4335 while (value && *value) {
4336 char *comma = strchr(value, ',');
4337 #ifdef USE_UEF_CLIENT
4338 struct digest_cipher *cipher = available_ciphers1;
4339 #else
4340 struct digest_cipher *cipher = available_ciphers;
4341 #endif
4343 if (comma != NULL) {
4344 *comma++ = '\0';
4347 /* do we support this cipher? */
4348 while (cipher->name) {
4349 if (!strcasecmp(value, cipher->name)) break;
4350 cipher++;
4352 if (cipher->name) {
4353 ciphers |= cipher->flag;
4354 } else {
4355 params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4356 "Server supports unknown cipher: %s\n",
4357 value);
4360 value = comma;
4362 } else if (strcasecmp(name, "stale") == 0 && ctext->password) {
4363 /* clear any cached password */
4364 if (ctext->free_password)
4365 _plug_free_secret(params->utils, &ctext->password);
4366 ctext->password = NULL;
4367 } else if (strcasecmp(name, "maxbuf") == 0) {
4368 /* maxbuf A number indicating the size of the largest
4369 * buffer the server is able to receive when using
4370 * "auth-int". If this directive is missing, the default
4371 * value is 65536. This directive may appear at most once;
4372 * if multiple instances are present, the client should
4373 * abort the authentication exchange.
4375 maxbuf_count++;
4377 if (maxbuf_count != 1) {
4378 result = SASL_BADAUTH;
4379 #ifdef _SUN_SDK_
4380 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4381 "At least two maxbuf directives found."
4382 " Authentication aborted");
4383 #else
4384 params->utils->seterror(params->utils->conn, 0,
4385 "At least two maxbuf directives found. Authentication aborted");
4386 #endif /* _SUN_SDK_ */
4387 goto FreeAllocatedMem;
4388 } else if (sscanf(value, "%u", &ctext->server_maxbuf) != 1) {
4389 result = SASL_BADAUTH;
4390 #ifdef _SUN_SDK_
4391 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4392 "Invalid maxbuf parameter received from server");
4393 #else
4394 params->utils->seterror(params->utils->conn, 0,
4395 "Invalid maxbuf parameter received from server");
4396 #endif /* _SUN_SDK_ */
4397 goto FreeAllocatedMem;
4398 } else {
4399 if (ctext->server_maxbuf<=16) {
4400 result = SASL_BADAUTH;
4401 #ifdef _SUN_SDK_
4402 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4403 "Invalid maxbuf parameter received from server"
4404 " (too small: %s)", value);
4405 #else
4406 params->utils->seterror(params->utils->conn, 0,
4407 "Invalid maxbuf parameter received from server (too small: %s)", value);
4408 #endif /* _SUN_SDK_ */
4409 goto FreeAllocatedMem;
4412 } else if (strcasecmp(name, "charset") == 0) {
4413 if (strcasecmp(value, "utf-8") != 0) {
4414 result = SASL_BADAUTH;
4415 #ifdef _SUN_SDK_
4416 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4417 "Charset must be UTF-8");
4418 #else
4419 params->utils->seterror(params->utils->conn, 0,
4420 "Charset must be UTF-8");
4421 #endif /* _SUN_SDK_ */
4422 goto FreeAllocatedMem;
4423 } else {
4424 #ifndef _SUN_SDK_
4425 IsUTF8 = TRUE;
4426 #endif /* !_SUN_SDK_ */
4428 } else if (strcasecmp(name,"algorithm")==0) {
4429 if (strcasecmp(value, "md5-sess") != 0)
4431 #ifdef _SUN_SDK_
4432 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4433 "'algorithm' isn't 'md5-sess'");
4434 #else
4435 params->utils->seterror(params->utils->conn, 0,
4436 "'algorithm' isn't 'md5-sess'");
4437 #endif /* _SUN_SDK_ */
4438 result = SASL_FAIL;
4439 goto FreeAllocatedMem;
4442 algorithm_count++;
4443 if (algorithm_count > 1)
4445 #ifdef _SUN_SDK_
4446 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4447 "Must see 'algorithm' only once");
4448 #else
4449 params->utils->seterror(params->utils->conn, 0,
4450 "Must see 'algorithm' only once");
4451 #endif /* _SUN_SDK_ */
4452 result = SASL_FAIL;
4453 goto FreeAllocatedMem;
4455 } else {
4456 params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4457 "DIGEST-MD5 unrecognized pair %s/%s: ignoring",
4458 name, value);
4462 if (algorithm_count != 1) {
4463 #ifdef _SUN_SDK_
4464 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4465 "Must see 'algorithm' once. Didn't see at all");
4466 #else
4467 params->utils->seterror(params->utils->conn, 0,
4468 "Must see 'algorithm' once. Didn't see at all");
4469 #endif /* _SUN_SDK_ */
4470 result = SASL_FAIL;
4471 goto FreeAllocatedMem;
4474 /* make sure we have everything we require */
4475 if (text->nonce == NULL) {
4476 #ifdef _SUN_SDK_
4477 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4478 "Don't have nonce.");
4479 #else
4480 params->utils->seterror(params->utils->conn, 0,
4481 "Don't have nonce.");
4482 #endif /* _SUN_SDK_ */
4483 result = SASL_FAIL;
4484 goto FreeAllocatedMem;
4487 /* get requested ssf */
4488 external = params->external_ssf;
4490 /* what do we _need_? how much is too much? */
4491 if (params->props.maxbufsize == 0) {
4492 musthave = 0;
4493 limit = 0;
4494 } else {
4495 if (params->props.max_ssf > external) {
4496 limit = params->props.max_ssf - external;
4497 } else {
4498 limit = 0;
4500 if (params->props.min_ssf > external) {
4501 musthave = params->props.min_ssf - external;
4502 } else {
4503 musthave = 0;
4507 /* we now go searching for an option that gives us at least "musthave"
4508 and at most "limit" bits of ssf. */
4509 if ((limit > 1) && (protection & DIGEST_PRIVACY)) {
4510 struct digest_cipher *cipher;
4512 /* let's find an encryption scheme that we like */
4513 #ifdef USE_UEF_CLIENT
4514 cipher = available_ciphers1;
4515 #else
4516 cipher = available_ciphers;
4517 #endif
4518 while (cipher->name) {
4519 /* examine each cipher we support, see if it meets our security
4520 requirements, and see if the server supports it.
4521 choose the best one of these */
4522 if ((limit >= cipher->ssf) && (musthave <= cipher->ssf) &&
4523 (ciphers & cipher->flag) &&
4524 (!ctext->cipher || (cipher->ssf > ctext->cipher->ssf))) {
4525 ctext->cipher = cipher;
4527 cipher++;
4530 if (ctext->cipher) {
4531 /* we found a cipher we like */
4532 ctext->protection = DIGEST_PRIVACY;
4533 } else {
4534 /* we didn't find any ciphers we like */
4535 #ifdef _INTEGRATED_SOLARIS_
4536 params->utils->seterror(params->utils->conn, 0,
4537 gettext("No good privacy layers"));
4538 #else
4539 params->utils->seterror(params->utils->conn, 0,
4540 "No good privacy layers");
4541 #endif /* _INTEGRATED_SOLARIS_ */
4545 if (ctext->cipher == NULL) {
4546 /* we failed to find an encryption layer we liked;
4547 can we use integrity or nothing? */
4549 if ((limit >= 1) && (musthave <= 1)
4550 && (protection & DIGEST_INTEGRITY)) {
4551 /* integrity */
4552 ctext->protection = DIGEST_INTEGRITY;
4553 #ifdef _SUN_SDK_
4554 } else if (musthave == 0) {
4555 #else
4556 } else if (musthave <= 0) {
4557 #endif /* _SUN_SDK_ */
4558 /* no layer */
4559 ctext->protection = DIGEST_NOLAYER;
4561 /* See if server supports not having a layer */
4562 if ((protection & DIGEST_NOLAYER) != DIGEST_NOLAYER) {
4563 #ifdef _INTEGRATED_SOLARIS_
4564 params->utils->seterror(params->utils->conn, 0,
4565 gettext("Server doesn't support \"no layer\""));
4566 #else
4567 params->utils->seterror(params->utils->conn, 0,
4568 "Server doesn't support \"no layer\"");
4569 #endif /* _INTEGRATED_SOLARIS_ */
4570 result = SASL_FAIL;
4571 goto FreeAllocatedMem;
4573 } else {
4574 #ifdef _INTEGRATED_SOLARIS_
4575 params->utils->seterror(params->utils->conn, 0,
4576 gettext("Can't find an acceptable layer"));
4577 #else
4578 params->utils->seterror(params->utils->conn, 0,
4579 "Can't find an acceptable layer");
4580 #endif /* _INTEGRATED_SOLARIS_ */
4581 result = SASL_TOOWEAK;
4582 goto FreeAllocatedMem;
4586 *outrealms = realms;
4587 *noutrealm = nrealm;
4589 FreeAllocatedMem:
4590 if (in_start) params->utils->free(in_start);
4592 if (result != SASL_OK && realms) {
4593 int lup;
4595 /* need to free all the realms */
4596 for (lup = 0;lup < nrealm; lup++)
4597 params->utils->free(realms[lup]);
4599 params->utils->free(realms);
4602 return result;
4605 static int ask_user_info(client_context_t *ctext,
4606 sasl_client_params_t *params,
4607 char **realms, int nrealm,
4608 sasl_interact_t **prompt_need,
4609 sasl_out_params_t *oparams)
4611 context_t *text = (context_t *) ctext;
4612 int result = SASL_OK;
4613 const char *authid = NULL, *userid = NULL, *realm = NULL;
4614 char *realm_chal = NULL;
4615 int user_result = SASL_OK;
4616 int auth_result = SASL_OK;
4617 int pass_result = SASL_OK;
4618 int realm_result = SASL_FAIL;
4620 /* try to get the authid */
4621 if (oparams->authid == NULL) {
4622 auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
4624 if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) {
4625 return auth_result;
4629 /* try to get the userid */
4630 if (oparams->user == NULL) {
4631 user_result = _plug_get_userid(params->utils, &userid, prompt_need);
4633 if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) {
4634 return user_result;
4638 /* try to get the password */
4639 if (ctext->password == NULL) {
4640 pass_result = _plug_get_password(params->utils, &ctext->password,
4641 &ctext->free_password, prompt_need);
4642 if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) {
4643 return pass_result;
4647 /* try to get the realm */
4648 if (text->realm == NULL) {
4649 if (realms) {
4650 if(nrealm == 1) {
4651 /* only one choice */
4652 realm = realms[0];
4653 realm_result = SASL_OK;
4654 } else {
4655 /* ask the user */
4656 realm_result = _plug_get_realm(params->utils,
4657 (const char **) realms,
4658 (const char **) &realm,
4659 prompt_need);
4663 /* fake the realm if we must */
4664 if ((realm_result != SASL_OK) && (realm_result != SASL_INTERACT)) {
4665 if (params->serverFQDN) {
4666 realm = params->serverFQDN;
4667 } else {
4668 return realm_result;
4673 /* free prompts we got */
4674 if (prompt_need && *prompt_need) {
4675 params->utils->free(*prompt_need);
4676 *prompt_need = NULL;
4679 /* if there are prompts not filled in */
4680 if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) ||
4681 (pass_result == SASL_INTERACT) || (realm_result == SASL_INTERACT)) {
4683 /* make our default realm */
4684 if ((realm_result == SASL_INTERACT) && params->serverFQDN) {
4685 realm_chal = params->utils->malloc(3+strlen(params->serverFQDN));
4686 if (realm_chal) {
4687 sprintf(realm_chal, "{%s}", params->serverFQDN);
4688 } else {
4689 return SASL_NOMEM;
4693 /* make the prompt list */
4694 result =
4695 #if defined _INTEGRATED_SOLARIS_
4696 _plug_make_prompts(params->utils, &ctext->h, prompt_need,
4697 user_result == SASL_INTERACT ?
4698 convert_prompt(params->utils, &ctext->h,
4699 gettext("Please enter your authorization name"))
4700 : NULL,
4701 NULL,
4702 auth_result == SASL_INTERACT ?
4703 convert_prompt(params->utils, &ctext->h,
4704 gettext("Please enter your authentication name"))
4705 : NULL,
4706 NULL,
4707 pass_result == SASL_INTERACT ?
4708 convert_prompt(params->utils, &ctext->h,
4709 gettext("Please enter your password"))
4710 : NULL, NULL,
4711 NULL, NULL, NULL,
4712 realm_chal ? realm_chal : "{}",
4713 realm_result == SASL_INTERACT ?
4714 convert_prompt(params->utils, &ctext->h,
4715 gettext("Please enter your realm")) : NULL,
4716 params->serverFQDN ? params->serverFQDN : NULL);
4717 #else
4718 _plug_make_prompts(params->utils, prompt_need,
4719 user_result == SASL_INTERACT ?
4720 "Please enter your authorization name" : NULL,
4721 NULL,
4722 auth_result == SASL_INTERACT ?
4723 "Please enter your authentication name" : NULL,
4724 NULL,
4725 pass_result == SASL_INTERACT ?
4726 "Please enter your password" : NULL, NULL,
4727 NULL, NULL, NULL,
4728 realm_chal ? realm_chal : "{}",
4729 realm_result == SASL_INTERACT ?
4730 "Please enter your realm" : NULL,
4731 params->serverFQDN ? params->serverFQDN : NULL);
4732 #endif /* _INTEGRATED_SOLARIS_ */
4734 if (result == SASL_OK) return SASL_INTERACT;
4736 return result;
4739 if (oparams->authid == NULL) {
4740 if (!userid || !*userid) {
4741 result = params->canon_user(params->utils->conn, authid, 0,
4742 SASL_CU_AUTHID | SASL_CU_AUTHZID,
4743 oparams);
4745 else {
4746 result = params->canon_user(params->utils->conn,
4747 authid, 0, SASL_CU_AUTHID, oparams);
4748 if (result != SASL_OK) return result;
4750 result = params->canon_user(params->utils->conn,
4751 userid, 0, SASL_CU_AUTHZID, oparams);
4753 if (result != SASL_OK) return result;
4756 /* Get an allocated version of the realm into the structure */
4757 if (realm && text->realm == NULL) {
4758 _plug_strdup(params->utils, realm, (char **) &text->realm, NULL);
4761 return result;
4764 static int
4765 digestmd5_client_mech_new(void *glob_context,
4766 sasl_client_params_t * params,
4767 void **conn_context)
4769 context_t *text;
4771 /* holds state are in -- allocate client size */
4772 text = params->utils->malloc(sizeof(client_context_t));
4773 if (text == NULL)
4774 return SASL_NOMEM;
4775 memset(text, 0, sizeof(client_context_t));
4777 text->state = 1;
4778 text->i_am = CLIENT;
4779 text->reauth = glob_context;
4781 *conn_context = text;
4783 return SASL_OK;
4786 static int
4787 digestmd5_client_mech_step1(client_context_t *ctext,
4788 sasl_client_params_t *params,
4789 const char *serverin __attribute__((unused)),
4790 unsigned serverinlen __attribute__((unused)),
4791 sasl_interact_t **prompt_need,
4792 const char **clientout,
4793 unsigned *clientoutlen,
4794 sasl_out_params_t *oparams)
4796 context_t *text = (context_t *) ctext;
4797 int result = SASL_FAIL;
4798 unsigned val;
4800 params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4801 "DIGEST-MD5 client step 1");
4803 result = ask_user_info(ctext, params, NULL, 0, prompt_need, oparams);
4804 if (result != SASL_OK) return result;
4806 /* check if we have cached info for this user on this server */
4807 val = hash(params->serverFQDN) % text->reauth->size;
4808 if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
4809 if (text->reauth->e[val].u.c.serverFQDN &&
4810 !strcasecmp(text->reauth->e[val].u.c.serverFQDN,
4811 params->serverFQDN) &&
4812 !strcmp(text->reauth->e[val].authid, oparams->authid)) {
4814 #ifdef _SUN_SDK_
4815 if (text->realm) params->utils->free(text->realm);
4816 if (text->nonce) params->utils->free(text->nonce);
4817 if (text->cnonce) params->utils->free(text->cnonce);
4818 #endif /* _SUN_SDK_ */
4819 /* we have info, so use it */
4820 _plug_strdup(params->utils, text->reauth->e[val].realm,
4821 &text->realm, NULL);
4822 #ifdef _SUN_SDK_
4823 _plug_strdup(params->utils, (char *)text->reauth->e[val].nonce,
4824 (char **) &text->nonce, NULL);
4825 #else
4826 _plug_strdup(params->utils, text->reauth->e[val].nonce,
4827 (char **) &text->nonce, NULL);
4828 #endif /* _SUN_SDK_ */
4829 text->nonce_count = ++text->reauth->e[val].nonce_count;
4830 #ifdef _SUN_SDK_
4831 _plug_strdup(params->utils, (char *)text->reauth->e[val].cnonce,
4832 (char **) &text->cnonce, NULL);
4833 #else
4834 _plug_strdup(params->utils, text->reauth->e[val].cnonce,
4835 (char **) &text->cnonce, NULL);
4836 #endif /* _SUN_SDK_ */
4837 ctext->protection = text->reauth->e[val].u.c.protection;
4838 ctext->cipher = text->reauth->e[val].u.c.cipher;
4839 ctext->server_maxbuf = text->reauth->e[val].u.c.server_maxbuf;
4841 params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
4844 if (!text->nonce) {
4845 /* we don't have any reauth info, so just return
4846 * that there is no initial client send */
4847 text->state = 2;
4848 return SASL_CONTINUE;
4852 * (username | realm | nonce | cnonce | nonce-count | qop digest-uri |
4853 * response | maxbuf | charset | auth-param )
4856 result = make_client_response(text, params, oparams);
4857 if (result != SASL_OK) return result;
4859 *clientoutlen = strlen(text->out_buf);
4860 *clientout = text->out_buf;
4862 text->state = 3;
4863 return SASL_CONTINUE;
4866 static int
4867 digestmd5_client_mech_step2(client_context_t *ctext,
4868 sasl_client_params_t *params,
4869 const char *serverin,
4870 unsigned serverinlen,
4871 sasl_interact_t **prompt_need,
4872 const char **clientout,
4873 unsigned *clientoutlen,
4874 sasl_out_params_t *oparams)
4876 context_t *text = (context_t *) ctext;
4877 int result = SASL_FAIL;
4878 char **realms = NULL;
4879 int nrealm = 0;
4881 params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4882 "DIGEST-MD5 client step 2");
4884 if (params->props.min_ssf > params->props.max_ssf) {
4885 return SASL_BADPARAM;
4888 /* don't bother parsing the challenge more than once */
4889 if (text->nonce == NULL) {
4890 result = parse_server_challenge(ctext, params, serverin, serverinlen,
4891 &realms, &nrealm);
4892 if (result != SASL_OK) goto FreeAllocatedMem;
4894 if (nrealm == 1) {
4895 /* only one choice! */
4896 text->realm = realms[0];
4898 /* free realms */
4899 params->utils->free(realms);
4900 realms = NULL;
4904 result = ask_user_info(ctext, params, realms, nrealm,
4905 prompt_need, oparams);
4906 if (result != SASL_OK) goto FreeAllocatedMem;
4909 * (username | realm | nonce | cnonce | nonce-count | qop digest-uri |
4910 * response | maxbuf | charset | auth-param )
4913 result = make_client_response(text, params, oparams);
4914 if (result != SASL_OK) goto FreeAllocatedMem;
4916 *clientoutlen = strlen(text->out_buf);
4917 *clientout = text->out_buf;
4919 text->state = 3;
4921 result = SASL_CONTINUE;
4923 FreeAllocatedMem:
4924 if (realms) {
4925 int lup;
4927 /* need to free all the realms */
4928 for (lup = 0;lup < nrealm; lup++)
4929 params->utils->free(realms[lup]);
4931 params->utils->free(realms);
4934 return result;
4937 static int
4938 digestmd5_client_mech_step3(client_context_t *ctext,
4939 sasl_client_params_t *params,
4940 const char *serverin,
4941 unsigned serverinlen,
4942 sasl_interact_t **prompt_need __attribute__((unused)),
4943 const char **clientout __attribute__((unused)),
4944 unsigned *clientoutlen __attribute__((unused)),
4945 sasl_out_params_t *oparams)
4947 context_t *text = (context_t *) ctext;
4948 char *in = NULL;
4949 char *in_start;
4950 int result = SASL_FAIL;
4952 params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4953 "DIGEST-MD5 client step 3");
4955 /* Verify that server is really what they claim to be */
4956 in_start = in = params->utils->malloc(serverinlen + 1);
4957 if (in == NULL) return SASL_NOMEM;
4959 memcpy(in, serverin, serverinlen);
4960 in[serverinlen] = 0;
4962 /* parse the response */
4963 while (in[0] != '\0') {
4964 char *name, *value;
4965 get_pair(&in, &name, &value);
4967 if (name == NULL) {
4968 #ifdef _SUN_SDK_
4969 params->utils->log(params->utils->conn, SASL_LOG_ERR,
4970 "DIGEST-MD5 Received Garbage");
4971 #else
4972 params->utils->seterror(params->utils->conn, 0,
4973 "DIGEST-MD5 Received Garbage");
4974 #endif /* _SUN_SDK_ */
4975 break;
4978 if (strcasecmp(name, "rspauth") == 0) {
4980 if (strcmp(text->response_value, value) != 0) {
4981 #ifdef _INTEGRATED_SOLARIS_
4982 params->utils->seterror(params->utils->conn, 0,
4983 gettext("Server authentication failed"));
4984 #else
4985 params->utils->seterror(params->utils->conn, 0,
4986 "DIGEST-MD5: This server wants us to believe that he knows shared secret");
4987 #endif /* _INTEGRATED_SOLARIS_ */
4988 result = SASL_FAIL;
4989 } else {
4990 oparams->doneflag = 1;
4991 oparams->param_version = 0;
4993 result = SASL_OK;
4995 break;
4996 } else {
4997 params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4998 "DIGEST-MD5 unrecognized pair %s/%s: ignoring",
4999 name, value);
5003 params->utils->free(in_start);
5005 if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
5006 unsigned val = hash(params->serverFQDN) % text->reauth->size;
5007 switch (result) {
5008 case SASL_OK:
5009 if (text->nonce_count == 1) {
5010 /* successful initial auth, setup for future reauth */
5011 clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils);
5012 _plug_strdup(params->utils, oparams->authid,
5013 &text->reauth->e[val].authid, NULL);
5014 text->reauth->e[val].realm = text->realm; text->realm = NULL;
5015 text->reauth->e[val].nonce = text->nonce; text->nonce = NULL;
5016 text->reauth->e[val].nonce_count = text->nonce_count;
5017 text->reauth->e[val].cnonce = text->cnonce; text->cnonce = NULL;
5018 _plug_strdup(params->utils, params->serverFQDN,
5019 &text->reauth->e[val].u.c.serverFQDN, NULL);
5020 text->reauth->e[val].u.c.protection = ctext->protection;
5021 text->reauth->e[val].u.c.cipher = ctext->cipher;
5022 text->reauth->e[val].u.c.server_maxbuf = ctext->server_maxbuf;
5024 #ifndef _SUN_SDK_
5025 else {
5026 /* reauth, we already incremented nonce_count */
5028 #endif /* !_SUN_SDK_ */
5029 break;
5030 default:
5031 if (text->nonce_count > 1) {
5032 /* failed reauth, clear cache */
5033 clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils);
5035 else {
5036 /* failed initial auth, leave existing cache */
5039 params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
5042 return result;
5045 static int
5046 digestmd5_client_mech_step(void *conn_context,
5047 sasl_client_params_t *params,
5048 const char *serverin,
5049 unsigned serverinlen,
5050 sasl_interact_t **prompt_need,
5051 const char **clientout,
5052 unsigned *clientoutlen,
5053 sasl_out_params_t *oparams)
5055 context_t *text = (context_t *) conn_context;
5056 client_context_t *ctext = (client_context_t *) conn_context;
5057 unsigned val = hash(params->serverFQDN) % text->reauth->size;
5059 if (serverinlen > 2048) return SASL_BADPROT;
5061 *clientout = NULL;
5062 *clientoutlen = 0;
5064 switch (text->state) {
5066 case 1:
5067 if (!serverin) {
5068 /* here's where we attempt fast reauth if possible */
5069 int reauth = 0;
5071 /* check if we have saved info for this server */
5072 if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
5073 reauth = text->reauth->e[val].u.c.serverFQDN &&
5074 !strcasecmp(text->reauth->e[val].u.c.serverFQDN,
5075 params->serverFQDN);
5076 params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
5078 if (reauth) {
5079 return digestmd5_client_mech_step1(ctext, params,
5080 serverin, serverinlen,
5081 prompt_need,
5082 clientout, clientoutlen,
5083 oparams);
5085 else {
5086 /* we don't have any reauth info, so just return
5087 * that there is no initial client send */
5088 text->state = 2;
5089 return SASL_CONTINUE;
5093 /* fall through and respond to challenge */
5094 /* FALLTHROUGH */
5096 case 3:
5097 if (serverin && !strncasecmp(serverin, "rspauth=", 8)) {
5098 return digestmd5_client_mech_step3(ctext, params,
5099 serverin, serverinlen,
5100 prompt_need,
5101 clientout, clientoutlen,
5102 oparams);
5105 /* fall through and respond to challenge */
5106 text->state = 2;
5108 /* cleanup after a failed reauth attempt */
5109 if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
5110 clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils);
5112 params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
5115 if (text->realm) params->utils->free(text->realm);
5116 if (text->nonce) params->utils->free(text->nonce);
5117 if (text->cnonce) params->utils->free(text->cnonce);
5118 #ifdef _SUN_SDK_
5119 text->realm = NULL;
5120 text->nonce = text->cnonce = NULL;
5121 #else
5122 text->realm = text->nonce = text->cnonce = NULL;
5123 #endif /* _SUN_SDK_ */
5124 ctext->cipher = NULL;
5125 /* FALLTHROUGH */
5127 case 2:
5128 return digestmd5_client_mech_step2(ctext, params,
5129 serverin, serverinlen,
5130 prompt_need,
5131 clientout, clientoutlen,
5132 oparams);
5134 default:
5135 #ifdef _SUN_SDK_
5136 params->utils->log(params->utils->conn, SASL_LOG_ERR,
5137 "Invalid DIGEST-MD5 client step %d", text->state);
5138 #else
5139 params->utils->log(NULL, SASL_LOG_ERR,
5140 "Invalid DIGEST-MD5 client step %d\n", text->state);
5141 #endif /* _SUN_SDK_ */
5142 return SASL_FAIL;
5145 return SASL_FAIL; /* should never get here */
5148 static void
5149 digestmd5_client_mech_dispose(void *conn_context, const sasl_utils_t *utils)
5151 client_context_t *ctext = (client_context_t *) conn_context;
5153 if (!ctext || !utils) return;
5155 #ifdef _INTEGRATED_SOLARIS_
5156 convert_prompt(utils, &ctext->h, NULL);
5157 #endif /* _INTEGRATED_SOLARIS_ */
5159 if (ctext->free_password) _plug_free_secret(utils, &ctext->password);
5161 digestmd5_common_mech_dispose(conn_context, utils);
5164 static sasl_client_plug_t digestmd5_client_plugins[] =
5167 "DIGEST-MD5",
5168 #ifdef WITH_RC4 /* mech_name */
5169 128, /* max ssf */
5170 #elif WITH_DES
5171 112,
5172 #else
5174 #endif
5175 SASL_SEC_NOPLAINTEXT
5176 | SASL_SEC_NOANONYMOUS
5177 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
5178 SASL_FEAT_ALLOWS_PROXY, /* features */
5179 NULL, /* required_prompts */
5180 NULL, /* glob_context */
5181 &digestmd5_client_mech_new, /* mech_new */
5182 &digestmd5_client_mech_step, /* mech_step */
5183 &digestmd5_client_mech_dispose, /* mech_dispose */
5184 &digestmd5_common_mech_free, /* mech_free */
5185 NULL, /* idle */
5186 NULL, /* spare1 */
5187 NULL /* spare2 */
5191 int digestmd5_client_plug_init(sasl_utils_t *utils,
5192 int maxversion,
5193 int *out_version,
5194 sasl_client_plug_t **pluglist,
5195 int *plugcount)
5197 reauth_cache_t *reauth_cache;
5198 #if defined _SUN_SDK_ && defined USE_UEF
5199 int ret;
5200 #endif /* _SUN_SDK_ && USE_UEF */
5202 if (maxversion < SASL_CLIENT_PLUG_VERSION)
5203 return SASL_BADVERS;
5205 #if defined _SUN_SDK_ && defined USE_UEF
5206 if ((ret = uef_init(utils)) != SASL_OK)
5207 return ret;
5208 #endif /* _SUN_SDK_ && USE_UEF */
5210 /* reauth cache */
5211 reauth_cache = utils->malloc(sizeof(reauth_cache_t));
5212 if (reauth_cache == NULL)
5213 return SASL_NOMEM;
5214 memset(reauth_cache, 0, sizeof(reauth_cache_t));
5215 reauth_cache->i_am = CLIENT;
5217 /* mutex */
5218 reauth_cache->mutex = utils->mutex_alloc();
5219 if (!reauth_cache->mutex)
5220 return SASL_FAIL;
5222 /* entries */
5223 reauth_cache->size = 10;
5224 reauth_cache->e = utils->malloc(reauth_cache->size *
5225 sizeof(reauth_entry_t));
5226 if (reauth_cache->e == NULL)
5227 return SASL_NOMEM;
5228 memset(reauth_cache->e, 0, reauth_cache->size * sizeof(reauth_entry_t));
5230 digestmd5_client_plugins[0].glob_context = reauth_cache;
5231 #ifdef _SUN_SDK_
5232 #ifdef USE_UEF_CLIENT
5233 digestmd5_client_plugins[0].max_ssf = uef_max_ssf;
5234 #endif /* USE_UEF_CLIENT */
5235 #endif /* _SUN_SDK_ */
5237 #ifdef _INTEGRATED_SOLARIS_
5239 * Let libsasl know that we are a "Sun" plugin so that privacy
5240 * and integrity will be allowed.
5242 REG_PLUG("DIGEST-MD5", digestmd5_client_plugins);
5243 #endif /* _INTEGRATED_SOLARIS_ */
5245 *out_version = SASL_CLIENT_PLUG_VERSION;
5246 *pluglist = digestmd5_client_plugins;
5247 *plugcount = 1;
5249 return SASL_OK;
5252 #ifdef _SUN_SDK_
5253 #ifdef USE_UEF
5254 /* If we fail here - we should just not offer privacy or integrity */
5255 static int
5256 getSlotID(const sasl_utils_t *utils, CK_MECHANISM_TYPE mech_type,
5257 CK_SLOT_ID *slot_id)
5259 CK_RV rv;
5260 CK_ULONG ulSlotCount;
5261 CK_ULONG ulMechTypeCount;
5262 CK_SLOT_ID *pSlotList = NULL;
5263 CK_SLOT_ID slotID;
5264 CK_MECHANISM_TYPE_PTR pMechTypeList = NULL;
5265 int i, m;
5267 rv = C_GetSlotList(CK_FALSE, NULL_PTR, &ulSlotCount);
5268 if (rv != CKR_OK || ulSlotCount == 0) {
5269 #ifdef DEBUG
5270 utils->log(utils->conn, SASL_LOG_DEBUG,
5271 "C_GetSlotList: 0x%.8X count:%d\n", rv, ulSlotCount);
5272 #endif
5273 return SASL_FAIL;
5276 pSlotList = utils->calloc(sizeof (CK_SLOT_ID), ulSlotCount);
5277 if (pSlotList == NULL)
5278 return SASL_NOMEM;
5280 rv = C_GetSlotList(CK_FALSE, pSlotList, &ulSlotCount);
5281 if (rv != CKR_OK) {
5282 #ifdef DEBUG
5283 utils->log(utils->conn, SASL_LOG_DEBUG,
5284 "C_GetSlotList: 0x%.8X count:%d\n", rv, ulSlotCount);
5285 #endif
5286 return SASL_FAIL;
5289 for (i = 0; i < ulSlotCount; i++) {
5290 slotID = pSlotList[i];
5291 rv = C_GetMechanismList(slotID, NULL_PTR, &ulMechTypeCount);
5292 if (rv != CKR_OK) {
5293 #ifdef DEBUG
5294 utils->log(utils->conn, SASL_LOG_DEBUG,
5295 "C_GetMechanismList returned 0x%.8X count:%d\n", rv,
5296 ulMechTypeCount);
5297 #endif
5298 utils->free(pSlotList);
5299 return SASL_FAIL;
5301 pMechTypeList =
5302 utils->calloc(sizeof (CK_MECHANISM_TYPE), ulMechTypeCount);
5303 if (pMechTypeList == NULL_PTR) {
5304 utils->free(pSlotList);
5305 return SASL_NOMEM;
5307 rv = C_GetMechanismList(slotID, pMechTypeList, &ulMechTypeCount);
5308 if (rv != CKR_OK) {
5309 #ifdef DEBUG
5310 utils->log(utils->conn, SASL_LOG_DEBUG,
5311 "C_GetMechanismList returned 0x%.8X count:%d\n", rv,
5312 ulMechTypeCount);
5313 #endif
5314 utils->free(pMechTypeList);
5315 utils->free(pSlotList);
5316 return SASL_FAIL;
5319 for (m = 0; m < ulMechTypeCount; m++) {
5320 if (pMechTypeList[m] == mech_type)
5321 break;
5323 utils->free(pMechTypeList);
5324 pMechTypeList = NULL;
5325 if (m < ulMechTypeCount)
5326 break;
5328 utils->free(pSlotList);
5329 if (i < ulSlotCount) {
5330 *slot_id = slotID;
5331 return SASL_OK;
5333 return SASL_FAIL;
5336 static int
5337 uef_init(const sasl_utils_t *utils)
5339 int got_rc4;
5340 int got_des;
5341 int got_3des;
5342 int next_c;
5343 CK_RV rv;
5345 if (got_uef_slot)
5346 return (SASL_OK);
5348 if (LOCK_MUTEX(&uef_init_mutex) < 0)
5349 return (SASL_FAIL);
5351 rv = C_Initialize(NULL_PTR);
5352 if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
5353 #ifdef DEBUG
5354 utils->log(utils->conn, SASL_LOG_DEBUG,
5355 "C_Initialize returned 0x%.8X\n", rv);
5356 #endif
5357 return SASL_FAIL;
5360 got_rc4 = getSlotID(utils, CKM_RC4, &rc4_slot_id) == SASL_OK;
5361 if (!got_rc4)
5362 utils->log(utils->conn, SASL_LOG_WARN, "Could not get rc4");
5364 got_des = getSlotID(utils, CKM_DES_CBC, &des_slot_id) == SASL_OK;
5365 if (!got_des)
5366 utils->log(utils->conn, SASL_LOG_WARN, "Could not get des");
5368 got_3des = getSlotID(utils, CKM_DES3_CBC, &des3_slot_id) == SASL_OK;
5369 if (!got_3des)
5370 utils->log(utils->conn, SASL_LOG_WARN, "Could not get 3des");
5372 uef_max_ssf = got_rc4 ? 128 : got_3des ? 112 : got_des ? 55 : 0;
5374 /* adjust the available ciphers */
5375 next_c = (got_rc4) ? 3 : 0;
5377 if (got_des) {
5378 uef_ciphers[next_c].name = uef_ciphers[DES_CIPHER_INDEX].name;
5379 uef_ciphers[next_c].ssf = uef_ciphers[DES_CIPHER_INDEX].ssf;
5380 uef_ciphers[next_c].n = uef_ciphers[DES_CIPHER_INDEX].n;
5381 uef_ciphers[next_c].flag = uef_ciphers[DES_CIPHER_INDEX].flag;
5382 uef_ciphers[next_c].cipher_enc =
5383 uef_ciphers[DES_CIPHER_INDEX].cipher_enc;
5384 uef_ciphers[next_c].cipher_dec =
5385 uef_ciphers[DES_CIPHER_INDEX].cipher_dec;
5386 uef_ciphers[next_c].cipher_init =
5387 uef_ciphers[DES_CIPHER_INDEX].cipher_init;
5388 next_c++;
5391 if (got_3des) {
5392 uef_ciphers[next_c].name = uef_ciphers[DES3_CIPHER_INDEX].name;
5393 uef_ciphers[next_c].ssf = uef_ciphers[DES3_CIPHER_INDEX].ssf;
5394 uef_ciphers[next_c].n = uef_ciphers[DES3_CIPHER_INDEX].n;
5395 uef_ciphers[next_c].flag = uef_ciphers[DES3_CIPHER_INDEX].flag;
5396 uef_ciphers[next_c].cipher_enc =
5397 uef_ciphers[DES3_CIPHER_INDEX].cipher_enc;
5398 uef_ciphers[next_c].cipher_dec =
5399 uef_ciphers[DES3_CIPHER_INDEX].cipher_dec;
5400 uef_ciphers[next_c].cipher_init =
5401 uef_ciphers[DES3_CIPHER_INDEX].cipher_init;
5402 next_c++;
5404 uef_ciphers[next_c].name = NULL;
5406 got_uef_slot = TRUE;
5407 UNLOCK_MUTEX(&uef_init_mutex);
5409 return (SASL_OK);
5411 #endif /* USE_UEF */
5412 #endif /* _SUN_SDK_ */