2 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 __RCSID("$Heimdal: digest.c 22374 2007-12-28 18:36:52Z lha $"
38 "$NetBSD: digest.c,v 1.1 2008/03/22 08:37:03 mlelstv Exp $");
40 #define MS_CHAP_V2 0x20
42 #define DIGEST_MD5 0x08
44 #define NTLM_V1_SESSION 0x02
47 const struct units _kdc_digestunits
[] = {
48 {"ms-chap-v2", 1U << 5},
49 {"chap-md5", 1U << 4},
50 {"digest-md5", 1U << 3},
52 {"ntlm-v1-session", 1U << 1},
58 static krb5_error_code
59 get_digest_key(krb5_context context
,
60 krb5_kdc_configuration
*config
,
68 ret
= _kdc_get_preferred_key(context
,
76 return krb5_crypto_init(context
, &key
->key
, 0, crypto
);
84 get_ntlm_targetname(krb5_context context
,
89 targetname
= strdup(krb5_principal_get_realm(context
,
90 client
->entry
.principal
));
91 if (targetname
== NULL
)
94 p
= strchr(targetname
, '.');
102 static krb5_error_code
103 fill_targetinfo(krb5_context context
,
105 hdb_entry_ex
*client
,
108 struct ntlm_targetinfo ti
;
114 memset(&ti
, 0, sizeof(ti
));
116 ti
.domainname
= targetname
;
117 p
= client
->entry
.principal
;
118 str
= krb5_principal_get_comp_string(context
, p
, 0);
120 (strcmp("host", str
) == 0 ||
121 strcmp("ftp", str
) == 0 ||
122 strcmp("imap", str
) == 0 ||
123 strcmp("pop", str
) == 0 ||
124 strcmp("smtp", str
)))
126 str
= krb5_principal_get_comp_string(context
, p
, 1);
127 ti
.dnsservername
= rk_UNCONST(str
);
130 ret
= heim_ntlm_encode_targetinfo(&ti
, 1, &d
);
135 data
->length
= d
.length
;
141 static const unsigned char ms_chap_v2_magic1
[39] = {
142 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
143 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
144 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
145 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
147 static const unsigned char ms_chap_v2_magic2
[41] = {
148 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
149 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
150 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
151 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
154 static const unsigned char ms_rfc3079_magic1
[27] = {
155 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
156 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
157 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
164 static krb5_error_code
165 get_password_entry(krb5_context context
,
166 krb5_kdc_configuration
*config
,
167 const char *username
,
170 krb5_principal clientprincipal
;
176 ret
= krb5_parse_name(context
, username
, &clientprincipal
);
180 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
181 HDB_F_GET_CLIENT
, &db
, &user
);
182 krb5_free_principal(context
, clientprincipal
);
186 ret
= hdb_entry_get_password(context
, db
, &user
->entry
, password
);
187 if (ret
|| password
== NULL
) {
190 krb5_set_error_string(context
, "password missing");
192 memset(user
, 0, sizeof(*user
));
194 _kdc_free_ent (context
, user
);
203 _kdc_do_digest(krb5_context context
,
204 krb5_kdc_configuration
*config
,
205 const DigestREQ
*req
, krb5_data
*reply
,
206 const char *from
, struct sockaddr
*addr
)
208 krb5_error_code ret
= 0;
209 krb5_ticket
*ticket
= NULL
;
210 krb5_auth_context ac
= NULL
;
211 krb5_keytab id
= NULL
;
212 krb5_crypto crypto
= NULL
;
216 krb5_flags ap_req_options
;
219 krb5_storage
*sp
= NULL
;
221 hdb_entry_ex
*server
= NULL
, *user
= NULL
;
222 hdb_entry_ex
*client
= NULL
;
223 char *client_name
= NULL
, *password
= NULL
;
224 krb5_data serverNonce
;
226 if(!config
->enable_digest
) {
227 kdc_log(context
, config
, 0,
228 "Rejected digest request (disabled) from %s", from
);
229 return KRB5KDC_ERR_POLICY
;
232 krb5_data_zero(&buf
);
233 krb5_data_zero(reply
);
234 krb5_data_zero(&serverNonce
);
235 memset(&ireq
, 0, sizeof(ireq
));
236 memset(&r
, 0, sizeof(r
));
237 memset(&rep
, 0, sizeof(rep
));
239 kdc_log(context
, config
, 0, "Digest request from %s", from
);
241 ret
= krb5_kt_resolve(context
, "HDB:", &id
);
243 kdc_log(context
, config
, 0, "Can't open database for digest");
247 ret
= krb5_rd_req(context
,
257 /* check the server principal in the ticket matches digest/R@R */
259 krb5_principal principal
= NULL
;
262 ret
= krb5_ticket_get_server(context
, ticket
, &principal
);
267 krb5_set_error_string(context
, "Wrong digest server principal used");
268 p
= krb5_principal_get_comp_string(context
, principal
, 0);
270 krb5_free_principal(context
, principal
);
273 if (strcmp(p
, KRB5_DIGEST_NAME
) != 0) {
274 krb5_free_principal(context
, principal
);
278 p
= krb5_principal_get_comp_string(context
, principal
, 1);
280 krb5_free_principal(context
, principal
);
283 r
= krb5_principal_get_realm(context
, principal
);
285 krb5_free_principal(context
, principal
);
288 if (strcmp(p
, r
) != 0) {
289 krb5_free_principal(context
, principal
);
292 krb5_clear_error_string(context
);
294 ret
= _kdc_db_fetch(context
, config
, principal
,
295 HDB_F_GET_SERVER
, NULL
, &server
);
299 krb5_free_principal(context
, principal
);
302 /* check the client is allowed to do digest auth */
304 krb5_principal principal
= NULL
;
306 ret
= krb5_ticket_get_client(context
, ticket
, &principal
);
310 ret
= krb5_unparse_name(context
, principal
, &client_name
);
312 krb5_free_principal(context
, principal
);
316 ret
= _kdc_db_fetch(context
, config
, principal
,
317 HDB_F_GET_CLIENT
, NULL
, &client
);
318 krb5_free_principal(context
, principal
);
322 if (client
->entry
.flags
.allow_digest
== 0) {
323 kdc_log(context
, config
, 0,
324 "Client %s tried to use digest "
325 "but is not allowed to",
327 krb5_set_error_string(context
,
328 "Client is not permitted to use digest");
329 ret
= KRB5KDC_ERR_POLICY
;
338 ret
= krb5_auth_con_getremotesubkey(context
, ac
, &key
);
342 krb5_set_error_string(context
, "digest: remote subkey not found");
347 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
348 krb5_free_keyblock (context
, key
);
353 ret
= krb5_decrypt_EncryptedData(context
, crypto
, KRB5_KU_DIGEST_ENCRYPT
,
354 &req
->innerReq
, &buf
);
355 krb5_crypto_destroy(context
, crypto
);
360 ret
= decode_DigestReqInner(buf
.data
, buf
.length
, &ireq
, NULL
);
361 krb5_data_free(&buf
);
363 krb5_set_error_string(context
, "Failed to decode digest inner request");
367 kdc_log(context
, config
, 0, "Valid digest request from %s (%s)",
371 * Process the inner request
374 switch (ireq
.element
) {
375 case choice_DigestReqInner_init
: {
376 unsigned char server_nonce
[16], identifier
;
378 RAND_pseudo_bytes(&identifier
, sizeof(identifier
));
379 RAND_pseudo_bytes(server_nonce
, sizeof(server_nonce
));
381 server_nonce
[0] = kdc_time
& 0xff;
382 server_nonce
[1] = (kdc_time
>> 8) & 0xff;
383 server_nonce
[2] = (kdc_time
>> 16) & 0xff;
384 server_nonce
[3] = (kdc_time
>> 24) & 0xff;
386 r
.element
= choice_DigestRepInner_initReply
;
388 hex_encode(server_nonce
, sizeof(server_nonce
), &r
.u
.initReply
.nonce
);
389 if (r
.u
.initReply
.nonce
== NULL
) {
390 krb5_set_error_string(context
, "Failed to decode server nonce");
395 sp
= krb5_storage_emem();
398 krb5_set_error_string(context
, "out of memory");
401 ret
= krb5_store_stringz(sp
, ireq
.u
.init
.type
);
403 krb5_clear_error_string(context
);
407 if (ireq
.u
.init
.channel
) {
410 asprintf(&s
, "%s-%s:%s", r
.u
.initReply
.nonce
,
411 ireq
.u
.init
.channel
->cb_type
,
412 ireq
.u
.init
.channel
->cb_binding
);
414 krb5_set_error_string(context
, "Failed to allocate "
419 free(r
.u
.initReply
.nonce
);
420 r
.u
.initReply
.nonce
= s
;
423 ret
= krb5_store_stringz(sp
, r
.u
.initReply
.nonce
);
425 krb5_clear_error_string(context
);
429 if (strcasecmp(ireq
.u
.init
.type
, "CHAP") == 0) {
430 r
.u
.initReply
.identifier
=
431 malloc(sizeof(*r
.u
.initReply
.identifier
));
432 if (r
.u
.initReply
.identifier
== NULL
) {
433 krb5_set_error_string(context
, "out of memory");
438 asprintf(r
.u
.initReply
.identifier
, "%02X", identifier
& 0xff);
439 if (*r
.u
.initReply
.identifier
== NULL
) {
440 krb5_set_error_string(context
, "out of memory");
446 r
.u
.initReply
.identifier
= NULL
;
448 if (ireq
.u
.init
.hostname
) {
449 ret
= krb5_store_stringz(sp
, *ireq
.u
.init
.hostname
);
451 krb5_clear_error_string(context
);
456 ret
= krb5_storage_to_data(sp
, &buf
);
458 krb5_clear_error_string(context
);
462 ret
= get_digest_key(context
, config
, server
, &crypto
);
466 ret
= krb5_create_checksum(context
,
468 KRB5_KU_DIGEST_OPAQUE
,
473 krb5_crypto_destroy(context
, crypto
);
475 krb5_data_free(&buf
);
479 ASN1_MALLOC_ENCODE(Checksum
, buf
.data
, buf
.length
, &res
, &size
, ret
);
482 krb5_set_error_string(context
, "Failed to encode "
483 "checksum in digest request");
486 if (size
!= buf
.length
)
487 krb5_abortx(context
, "ASN1 internal error");
489 hex_encode(buf
.data
, buf
.length
, &r
.u
.initReply
.opaque
);
492 if (r
.u
.initReply
.opaque
== NULL
) {
493 krb5_clear_error_string(context
);
498 kdc_log(context
, config
, 0, "Digest %s init request successful from %s",
499 ireq
.u
.init
.type
, from
);
503 case choice_DigestReqInner_digestRequest
: {
504 sp
= krb5_storage_emem();
507 krb5_set_error_string(context
, "out of memory");
510 ret
= krb5_store_stringz(sp
, ireq
.u
.digestRequest
.type
);
512 krb5_clear_error_string(context
);
516 krb5_store_stringz(sp
, ireq
.u
.digestRequest
.serverNonce
);
518 if (ireq
.u
.digestRequest
.hostname
) {
519 ret
= krb5_store_stringz(sp
, *ireq
.u
.digestRequest
.hostname
);
521 krb5_clear_error_string(context
);
526 buf
.length
= strlen(ireq
.u
.digestRequest
.opaque
);
527 buf
.data
= malloc(buf
.length
);
528 if (buf
.data
== NULL
) {
529 krb5_set_error_string(context
, "out of memory");
534 ret
= hex_decode(ireq
.u
.digestRequest
.opaque
, buf
.data
, buf
.length
);
536 krb5_set_error_string(context
, "Failed to decode opaque");
542 ret
= decode_Checksum(buf
.data
, buf
.length
, &res
, NULL
);
545 krb5_set_error_string(context
, "Failed to decode digest Checksum");
549 ret
= krb5_storage_to_data(sp
, &buf
);
551 krb5_clear_error_string(context
);
555 serverNonce
.length
= strlen(ireq
.u
.digestRequest
.serverNonce
);
556 serverNonce
.data
= malloc(serverNonce
.length
);
557 if (serverNonce
.data
== NULL
) {
558 krb5_set_error_string(context
, "out of memory");
564 * CHAP does the checksum of the raw nonce, but do it for all
565 * types, since we need to check the timestamp.
570 ssize
= hex_decode(ireq
.u
.digestRequest
.serverNonce
,
571 serverNonce
.data
, serverNonce
.length
);
573 krb5_set_error_string(context
, "Failed to decode serverNonce");
577 serverNonce
.length
= ssize
;
580 ret
= get_digest_key(context
, config
, server
, &crypto
);
584 ret
= krb5_verify_checksum(context
, crypto
,
585 KRB5_KU_DIGEST_OPAQUE
,
586 buf
.data
, buf
.length
, &res
);
587 krb5_crypto_destroy(context
, crypto
);
594 unsigned char *p
= serverNonce
.data
;
597 if (serverNonce
.length
< 4) {
598 krb5_set_error_string(context
, "server nonce too short");
602 t
= p
[0] | (p
[1] << 8) | (p
[2] << 16) | (p
[3] << 24);
604 if (abs((kdc_time
& 0xffffffff) - t
) > context
->max_skew
) {
605 krb5_set_error_string(context
, "time screw in server nonce ");
611 if (strcasecmp(ireq
.u
.digestRequest
.type
, "CHAP") == 0) {
613 unsigned char md
[MD5_DIGEST_LENGTH
];
617 if ((config
->digests_allowed
& CHAP_MD5
) == 0) {
618 kdc_log(context
, config
, 0, "Digest CHAP MD5 not allowed");
622 if (ireq
.u
.digestRequest
.identifier
== NULL
) {
623 krb5_set_error_string(context
, "Identifier missing "
624 "from CHAP request");
629 if (hex_decode(*ireq
.u
.digestRequest
.identifier
, &id
, 1) != 1) {
630 krb5_set_error_string(context
, "failed to decode identifier");
635 ret
= get_password_entry(context
, config
,
636 ireq
.u
.digestRequest
.username
,
642 MD5_Update(&ctx
, &id
, 1);
643 MD5_Update(&ctx
, password
, strlen(password
));
644 MD5_Update(&ctx
, serverNonce
.data
, serverNonce
.length
);
647 hex_encode(md
, sizeof(md
), &mdx
);
649 krb5_clear_error_string(context
);
654 r
.element
= choice_DigestRepInner_response
;
656 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
659 r
.u
.response
.success
= TRUE
;
661 kdc_log(context
, config
, 0,
662 "CHAP reply mismatch for %s",
663 ireq
.u
.digestRequest
.username
);
664 r
.u
.response
.success
= FALSE
;
667 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "SASL-DIGEST-MD5") == 0) {
669 unsigned char md
[MD5_DIGEST_LENGTH
];
673 if ((config
->digests_allowed
& DIGEST_MD5
) == 0) {
674 kdc_log(context
, config
, 0, "Digest SASL MD5 not allowed");
678 if (ireq
.u
.digestRequest
.nonceCount
== NULL
)
680 if (ireq
.u
.digestRequest
.clientNonce
== NULL
)
682 if (ireq
.u
.digestRequest
.qop
== NULL
)
684 if (ireq
.u
.digestRequest
.realm
== NULL
)
687 ret
= get_password_entry(context
, config
,
688 ireq
.u
.digestRequest
.username
,
694 MD5_Update(&ctx
, ireq
.u
.digestRequest
.username
,
695 strlen(ireq
.u
.digestRequest
.username
));
696 MD5_Update(&ctx
, ":", 1);
697 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.realm
,
698 strlen(*ireq
.u
.digestRequest
.realm
));
699 MD5_Update(&ctx
, ":", 1);
700 MD5_Update(&ctx
, password
, strlen(password
));
704 MD5_Update(&ctx
, md
, sizeof(md
));
705 MD5_Update(&ctx
, ":", 1);
706 MD5_Update(&ctx
, ireq
.u
.digestRequest
.serverNonce
,
707 strlen(ireq
.u
.digestRequest
.serverNonce
));
708 MD5_Update(&ctx
, ":", 1);
709 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.nonceCount
,
710 strlen(*ireq
.u
.digestRequest
.nonceCount
));
711 if (ireq
.u
.digestRequest
.authid
) {
712 MD5_Update(&ctx
, ":", 1);
713 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.authid
,
714 strlen(*ireq
.u
.digestRequest
.authid
));
717 hex_encode(md
, sizeof(md
), &A1
);
719 krb5_set_error_string(context
, "out of memory");
725 MD5_Update(&ctx
, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
726 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.uri
,
727 strlen(*ireq
.u
.digestRequest
.uri
));
730 if (strcmp(ireq
.u
.digestRequest
.digest
, "clear") != 0) {
731 static char conf_zeros
[] = ":00000000000000000000000000000000";
732 MD5_Update(&ctx
, conf_zeros
, sizeof(conf_zeros
) - 1);
736 hex_encode(md
, sizeof(md
), &A2
);
738 krb5_set_error_string(context
, "out of memory");
745 MD5_Update(&ctx
, A1
, strlen(A2
));
746 MD5_Update(&ctx
, ":", 1);
747 MD5_Update(&ctx
, ireq
.u
.digestRequest
.serverNonce
,
748 strlen(ireq
.u
.digestRequest
.serverNonce
));
749 MD5_Update(&ctx
, ":", 1);
750 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.nonceCount
,
751 strlen(*ireq
.u
.digestRequest
.nonceCount
));
752 MD5_Update(&ctx
, ":", 1);
753 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.clientNonce
,
754 strlen(*ireq
.u
.digestRequest
.clientNonce
));
755 MD5_Update(&ctx
, ":", 1);
756 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.qop
,
757 strlen(*ireq
.u
.digestRequest
.qop
));
758 MD5_Update(&ctx
, ":", 1);
759 MD5_Update(&ctx
, A2
, strlen(A2
));
766 hex_encode(md
, sizeof(md
), &mdx
);
768 krb5_clear_error_string(context
);
773 r
.element
= choice_DigestRepInner_response
;
774 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
777 r
.u
.response
.success
= TRUE
;
779 kdc_log(context
, config
, 0,
780 "DIGEST-MD5 reply mismatch for %s",
781 ireq
.u
.digestRequest
.username
);
782 r
.u
.response
.success
= FALSE
;
785 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "MS-CHAP-V2") == 0) {
786 unsigned char md
[SHA_DIGEST_LENGTH
], challange
[SHA_DIGEST_LENGTH
];
787 krb5_principal clientprincipal
= NULL
;
789 const char *username
;
790 struct ntlm_buf answer
;
794 if ((config
->digests_allowed
& MS_CHAP_V2
) == 0) {
795 kdc_log(context
, config
, 0, "MS-CHAP-V2 not allowed");
799 if (ireq
.u
.digestRequest
.clientNonce
== NULL
) {
800 krb5_set_error_string(context
,
801 "MS-CHAP-V2 clientNonce missing");
805 if (serverNonce
.length
!= 16) {
806 krb5_set_error_string(context
,
807 "MS-CHAP-V2 serverNonce wrong length");
812 /* strip of the domain component */
813 username
= strchr(ireq
.u
.digestRequest
.username
, '\\');
814 if (username
== NULL
)
815 username
= ireq
.u
.digestRequest
.username
;
823 krb5_data clientNonce
;
825 clientNonce
.length
= strlen(*ireq
.u
.digestRequest
.clientNonce
);
826 clientNonce
.data
= malloc(clientNonce
.length
);
827 if (clientNonce
.data
== NULL
) {
829 krb5_set_error_string(context
, "out of memory");
833 ssize
= hex_decode(*ireq
.u
.digestRequest
.clientNonce
,
834 clientNonce
.data
, clientNonce
.length
);
836 krb5_set_error_string(context
,
837 "Failed to decode clientNonce");
841 SHA1_Update(&ctx
, clientNonce
.data
, ssize
);
842 free(clientNonce
.data
);
844 SHA1_Update(&ctx
, serverNonce
.data
, serverNonce
.length
);
845 SHA1_Update(&ctx
, username
, strlen(username
));
846 SHA1_Final(challange
, &ctx
);
849 ret
= krb5_parse_name(context
, username
, &clientprincipal
);
853 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
854 HDB_F_GET_CLIENT
, NULL
, &user
);
855 krb5_free_principal(context
, clientprincipal
);
857 krb5_set_error_string(context
,
858 "MS-CHAP-V2 user %s not in database",
863 ret
= hdb_enctype2key(context
, &user
->entry
,
864 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
866 krb5_set_error_string(context
,
867 "MS-CHAP-V2 missing arcfour key %s",
872 /* ChallengeResponse */
873 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
874 key
->key
.keyvalue
.length
,
877 krb5_set_error_string(context
, "NTLM missing arcfour key");
881 hex_encode(answer
.data
, answer
.length
, &mdx
);
884 krb5_clear_error_string(context
);
889 r
.element
= choice_DigestRepInner_response
;
890 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
892 r
.u
.response
.success
= TRUE
;
894 kdc_log(context
, config
, 0,
895 "MS-CHAP-V2 hash mismatch for %s",
896 ireq
.u
.digestRequest
.username
);
897 r
.u
.response
.success
= FALSE
;
901 if (r
.u
.response
.success
) {
902 unsigned char hashhash
[MD4_DIGEST_LENGTH
];
909 MD4_Update(&hctx
, key
->key
.keyvalue
.data
,
910 key
->key
.keyvalue
.length
);
911 MD4_Final(hashhash
, &hctx
);
914 /* GenerateAuthenticatorResponse */
916 SHA1_Update(&ctx
, hashhash
, sizeof(hashhash
));
917 SHA1_Update(&ctx
, answer
.data
, answer
.length
);
918 SHA1_Update(&ctx
, ms_chap_v2_magic1
,sizeof(ms_chap_v2_magic1
));
919 SHA1_Final(md
, &ctx
);
922 SHA1_Update(&ctx
, md
, sizeof(md
));
923 SHA1_Update(&ctx
, challange
, 8);
924 SHA1_Update(&ctx
, ms_chap_v2_magic2
, sizeof(ms_chap_v2_magic2
));
925 SHA1_Final(md
, &ctx
);
927 r
.u
.response
.rsp
= calloc(1, sizeof(*r
.u
.response
.rsp
));
928 if (r
.u
.response
.rsp
== NULL
) {
930 krb5_clear_error_string(context
);
935 hex_encode(md
, sizeof(md
), r
.u
.response
.rsp
);
936 if (r
.u
.response
.rsp
== NULL
) {
938 krb5_clear_error_string(context
);
943 /* get_master, rfc 3079 3.4 */
945 SHA1_Update(&ctx
, hashhash
, 16); /* md4(hash) */
946 SHA1_Update(&ctx
, answer
.data
, answer
.length
);
947 SHA1_Update(&ctx
, ms_rfc3079_magic1
, sizeof(ms_rfc3079_magic1
));
948 SHA1_Final(md
, &ctx
);
952 r
.u
.response
.session_key
=
953 calloc(1, sizeof(*r
.u
.response
.session_key
));
954 if (r
.u
.response
.session_key
== NULL
) {
955 krb5_clear_error_string(context
);
960 ret
= krb5_data_copy(r
.u
.response
.session_key
, md
, 16);
962 krb5_clear_error_string(context
);
968 r
.element
= choice_DigestRepInner_error
;
969 asprintf(&r
.u
.error
.reason
, "Unsupported digest type %s",
970 ireq
.u
.digestRequest
.type
);
971 if (r
.u
.error
.reason
== NULL
) {
972 krb5_set_error_string(context
, "out of memory");
976 r
.u
.error
.code
= EINVAL
;
979 kdc_log(context
, config
, 0, "Digest %s request successful %s",
980 ireq
.u
.digestRequest
.type
, ireq
.u
.digestRequest
.username
);
984 case choice_DigestReqInner_ntlmInit
:
986 if ((config
->digests_allowed
& (NTLM_V1
|NTLM_V1_SESSION
|NTLM_V2
)) == 0) {
987 kdc_log(context
, config
, 0, "NTLM not allowed");
991 r
.element
= choice_DigestRepInner_ntlmInitReply
;
993 r
.u
.ntlmInitReply
.flags
= NTLM_NEG_UNICODE
;
995 if ((ireq
.u
.ntlmInit
.flags
& NTLM_NEG_UNICODE
) == 0) {
996 kdc_log(context
, config
, 0, "NTLM client have no unicode");
1000 if (ireq
.u
.ntlmInit
.flags
& NTLM_NEG_NTLM
)
1001 r
.u
.ntlmInitReply
.flags
|= NTLM_NEG_NTLM
;
1003 kdc_log(context
, config
, 0, "NTLM client doesn't support NTLM");
1007 r
.u
.ntlmInitReply
.flags
|=
1009 NTLM_TARGET_DOMAIN
|
1015 NTLM_NEG_ALWAYS_SIGN| \
1016 NTLM_NEG_NTLM2_SESSION| \
1019 r
.u
.ntlmInitReply
.flags
|= (ireq
.u
.ntlmInit
.flags
& (ALL
));
1023 r
.u
.ntlmInitReply
.targetname
=
1024 get_ntlm_targetname(context
, client
);
1025 if (r
.u
.ntlmInitReply
.targetname
== NULL
) {
1026 krb5_set_error_string(context
, "out of memory");
1030 r
.u
.ntlmInitReply
.challange
.data
= malloc(8);
1031 if (r
.u
.ntlmInitReply
.challange
.data
== NULL
) {
1032 krb5_set_error_string(context
, "out of memory");
1036 r
.u
.ntlmInitReply
.challange
.length
= 8;
1037 if (RAND_bytes(r
.u
.ntlmInitReply
.challange
.data
,
1038 r
.u
.ntlmInitReply
.challange
.length
) != 1)
1040 krb5_set_error_string(context
, "out of random error");
1044 /* XXX fix targetinfo */
1045 ALLOC(r
.u
.ntlmInitReply
.targetinfo
);
1046 if (r
.u
.ntlmInitReply
.targetinfo
== NULL
) {
1047 krb5_set_error_string(context
, "out of memory");
1052 ret
= fill_targetinfo(context
,
1053 r
.u
.ntlmInitReply
.targetname
,
1055 r
.u
.ntlmInitReply
.targetinfo
);
1057 krb5_set_error_string(context
, "out of memory");
1063 * Save data encryted in opaque for the second part of the
1064 * ntlm authentication
1066 sp
= krb5_storage_emem();
1069 krb5_set_error_string(context
, "out of memory");
1073 ret
= krb5_storage_write(sp
, r
.u
.ntlmInitReply
.challange
.data
, 8);
1076 krb5_set_error_string(context
, "storage write challange");
1079 ret
= krb5_store_uint32(sp
, r
.u
.ntlmInitReply
.flags
);
1081 krb5_clear_error_string(context
);
1085 ret
= krb5_storage_to_data(sp
, &buf
);
1087 krb5_clear_error_string(context
);
1091 ret
= get_digest_key(context
, config
, server
, &crypto
);
1095 ret
= krb5_encrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1096 buf
.data
, buf
.length
, &r
.u
.ntlmInitReply
.opaque
);
1097 krb5_data_free(&buf
);
1098 krb5_crypto_destroy(context
, crypto
);
1103 kdc_log(context
, config
, 0, "NTLM init from %s", from
);
1107 case choice_DigestReqInner_ntlmRequest
: {
1108 krb5_principal clientprincipal
;
1109 unsigned char sessionkey
[16];
1110 unsigned char challange
[8];
1115 r
.element
= choice_DigestRepInner_ntlmResponse
;
1116 r
.u
.ntlmResponse
.success
= 0;
1117 r
.u
.ntlmResponse
.flags
= 0;
1118 r
.u
.ntlmResponse
.sessionkey
= NULL
;
1119 r
.u
.ntlmResponse
.tickets
= NULL
;
1122 ret
= krb5_parse_name(context
,
1123 ireq
.u
.ntlmRequest
.username
,
1128 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
1129 HDB_F_GET_CLIENT
, NULL
, &user
);
1130 krb5_free_principal(context
, clientprincipal
);
1132 krb5_set_error_string(context
, "NTLM user %s not in database",
1133 ireq
.u
.ntlmRequest
.username
);
1137 ret
= get_digest_key(context
, config
, server
, &crypto
);
1141 ret
= krb5_decrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1142 ireq
.u
.ntlmRequest
.opaque
.data
,
1143 ireq
.u
.ntlmRequest
.opaque
.length
, &buf
);
1144 krb5_crypto_destroy(context
, crypto
);
1147 kdc_log(context
, config
, 0,
1148 "Failed to decrypt nonce from %s", from
);
1152 sp
= krb5_storage_from_data(&buf
);
1155 krb5_set_error_string(context
, "out of memory");
1159 ret
= krb5_storage_read(sp
, challange
, sizeof(challange
));
1160 if (ret
!= sizeof(challange
)) {
1161 krb5_set_error_string(context
, "NTLM storage read challange");
1165 ret
= krb5_ret_uint32(sp
, &flags
);
1167 krb5_set_error_string(context
, "NTLM storage read flags");
1170 krb5_data_free(&buf
);
1172 if ((flags
& NTLM_NEG_NTLM
) == 0) {
1174 krb5_set_error_string(context
, "NTLM not negotiated");
1178 ret
= hdb_enctype2key(context
, &user
->entry
,
1179 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
1181 krb5_set_error_string(context
, "NTLM missing arcfour key");
1185 /* check if this is NTLMv2 */
1186 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= 24) {
1187 struct ntlm_buf infotarget
, answer
;
1190 if ((config
->digests_allowed
& NTLM_V2
) == 0) {
1191 kdc_log(context
, config
, 0, "NTLM v2 not allowed");
1197 targetname
= get_ntlm_targetname(context
, client
);
1198 if (targetname
== NULL
) {
1199 krb5_set_error_string(context
, "out of memory");
1204 answer
.length
= ireq
.u
.ntlmRequest
.ntlm
.length
;
1205 answer
.data
= ireq
.u
.ntlmRequest
.ntlm
.data
;
1207 ret
= heim_ntlm_verify_ntlm2(key
->key
.keyvalue
.data
,
1208 key
->key
.keyvalue
.length
,
1209 ireq
.u
.ntlmRequest
.username
,
1218 krb5_set_error_string(context
, "NTLM v2 verify failed");
1222 /* XXX verify infotarget matches client (checksum ?) */
1224 free(infotarget
.data
);
1228 struct ntlm_buf answer
;
1232 if (flags
& NTLM_NEG_NTLM2_SESSION
) {
1233 unsigned char sessionhash
[MD5_DIGEST_LENGTH
];
1236 if ((config
->digests_allowed
& NTLM_V1_SESSION
) == 0) {
1237 kdc_log(context
, config
, 0, "NTLM v1-session not allowed");
1242 if (ireq
.u
.ntlmRequest
.lm
.length
!= 24) {
1243 krb5_set_error_string(context
, "LM hash have wrong length "
1244 "for NTLM session key");
1250 MD5_Update(&md5ctx
, challange
, sizeof(challange
));
1251 MD5_Update(&md5ctx
, ireq
.u
.ntlmRequest
.lm
.data
, 8);
1252 MD5_Final(sessionhash
, &md5ctx
);
1253 memcpy(challange
, sessionhash
, sizeof(challange
));
1255 if ((config
->digests_allowed
& NTLM_V1
) == 0) {
1256 kdc_log(context
, config
, 0, "NTLM v1 not allowed");
1261 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
1262 key
->key
.keyvalue
.length
,
1263 challange
, &answer
);
1265 krb5_set_error_string(context
, "NTLM missing arcfour key");
1269 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= answer
.length
||
1270 memcmp(ireq
.u
.ntlmRequest
.ntlm
.data
, answer
.data
, answer
.length
) != 0)
1274 krb5_set_error_string(context
, "NTLM hash mismatch");
1284 key
->key
.keyvalue
.data
, key
->key
.keyvalue
.length
);
1285 MD4_Final(sessionkey
, &ctx
);
1289 if (ireq
.u
.ntlmRequest
.sessionkey
) {
1290 unsigned char masterkey
[MD4_DIGEST_LENGTH
];
1294 if ((flags
& NTLM_NEG_KEYEX
) == 0) {
1295 krb5_set_error_string(context
,
1296 "NTLM client failed to neg key "
1297 "exchange but still sent key");
1302 len
= ireq
.u
.ntlmRequest
.sessionkey
->length
;
1303 if (len
!= sizeof(masterkey
)){
1304 krb5_set_error_string(context
,
1305 "NTLM master key wrong length: %lu",
1306 (unsigned long)len
);
1310 RC4_set_key(&rc4
, sizeof(sessionkey
), sessionkey
);
1312 RC4(&rc4
, sizeof(masterkey
),
1313 ireq
.u
.ntlmRequest
.sessionkey
->data
,
1315 memset(&rc4
, 0, sizeof(rc4
));
1317 r
.u
.ntlmResponse
.sessionkey
=
1318 malloc(sizeof(*r
.u
.ntlmResponse
.sessionkey
));
1319 if (r
.u
.ntlmResponse
.sessionkey
== NULL
) {
1320 krb5_set_error_string(context
, "out of memory");
1324 ret
= krb5_data_copy(r
.u
.ntlmResponse
.sessionkey
,
1325 masterkey
, sizeof(masterkey
));
1327 krb5_set_error_string(context
, "out of memory");
1332 r
.u
.ntlmResponse
.success
= 1;
1333 kdc_log(context
, config
, 0, "NTLM version %d successful for %s",
1334 version
, ireq
.u
.ntlmRequest
.username
);
1337 case choice_DigestReqInner_supportedMechs
:
1339 kdc_log(context
, config
, 0, "digest supportedMechs from %s", from
);
1341 r
.element
= choice_DigestRepInner_supportedMechs
;
1342 memset(&r
.u
.supportedMechs
, 0, sizeof(r
.u
.supportedMechs
));
1344 if (config
->digests_allowed
& NTLM_V1
)
1345 r
.u
.supportedMechs
.ntlm_v1
= 1;
1346 if (config
->digests_allowed
& NTLM_V1_SESSION
)
1347 r
.u
.supportedMechs
.ntlm_v1_session
= 1;
1348 if (config
->digests_allowed
& NTLM_V2
)
1349 r
.u
.supportedMechs
.ntlm_v2
= 1;
1350 if (config
->digests_allowed
& DIGEST_MD5
)
1351 r
.u
.supportedMechs
.digest_md5
= 1;
1352 if (config
->digests_allowed
& CHAP_MD5
)
1353 r
.u
.supportedMechs
.chap_md5
= 1;
1354 if (config
->digests_allowed
& MS_CHAP_V2
)
1355 r
.u
.supportedMechs
.ms_chap_v2
= 1;
1360 krb5_set_error_string(context
, "unknown operation to digest");
1365 s
= krb5_get_error_message(context
, ret
);
1367 krb5_clear_error_string(context
);
1371 kdc_log(context
, config
, 0, "Digest failed with: %s", s
);
1373 r
.element
= choice_DigestRepInner_error
;
1374 r
.u
.error
.reason
= strdup("unknown error");
1375 krb5_free_error_string(context
, s
);
1376 if (r
.u
.error
.reason
== NULL
) {
1377 krb5_set_error_string(context
, "out of memory");
1381 r
.u
.error
.code
= EINVAL
;
1386 ASN1_MALLOC_ENCODE(DigestRepInner
, buf
.data
, buf
.length
, &r
, &size
, ret
);
1388 krb5_set_error_string(context
, "Failed to encode inner digest reply");
1391 if (size
!= buf
.length
)
1392 krb5_abortx(context
, "ASN1 internal error");
1394 krb5_auth_con_addflags(context
, ac
, KRB5_AUTH_CONTEXT_USE_SUBKEY
, NULL
);
1396 ret
= krb5_mk_rep (context
, ac
, &rep
.apRep
);
1403 ret
= krb5_auth_con_getlocalsubkey(context
, ac
, &key
);
1407 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
1408 krb5_free_keyblock (context
, key
);
1413 ret
= krb5_encrypt_EncryptedData(context
, crypto
, KRB5_KU_DIGEST_ENCRYPT
,
1414 buf
.data
, buf
.length
, 0,
1417 ASN1_MALLOC_ENCODE(DigestREP
, reply
->data
, reply
->length
, &rep
, &size
, ret
);
1419 krb5_set_error_string(context
, "Failed to encode digest reply");
1422 if (size
!= reply
->length
)
1423 krb5_abortx(context
, "ASN1 internal error");
1428 krb5_auth_con_free(context
, ac
);
1430 krb5_warn(context
, ret
, "Digest request from %s failed", from
);
1432 krb5_free_ticket(context
, ticket
);
1434 krb5_kt_close(context
, id
);
1436 krb5_crypto_destroy(context
, crypto
);
1438 krb5_storage_free(sp
);
1440 _kdc_free_ent (context
, user
);
1442 _kdc_free_ent (context
, server
);
1444 _kdc_free_ent (context
, client
);
1446 memset(password
, 0, strlen(password
));
1451 krb5_data_free(&buf
);
1452 krb5_data_free(&serverNonce
);
1453 free_DigestREP(&rep
);
1454 free_DigestRepInner(&r
);
1455 free_DigestReqInner(&ireq
);