1 /* digest-md5.c implementation of SASL mechanism DIGEST-MD5 from RFC 2831
2 * Copyright (C) 2002 Simon Josefsson
3 * Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc. (getsubopt)
5 * This file is part of libgsasl.
7 * Libgsasl is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * Libgsasl is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with libgsasl; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 # include <inttypes.h>
42 #define NONCE_ENTROPY_BITS 64
43 #define CNONCE_ENTROPY_BITS 64
46 #define REALM_PRE "realm=\""
47 #define REALM_POST "\"" DELIM
48 #define NONCE_PRE "nonce=\""
49 #define NONCE_POST "\"" DELIM
50 #define QOP_LIST_PRE "qop=\""
51 #define QOP_LIST_POST "\"" DELIM
52 #define QOP_DELIM DELIM
53 #define QOP_AUTH "auth"
54 #define QOP_AUTH_INT "auth-int"
55 #define QOP_AUTH_CONF "auth-conf"
56 #define MAXBUF_PRE "maxbuf="
57 #define MAXBUF_POST DELIM
58 #define DEFAULT_CHARSET "utf-8"
59 #define CHARSET "charset=" DEFAULT_CHARSET DELIM
60 #define DEFAULT_ALGORITHM "md5-sess"
61 #define ALGORITHM "algorithm=" DEFAULT_ALGORITHM DELIM
62 #define CIPHER_PRE "cipher=\""
63 #define CIPHER_DELIM DELIM
64 #define CIPHER_DES "des"
65 #define CIPHER_3DES "3des"
66 #define CIPHER_RC4_40 "rc4-40"
67 #define CIPHER_RC4 "rc4"
68 #define CIPHER_RC4_56 "rc4-56"
69 #define CIPHER_AES "aes"
70 #define CIPHER_POST "\"" DELIM
72 #define USERNAME_PRE "username=\""
73 #define USERNAME_POST "\"" DELIM
74 #define CNONCE_PRE "cnonce=\""
75 #define CNONCE_POST "\"" DELIM
76 #define NONCE_COUNT_PRE "nc="
77 #define NONCE_COUNT_POST DELIM
78 #define QOP_PRE "qop="
79 #define QOP_POST DELIM
80 #define RESPONSE_PRE "response="
81 #define RESPONSE_POST "" DELIM
82 #define AUTHZID_PRE "authzid=\""
83 #define AUTHZID_POST "\"" DELIM
84 #define DIGEST_URI_PRE "digest-uri=\""
85 #define DIGEST_URI_POST "\"" DELIM
87 #define RSPAUTH_PRE "rspauth="
88 #define RSPAUTH_POST ""
90 #define A2_PRE "AUTHENTICATE:"
91 #define A2_POST ":00000000000000000000000000000000"
95 #define MAXBUF_DEFAULT 65536
96 #define RESPONSE_LENGTH 32
97 #define RSPAUTH_LENGTH RESPONSE_LENGTH
99 #define PRINT_OUTPUT 0
103 /* the order must match the following struct */
114 const char *digest_challenge_opts
[] =
116 /* the order must match the previous enum */
130 /* the order must match the following struct */
131 RESPONSE_USERNAME
= 0,
145 const char *digest_response_opts
[] =
147 /* the order must match the previous enum */
165 /* the order must match the following struct */
166 RESPONSEAUTH_RSPAUTH
= 0
169 const char *digest_responseauth_opts
[] =
171 /* the order must match the previous enum */
178 /* the order must match the following struct */
184 const char *qop_opts
[] =
186 /* the order must match the previous enum */
195 /* the order must match the following struct */
196 CIPHER_DES_OPTION
= 0,
199 CIPHER_RC4_40_OPTION
,
200 CIPHER_RC4_56_OPTION
,
204 const char *cipher_opts
[] =
206 /* the order must match the previous enum */
216 /* Parse comma separate list into words.
217 Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
218 From the GNU C Library, under GNU LGPL version 2.1.
219 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
220 Modified for Libgsasl by Simon Josefsson <simon@josefsson.org>
221 Copyright (C) 2002 Simon Josefsson
223 Parse comma separated suboption from *OPTIONP and match against
224 strings in TOKENS. If found return index and set *VALUEP to
225 optional value introduced by an equal sign. If the suboption is
226 not part of TOKENS return in *VALUEP beginning of unknown
227 suboption. On exit *OPTIONP is set to the beginning of the next
228 token or at the terminating NUL character. */
230 _gsasl_getsubopt (optionp
, tokens
, valuep
)
237 int inside_quote
= 0;
239 if (**optionp
== '\0')
242 /* Find end of next token. */
244 while (*endp
!= '\0' && (inside_quote
|| (!inside_quote
&& *endp
!= ',')))
247 inside_quote
= !inside_quote
;
251 /* Find start of value. */
252 vstart
= memchr (*optionp
, '=', endp
- *optionp
);
256 /* Try to match the characters between *OPTIONP and VSTART against
257 one of the TOKENS. */
258 for (cnt
= 0; tokens
[cnt
] != NULL
; ++cnt
)
259 if (memcmp (*optionp
, tokens
[cnt
], vstart
- *optionp
) == 0
260 && tokens
[cnt
][vstart
- *optionp
] == '\0')
262 /* We found the current option in TOKENS. */
263 *valuep
= vstart
!= endp
? vstart
+ 1 : NULL
;
265 while (*valuep
&& (**valuep
== ' ' ||
276 while (*endp
== ' ' ||
282 while (**optionp
== ' ' ||
291 /* The current suboption does not match any option. */
297 while (**optionp
== ' ' ||
307 _gsasl_digest (char *output
, /* must have 2*MD5LEN available bytes */
308 unsigned char secret
[MD5LEN
],
309 unsigned char *nonce
,
315 char *a2string
) /* "AUTHENTICATE:" or ":" */
318 char a1hexhash
[2*MD5LEN
];
319 char a2hexhash
[2*MD5LEN
];
326 md5h
= gcry_md_open (GCRY_MD_MD5
, 0);
328 return GSASL_GCRYPT_ERROR
;
330 gcry_md_write (md5h
, secret
, MD5LEN
);
331 gcry_md_write (md5h
, COLON
, strlen(COLON
));
332 gcry_md_write (md5h
, nonce
, strlen(nonce
));
333 gcry_md_write (md5h
, COLON
, strlen(COLON
));
334 gcry_md_write (md5h
, cnonce
, strlen(cnonce
));
335 if (authzid
&& strlen(authzid
) > 0)
337 gcry_md_write (md5h
, COLON
, strlen(COLON
));
338 gcry_md_write (md5h
, authzid
, strlen(authzid
));
341 hash
= gcry_md_read (md5h
, GCRY_MD_MD5
);
343 return GSASL_GCRYPT_ERROR
;
345 for (i
= 0; i
< MD5LEN
; i
++)
347 a1hexhash
[2*i
+ 1] = HEXCHAR(hash
[i
]);
348 a1hexhash
[2*i
+ 0] = HEXCHAR(hash
[i
] >> 4);
354 md5h
= gcry_md_open (GCRY_MD_MD5
, 0);
356 return GSASL_GCRYPT_ERROR
;
358 gcry_md_write (md5h
, a2string
, strlen(a2string
));
359 gcry_md_write (md5h
, digesturi
, strlen(digesturi
));
360 if (qop
& GSASL_QOP_AUTH_INT
|| qop
& GSASL_QOP_AUTH_CONF
)
362 gcry_md_write (md5h
, A2_POST
, strlen(A2_POST
));
365 hash
= gcry_md_read (md5h
, GCRY_MD_MD5
);
367 return GSASL_GCRYPT_ERROR
;
369 for (i
= 0; i
< MD5LEN
; i
++)
371 a2hexhash
[2*i
+ 1] = HEXCHAR(hash
[i
]);
372 a2hexhash
[2*i
+ 0] = HEXCHAR(hash
[i
] >> 4);
378 md5h
= gcry_md_open (GCRY_MD_MD5
, 0);
380 return GSASL_GCRYPT_ERROR
;
382 gcry_md_write (md5h
, a1hexhash
, 2*MD5LEN
);
383 gcry_md_write (md5h
, COLON
, strlen(COLON
));
384 gcry_md_write (md5h
, nonce
, strlen(nonce
));
385 gcry_md_write (md5h
, COLON
, strlen(COLON
));
386 sprintf(nchex
, "%0*x", NCLEN
, nc
);
387 gcry_md_write (md5h
, nchex
, strlen(nchex
));
388 gcry_md_write (md5h
, COLON
, strlen(COLON
));
389 gcry_md_write (md5h
, cnonce
, strlen(cnonce
));
390 gcry_md_write (md5h
, COLON
, strlen(COLON
));
391 if (qop
& GSASL_QOP_AUTH
)
392 gcry_md_write (md5h
, QOP_AUTH
, strlen(QOP_AUTH
));
393 else if (qop
& GSASL_QOP_AUTH_INT
)
394 gcry_md_write (md5h
, QOP_AUTH_INT
, strlen(QOP_AUTH_INT
));
395 else if (qop
& GSASL_QOP_AUTH_CONF
)
396 gcry_md_write (md5h
, QOP_AUTH_CONF
, strlen(QOP_AUTH_CONF
));
397 gcry_md_write (md5h
, COLON
, strlen(COLON
));
398 gcry_md_write (md5h
, a2hexhash
, 2*MD5LEN
);
400 hash
= gcry_md_read (md5h
, GCRY_MD_MD5
);
402 return GSASL_GCRYPT_ERROR
;
404 for (i
= 0; i
< MD5LEN
; i
++)
406 output
[2*i
+ 1] = HEXCHAR(hash
[i
]);
407 output
[2*i
+ 0] = HEXCHAR(hash
[i
] >> 4);
416 struct _Gsasl_digest_md5_client_state
{
418 unsigned char secret
[MD5LEN
];
419 unsigned char *nonce
;
421 unsigned char cnonce
[2*CNONCE_ENTROPY_BITS
/8+1];
423 unsigned char *authzid
;
424 unsigned char *digesturi
;
425 unsigned char response
[RESPONSE_LENGTH
+1];
427 typedef struct _Gsasl_digest_md5_client_state _Gsasl_digest_md5_client_state
;
430 _gsasl_digest_md5_client_init (Gsasl_ctx
*ctx
)
434 if (gcry_check_version(GCRYPT_VERSION
) == NULL
)
435 return GSASL_GCRYPT_ERROR
;
437 res
= gcry_control (GCRYCTL_INIT_SECMEM
, 512, 0);
438 if (res
!= GCRYERR_SUCCESS
)
439 return GSASL_GCRYPT_ERROR
;
445 _gsasl_digest_md5_client_done (Gsasl_ctx
*ctx
)
451 _gsasl_digest_md5_client_start (Gsasl_session_ctx
*cctx
,
454 _Gsasl_digest_md5_client_state
*state
;
457 ctx
= gsasl_client_ctx_get (cctx
);
459 return GSASL_CANNOT_GET_CTX
;
461 if (gsasl_client_callback_authentication_id_get (ctx
) == NULL
)
462 return GSASL_NEED_CLIENT_AUTHENTICATION_ID_CALLBACK
;
464 if (gsasl_client_callback_password_get (ctx
) == NULL
)
465 return GSASL_NEED_CLIENT_PASSWORD_CALLBACK
;
467 state
= (_Gsasl_digest_md5_client_state
*) malloc(sizeof(*state
));
469 return GSASL_MALLOC_ERROR
;
474 state
->qop
= GSASL_QOP_AUTH
;
475 state
->authzid
= NULL
;
476 state
->digesturi
= NULL
;
484 _gsasl_digest_md5_client_step (Gsasl_session_ctx
*cctx
,
491 _Gsasl_digest_md5_client_state
*state
= mech_data
;
492 Gsasl_client_callback_authorization_id cb_authorization_id
;
493 Gsasl_client_callback_authentication_id cb_authentication_id
;
494 Gsasl_client_callback_password cb_password
;
495 Gsasl_client_callback_service cb_service
;
496 Gsasl_client_callback_qop cb_qop
;
497 Gsasl_client_callback_maxbuf cb_maxbuf
;
504 ctx
= gsasl_client_ctx_get (cctx
);
506 return GSASL_CANNOT_GET_CTX
;
508 cb_qop
= gsasl_client_callback_qop_get (ctx
);
509 cb_authorization_id
= gsasl_client_callback_authorization_id_get (ctx
);
510 cb_maxbuf
= gsasl_client_callback_maxbuf_get (ctx
);
512 cb_authentication_id
= gsasl_client_callback_authentication_id_get (ctx
);
513 if (cb_authentication_id
== NULL
)
514 return GSASL_NEED_CLIENT_AUTHENTICATION_ID_CALLBACK
;
516 cb_password
= gsasl_client_callback_password_get (ctx
);
517 if (cb_password
== NULL
)
518 return GSASL_NEED_CLIENT_PASSWORD_CALLBACK
;
520 cb_service
= gsasl_client_callback_service_get (ctx
);
521 if (cb_service
== NULL
)
522 return GSASL_NEED_CLIENT_SERVICE_CALLBACK
;
525 return GSASL_TOO_SMALL_BUFFER
;
531 if (input
&& input_len
> 0)
541 int maxbuf
= MAXBUF_DEFAULT
;
547 return GSASL_NEEDS_MORE
;
550 gcry_randomize (state
->cnonce
, CNONCE_ENTROPY_BITS
/8,GCRY_WEAK_RANDOM
);
551 for (i
= 0; i
< CNONCE_ENTROPY_BITS
/8; i
++)
553 state
->cnonce
[CNONCE_ENTROPY_BITS
/8 + i
] =
554 HEXCHAR(state
->cnonce
[i
]);
555 state
->cnonce
[i
] = HEXCHAR(state
->cnonce
[i
] >> 4);
557 state
->cnonce
[2*CNONCE_ENTROPY_BITS
/8] = '\0';
560 while (*subopts
!= '\0')
561 switch (_gsasl_getsubopt (&subopts
, digest_challenge_opts
, &value
))
563 case CHALLENGE_REALM
:
565 realm
= (char**) malloc(sizeof(*realm
));
567 realm
= realloc(realm
, (nrealm
+ 1) * sizeof(*realm
));
570 res
= GSASL_MALLOC_ERROR
;
573 realm
[nrealm
] = strdup(value
);
577 case CHALLENGE_NONCE
:
578 if (state
->nonce
!= NULL
)
580 res
= GSASL_MECHANISM_PARSE_ERROR
;
583 state
->nonce
= strdup(value
);
593 while (*subsubopts
!= '\0')
594 switch (_gsasl_getsubopt (&subsubopts
, qop_opts
, &val
))
596 case QOP_AUTH_OPTION
:
597 state
->qop
|= GSASL_QOP_AUTH
;
600 case QOP_AUTH_INT_OPTION
:
601 state
->qop
|= GSASL_QOP_AUTH_INT
;
604 case QOP_AUTH_CONF_OPTION
:
605 state
->qop
|= GSASL_QOP_AUTH_CONF
;
609 /* Ignore unknown qop */
615 case CHALLENGE_STALE
:
616 printf("XXX stale: %s\n", value
);
619 case CHALLENGE_MAXBUF
:
620 maxbuf
= strtol(value
, NULL
, 10);
623 case CHALLENGE_CHARSET
:
624 if (strcmp(DEFAULT_CHARSET
, value
) != 0)
626 res
= GSASL_MECHANISM_PARSE_ERROR
;
631 case CHALLENGE_ALGORITHM
:
632 if (strcmp(DEFAULT_ALGORITHM
, value
) != 0)
634 res
= GSASL_MECHANISM_PARSE_ERROR
;
639 case CHALLENGE_CIPHER
:
645 while (*subsubopts
!= '\0')
646 switch (_gsasl_getsubopt (&subsubopts
, cipher_opts
, &val
))
648 case CIPHER_DES_OPTION
:
649 cipher
|= GSASL_CIPHER_DES
;
652 case CIPHER_3DES_OPTION
:
653 cipher
|= GSASL_CIPHER_3DES
;
656 case CIPHER_RC4_OPTION
:
657 cipher
|= GSASL_CIPHER_RC4
;
660 case CIPHER_RC4_40_OPTION
:
661 cipher
|= GSASL_CIPHER_RC4_40
;
664 case CIPHER_RC4_56_OPTION
:
665 cipher
|= GSASL_CIPHER_RC4_56
;
668 case CIPHER_AES_OPTION
:
669 cipher
|= GSASL_CIPHER_AES
;
673 /* Ignoring unknown cipher. */
680 /* Ignoring unknown parameter. */
683 if (state
->qop
== 0 || cipher
== 0 || state
->nonce
== NULL
)
685 res
= GSASL_MECHANISM_PARSE_ERROR
;
689 state
->qop
= cb_qop (cctx
, state
->qop
);
691 state
->qop
= GSASL_QOP_AUTH
;
692 if (cb_authorization_id
)
696 res
= cb_authorization_id (cctx
, NULL
, &authzidlen
);
699 state
->authzid
= (char*) malloc(authzidlen
+1);
700 if (state
->authzid
== NULL
)
702 res
= GSASL_MALLOC_ERROR
;
705 res
= cb_authorization_id (cctx
, state
->authzid
, &authzidlen
);
708 state
->authzid
[authzidlen
] = '\0';
714 res
= cb_authentication_id (cctx
, NULL
, &usernamelen
);
719 strlen(USERNAME_PRE
) +
721 strlen(USERNAME_POST
) >= *output_len
)
723 res
= GSASL_TOO_SMALL_BUFFER
;
727 strcat(output
, USERNAME_PRE
);
728 outlen
+= strlen(USERNAME_PRE
);
730 res
= cb_authentication_id (cctx
, &output
[outlen
], &usernamelen
);
733 outlen
+= usernamelen
;
735 strcat(output
, USERNAME_POST
);
736 outlen
+= strlen(USERNAME_POST
);
744 strlen(REALM_POST
) >= *output_len
)
746 res
= GSASL_TOO_SMALL_BUFFER
;
750 strcat(output
, REALM_PRE
);
751 outlen
+= strlen(REALM_PRE
);
753 strcat(output
, realm
[0]);
754 outlen
+= strlen(realm
[0]);
756 strcat(output
, REALM_POST
);
757 outlen
+= strlen(REALM_POST
);
763 strlen(state
->nonce
) +
764 strlen(NONCE_POST
) >= *output_len
)
766 res
= GSASL_TOO_SMALL_BUFFER
;
770 strcat(output
, NONCE_PRE
);
771 outlen
+= strlen(NONCE_PRE
);
773 strcat(output
, state
->nonce
);
774 outlen
+= strlen(state
->nonce
);
776 strcat(output
, NONCE_POST
);
777 outlen
+= strlen(NONCE_POST
);
783 strlen(state
->cnonce
) +
784 strlen(CNONCE_POST
) >= *output_len
)
786 res
= GSASL_TOO_SMALL_BUFFER
;
790 strcat(output
, CNONCE_PRE
);
791 outlen
+= strlen(CNONCE_PRE
);
793 strcat(output
, state
->cnonce
);
794 outlen
+= strlen(state
->cnonce
);
796 strcat(output
, CNONCE_POST
);
797 outlen
+= strlen(CNONCE_POST
);
802 strlen(NONCE_COUNT_PRE
) +
804 strlen(NONCE_COUNT_POST
) >= *output_len
)
806 res
= GSASL_TOO_SMALL_BUFFER
;
810 strcat(output
, NONCE_COUNT_PRE
);
811 outlen
+= strlen(NONCE_COUNT_PRE
);
813 sprintf(output
+outlen
, "%0*x", NCLEN
, state
->nc
);
816 strcat(output
, NONCE_COUNT_POST
);
817 outlen
+= strlen(NONCE_COUNT_POST
);
823 if (state
->qop
& GSASL_QOP_AUTH
)
825 else if (state
->qop
& GSASL_QOP_AUTH_INT
)
826 qopstr
= QOP_AUTH_INT
;
827 else if (state
->qop
& GSASL_QOP_AUTH_CONF
)
828 qopstr
= QOP_AUTH_CONF
;
833 strlen(QOP_POST
) >= *output_len
)
835 res
= GSASL_TOO_SMALL_BUFFER
;
839 strcat(output
, QOP_PRE
);
840 outlen
+= strlen(QOP_PRE
);
842 strcat(output
, qopstr
);
843 outlen
+= strlen(qopstr
);
845 strcat(output
, QOP_POST
);
846 outlen
+= strlen(QOP_POST
);
850 char *service
= NULL
;
851 size_t servicelen
= 0;
852 char *hostname
= NULL
;
853 size_t hostnamelen
= 0;
854 char *servicename
= NULL
;
855 size_t servicenamelen
= 0;
858 res
= cb_service (cctx
, NULL
, &servicelen
,
860 NULL
, &servicenamelen
);
863 len
= servicelen
+ strlen("/") + hostnamelen
+
864 (servicenamelen
> 0 ? strlen("/") + servicenamelen
: 0) + 1;
865 state
->digesturi
= malloc(len
);
866 if (state
->digesturi
== NULL
)
868 res
= GSASL_MALLOC_ERROR
;
871 res
= cb_service (cctx
, state
->digesturi
, &servicelen
,
872 state
->digesturi
+ 1 + servicelen
, &hostnamelen
,
873 (servicenamelen
> 0 ?
874 state
->digesturi
+ 1 + servicelen
+ 1 +
875 hostnamelen
: NULL
), &servicenamelen
);
878 state
->digesturi
[servicelen
] = '/';
879 state
->digesturi
[servicelen
+ 1 + hostnamelen
] = '/';
880 state
->digesturi
[len
-1] = '\0';
883 strlen(DIGEST_URI_PRE
) +
885 strlen(DIGEST_URI_POST
) >= *output_len
)
887 res
= GSASL_TOO_SMALL_BUFFER
;
891 strcat(output
, DIGEST_URI_PRE
);
892 outlen
+= strlen(DIGEST_URI_PRE
);
894 strcat(output
, state
->digesturi
);
895 outlen
+= strlen(state
->digesturi
);
897 strcat(output
, DIGEST_URI_POST
);
898 outlen
+= strlen(DIGEST_URI_POST
);
908 strlen(RESPONSE_PRE
) +
910 strlen(RESPONSE_POST
) >= *output_len
)
912 res
= GSASL_TOO_SMALL_BUFFER
;
916 strcat(output
, RESPONSE_PRE
);
917 outlen
+= strlen(RESPONSE_PRE
);
919 md5h
= gcry_md_open (GCRY_MD_MD5
, 0);
922 res
= GSASL_GCRYPT_ERROR
;
925 len
= *output_len
- outlen
;
926 res
= cb_authentication_id (cctx
, output
+outlen
, &len
);
930 gcry_md_write (md5h
, output
+outlen
, len
);
931 gcry_md_write (md5h
, COLON
, strlen(COLON
));
933 gcry_md_write (md5h
, realm
[0], strlen(realm
[0]));
934 gcry_md_write (md5h
, COLON
, strlen(COLON
));
936 len
= *output_len
- outlen
;
937 /* XXX? password stored in callee's output buffer */
938 res
= cb_password (cctx
, output
+outlen
, &len
);
941 tmp
= gsasl_utf8_nfkc_normalize (output
+outlen
, len
);
944 res
= GSASL_UNICODE_NORMALIZATION_ERROR
;
947 gcry_md_write (md5h
, tmp
, strlen(tmp
));
949 if (res
!= GCRYERR_SUCCESS
)
950 return GSASL_GCRYPT_ERROR
;
952 tmp
= gcry_md_read (md5h
, GCRY_MD_MD5
);
955 res
= GSASL_GCRYPT_ERROR
;
958 memcpy(state
->secret
, tmp
, MD5LEN
);
961 res
= _gsasl_digest (state
->response
, state
->secret
,
962 state
->nonce
, state
->nc
, state
->cnonce
,
963 state
->qop
, state
->authzid
,
964 state
->digesturi
, A2_PRE
);
967 state
->response
[RESPONSE_LENGTH
] = '\0';
968 memcpy(output
+outlen
, state
->response
, RESPONSE_LENGTH
+1);
969 outlen
+= RESPONSE_LENGTH
;
971 strcat(output
, RESPONSE_POST
);
972 outlen
+= strlen(RESPONSE_POST
);
974 if (cb_maxbuf
&& (maxbuf
= cb_maxbuf(cctx
, maxbuf
)) != MAXBUF_DEFAULT
)
978 /* XXX don't require -lm, how? */
979 1+floor(log10(INT_MAX
)) +
980 strlen(MAXBUF_POST
) >= *output_len
)
981 return GSASL_TOO_SMALL_BUFFER
;
983 strcat(output
, MAXBUF_PRE
);
984 outlen
+= strlen(MAXBUF_PRE
);
986 sprintf(&output
[outlen
], "%d", maxbuf
);
987 outlen
+= strlen(&output
[outlen
]);
989 strcat(output
, MAXBUF_POST
);
990 outlen
+= strlen(MAXBUF_POST
);
993 if (state
->authzid
&& strlen(state
->authzid
) > 0)
996 strlen(AUTHZID_PRE
) +
997 strlen(state
->authzid
) +
998 strlen(AUTHZID_POST
) >= *output_len
)
1000 res
= GSASL_TOO_SMALL_BUFFER
;
1004 strcat(output
, AUTHZID_PRE
);
1005 outlen
+= strlen(AUTHZID_PRE
);
1007 strcat(output
, state
->authzid
);
1008 outlen
+= strlen(state
->authzid
);
1010 strcat(output
, AUTHZID_POST
);
1011 outlen
+= strlen(AUTHZID_POST
);
1014 res
= GSASL_NEEDS_MORE
;
1019 *output_len
= outlen
;
1027 res
= GSASL_MECHANISM_PARSE_ERROR
;
1031 res
= GSASL_AUTHENTICATION_ERROR
;
1033 while (*subopts
!= '\0')
1034 switch (_gsasl_getsubopt (&subopts
, digest_responseauth_opts
, &value
))
1036 case RESPONSEAUTH_RSPAUTH
:
1037 res
= _gsasl_digest (output
+outlen
, state
->secret
,
1038 state
->nonce
, state
->nc
,
1039 state
->cnonce
, state
->qop
,
1040 state
->authzid
, state
->digesturi
, COLON
);
1041 if (res
!= GSASL_OK
)
1044 if (memcmp(value
, output
+ outlen
, RESPONSE_LENGTH
) == 0)
1047 res
= GSASL_AUTHENTICATION_ERROR
;
1051 /* Unknown suboption. */
1059 res
= GSASL_MECHANISM_CALLED_TOO_MANY_TIMES
;
1064 if (output
&& *output_len
> 0)
1072 _gsasl_digest_md5_client_finish (Gsasl_session_ctx
*cctx
,
1075 _Gsasl_digest_md5_client_state
*state
= mech_data
;
1078 free (state
->authzid
);
1080 free (state
->nonce
);
1081 if (state
->digesturi
)
1082 free (state
->digesturi
);
1090 struct _Gsasl_digest_md5_server_state
{
1092 char nonce
[NONCE_ENTROPY_BITS
/8];
1094 typedef struct _Gsasl_digest_md5_server_state _Gsasl_digest_md5_server_state
;
1097 _gsasl_digest_md5_server_init (Gsasl_ctx
*ctx
)
1099 if (gcry_check_version(GCRYPT_VERSION
) == NULL
)
1100 return GSASL_GCRYPT_ERROR
;
1106 _gsasl_digest_md5_server_done (Gsasl_ctx
*ctx
)
1112 _gsasl_digest_md5_server_start (Gsasl_session_ctx
*sctx
,
1115 _Gsasl_digest_md5_server_state
*state
;
1116 Gsasl_server_callback_retrieve cb_retrieve
;
1117 Gsasl_server_callback_digest_md5 cb_digest_md5
;
1120 ctx
= gsasl_server_ctx_get (sctx
);
1122 return GSASL_CANNOT_GET_CTX
;
1124 cb_retrieve
= gsasl_server_callback_retrieve_get (ctx
);
1125 cb_digest_md5
= gsasl_server_callback_digest_md5_get (ctx
);
1127 if (gsasl_server_callback_digest_md5_get (ctx
) == NULL
&&
1128 gsasl_server_callback_retrieve_get (ctx
) == NULL
)
1129 return GSASL_NEED_SERVER_DIGEST_MD5_CALLBACK
;
1131 state
= (_Gsasl_digest_md5_server_state
*) malloc(sizeof(*state
));
1133 return GSASL_MALLOC_ERROR
;
1136 gcry_randomize (state
->nonce
, NONCE_ENTROPY_BITS
/8, GCRY_WEAK_RANDOM
);
1144 _gsasl_digest_md5_server_step (Gsasl_session_ctx
*sctx
,
1151 _Gsasl_digest_md5_server_state
*state
= mech_data
;
1152 Gsasl_server_callback_realm cb_realm
;
1153 Gsasl_server_callback_qop cb_qop
;
1154 Gsasl_server_callback_maxbuf cb_maxbuf
;
1155 Gsasl_server_callback_cipher cb_cipher
;
1156 Gsasl_server_callback_retrieve cb_retrieve
;
1157 Gsasl_server_callback_digest_md5 cb_digest_md5
;
1163 ctx
= gsasl_server_ctx_get (sctx
);
1165 return GSASL_CANNOT_GET_CTX
;
1167 cb_realm
= gsasl_server_callback_realm_get (ctx
);
1168 cb_qop
= gsasl_server_callback_qop_get (ctx
);
1169 cb_maxbuf
= gsasl_server_callback_maxbuf_get (ctx
);
1170 cb_cipher
= gsasl_server_callback_cipher_get (ctx
);
1171 cb_retrieve
= gsasl_server_callback_retrieve_get (ctx
);
1172 cb_digest_md5
= gsasl_server_callback_digest_md5_get (ctx
);
1174 if (gsasl_server_callback_digest_md5_get (ctx
) == NULL
&&
1175 gsasl_server_callback_retrieve_get (ctx
) == NULL
)
1176 return GSASL_NEED_SERVER_DIGEST_MD5_CALLBACK
;
1178 if (*output_len
< 1)
1179 return GSASL_TOO_SMALL_BUFFER
;
1185 if (input
&& input_len
> 0)
1189 switch (state
->step
)
1197 realmlen
= *output_len
;
1198 for (i
= 0; cb_realm(sctx
, NULL
, &realmlen
, i
) == GSASL_OK
; i
++)
1200 if (outlen
+ strlen(REALM_PRE
) +
1201 realmlen
+ strlen(REALM_POST
) >= *output_len
)
1202 return GSASL_TOO_SMALL_BUFFER
;
1204 strcat(output
, REALM_PRE
);
1205 outlen
+= strlen(REALM_PRE
);
1207 cb_realm(sctx
, &output
[outlen
], &realmlen
, i
);
1209 output
[outlen
] = '\0';
1211 strcat(output
, REALM_POST
);
1212 outlen
+= strlen(REALM_POST
);
1214 realmlen
= *output_len
- outlen
;
1221 if (outlen
+ strlen(NONCE_PRE
) +
1222 2 * NONCE_ENTROPY_BITS
/8 + strlen(NONCE_POST
) >= *output_len
)
1223 return GSASL_TOO_SMALL_BUFFER
;
1225 strcat(output
, NONCE_PRE
);
1226 outlen
+= strlen(NONCE_PRE
);
1228 for (i
= 0; i
< NONCE_ENTROPY_BITS
/8; i
++)
1230 output
[outlen
+ 2*i
+ 1] = HEXCHAR(state
->nonce
[i
]);
1231 output
[outlen
+ 2*i
+ 0] = HEXCHAR(state
->nonce
[i
] >> 4);
1233 output
[outlen
+ 2 * NONCE_ENTROPY_BITS
/8] = '\0';
1234 outlen
+= 2 * NONCE_ENTROPY_BITS
/8;
1236 strcat(output
, NONCE_POST
);
1237 outlen
+= strlen(NONCE_POST
);
1244 strlen(QOP_LIST_PRE
) +
1246 strlen(QOP_AUTH_INT
) +
1247 strlen(QOP_AUTH_CONF
) +
1248 strlen(QOP_LIST_POST
) >= *output_len
)
1249 return GSASL_TOO_SMALL_BUFFER
;
1254 qop
= GSASL_QOP_AUTH
| GSASL_QOP_AUTH_INT
| GSASL_QOP_AUTH_CONF
;
1256 strcat(output
, QOP_LIST_PRE
);
1257 outlen
+= strlen(QOP_LIST_PRE
);
1259 if (qop
& GSASL_QOP_AUTH
)
1261 strcat(output
, QOP_AUTH
);
1262 outlen
+= strlen(QOP_AUTH
);
1264 strcat(output
, QOP_DELIM
);
1265 outlen
+= strlen(QOP_DELIM
);
1268 if (qop
& GSASL_QOP_AUTH_INT
)
1270 strcat(output
, QOP_AUTH_INT
);
1271 outlen
+= strlen(QOP_AUTH_INT
);
1273 strcat(output
, QOP_DELIM
);
1274 outlen
+= strlen(QOP_DELIM
);
1277 if (qop
& GSASL_QOP_AUTH_CONF
)
1279 strcat(output
, QOP_AUTH_CONF
);
1280 outlen
+= strlen(QOP_AUTH_CONF
);
1283 strcat(output
, QOP_LIST_POST
);
1284 outlen
+= strlen(QOP_LIST_POST
);
1287 if (cb_maxbuf
&& (maxbuf
= cb_maxbuf(sctx
)) != MAXBUF_DEFAULT
)
1290 strlen(MAXBUF_PRE
) +
1291 /* XXX don't require -lm, how? */
1292 1+floor(log10(INT_MAX
)) +
1293 strlen(MAXBUF_POST
) >= *output_len
)
1294 return GSASL_TOO_SMALL_BUFFER
;
1296 strcat(output
, MAXBUF_PRE
);
1297 outlen
+= strlen(MAXBUF_PRE
);
1299 sprintf(&output
[outlen
], "%d", maxbuf
);
1300 outlen
+= strlen(&output
[outlen
]);
1302 strcat(output
, MAXBUF_POST
);
1303 outlen
+= strlen(MAXBUF_POST
);
1307 if (outlen
+ strlen(CHARSET
) >= *output_len
)
1308 return GSASL_TOO_SMALL_BUFFER
;
1310 strcat(output
, CHARSET
);
1311 outlen
+= strlen(CHARSET
);
1315 if (outlen
+ strlen(ALGORITHM
) >= *output_len
)
1316 return GSASL_TOO_SMALL_BUFFER
;
1318 strcat(output
, ALGORITHM
);
1319 outlen
+= strlen(ALGORITHM
);
1323 Gsasl_cipher cipher
;
1326 strlen(CIPHER_PRE
) +
1327 strlen(CIPHER_DES
) +
1328 strlen(CIPHER_DELIM
) +
1329 strlen(CIPHER_3DES
) +
1330 strlen(CIPHER_DELIM
) +
1331 strlen(CIPHER_RC4
) +
1332 strlen(CIPHER_DELIM
) +
1333 strlen(CIPHER_RC4_40
) +
1334 strlen(CIPHER_DELIM
) +
1335 strlen(CIPHER_RC4_56
) +
1336 strlen(CIPHER_DELIM
) +
1337 strlen(CIPHER_AES
) +
1338 strlen(CIPHER_DELIM
) +
1339 strlen(CIPHER_POST
) >= *output_len
)
1340 return GSASL_TOO_SMALL_BUFFER
;
1343 cipher
= cb_cipher(sctx
);
1345 cipher
= GSASL_CIPHER_DES
| GSASL_CIPHER_3DES
| GSASL_CIPHER_RC4
|
1346 GSASL_CIPHER_RC4_40
| GSASL_CIPHER_RC4_56
| GSASL_CIPHER_AES
;
1348 strcat(output
, CIPHER_PRE
);
1349 outlen
+= strlen(CIPHER_PRE
);
1351 if (cipher
& GSASL_CIPHER_DES
)
1353 strcat(output
, CIPHER_DES
);
1354 outlen
+= strlen(CIPHER_DES
);
1356 strcat(output
, CIPHER_DELIM
);
1357 outlen
+= strlen(CIPHER_DELIM
);
1360 if (cipher
& GSASL_CIPHER_3DES
)
1362 strcat(output
, CIPHER_3DES
);
1363 outlen
+= strlen(CIPHER_3DES
);
1365 strcat(output
, CIPHER_DELIM
);
1366 outlen
+= strlen(CIPHER_DELIM
);
1369 if (cipher
& GSASL_CIPHER_RC4
)
1371 strcat(output
, CIPHER_RC4
);
1372 outlen
+= strlen(CIPHER_RC4
);
1374 strcat(output
, CIPHER_DELIM
);
1375 outlen
+= strlen(CIPHER_DELIM
);
1378 if (cipher
& GSASL_CIPHER_RC4_40
)
1380 strcat(output
, CIPHER_RC4_40
);
1381 outlen
+= strlen(CIPHER_RC4_40
);
1383 strcat(output
, CIPHER_DELIM
);
1384 outlen
+= strlen(CIPHER_DELIM
);
1387 if (cipher
& GSASL_CIPHER_RC4_56
)
1389 strcat(output
, CIPHER_RC4_56
);
1390 outlen
+= strlen(CIPHER_RC4_56
);
1392 strcat(output
, CIPHER_DELIM
);
1393 outlen
+= strlen(CIPHER_DELIM
);
1396 if (cipher
& GSASL_CIPHER_AES
)
1398 strcat(output
, CIPHER_AES
);
1399 outlen
+= strlen(CIPHER_AES
);
1401 strcat(output
, CIPHER_DELIM
);
1402 outlen
+= strlen(CIPHER_DELIM
);
1405 strcat(output
, CIPHER_POST
);
1406 outlen
+= strlen(CIPHER_POST
);
1408 *output_len
= outlen
;
1410 res
= GSASL_NEEDS_MORE
;
1415 unsigned char *nonce
= NULL
;
1416 char *cnonce
= NULL
;
1418 char *authzid
= NULL
;
1419 char *digesturi
= NULL
;
1420 const char *subopts
, *value
;
1421 unsigned char *realm
= NULL
;
1422 unsigned char *username
= NULL
;
1423 unsigned char *response
= NULL
;
1424 int maxbuf
= MAXBUF_DEFAULT
;
1425 int qop
= GSASL_QOP_AUTH
;
1428 GCRY_MD_HD md5h
= NULL
;
1429 unsigned char secret
[MD5LEN
];
1434 return GSASL_MECHANISM_PARSE_ERROR
;
1438 while (*subopts
!= '\0')
1439 switch (_gsasl_getsubopt (&subopts
, digest_response_opts
, &value
))
1441 case RESPONSE_USERNAME
:
1442 if (username
!= NULL
)
1444 res
= GSASL_MECHANISM_PARSE_ERROR
;
1447 username
= strdup(value
);
1450 case RESPONSE_REALM
:
1453 res
= GSASL_MECHANISM_PARSE_ERROR
;
1456 realm
= strdup(value
);
1459 case RESPONSE_NONCE
:
1462 res
= GSASL_MECHANISM_PARSE_ERROR
;
1465 nonce
= strdup(value
);
1467 for (i
= 0; i
< MIN(strlen(value
), NONCE_ENTROPY_BITS
/8); i
++)
1468 if ((nonce
[2*i
+ 1] != HEXCHAR(state
->nonce
[i
])) ||
1469 (nonce
[2*i
+ 0] != HEXCHAR(state
->nonce
[i
] >> 4)))
1470 res
= GSASL_MECHANISM_PARSE_ERROR
;
1471 if (res
!= GSASL_OK
)
1475 case RESPONSE_CNONCE
:
1478 res
= GSASL_MECHANISM_PARSE_ERROR
;
1481 cnonce
= strdup(value
);
1487 res
= GSASL_MECHANISM_PARSE_ERROR
;
1490 nc
= strtoul(value
, NULL
, 16);
1494 if (strcmp(value
, QOP_AUTH
) == 0)
1495 qop
= GSASL_QOP_AUTH
;
1496 else if (strcmp(value
, QOP_AUTH_INT
) == 0)
1497 qop
= GSASL_QOP_AUTH_INT
;
1498 else if (strcmp(value
, QOP_AUTH_CONF
) == 0)
1499 qop
= GSASL_QOP_AUTH_CONF
;
1502 res
= GSASL_MECHANISM_PARSE_ERROR
;
1507 case RESPONSE_DIGEST_URI
:
1508 if (digesturi
!= NULL
)
1510 res
= GSASL_MECHANISM_PARSE_ERROR
;
1513 digesturi
= strdup(value
);
1516 case RESPONSE_RESPONSE
:
1517 if (response
!= NULL
)
1519 res
= GSASL_MECHANISM_PARSE_ERROR
;
1522 response
= strdup(value
);
1525 case RESPONSE_MAXBUF
:
1526 maxbuf
= strtol(value
, NULL
, 10);
1529 case RESPONSE_CHARSET
:
1530 if (strcmp(DEFAULT_CHARSET
, value
) != 0)
1532 res
= GSASL_MECHANISM_PARSE_ERROR
;
1537 case RESPONSE_CIPHER
:
1540 res
= GSASL_MECHANISM_PARSE_ERROR
;
1543 if (strcmp(value
, CIPHER_DES
) == 0)
1544 cipher
= GSASL_CIPHER_DES
;
1545 else if (strcmp(value
, CIPHER_3DES
) == 0)
1546 cipher
= GSASL_CIPHER_3DES
;
1547 else if (strcmp(value
, CIPHER_RC4
) == 0)
1548 cipher
= GSASL_CIPHER_RC4
;
1549 else if (strcmp(value
, CIPHER_RC4_40
) == 0)
1550 cipher
= GSASL_CIPHER_RC4_40
;
1551 else if (strcmp(value
, CIPHER_RC4_56
) == 0)
1552 cipher
= GSASL_CIPHER_RC4_56
;
1553 else if (strcmp(value
, CIPHER_AES
) == 0)
1554 cipher
= GSASL_CIPHER_AES
;
1557 res
= GSASL_MECHANISM_PARSE_ERROR
;
1562 case RESPONSE_AUTHZID
:
1563 if (authzid
!= NULL
)
1565 res
= GSASL_MECHANISM_PARSE_ERROR
;
1568 authzid
= strdup(value
);
1572 /* Ignoring unknown parameter. */
1576 if (username
== NULL
|| nonce
== NULL
||
1577 cnonce
== NULL
|| response
== NULL
||
1578 (qop
== GSASL_QOP_AUTH_CONF
&& cipher
== 0))
1580 res
= GSASL_MECHANISM_PARSE_ERROR
;
1585 strlen(RSPAUTH_PRE
) +
1587 strlen(RSPAUTH_POST
) >= *output_len
)
1589 res
= GSASL_TOO_SMALL_BUFFER
;
1600 res
= cb_retrieve(sctx
, username
, authzid
, realm
, NULL
, &keylen
);
1601 if (res
!= GSASL_OK
)
1603 key
= malloc(keylen
);
1606 res
= GSASL_MALLOC_ERROR
;
1609 res
= cb_retrieve(sctx
, username
, authzid
, realm
, key
, &keylen
);
1610 if (res
!= GSASL_OK
)
1615 normkey
= gsasl_utf8_nfkc_normalize (key
, keylen
);
1617 if (normkey
== NULL
)
1619 res
= GSASL_UNICODE_NORMALIZATION_ERROR
;
1623 md5h
= gcry_md_open (GCRY_MD_MD5
, 0);
1626 res
= GSASL_GCRYPT_ERROR
;
1629 gcry_md_write (md5h
, username
, strlen(username
));
1630 gcry_md_write (md5h
, COLON
, strlen(COLON
));
1632 gcry_md_write (md5h
, realm
, strlen(realm
));
1633 gcry_md_write (md5h
, COLON
, strlen(COLON
));
1634 gcry_md_write (md5h
, normkey
, strlen(normkey
));
1636 tmp
= gcry_md_read (md5h
, GCRY_MD_MD5
);
1639 res
= GSASL_GCRYPT_ERROR
;
1642 memcpy(secret
, tmp
, MD5LEN
);
1644 else /* if (cb_digest_md5) */
1646 /* XXX? secret hash stored in callee's output buffer */
1647 res
= cb_digest_md5 (sctx
, username
, realm
, output
+outlen
);
1648 if (res
!= GSASL_OK
)
1651 memcpy(secret
, output
+outlen
, MD5LEN
);
1654 /* verify response */
1655 res
= _gsasl_digest (output
+outlen
, secret
,
1656 nonce
, nc
, cnonce
, qop
, authzid
,
1658 if (res
!= GSASL_OK
)
1661 if (memcmp(response
, output
+ outlen
, RESPONSE_LENGTH
) != 0)
1663 res
= GSASL_AUTHENTICATION_ERROR
;
1667 output
[outlen
] = '\0';
1669 /* XXX check more things here. digest-uri? valid qop, cipher,
1670 nc etc. nonce, which is the most important, is checked
1673 /* generate rspauth */
1675 strcat(output
, RSPAUTH_PRE
);
1676 outlen
+= strlen(RSPAUTH_PRE
);
1678 res
= _gsasl_digest (output
+outlen
, secret
,
1679 nonce
, nc
, cnonce
, qop
, authzid
,
1681 if (res
!= GSASL_OK
)
1683 outlen
+= RSPAUTH_LENGTH
;
1685 strcat(output
, RSPAUTH_POST
);
1686 outlen
+= strlen(RSPAUTH_POST
);
1689 gcry_md_close(md5h
);
1691 res
= GSASL_NEEDS_MORE
;
1708 *output_len
= outlen
;
1719 res
= GSASL_MECHANISM_CALLED_TOO_MANY_TIMES
;
1724 if (output
&& *output_len
> 0)
1732 _gsasl_digest_md5_server_finish (Gsasl_session_ctx
*sctx
,
1735 _Gsasl_digest_md5_server_state
*state
= mech_data
;
1742 #endif /* USE_DIGEST_MD5 */