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("$Id: digest.c 22374 2007-12-28 18:36:52Z lha $");
39 #define MS_CHAP_V2 0x20
41 #define DIGEST_MD5 0x08
43 #define NTLM_V1_SESSION 0x02
46 const struct units _kdc_digestunits
[] = {
47 {"ms-chap-v2", 1U << 5},
48 {"chap-md5", 1U << 4},
49 {"digest-md5", 1U << 3},
51 {"ntlm-v1-session", 1U << 1},
57 static krb5_error_code
58 get_digest_key(krb5_context context
,
59 krb5_kdc_configuration
*config
,
67 ret
= _kdc_get_preferred_key(context
,
75 return krb5_crypto_init(context
, &key
->key
, 0, crypto
);
83 get_ntlm_targetname(krb5_context context
,
88 targetname
= strdup(krb5_principal_get_realm(context
,
89 client
->entry
.principal
));
90 if (targetname
== NULL
)
93 p
= strchr(targetname
, '.');
101 static krb5_error_code
102 fill_targetinfo(krb5_context context
,
104 hdb_entry_ex
*client
,
107 struct ntlm_targetinfo ti
;
113 memset(&ti
, 0, sizeof(ti
));
115 ti
.domainname
= targetname
;
116 p
= client
->entry
.principal
;
117 str
= krb5_principal_get_comp_string(context
, p
, 0);
119 (strcmp("host", str
) == 0 ||
120 strcmp("ftp", str
) == 0 ||
121 strcmp("imap", str
) == 0 ||
122 strcmp("pop", str
) == 0 ||
123 strcmp("smtp", str
)))
125 str
= krb5_principal_get_comp_string(context
, p
, 1);
126 ti
.dnsservername
= rk_UNCONST(str
);
129 ret
= heim_ntlm_encode_targetinfo(&ti
, 1, &d
);
134 data
->length
= d
.length
;
140 static const unsigned char ms_chap_v2_magic1
[39] = {
141 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
142 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
143 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
144 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
146 static const unsigned char ms_chap_v2_magic2
[41] = {
147 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
148 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
149 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
150 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
153 static const unsigned char ms_rfc3079_magic1
[27] = {
154 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
155 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
156 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
163 static krb5_error_code
164 get_password_entry(krb5_context context
,
165 krb5_kdc_configuration
*config
,
166 const char *username
,
169 krb5_principal clientprincipal
;
175 ret
= krb5_parse_name(context
, username
, &clientprincipal
);
179 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
180 HDB_F_GET_CLIENT
, &db
, &user
);
181 krb5_free_principal(context
, clientprincipal
);
185 ret
= hdb_entry_get_password(context
, db
, &user
->entry
, password
);
186 if (ret
|| password
== NULL
) {
189 krb5_set_error_string(context
, "password missing");
191 memset(user
, 0, sizeof(*user
));
193 _kdc_free_ent (context
, user
);
202 _kdc_do_digest(krb5_context context
,
203 krb5_kdc_configuration
*config
,
204 const DigestREQ
*req
, krb5_data
*reply
,
205 const char *from
, struct sockaddr
*addr
)
207 krb5_error_code ret
= 0;
208 krb5_ticket
*ticket
= NULL
;
209 krb5_auth_context ac
= NULL
;
210 krb5_keytab id
= NULL
;
211 krb5_crypto crypto
= NULL
;
215 krb5_flags ap_req_options
;
218 krb5_storage
*sp
= NULL
;
220 hdb_entry_ex
*server
= NULL
, *user
= NULL
;
221 hdb_entry_ex
*client
= NULL
;
222 char *client_name
= NULL
, *password
= NULL
;
223 krb5_data serverNonce
;
225 if(!config
->enable_digest
) {
226 kdc_log(context
, config
, 0,
227 "Rejected digest request (disabled) from %s", from
);
228 return KRB5KDC_ERR_POLICY
;
231 krb5_data_zero(&buf
);
232 krb5_data_zero(reply
);
233 krb5_data_zero(&serverNonce
);
234 memset(&ireq
, 0, sizeof(ireq
));
235 memset(&r
, 0, sizeof(r
));
236 memset(&rep
, 0, sizeof(rep
));
238 kdc_log(context
, config
, 0, "Digest request from %s", from
);
240 ret
= krb5_kt_resolve(context
, "HDB:", &id
);
242 kdc_log(context
, config
, 0, "Can't open database for digest");
246 ret
= krb5_rd_req(context
,
256 /* check the server principal in the ticket matches digest/R@R */
258 krb5_principal principal
= NULL
;
261 ret
= krb5_ticket_get_server(context
, ticket
, &principal
);
266 krb5_set_error_string(context
, "Wrong digest server principal used");
267 p
= krb5_principal_get_comp_string(context
, principal
, 0);
269 krb5_free_principal(context
, principal
);
272 if (strcmp(p
, KRB5_DIGEST_NAME
) != 0) {
273 krb5_free_principal(context
, principal
);
277 p
= krb5_principal_get_comp_string(context
, principal
, 1);
279 krb5_free_principal(context
, principal
);
282 r
= krb5_principal_get_realm(context
, principal
);
284 krb5_free_principal(context
, principal
);
287 if (strcmp(p
, r
) != 0) {
288 krb5_free_principal(context
, principal
);
291 krb5_clear_error_string(context
);
293 ret
= _kdc_db_fetch(context
, config
, principal
,
294 HDB_F_GET_SERVER
, NULL
, &server
);
298 krb5_free_principal(context
, principal
);
301 /* check the client is allowed to do digest auth */
303 krb5_principal principal
= NULL
;
305 ret
= krb5_ticket_get_client(context
, ticket
, &principal
);
309 ret
= krb5_unparse_name(context
, principal
, &client_name
);
311 krb5_free_principal(context
, principal
);
315 ret
= _kdc_db_fetch(context
, config
, principal
,
316 HDB_F_GET_CLIENT
, NULL
, &client
);
317 krb5_free_principal(context
, principal
);
321 if (client
->entry
.flags
.allow_digest
== 0) {
322 kdc_log(context
, config
, 0,
323 "Client %s tried to use digest "
324 "but is not allowed to",
326 krb5_set_error_string(context
,
327 "Client is not permitted to use digest");
328 ret
= KRB5KDC_ERR_POLICY
;
337 ret
= krb5_auth_con_getremotesubkey(context
, ac
, &key
);
341 krb5_set_error_string(context
, "digest: remote subkey not found");
346 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
347 krb5_free_keyblock (context
, key
);
352 ret
= krb5_decrypt_EncryptedData(context
, crypto
, KRB5_KU_DIGEST_ENCRYPT
,
353 &req
->innerReq
, &buf
);
354 krb5_crypto_destroy(context
, crypto
);
359 ret
= decode_DigestReqInner(buf
.data
, buf
.length
, &ireq
, NULL
);
360 krb5_data_free(&buf
);
362 krb5_set_error_string(context
, "Failed to decode digest inner request");
366 kdc_log(context
, config
, 0, "Valid digest request from %s (%s)",
370 * Process the inner request
373 switch (ireq
.element
) {
374 case choice_DigestReqInner_init
: {
375 unsigned char server_nonce
[16], identifier
;
377 RAND_pseudo_bytes(&identifier
, sizeof(identifier
));
378 RAND_pseudo_bytes(server_nonce
, sizeof(server_nonce
));
380 server_nonce
[0] = kdc_time
& 0xff;
381 server_nonce
[1] = (kdc_time
>> 8) & 0xff;
382 server_nonce
[2] = (kdc_time
>> 16) & 0xff;
383 server_nonce
[3] = (kdc_time
>> 24) & 0xff;
385 r
.element
= choice_DigestRepInner_initReply
;
387 hex_encode(server_nonce
, sizeof(server_nonce
), &r
.u
.initReply
.nonce
);
388 if (r
.u
.initReply
.nonce
== NULL
) {
389 krb5_set_error_string(context
, "Failed to decode server nonce");
394 sp
= krb5_storage_emem();
397 krb5_set_error_string(context
, "out of memory");
400 ret
= krb5_store_stringz(sp
, ireq
.u
.init
.type
);
402 krb5_clear_error_string(context
);
406 if (ireq
.u
.init
.channel
) {
409 asprintf(&s
, "%s-%s:%s", r
.u
.initReply
.nonce
,
410 ireq
.u
.init
.channel
->cb_type
,
411 ireq
.u
.init
.channel
->cb_binding
);
413 krb5_set_error_string(context
, "Failed to allocate "
418 free(r
.u
.initReply
.nonce
);
419 r
.u
.initReply
.nonce
= s
;
422 ret
= krb5_store_stringz(sp
, r
.u
.initReply
.nonce
);
424 krb5_clear_error_string(context
);
428 if (strcasecmp(ireq
.u
.init
.type
, "CHAP") == 0) {
429 r
.u
.initReply
.identifier
=
430 malloc(sizeof(*r
.u
.initReply
.identifier
));
431 if (r
.u
.initReply
.identifier
== NULL
) {
432 krb5_set_error_string(context
, "out of memory");
437 asprintf(r
.u
.initReply
.identifier
, "%02X", identifier
& 0xff);
438 if (*r
.u
.initReply
.identifier
== NULL
) {
439 krb5_set_error_string(context
, "out of memory");
445 r
.u
.initReply
.identifier
= NULL
;
447 if (ireq
.u
.init
.hostname
) {
448 ret
= krb5_store_stringz(sp
, *ireq
.u
.init
.hostname
);
450 krb5_clear_error_string(context
);
455 ret
= krb5_storage_to_data(sp
, &buf
);
457 krb5_clear_error_string(context
);
461 ret
= get_digest_key(context
, config
, server
, &crypto
);
465 ret
= krb5_create_checksum(context
,
467 KRB5_KU_DIGEST_OPAQUE
,
472 krb5_crypto_destroy(context
, crypto
);
474 krb5_data_free(&buf
);
478 ASN1_MALLOC_ENCODE(Checksum
, buf
.data
, buf
.length
, &res
, &size
, ret
);
481 krb5_set_error_string(context
, "Failed to encode "
482 "checksum in digest request");
485 if (size
!= buf
.length
)
486 krb5_abortx(context
, "ASN1 internal error");
488 hex_encode(buf
.data
, buf
.length
, &r
.u
.initReply
.opaque
);
490 if (r
.u
.initReply
.opaque
== NULL
) {
491 krb5_clear_error_string(context
);
496 kdc_log(context
, config
, 0, "Digest %s init request successful from %s",
497 ireq
.u
.init
.type
, from
);
501 case choice_DigestReqInner_digestRequest
: {
502 sp
= krb5_storage_emem();
505 krb5_set_error_string(context
, "out of memory");
508 ret
= krb5_store_stringz(sp
, ireq
.u
.digestRequest
.type
);
510 krb5_clear_error_string(context
);
514 krb5_store_stringz(sp
, ireq
.u
.digestRequest
.serverNonce
);
516 if (ireq
.u
.digestRequest
.hostname
) {
517 ret
= krb5_store_stringz(sp
, *ireq
.u
.digestRequest
.hostname
);
519 krb5_clear_error_string(context
);
524 buf
.length
= strlen(ireq
.u
.digestRequest
.opaque
);
525 buf
.data
= malloc(buf
.length
);
526 if (buf
.data
== NULL
) {
527 krb5_set_error_string(context
, "out of memory");
532 ret
= hex_decode(ireq
.u
.digestRequest
.opaque
, buf
.data
, buf
.length
);
534 krb5_set_error_string(context
, "Failed to decode opaque");
540 ret
= decode_Checksum(buf
.data
, buf
.length
, &res
, NULL
);
543 krb5_set_error_string(context
, "Failed to decode digest Checksum");
547 ret
= krb5_storage_to_data(sp
, &buf
);
549 krb5_clear_error_string(context
);
553 serverNonce
.length
= strlen(ireq
.u
.digestRequest
.serverNonce
);
554 serverNonce
.data
= malloc(serverNonce
.length
);
555 if (serverNonce
.data
== NULL
) {
556 krb5_set_error_string(context
, "out of memory");
562 * CHAP does the checksum of the raw nonce, but do it for all
563 * types, since we need to check the timestamp.
568 ssize
= hex_decode(ireq
.u
.digestRequest
.serverNonce
,
569 serverNonce
.data
, serverNonce
.length
);
571 krb5_set_error_string(context
, "Failed to decode serverNonce");
575 serverNonce
.length
= ssize
;
578 ret
= get_digest_key(context
, config
, server
, &crypto
);
582 ret
= krb5_verify_checksum(context
, crypto
,
583 KRB5_KU_DIGEST_OPAQUE
,
584 buf
.data
, buf
.length
, &res
);
585 krb5_crypto_destroy(context
, crypto
);
592 unsigned char *p
= serverNonce
.data
;
595 if (serverNonce
.length
< 4) {
596 krb5_set_error_string(context
, "server nonce too short");
600 t
= p
[0] | (p
[1] << 8) | (p
[2] << 16) | (p
[3] << 24);
602 if (abs((kdc_time
& 0xffffffff) - t
) > context
->max_skew
) {
603 krb5_set_error_string(context
, "time screw in server nonce ");
609 if (strcasecmp(ireq
.u
.digestRequest
.type
, "CHAP") == 0) {
611 unsigned char md
[MD5_DIGEST_LENGTH
];
615 if ((config
->digests_allowed
& CHAP_MD5
) == 0) {
616 kdc_log(context
, config
, 0, "Digest CHAP MD5 not allowed");
620 if (ireq
.u
.digestRequest
.identifier
== NULL
) {
621 krb5_set_error_string(context
, "Identifier missing "
622 "from CHAP request");
627 if (hex_decode(*ireq
.u
.digestRequest
.identifier
, &id
, 1) != 1) {
628 krb5_set_error_string(context
, "failed to decode identifier");
633 ret
= get_password_entry(context
, config
,
634 ireq
.u
.digestRequest
.username
,
640 MD5_Update(&ctx
, &id
, 1);
641 MD5_Update(&ctx
, password
, strlen(password
));
642 MD5_Update(&ctx
, serverNonce
.data
, serverNonce
.length
);
645 hex_encode(md
, sizeof(md
), &mdx
);
647 krb5_clear_error_string(context
);
652 r
.element
= choice_DigestRepInner_response
;
654 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
657 r
.u
.response
.success
= TRUE
;
659 kdc_log(context
, config
, 0,
660 "CHAP reply mismatch for %s",
661 ireq
.u
.digestRequest
.username
);
662 r
.u
.response
.success
= FALSE
;
665 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "SASL-DIGEST-MD5") == 0) {
667 unsigned char md
[MD5_DIGEST_LENGTH
];
671 if ((config
->digests_allowed
& DIGEST_MD5
) == 0) {
672 kdc_log(context
, config
, 0, "Digest SASL MD5 not allowed");
676 if (ireq
.u
.digestRequest
.nonceCount
== NULL
)
678 if (ireq
.u
.digestRequest
.clientNonce
== NULL
)
680 if (ireq
.u
.digestRequest
.qop
== NULL
)
682 if (ireq
.u
.digestRequest
.realm
== NULL
)
685 ret
= get_password_entry(context
, config
,
686 ireq
.u
.digestRequest
.username
,
692 MD5_Update(&ctx
, ireq
.u
.digestRequest
.username
,
693 strlen(ireq
.u
.digestRequest
.username
));
694 MD5_Update(&ctx
, ":", 1);
695 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.realm
,
696 strlen(*ireq
.u
.digestRequest
.realm
));
697 MD5_Update(&ctx
, ":", 1);
698 MD5_Update(&ctx
, password
, strlen(password
));
702 MD5_Update(&ctx
, md
, sizeof(md
));
703 MD5_Update(&ctx
, ":", 1);
704 MD5_Update(&ctx
, ireq
.u
.digestRequest
.serverNonce
,
705 strlen(ireq
.u
.digestRequest
.serverNonce
));
706 MD5_Update(&ctx
, ":", 1);
707 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.nonceCount
,
708 strlen(*ireq
.u
.digestRequest
.nonceCount
));
709 if (ireq
.u
.digestRequest
.authid
) {
710 MD5_Update(&ctx
, ":", 1);
711 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.authid
,
712 strlen(*ireq
.u
.digestRequest
.authid
));
715 hex_encode(md
, sizeof(md
), &A1
);
717 krb5_set_error_string(context
, "out of memory");
723 MD5_Update(&ctx
, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
724 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.uri
,
725 strlen(*ireq
.u
.digestRequest
.uri
));
728 if (strcmp(ireq
.u
.digestRequest
.digest
, "clear") != 0) {
729 static char conf_zeros
[] = ":00000000000000000000000000000000";
730 MD5_Update(&ctx
, conf_zeros
, sizeof(conf_zeros
) - 1);
734 hex_encode(md
, sizeof(md
), &A2
);
736 krb5_set_error_string(context
, "out of memory");
743 MD5_Update(&ctx
, A1
, strlen(A2
));
744 MD5_Update(&ctx
, ":", 1);
745 MD5_Update(&ctx
, ireq
.u
.digestRequest
.serverNonce
,
746 strlen(ireq
.u
.digestRequest
.serverNonce
));
747 MD5_Update(&ctx
, ":", 1);
748 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.nonceCount
,
749 strlen(*ireq
.u
.digestRequest
.nonceCount
));
750 MD5_Update(&ctx
, ":", 1);
751 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.clientNonce
,
752 strlen(*ireq
.u
.digestRequest
.clientNonce
));
753 MD5_Update(&ctx
, ":", 1);
754 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.qop
,
755 strlen(*ireq
.u
.digestRequest
.qop
));
756 MD5_Update(&ctx
, ":", 1);
757 MD5_Update(&ctx
, A2
, strlen(A2
));
764 hex_encode(md
, sizeof(md
), &mdx
);
766 krb5_clear_error_string(context
);
771 r
.element
= choice_DigestRepInner_response
;
772 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
775 r
.u
.response
.success
= TRUE
;
777 kdc_log(context
, config
, 0,
778 "DIGEST-MD5 reply mismatch for %s",
779 ireq
.u
.digestRequest
.username
);
780 r
.u
.response
.success
= FALSE
;
783 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "MS-CHAP-V2") == 0) {
784 unsigned char md
[SHA_DIGEST_LENGTH
], challange
[SHA_DIGEST_LENGTH
];
785 krb5_principal clientprincipal
= NULL
;
787 const char *username
;
788 struct ntlm_buf answer
;
792 if ((config
->digests_allowed
& MS_CHAP_V2
) == 0) {
793 kdc_log(context
, config
, 0, "MS-CHAP-V2 not allowed");
797 if (ireq
.u
.digestRequest
.clientNonce
== NULL
) {
798 krb5_set_error_string(context
,
799 "MS-CHAP-V2 clientNonce missing");
803 if (serverNonce
.length
!= 16) {
804 krb5_set_error_string(context
,
805 "MS-CHAP-V2 serverNonce wrong length");
810 /* strip of the domain component */
811 username
= strchr(ireq
.u
.digestRequest
.username
, '\\');
812 if (username
== NULL
)
813 username
= ireq
.u
.digestRequest
.username
;
821 krb5_data clientNonce
;
823 clientNonce
.length
= strlen(*ireq
.u
.digestRequest
.clientNonce
);
824 clientNonce
.data
= malloc(clientNonce
.length
);
825 if (clientNonce
.data
== NULL
) {
827 krb5_set_error_string(context
, "out of memory");
831 ssize
= hex_decode(*ireq
.u
.digestRequest
.clientNonce
,
832 clientNonce
.data
, clientNonce
.length
);
834 krb5_set_error_string(context
,
835 "Failed to decode clientNonce");
839 SHA1_Update(&ctx
, clientNonce
.data
, ssize
);
840 free(clientNonce
.data
);
842 SHA1_Update(&ctx
, serverNonce
.data
, serverNonce
.length
);
843 SHA1_Update(&ctx
, username
, strlen(username
));
844 SHA1_Final(challange
, &ctx
);
847 ret
= krb5_parse_name(context
, username
, &clientprincipal
);
851 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
852 HDB_F_GET_CLIENT
, NULL
, &user
);
853 krb5_free_principal(context
, clientprincipal
);
855 krb5_set_error_string(context
,
856 "MS-CHAP-V2 user %s not in database",
861 ret
= hdb_enctype2key(context
, &user
->entry
,
862 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
864 krb5_set_error_string(context
,
865 "MS-CHAP-V2 missing arcfour key %s",
870 /* ChallengeResponse */
871 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
872 key
->key
.keyvalue
.length
,
875 krb5_set_error_string(context
, "NTLM missing arcfour key");
879 hex_encode(answer
.data
, answer
.length
, &mdx
);
882 krb5_clear_error_string(context
);
887 r
.element
= choice_DigestRepInner_response
;
888 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
890 r
.u
.response
.success
= TRUE
;
892 kdc_log(context
, config
, 0,
893 "MS-CHAP-V2 hash mismatch for %s",
894 ireq
.u
.digestRequest
.username
);
895 r
.u
.response
.success
= FALSE
;
899 if (r
.u
.response
.success
) {
900 unsigned char hashhash
[MD4_DIGEST_LENGTH
];
907 MD4_Update(&hctx
, key
->key
.keyvalue
.data
,
908 key
->key
.keyvalue
.length
);
909 MD4_Final(hashhash
, &hctx
);
912 /* GenerateAuthenticatorResponse */
914 SHA1_Update(&ctx
, hashhash
, sizeof(hashhash
));
915 SHA1_Update(&ctx
, answer
.data
, answer
.length
);
916 SHA1_Update(&ctx
, ms_chap_v2_magic1
,sizeof(ms_chap_v2_magic1
));
917 SHA1_Final(md
, &ctx
);
920 SHA1_Update(&ctx
, md
, sizeof(md
));
921 SHA1_Update(&ctx
, challange
, 8);
922 SHA1_Update(&ctx
, ms_chap_v2_magic2
, sizeof(ms_chap_v2_magic2
));
923 SHA1_Final(md
, &ctx
);
925 r
.u
.response
.rsp
= calloc(1, sizeof(*r
.u
.response
.rsp
));
926 if (r
.u
.response
.rsp
== NULL
) {
928 krb5_clear_error_string(context
);
933 hex_encode(md
, sizeof(md
), r
.u
.response
.rsp
);
934 if (r
.u
.response
.rsp
== NULL
) {
936 krb5_clear_error_string(context
);
941 /* get_master, rfc 3079 3.4 */
943 SHA1_Update(&ctx
, hashhash
, 16); /* md4(hash) */
944 SHA1_Update(&ctx
, answer
.data
, answer
.length
);
945 SHA1_Update(&ctx
, ms_rfc3079_magic1
, sizeof(ms_rfc3079_magic1
));
946 SHA1_Final(md
, &ctx
);
950 r
.u
.response
.session_key
=
951 calloc(1, sizeof(*r
.u
.response
.session_key
));
952 if (r
.u
.response
.session_key
== NULL
) {
953 krb5_clear_error_string(context
);
958 ret
= krb5_data_copy(r
.u
.response
.session_key
, md
, 16);
960 krb5_clear_error_string(context
);
966 r
.element
= choice_DigestRepInner_error
;
967 asprintf(&r
.u
.error
.reason
, "Unsupported digest type %s",
968 ireq
.u
.digestRequest
.type
);
969 if (r
.u
.error
.reason
== NULL
) {
970 krb5_set_error_string(context
, "out of memory");
974 r
.u
.error
.code
= EINVAL
;
977 kdc_log(context
, config
, 0, "Digest %s request successful %s",
978 ireq
.u
.digestRequest
.type
, ireq
.u
.digestRequest
.username
);
982 case choice_DigestReqInner_ntlmInit
:
984 if ((config
->digests_allowed
& (NTLM_V1
|NTLM_V1_SESSION
|NTLM_V2
)) == 0) {
985 kdc_log(context
, config
, 0, "NTLM not allowed");
989 r
.element
= choice_DigestRepInner_ntlmInitReply
;
991 r
.u
.ntlmInitReply
.flags
= NTLM_NEG_UNICODE
;
993 if ((ireq
.u
.ntlmInit
.flags
& NTLM_NEG_UNICODE
) == 0) {
994 kdc_log(context
, config
, 0, "NTLM client have no unicode");
998 if (ireq
.u
.ntlmInit
.flags
& NTLM_NEG_NTLM
)
999 r
.u
.ntlmInitReply
.flags
|= NTLM_NEG_NTLM
;
1001 kdc_log(context
, config
, 0, "NTLM client doesn't support NTLM");
1005 r
.u
.ntlmInitReply
.flags
|=
1007 NTLM_TARGET_DOMAIN
|
1013 NTLM_NEG_ALWAYS_SIGN| \
1014 NTLM_NEG_NTLM2_SESSION| \
1017 r
.u
.ntlmInitReply
.flags
|= (ireq
.u
.ntlmInit
.flags
& (ALL
));
1021 r
.u
.ntlmInitReply
.targetname
=
1022 get_ntlm_targetname(context
, client
);
1023 if (r
.u
.ntlmInitReply
.targetname
== NULL
) {
1024 krb5_set_error_string(context
, "out of memory");
1028 r
.u
.ntlmInitReply
.challange
.data
= malloc(8);
1029 if (r
.u
.ntlmInitReply
.challange
.data
== NULL
) {
1030 krb5_set_error_string(context
, "out of memory");
1034 r
.u
.ntlmInitReply
.challange
.length
= 8;
1035 if (RAND_bytes(r
.u
.ntlmInitReply
.challange
.data
,
1036 r
.u
.ntlmInitReply
.challange
.length
) != 1)
1038 krb5_set_error_string(context
, "out of random error");
1042 /* XXX fix targetinfo */
1043 ALLOC(r
.u
.ntlmInitReply
.targetinfo
);
1044 if (r
.u
.ntlmInitReply
.targetinfo
== NULL
) {
1045 krb5_set_error_string(context
, "out of memory");
1050 ret
= fill_targetinfo(context
,
1051 r
.u
.ntlmInitReply
.targetname
,
1053 r
.u
.ntlmInitReply
.targetinfo
);
1055 krb5_set_error_string(context
, "out of memory");
1061 * Save data encryted in opaque for the second part of the
1062 * ntlm authentication
1064 sp
= krb5_storage_emem();
1067 krb5_set_error_string(context
, "out of memory");
1071 ret
= krb5_storage_write(sp
, r
.u
.ntlmInitReply
.challange
.data
, 8);
1074 krb5_set_error_string(context
, "storage write challange");
1077 ret
= krb5_store_uint32(sp
, r
.u
.ntlmInitReply
.flags
);
1079 krb5_clear_error_string(context
);
1083 ret
= krb5_storage_to_data(sp
, &buf
);
1085 krb5_clear_error_string(context
);
1089 ret
= get_digest_key(context
, config
, server
, &crypto
);
1093 ret
= krb5_encrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1094 buf
.data
, buf
.length
, &r
.u
.ntlmInitReply
.opaque
);
1095 krb5_data_free(&buf
);
1096 krb5_crypto_destroy(context
, crypto
);
1101 kdc_log(context
, config
, 0, "NTLM init from %s", from
);
1105 case choice_DigestReqInner_ntlmRequest
: {
1106 krb5_principal clientprincipal
;
1107 unsigned char sessionkey
[16];
1108 unsigned char challange
[8];
1113 r
.element
= choice_DigestRepInner_ntlmResponse
;
1114 r
.u
.ntlmResponse
.success
= 0;
1115 r
.u
.ntlmResponse
.flags
= 0;
1116 r
.u
.ntlmResponse
.sessionkey
= NULL
;
1117 r
.u
.ntlmResponse
.tickets
= NULL
;
1120 ret
= krb5_parse_name(context
,
1121 ireq
.u
.ntlmRequest
.username
,
1126 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
1127 HDB_F_GET_CLIENT
, NULL
, &user
);
1128 krb5_free_principal(context
, clientprincipal
);
1130 krb5_set_error_string(context
, "NTLM user %s not in database",
1131 ireq
.u
.ntlmRequest
.username
);
1135 ret
= get_digest_key(context
, config
, server
, &crypto
);
1139 ret
= krb5_decrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1140 ireq
.u
.ntlmRequest
.opaque
.data
,
1141 ireq
.u
.ntlmRequest
.opaque
.length
, &buf
);
1142 krb5_crypto_destroy(context
, crypto
);
1145 kdc_log(context
, config
, 0,
1146 "Failed to decrypt nonce from %s", from
);
1150 sp
= krb5_storage_from_data(&buf
);
1153 krb5_set_error_string(context
, "out of memory");
1157 ret
= krb5_storage_read(sp
, challange
, sizeof(challange
));
1158 if (ret
!= sizeof(challange
)) {
1159 krb5_set_error_string(context
, "NTLM storage read challange");
1163 ret
= krb5_ret_uint32(sp
, &flags
);
1165 krb5_set_error_string(context
, "NTLM storage read flags");
1168 krb5_data_free(&buf
);
1170 if ((flags
& NTLM_NEG_NTLM
) == 0) {
1172 krb5_set_error_string(context
, "NTLM not negotiated");
1176 ret
= hdb_enctype2key(context
, &user
->entry
,
1177 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
1179 krb5_set_error_string(context
, "NTLM missing arcfour key");
1183 /* check if this is NTLMv2 */
1184 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= 24) {
1185 struct ntlm_buf infotarget
, answer
;
1188 if ((config
->digests_allowed
& NTLM_V2
) == 0) {
1189 kdc_log(context
, config
, 0, "NTLM v2 not allowed");
1195 targetname
= get_ntlm_targetname(context
, client
);
1196 if (targetname
== NULL
) {
1197 krb5_set_error_string(context
, "out of memory");
1202 answer
.length
= ireq
.u
.ntlmRequest
.ntlm
.length
;
1203 answer
.data
= ireq
.u
.ntlmRequest
.ntlm
.data
;
1205 ret
= heim_ntlm_verify_ntlm2(key
->key
.keyvalue
.data
,
1206 key
->key
.keyvalue
.length
,
1207 ireq
.u
.ntlmRequest
.username
,
1216 krb5_set_error_string(context
, "NTLM v2 verify failed");
1220 /* XXX verify infotarget matches client (checksum ?) */
1222 free(infotarget
.data
);
1226 struct ntlm_buf answer
;
1230 if (flags
& NTLM_NEG_NTLM2_SESSION
) {
1231 unsigned char sessionhash
[MD5_DIGEST_LENGTH
];
1234 if ((config
->digests_allowed
& NTLM_V1_SESSION
) == 0) {
1235 kdc_log(context
, config
, 0, "NTLM v1-session not allowed");
1240 if (ireq
.u
.ntlmRequest
.lm
.length
!= 24) {
1241 krb5_set_error_string(context
, "LM hash have wrong length "
1242 "for NTLM session key");
1248 MD5_Update(&md5ctx
, challange
, sizeof(challange
));
1249 MD5_Update(&md5ctx
, ireq
.u
.ntlmRequest
.lm
.data
, 8);
1250 MD5_Final(sessionhash
, &md5ctx
);
1251 memcpy(challange
, sessionhash
, sizeof(challange
));
1253 if ((config
->digests_allowed
& NTLM_V1
) == 0) {
1254 kdc_log(context
, config
, 0, "NTLM v1 not allowed");
1259 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
1260 key
->key
.keyvalue
.length
,
1261 challange
, &answer
);
1263 krb5_set_error_string(context
, "NTLM missing arcfour key");
1267 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= answer
.length
||
1268 memcmp(ireq
.u
.ntlmRequest
.ntlm
.data
, answer
.data
, answer
.length
) != 0)
1272 krb5_set_error_string(context
, "NTLM hash mismatch");
1282 key
->key
.keyvalue
.data
, key
->key
.keyvalue
.length
);
1283 MD4_Final(sessionkey
, &ctx
);
1287 if (ireq
.u
.ntlmRequest
.sessionkey
) {
1288 unsigned char masterkey
[MD4_DIGEST_LENGTH
];
1292 if ((flags
& NTLM_NEG_KEYEX
) == 0) {
1293 krb5_set_error_string(context
,
1294 "NTLM client failed to neg key "
1295 "exchange but still sent key");
1300 len
= ireq
.u
.ntlmRequest
.sessionkey
->length
;
1301 if (len
!= sizeof(masterkey
)){
1302 krb5_set_error_string(context
,
1303 "NTLM master key wrong length: %lu",
1304 (unsigned long)len
);
1308 RC4_set_key(&rc4
, sizeof(sessionkey
), sessionkey
);
1310 RC4(&rc4
, sizeof(masterkey
),
1311 ireq
.u
.ntlmRequest
.sessionkey
->data
,
1313 memset(&rc4
, 0, sizeof(rc4
));
1315 r
.u
.ntlmResponse
.sessionkey
=
1316 malloc(sizeof(*r
.u
.ntlmResponse
.sessionkey
));
1317 if (r
.u
.ntlmResponse
.sessionkey
== NULL
) {
1318 krb5_set_error_string(context
, "out of memory");
1322 ret
= krb5_data_copy(r
.u
.ntlmResponse
.sessionkey
,
1323 masterkey
, sizeof(masterkey
));
1325 krb5_set_error_string(context
, "out of memory");
1330 r
.u
.ntlmResponse
.success
= 1;
1331 kdc_log(context
, config
, 0, "NTLM version %d successful for %s",
1332 version
, ireq
.u
.ntlmRequest
.username
);
1335 case choice_DigestReqInner_supportedMechs
:
1337 kdc_log(context
, config
, 0, "digest supportedMechs from %s", from
);
1339 r
.element
= choice_DigestRepInner_supportedMechs
;
1340 memset(&r
.u
.supportedMechs
, 0, sizeof(r
.u
.supportedMechs
));
1342 if (config
->digests_allowed
& NTLM_V1
)
1343 r
.u
.supportedMechs
.ntlm_v1
= 1;
1344 if (config
->digests_allowed
& NTLM_V1_SESSION
)
1345 r
.u
.supportedMechs
.ntlm_v1_session
= 1;
1346 if (config
->digests_allowed
& NTLM_V2
)
1347 r
.u
.supportedMechs
.ntlm_v2
= 1;
1348 if (config
->digests_allowed
& DIGEST_MD5
)
1349 r
.u
.supportedMechs
.digest_md5
= 1;
1350 if (config
->digests_allowed
& CHAP_MD5
)
1351 r
.u
.supportedMechs
.chap_md5
= 1;
1352 if (config
->digests_allowed
& MS_CHAP_V2
)
1353 r
.u
.supportedMechs
.ms_chap_v2
= 1;
1358 krb5_set_error_string(context
, "unknown operation to digest");
1363 s
= krb5_get_error_message(context
, ret
);
1365 krb5_clear_error_string(context
);
1369 kdc_log(context
, config
, 0, "Digest failed with: %s", s
);
1371 r
.element
= choice_DigestRepInner_error
;
1372 r
.u
.error
.reason
= strdup("unknown error");
1373 krb5_free_error_string(context
, s
);
1374 if (r
.u
.error
.reason
== NULL
) {
1375 krb5_set_error_string(context
, "out of memory");
1379 r
.u
.error
.code
= EINVAL
;
1384 ASN1_MALLOC_ENCODE(DigestRepInner
, buf
.data
, buf
.length
, &r
, &size
, ret
);
1386 krb5_set_error_string(context
, "Failed to encode inner digest reply");
1389 if (size
!= buf
.length
)
1390 krb5_abortx(context
, "ASN1 internal error");
1392 krb5_auth_con_addflags(context
, ac
, KRB5_AUTH_CONTEXT_USE_SUBKEY
, NULL
);
1394 ret
= krb5_mk_rep (context
, ac
, &rep
.apRep
);
1401 ret
= krb5_auth_con_getlocalsubkey(context
, ac
, &key
);
1405 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
1406 krb5_free_keyblock (context
, key
);
1411 ret
= krb5_encrypt_EncryptedData(context
, crypto
, KRB5_KU_DIGEST_ENCRYPT
,
1412 buf
.data
, buf
.length
, 0,
1415 ASN1_MALLOC_ENCODE(DigestREP
, reply
->data
, reply
->length
, &rep
, &size
, ret
);
1417 krb5_set_error_string(context
, "Failed to encode digest reply");
1420 if (size
!= reply
->length
)
1421 krb5_abortx(context
, "ASN1 internal error");
1426 krb5_auth_con_free(context
, ac
);
1428 krb5_warn(context
, ret
, "Digest request from %s failed", from
);
1430 krb5_free_ticket(context
, ticket
);
1432 krb5_kt_close(context
, id
);
1434 krb5_crypto_destroy(context
, crypto
);
1436 krb5_storage_free(sp
);
1438 _kdc_free_ent (context
, user
);
1440 _kdc_free_ent (context
, server
);
1442 _kdc_free_ent (context
, client
);
1444 memset(password
, 0, strlen(password
));
1449 krb5_data_free(&buf
);
1450 krb5_data_free(&serverNonce
);
1451 free_DigestREP(&rep
);
1452 free_DigestRepInner(&r
);
1453 free_DigestReqInner(&ireq
);