2 * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #include <sys/types.h>
39 #include <CommonCrypto/CommonDigest.h>
40 #include <CommonCrypto/CommonHMAC.h>
44 #include "heim-auth.h"
47 struct heim_digest_desc
{
51 #define F_USE_PREFIX 8
55 uint8_t SecretHash
[CC_MD5_DIGEST_LENGTH
];
69 char *serverAlgorithm
;
72 /* internally allocated objects returned to caller */
73 char *serverChallenge
;
78 #define FREE_AND_CLEAR(x) do { if ((x)) { free((x)); (x) = NULL; } } while(0)
79 #define MEMSET_FREE_AND_CLEAR(x) do { if ((x)) { memset(x, 0, strlen(x)); free((x)); (x) = NULL; } } while(0)
81 static const char digest_prefix
[] = "Digest ";
84 clear_context(heim_digest_t context
)
86 MEMSET_FREE_AND_CLEAR(context
->password
);
87 memset(context
->SecretHash
, 0, sizeof(context
->SecretHash
));
88 context
->flags
&= ~(F_HAVE_HASH
);
89 FREE_AND_CLEAR(context
->serverNonce
);
90 FREE_AND_CLEAR(context
->serverRealm
);
91 FREE_AND_CLEAR(context
->serverQOP
);
92 FREE_AND_CLEAR(context
->serverMethod
);
93 FREE_AND_CLEAR(context
->serverMaxbuf
);
94 FREE_AND_CLEAR(context
->serverOpaque
);
95 FREE_AND_CLEAR(context
->clientUsername
);
96 FREE_AND_CLEAR(context
->clientResponse
);
97 FREE_AND_CLEAR(context
->clientURI
);
98 FREE_AND_CLEAR(context
->clientRealm
);
99 FREE_AND_CLEAR(context
->clientNonce
);
100 FREE_AND_CLEAR(context
->clientQOP
);
101 FREE_AND_CLEAR(context
->clientNC
);
102 FREE_AND_CLEAR(context
->serverAlgorithm
);
103 FREE_AND_CLEAR(context
->auth_id
);
105 FREE_AND_CLEAR(context
->serverChallenge
);
106 FREE_AND_CLEAR(context
->clientReply
);
107 FREE_AND_CLEAR(context
->serverReply
);
111 digest_userhash(const char *user
, const char *realm
, const char *password
,
112 unsigned char md
[CC_MD5_DIGEST_LENGTH
])
117 CC_MD5_Update(&ctx
, user
, (CC_LONG
)strlen(user
));
118 CC_MD5_Update(&ctx
, ":", 1);
119 CC_MD5_Update(&ctx
, realm
, (CC_LONG
)strlen(realm
));
120 CC_MD5_Update(&ctx
, ":", 1);
121 CC_MD5_Update(&ctx
, password
, (CC_LONG
)strlen(password
));
122 CC_MD5_Final(md
, &ctx
);
126 build_A1_hash(heim_digest_t context
)
128 unsigned char md
[CC_MD5_DIGEST_LENGTH
];
132 if (context
->flags
& F_HAVE_HA1
) {
133 memcpy(md
, context
->SecretHash
, sizeof(md
));
134 } else if (context
->flags
& F_HAVE_HASH
) {
135 memcpy(md
, context
->SecretHash
, sizeof(md
));
136 } else if (context
->password
) {
137 if (context
->clientUsername
== NULL
)
139 if (context
->serverRealm
== NULL
)
141 digest_userhash(context
->clientUsername
,
142 context
->serverRealm
,
148 if ((context
->type
== HEIM_DIGEST_TYPE_RFC2617_MD5_SESS
|| context
->type
== HEIM_DIGEST_TYPE_RFC2831
) && (context
->flags
& F_HAVE_HA1
) == 0) {
149 if (context
->serverNonce
== NULL
)
153 CC_MD5_Update(&ctx
, md
, sizeof(md
));
154 memset(md
, 0, sizeof(md
));
155 CC_MD5_Update(&ctx
, ":", 1);
156 CC_MD5_Update(&ctx
, context
->serverNonce
, (CC_LONG
)strlen(context
->serverNonce
));
157 if (context
->clientNonce
) {
158 CC_MD5_Update(&ctx
, ":", 1);
159 CC_MD5_Update(&ctx
, context
->clientNonce
, (CC_LONG
)strlen(context
->clientNonce
));
161 if (context
->type
== HEIM_DIGEST_TYPE_RFC2831
&& context
->auth_id
) {
162 CC_MD5_Update(&ctx
, ":", 1);
163 CC_MD5_Update(&ctx
, context
->auth_id
, (CC_LONG
)strlen(context
->auth_id
));
165 CC_MD5_Final(md
, &ctx
);
167 hex_encode(md
, sizeof(md
), &A1
);
175 build_A2_hash(heim_digest_t context
, const char *method
)
177 unsigned char md
[CC_MD5_DIGEST_LENGTH
];
183 CC_MD5_Update(&ctx
, method
, (CC_LONG
)strlen(method
));
184 CC_MD5_Update(&ctx
, ":", 1);
185 CC_MD5_Update(&ctx
, context
->clientURI
, (CC_LONG
)strlen(context
->clientURI
));
188 if (context
->type
== HEIM_DIGEST_TYPE_RFC2831
) {
189 if (strcasecmp(context
->clientQOP
, "auth-int") == 0 || strcasecmp(context
->clientQOP
, "auth-conf") == 0) {
190 /* XXX if we have a body hash, use that */
191 static char conf_zeros
[] = ":00000000000000000000000000000000";
192 CC_MD5_Update(&ctx
, conf_zeros
, sizeof(conf_zeros
) - 1);
195 /* support auth-int ? */
196 if (context
->clientQOP
&& strcasecmp(context
->clientQOP
, "auth") != 0)
200 CC_MD5_Final(md
, &ctx
);
202 hex_encode(md
, sizeof(md
), &A2
);
216 struct md5_value
*mv_next
;
220 free_values(struct md5_value
*val
)
235 * Search for entry, if found, remove entry and return string to be freed.
239 values_find(struct md5_value
**val
, const char *v
)
241 struct md5_value
*cur
;
244 while (*val
!= NULL
) {
245 if (strcasecmp(v
, (*val
)->mv_name
) == 0)
247 val
= &(*val
)->mv_next
;
252 *val
= (*val
)->mv_next
;
262 parse_values(const char *string
, struct md5_value
**val
)
271 if ((str
= strdup(string
)) == NULL
)
278 while (p1
- str
< size
) {
279 sz
= strspn(p1
, " \t\n\r,");
283 sz
= strcspn(p1
, " \t\n\r=");
284 if (sz
== 0 || p1
[sz
] == '\0')
288 if ((v
= malloc(sizeof(*v
))) == NULL
)
290 v
->mv_name
= v
->mv_value
= NULL
;
293 if ((v
->mv_name
= malloc(p2
- p1
+ 1)) == NULL
)
295 strncpy(v
->mv_name
, p1
, p2
- p1
);
296 v
->mv_name
[p2
- p1
] = '\0';
298 sz
= strspn(p2
, " \t\n\r");
307 sz
= strspn(p2
, " \t\n\r");
317 p2
= strchr(p2
, '\"');
326 sz
= strcspn(p2
, " \t\n\r=,");
330 #if 0 /* allow empty values */
335 if ((v
->mv_value
= malloc(p2
- p1
+ 1)) == NULL
)
337 strncpy(v
->mv_value
, p1
, p2
- p1
);
338 v
->mv_value
[p2
- p1
] = '\0';
345 sz
= strspn(p2
, " \t\n\r");
377 check_prefix(heim_digest_t context
, const char *challenge
)
379 if (strncasecmp(digest_prefix
, challenge
, sizeof(digest_prefix
) - 1) == 0) {
381 challenge
+= sizeof(digest_prefix
) - 1;
382 while (*challenge
== 0x20) /* remove extra space */
384 context
->flags
|= F_USE_PREFIX
;
395 heim_digest_create(int server
, int type
)
397 heim_digest_t context
;
399 context
= calloc(1, sizeof(*context
));
402 context
->flags
|= F_SERVER
;
403 context
->type
= type
;
414 if (CCRandomCopyBytes(kCCRandomDefault
, rand
, sizeof(rand
)) != kCCSuccess
)
417 if (rk_hex_encode(rand
, sizeof(rand
), &nonce
) < 0)
424 * Generate a challange, needs to set serverRealm before calling this function.
426 * If type is set to HEIM_DIGEST_TYPE_AUTO, the HEIM_DIGEST_TYPE_RFC2831 will be used instead.
428 * For RFC2617 and RFC2831 QOP is required, so if any qop other then "auth" is requested, it need to be set with heim_diest_set_key().
430 * @return returns the challenge or NULL on error or failure to build the string. the lifetime
431 * of the string is manage by heim_digest and last until the the context is
432 * freed or until next call to heim_digest_generate_challenge().
436 heim_digest_generate_challenge(heim_digest_t context
)
438 char *challenge
= NULL
;
440 if (context
->serverRealm
== NULL
)
443 if (context
->serverNonce
== NULL
) {
444 if ((context
->serverNonce
= generate_nonce()) == NULL
)
448 if (context
->serverQOP
== NULL
) {
449 if ((context
->serverQOP
= strdup("auth")) == NULL
)
453 if (context
->serverMaxbuf
== NULL
) {
454 if ((context
->serverMaxbuf
= strdup("65536")) == NULL
)
458 switch(context
->type
) {
459 case HEIM_DIGEST_TYPE_RFC2617_MD5
:
460 asprintf(&challenge
, "realm=\"%s\",nonce=\"%s\",algorithm=md5,qop=\"%s\"",
461 context
->serverRealm
, context
->serverNonce
,
464 case HEIM_DIGEST_TYPE_RFC2617_MD5_SESS
:
465 asprintf(&challenge
, "realm=\"%s\",nonce=\"%s\",algorithm=md5-sess,qop=\"%s\"",
466 context
->serverRealm
, context
->serverNonce
, context
->serverQOP
);
468 case HEIM_DIGEST_TYPE_RFC2069
:
469 asprintf(&challenge
, "realm=\"%s\",nonce=\"%s\"",
470 context
->serverRealm
, context
->serverNonce
);
472 case HEIM_DIGEST_TYPE_AUTO
:
473 context
->type
= HEIM_DIGEST_TYPE_RFC2831
;
475 case HEIM_DIGEST_TYPE_RFC2831
:
476 asprintf(&challenge
, "realm=\"%s\",nonce=\"%s\",qop=\"%s\",algorithm=md5-sess,charset=utf-8,maxbuf=%s",
477 context
->serverRealm
, context
->serverNonce
, context
->serverQOP
, context
->serverMaxbuf
);
481 FREE_AND_CLEAR(context
->serverChallenge
);
482 context
->serverChallenge
= challenge
;
488 heim_digest_parse_challenge(heim_digest_t context
, const char *challenge
)
490 struct md5_value
*val
= NULL
;
493 challenge
= check_prefix(context
, challenge
);
495 ret
= parse_values(challenge
, &val
);
501 context
->serverNonce
= values_find(&val
, "nonce");
502 if (context
->serverNonce
== NULL
) goto out
;
504 context
->serverRealm
= values_find(&val
, "realm");
505 if (context
->serverRealm
== NULL
) goto out
;
509 context
->serverAlgorithm
= values_find(&val
, "algorithm");
510 if (context
->serverAlgorithm
== NULL
|| strcasecmp(context
->serverAlgorithm
, "md5") == 0) {
511 type
= HEIM_DIGEST_TYPE_RFC2617_MD5
;
512 } else if (strcasecmp(context
->serverAlgorithm
, "md5-sess") == 0) {
513 type
= HEIM_DIGEST_TYPE_RFC2617_OR_RFC2831
;
518 context
->serverQOP
= values_find(&val
, "qop");
519 if (context
->serverQOP
== NULL
)
520 type
= HEIM_DIGEST_TYPE_RFC2069
;
522 context
->serverOpaque
= values_find(&val
, "opaque");
524 if (context
->type
!= HEIM_DIGEST_TYPE_AUTO
&& (context
->type
& type
) == 0)
526 else if (context
->type
== HEIM_DIGEST_TYPE_AUTO
)
527 context
->type
= type
;
533 clear_context(context
);
539 set_auth_method(heim_digest_t context
)
542 if (context
->serverMethod
== NULL
) {
543 if (context
->type
== HEIM_DIGEST_TYPE_RFC2831
)
544 context
->serverMethod
= strdup("AUTHENTICATE");
546 context
->serverMethod
= strdup("GET");
551 heim_digest_parse_response(heim_digest_t context
, const char *response
)
553 struct md5_value
*val
= NULL
;
557 response
= check_prefix(context
, response
);
559 ret
= parse_values(response
, &val
);
565 if (context
->type
== HEIM_DIGEST_TYPE_AUTO
) {
567 } else if (context
->type
== HEIM_DIGEST_TYPE_RFC2617_OR_RFC2831
) {
568 context
->clientURI
= values_find(&val
, "uri");
569 if (context
->clientURI
) {
570 context
->type
= HEIM_DIGEST_TYPE_RFC2617_MD5_SESS
;
572 context
->clientURI
= values_find(&val
, "digest-uri");
573 context
->type
= HEIM_DIGEST_TYPE_RFC2831
;
575 } else if (context
->type
== HEIM_DIGEST_TYPE_RFC2831
) {
576 context
->clientURI
= values_find(&val
, "digest-uri");
578 context
->clientURI
= values_find(&val
, "uri");
581 if (context
->clientURI
== NULL
)
584 context
->clientUsername
= values_find(&val
, "username");
585 if (context
->clientUsername
== NULL
) goto out
;
587 /* if client sent realm, make sure its the same of serverRealm if its set */
588 context
->clientRealm
= values_find(&val
, "realm");
589 if (context
->clientRealm
&& context
->serverRealm
&& strcmp(context
->clientRealm
, context
->serverRealm
) != 0)
592 context
->clientResponse
= values_find(&val
, "response");
593 if (context
->clientResponse
== NULL
) goto out
;
595 nonce
= values_find(&val
, "nonce");
596 if (nonce
== NULL
) goto out
;
598 if (strcmp(nonce
, context
->serverNonce
) != 0) {
604 if (context
->type
!= HEIM_DIGEST_TYPE_RFC2069
) {
606 context
->clientQOP
= values_find(&val
, "qop");
607 if (context
->clientQOP
== NULL
) goto out
;
610 * If we have serverQOP, lets check that clientQOP exists
611 * in the list of server entries.
614 if (context
->serverQOP
) {
615 Boolean found
= false;
617 size_t len
, clen
= strlen(context
->clientQOP
);
619 b
= context
->serverQOP
;
620 while (b
&& !found
) {
628 if (clen
== len
&& strncmp(b
, context
->clientQOP
, len
) == 0)
636 context
->clientNC
= values_find(&val
, "nc");
637 if (context
->clientNC
== NULL
) goto out
;
639 context
->clientNonce
= values_find(&val
, "cnonce");
640 if (context
->clientNonce
== NULL
) goto out
;
643 set_auth_method(context
);
652 heim_digest_userhash(const char *user
, const char *realm
, const char *password
)
654 unsigned char md
[CC_MD5_DIGEST_LENGTH
];
657 digest_userhash(user
, realm
, password
, md
);
659 hex_encode(md
, sizeof(md
), &str
);
666 build_digest(heim_digest_t context
, const char *a1
, const char *method
)
669 uint8_t md
[CC_MD5_DIGEST_LENGTH
];
670 char *a2
, *str
= NULL
;
672 a2
= build_A2_hash(context
, method
);
677 CC_MD5_Update(&ctx
, a1
, (CC_LONG
)strlen(a1
));
678 CC_MD5_Update(&ctx
, ":", 1);
679 CC_MD5_Update(&ctx
, context
->serverNonce
, (CC_LONG
)strlen(context
->serverNonce
));
680 if (context
->type
!= HEIM_DIGEST_TYPE_RFC2069
) {
681 CC_MD5_Update(&ctx
, ":", 1);
682 CC_MD5_Update(&ctx
, context
->clientNC
, (CC_LONG
)strlen(context
->clientNC
));
683 CC_MD5_Update(&ctx
, ":", 1);
684 CC_MD5_Update(&ctx
, context
->clientNonce
, (CC_LONG
)strlen(context
->clientNonce
));
685 CC_MD5_Update(&ctx
, ":", 1);
686 CC_MD5_Update(&ctx
, context
->clientQOP
, (CC_LONG
)strlen(context
->clientQOP
));
688 CC_MD5_Update(&ctx
, ":", 1);
689 CC_MD5_Update(&ctx
, a2
, (CC_LONG
)strlen(a2
));
690 CC_MD5_Final(md
, &ctx
);
694 hex_encode(md
, sizeof(md
), &str
);
702 build_server_response(heim_digest_t context
, char *a1
, char **response
)
706 str
= build_digest(context
, a1
, NULL
);
710 FREE_AND_CLEAR(context
->serverReply
);
711 asprintf(&context
->serverReply
, "%srspauth=%s",
712 (context
->flags
& F_USE_PREFIX
) ? digest_prefix
: "",
716 *response
= context
->serverReply
;
721 * Create response from server to client to server, server verification is in response.
722 * clientUsername and clientURI have to be given.
723 * If realm is not set, its used from server.
727 heim_digest_create_response(heim_digest_t context
, char **response
)
729 char *a1
, *str
, *cnonce
= NULL
, *opaque
= NULL
, *uri
= NULL
, *nc
= NULL
;
734 if (context
->clientUsername
== NULL
|| context
->clientURI
== NULL
)
737 if (context
->clientRealm
== NULL
) {
738 if (context
->serverRealm
== NULL
)
740 if ((context
->clientRealm
= strdup(context
->serverRealm
)) == NULL
)
744 if (context
->type
!= HEIM_DIGEST_TYPE_RFC2069
) {
745 if (context
->clientNC
== NULL
) {
746 if ((context
->clientNC
= strdup("00000001")) == NULL
)
749 if (context
->clientNonce
== NULL
) {
750 if ((context
->clientNonce
= generate_nonce()) == NULL
)
755 * If using non RFC2069, appropriate QOP should be set.
757 * Pick QOP from server if not given, if its a list, pick the first entry
759 if (context
->clientQOP
== NULL
) {
761 if (context
->serverQOP
== NULL
)
763 r
= strchr(context
->serverQOP
, ',');
765 if ((context
->clientQOP
= strdup(context
->serverQOP
)) == NULL
)
768 size_t len
= (r
- context
->serverQOP
) + 1;
769 if ((context
->clientQOP
= malloc(len
)) == NULL
)
771 strlcpy(context
->clientQOP
, context
->serverQOP
, len
);
776 set_auth_method(context
);
778 a1
= build_A1_hash(context
);
782 str
= build_digest(context
, a1
, context
->serverMethod
);
784 MEMSET_FREE_AND_CLEAR(a1
);
788 MEMSET_FREE_AND_CLEAR(context
->clientResponse
);
789 context
->clientResponse
= str
;
791 if (context
->clientURI
) {
792 const char *name
= "digest-uri";
793 if (context
->type
!= HEIM_DIGEST_TYPE_RFC2831
)
795 asprintf(&uri
, ",%s=\"%s\"", name
, context
->clientURI
);
798 if (context
->serverOpaque
)
799 asprintf(&opaque
, ",opaque=\"%s\"", context
->serverOpaque
);
801 if (context
->clientNonce
)
802 asprintf(&cnonce
, ",cnonce=\"%s\"", context
->clientNonce
);
804 if (context
->clientNC
)
805 asprintf(&nc
, ",nc=%s", context
->clientNC
);
807 asprintf(&context
->clientReply
,
808 "username=%s,realm=%s,nonce=\"%s\",qop=\"%s\"%s%s%s,response=\"%s\"%s",
809 context
->clientUsername
, context
->clientRealm
,
810 context
->serverNonce
,
813 cnonce
? cnonce
: "",
815 context
->clientResponse
,
816 opaque
? opaque
: "");
818 build_server_response(context
, a1
, response
);
819 MEMSET_FREE_AND_CLEAR(a1
);
821 FREE_AND_CLEAR(opaque
);
822 FREE_AND_CLEAR(cnonce
);
825 return context
->clientReply
;
829 heim_digest_verify(heim_digest_t context
, char **response
)
838 set_auth_method(context
);
840 a1
= build_A1_hash(context
);
844 str
= build_digest(context
, a1
, context
->serverMethod
);
846 MEMSET_FREE_AND_CLEAR(a1
);
850 res
= (strcmp(str
, context
->clientResponse
) == 0) ? 0 : EINVAL
;
853 MEMSET_FREE_AND_CLEAR(a1
);
857 /* build server_response */
858 build_server_response(context
, a1
, response
);
859 MEMSET_FREE_AND_CLEAR(a1
);
860 /* XXX break ABI and return internally allocated string instead */
862 *response
= strdup(*response
);
868 * Create a rspauth= response.
869 * Assumes that the A1hash/password serverNonce, clientNC, clientNonce, clientQOP is set.
871 * @return the rspauth string (including rspauth), return key are stored in serverReply and will be invalid after another call to heim_digest_*
875 heim_digest_server_response(heim_digest_t context
)
879 if (context
->serverNonce
== NULL
)
881 if (context
->clientURI
== NULL
)
884 a1
= build_A1_hash(context
);
888 build_server_response(context
, a1
, NULL
);
889 MEMSET_FREE_AND_CLEAR(a1
);
891 return context
->serverReply
;
895 heim_digest_get_session_key(heim_digest_t context
, void **key
, size_t *keySize
)
900 heim_digest_release(heim_digest_t context
)
902 clear_context(context
);
910 #define KVN(value) { #value, offsetof(struct heim_digest_desc, value) }
915 { "method", offsetof(struct heim_digest_desc
, serverMethod
) },
918 { "username", offsetof(struct heim_digest_desc
, clientUsername
) },
921 { "uri", offsetof(struct heim_digest_desc
, clientURI
) },
923 { "realm", offsetof(struct heim_digest_desc
, clientRealm
) },
927 KVN(serverAlgorithm
),
933 heim_digest_get_key(heim_digest_t context
, const char *key
)
937 for (n
= 0; n
< sizeof(keys
) / sizeof(keys
[0]); n
++) {
938 if (strcasecmp(key
, keys
[n
].name
) == 0) {
939 char **ptr
= (char **)((((char *)context
) + keys
[n
].offset
));
947 heim_digest_set_key(heim_digest_t context
, const char *key
, const char *value
)
950 if (strcmp(key
, "password") == 0) {
951 FREE_AND_CLEAR(context
->password
);
952 if ((context
->password
= strdup(value
)) == NULL
)
954 context
->flags
&= ~(F_HAVE_HASH
|F_HAVE_HA1
);
955 } else if (strcmp(key
, "userhash") == 0) {
957 FREE_AND_CLEAR(context
->password
);
959 ret
= hex_decode(value
, context
->SecretHash
, sizeof(context
->SecretHash
));
960 if (ret
!= sizeof(context
->SecretHash
))
962 context
->flags
&= ~F_HAVE_HA1
;
963 context
->flags
|= F_HAVE_HASH
;
964 } else if (strcmp(key
, "H(A1)") == 0) {
966 FREE_AND_CLEAR(context
->password
);
968 ret
= hex_decode(value
, context
->SecretHash
, sizeof(context
->SecretHash
));
969 if (ret
!= sizeof(context
->SecretHash
))
971 context
->flags
&= ~F_HAVE_HASH
;
972 context
->flags
|= F_HAVE_HA1
;
973 } else if (strcmp(key
, "method") == 0) {
974 FREE_AND_CLEAR(context
->serverMethod
);
975 if ((context
->serverMethod
= strdup(value
)) == NULL
)
980 for (n
= 0; n
< sizeof(keys
) / sizeof(keys
[0]); n
++) {
981 if (strcasecmp(key
, keys
[n
].name
) == 0) {
982 char **ptr
= (char **)((((char *)context
) + keys
[n
].offset
));
983 FREE_AND_CLEAR(*ptr
);
984 if (((*ptr
) = strdup(value
)) == NULL
)
989 if (n
== sizeof(keys
) / sizeof(keys
[0]))