1 /* $NetBSD: digest.c,v 1.1.1.2 2014/04/24 12:45:51 pettai Exp $ */
4 * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
8 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the Institute nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #include <sys/types.h>
41 #include <CommonCrypto/CommonDigest.h>
42 #include <CommonCrypto/CommonHMAC.h>
44 #include <krb5/roken.h>
46 #include "heim-auth.h"
47 #include <krb5/ntlm_err.h>
49 struct heim_digest_desc
{
53 uint8_t SecretHash
[CC_MD5_DIGEST_LENGTH
];
65 char *serverAlgorithm
;
69 #define FREE_AND_CLEAR(x) do { if ((x)) { free((x)); (x) = NULL; } } while(0)
70 #define MEMSET_FREE_AND_CLEAR(x) do { if ((x)) { memset(x, 0, strlen(x)); free((x)); (x) = NULL; } } while(0)
73 clear_context(heim_digest_t context
)
75 MEMSET_FREE_AND_CLEAR(context
->password
);
76 memset(context
->SecretHash
, 0, sizeof(context
->SecretHash
));
77 FREE_AND_CLEAR(context
->serverNonce
);
78 FREE_AND_CLEAR(context
->serverRealm
);
79 FREE_AND_CLEAR(context
->serverQOP
);
80 FREE_AND_CLEAR(context
->serverMethod
);
81 FREE_AND_CLEAR(context
->clientUsername
);
82 FREE_AND_CLEAR(context
->clientResponse
);
83 FREE_AND_CLEAR(context
->clientURI
);
84 FREE_AND_CLEAR(context
->clientRealm
);
85 FREE_AND_CLEAR(context
->clientNonce
);
86 FREE_AND_CLEAR(context
->clientQOP
);
87 FREE_AND_CLEAR(context
->clientNC
);
88 FREE_AND_CLEAR(context
->serverAlgorithm
);
89 FREE_AND_CLEAR(context
->auth_id
);
93 build_A1_hash(int type
,
94 const char *username
, const char *password
,
95 const char *realm
, const char *serverNonce
,
96 const char *clientNonce
,
99 unsigned char md
[CC_MD5_DIGEST_LENGTH
];
104 CC_MD5_Update(&ctx
, username
, strlen(username
));
105 CC_MD5_Update(&ctx
, ":", 1);
106 CC_MD5_Update(&ctx
, realm
, strlen(realm
));
107 CC_MD5_Update(&ctx
, ":", 1);
108 CC_MD5_Update(&ctx
, password
, strlen(password
));
109 CC_MD5_Final(md
, &ctx
);
111 if (type
!= HEIM_DIGEST_TYPE_RFC2069
) {
113 CC_MD5_Update(&ctx
, md
, sizeof(md
));
114 memset(md
, 0, sizeof(md
));
115 CC_MD5_Update(&ctx
, ":", 1);
116 CC_MD5_Update(&ctx
, serverNonce
, strlen(serverNonce
));
118 CC_MD5_Update(&ctx
, ":", 1);
119 CC_MD5_Update(&ctx
, clientNonce
, strlen(clientNonce
));
122 CC_MD5_Update(&ctx
, ":", 1);
123 CC_MD5_Update(&ctx
, auth_id
, strlen(auth_id
));
125 CC_MD5_Final(md
, &ctx
);
127 hex_encode(md
, sizeof(md
), &A1
);
135 build_A2_hash(heim_digest_t context
, const char *method
)
137 unsigned char md
[CC_MD5_DIGEST_LENGTH
];
143 CC_MD5_Update(&ctx
, method
, strlen(method
));
144 CC_MD5_Update(&ctx
, ":", 1);
145 CC_MD5_Update(&ctx
, context
->clientURI
, strlen(context
->clientURI
));
148 if (context
->type
!= HEIM_DIGEST_TYPE_RFC2069
) {
149 if (strcmp(context
->clientQOP
, "auth") != 0) {
150 /* XXX if we have a body hash, use that */
151 static char conf_zeros
[] = ":00000000000000000000000000000000";
152 CC_MD5_Update(&ctx
, conf_zeros
, sizeof(conf_zeros
) - 1);
155 /* support auth-int ? */
158 CC_MD5_Final(md
, &ctx
);
160 hex_encode(md
, sizeof(md
), &A2
);
174 struct md5_value
*mv_next
;
178 free_values(struct md5_value
*val
)
193 * Search for entry, if found, remove entry and return string to be freed.
197 values_find(struct md5_value
**val
, const char *v
)
199 struct md5_value
*cur
= *val
;
202 while (*val
!= NULL
) {
203 if (strcasecmp(v
, (*val
)->mv_name
) == 0)
205 val
= &(*val
)->mv_next
;
210 *val
= (*val
)->mv_next
;
220 parse_values(const char *string
, struct md5_value
**val
)
229 if ((str
= strdup(string
)) == NULL
)
236 while (p1
- str
< size
) {
237 sz
= strspn(p1
, " \t\n\r,");
241 sz
= strcspn(p1
, " \t\n\r=");
242 if (sz
== 0 || p1
[sz
] == '\0')
246 if ((v
= malloc(sizeof(*v
))) == NULL
)
248 v
->mv_name
= v
->mv_value
= NULL
;
251 if ((v
->mv_name
= malloc(p2
- p1
+ 1)) == NULL
)
253 strncpy(v
->mv_name
, p1
, p2
- p1
);
254 v
->mv_name
[p2
- p1
] = '\0';
256 sz
= strspn(p2
, " \t\n\r");
265 sz
= strspn(p2
, " \t\n\r");
275 p2
= strchr(p2
, '\"');
284 sz
= strcspn(p2
, " \t\n\r=,");
288 #if 0 /* allow empty values */
293 if ((v
->mv_value
= malloc(p2
- p1
+ 1)) == NULL
)
295 strncpy(v
->mv_value
, p1
, p2
- p1
);
296 v
->mv_value
[p2
- p1
] = '\0';
303 sz
= strspn(p2
, " \t\n\r");
335 heim_digest_create(int server
, int type
)
337 heim_digest_t context
;
339 context
= calloc(1, sizeof(*context
));
342 context
->server
= server
;
343 context
->type
= type
;
349 heim_digest_generate_challenge(heim_digest_t context
)
355 heim_digest_parse_challenge(heim_digest_t context
, const char *challenge
)
357 struct md5_value
*val
= NULL
;
360 ret
= parse_values(challenge
, &val
);
366 context
->serverNonce
= values_find(&val
, "nonce");
367 if (context
->serverNonce
== NULL
) goto out
;
369 context
->serverRealm
= values_find(&val
, "realm");
370 if (context
->serverRealm
== NULL
) goto out
;
372 context
->serverQOP
= values_find(&val
, "qop");
373 if (context
->serverQOP
== NULL
)
374 context
->serverQOP
= strdup("auth");
375 if (context
->serverQOP
== NULL
) goto out
;
379 context
->serverAlgorithm
= values_find(&val
, "algorithm");
380 if (context
->serverAlgorithm
== NULL
|| strcasecmp(context
->serverAlgorithm
, "md5") == 0) {
381 type
= HEIM_DIGEST_TYPE_RFC2069
;
382 } else if (strcasecmp(context
->serverAlgorithm
, "md5-sess") == 0) {
383 type
= HEIM_DIGEST_TYPE_MD5_SESS
;
388 if (context
->type
!= HEIM_DIGEST_TYPE_AUTO
&& context
->type
!= type
)
391 context
->type
= type
;
399 clear_context(context
);
404 heim_digest_parse_response(heim_digest_t context
, const char *response
)
406 struct md5_value
*val
= NULL
;
410 ret
= parse_values(response
, &val
);
416 if (context
->type
== HEIM_DIGEST_TYPE_AUTO
)
419 context
->clientUsername
= values_find(&val
, "username");
420 if (context
->clientUsername
== NULL
) goto out
;
422 context
->clientRealm
= values_find(&val
, "realm");
424 context
->clientResponse
= values_find(&val
, "response");
425 if (context
->clientResponse
== NULL
) goto out
;
427 nonce
= values_find(&val
, "nonce");
428 if (nonce
== NULL
) goto out
;
430 if (strcmp(nonce
, context
->serverNonce
) != 0) {
436 context
->clientQOP
= values_find(&val
, "qop");
437 if (context
->clientQOP
== NULL
)
438 context
->clientQOP
= strdup("auth");
439 if (context
->clientQOP
== NULL
) goto out
;
442 if (context
->type
!= HEIM_DIGEST_TYPE_RFC2069
) {
443 context
->clientNC
= values_find(&val
, "nc");
444 if (context
->clientNC
== NULL
) goto out
;
446 context
->clientNonce
= values_find(&val
, "cnonce");
447 if (context
->clientNonce
== NULL
) goto out
;
450 if (context
->type
== HEIM_DIGEST_TYPE_RFC2069
)
451 context
->clientURI
= values_find(&val
, "uri");
453 context
->clientURI
= values_find(&val
, "digest-uri");
454 if (context
->clientURI
== NULL
) goto out
;
463 heim_digest_get_key(heim_digest_t context
, const char *key
)
465 if (strcmp(key
, "username") == 0) {
466 return context
->clientUsername
;
467 } else if (strcmp(key
, "realm") == 0) {
468 return context
->clientRealm
;
475 heim_digest_set_key(heim_digest_t context
, const char *key
, const char *value
)
477 if (strcmp(key
, "password") == 0) {
478 FREE_AND_CLEAR(context
->password
);
479 if ((context
->password
= strdup(value
)) == NULL
)
481 } else if (strcmp(key
, "method") == 0) {
482 FREE_AND_CLEAR(context
->serverMethod
);
483 if ((context
->serverMethod
= strdup(value
)) != NULL
)
492 build_digest(heim_digest_t context
, const char *a1
, const char *method
)
495 uint8_t md
[CC_MD5_DIGEST_LENGTH
];
496 char *a2
, *str
= NULL
;
498 a2
= build_A2_hash(context
, method
);
503 CC_MD5_Update(&ctx
, a1
, strlen(a1
));
504 CC_MD5_Update(&ctx
, ":", 1);
505 CC_MD5_Update(&ctx
, context
->serverNonce
, strlen(context
->serverNonce
));
506 if (context
->type
!= HEIM_DIGEST_TYPE_RFC2069
) {
507 CC_MD5_Update(&ctx
, ":", 1);
508 CC_MD5_Update(&ctx
, context
->clientNC
, strlen(context
->clientNC
));
509 CC_MD5_Update(&ctx
, ":", 1);
510 CC_MD5_Update(&ctx
, context
->clientNonce
, strlen(context
->clientNonce
));
511 CC_MD5_Update(&ctx
, ":", 1);
512 CC_MD5_Update(&ctx
, context
->clientQOP
, strlen(context
->clientQOP
));
514 CC_MD5_Update(&ctx
, ":", 1);
515 CC_MD5_Update(&ctx
, a2
, strlen(a2
));
516 CC_MD5_Final(md
, &ctx
);
520 hex_encode(md
, sizeof(md
), &str
);
528 heim_digest_create_response(heim_digest_t context
)
534 heim_digest_verify(heim_digest_t context
, char **response
)
538 uint8_t md
[CC_MD5_DIGEST_LENGTH
];
545 if (context
->serverMethod
== NULL
) {
546 if (context
->type
!= HEIM_DIGEST_TYPE_RFC2069
)
547 context
->serverMethod
= strdup("AUTHENTICATE");
549 context
->serverMethod
= strdup("GET");
552 a1
= build_A1_hash(context
->type
,
553 context
->clientUsername
, context
->password
,
554 context
->serverRealm
, context
->serverNonce
,
555 context
->clientNonce
, context
->auth_id
);
559 str
= build_digest(context
, a1
, context
->serverMethod
);
561 MEMSET_FREE_AND_CLEAR(a1
);
565 res
= (strcmp(str
, context
->clientResponse
) == 0) ? 0 : EINVAL
;
568 MEMSET_FREE_AND_CLEAR(a1
);
572 /* build server_response */
574 str
= build_digest(context
, a1
, NULL
);
576 MEMSET_FREE_AND_CLEAR(a1
);
580 asprintf(response
, "rspauth=%s", str
);
583 MEMSET_FREE_AND_CLEAR(a1
);
589 heim_digest_get_session_key(heim_digest_t context
, void **key
, size_t *keySize
)
594 heim_digest_release(heim_digest_t context
)
596 clear_context(context
);