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
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
|=
1006 NTLM_NEG_TARGET_DOMAIN
|
1012 NTLM_NEG_ALWAYS_SIGN| \
1013 NTLM_NEG_NTLM2_SESSION| \
1016 r
.u
.ntlmInitReply
.flags
|= (ireq
.u
.ntlmInit
.flags
& (ALL
));
1020 r
.u
.ntlmInitReply
.targetname
=
1021 get_ntlm_targetname(context
, client
);
1022 if (r
.u
.ntlmInitReply
.targetname
== NULL
) {
1023 krb5_set_error_string(context
, "out of memory");
1027 r
.u
.ntlmInitReply
.challange
.data
= malloc(8);
1028 if (r
.u
.ntlmInitReply
.challange
.data
== NULL
) {
1029 krb5_set_error_string(context
, "out of memory");
1033 r
.u
.ntlmInitReply
.challange
.length
= 8;
1034 if (RAND_bytes(r
.u
.ntlmInitReply
.challange
.data
,
1035 r
.u
.ntlmInitReply
.challange
.length
) != 1)
1037 krb5_set_error_string(context
, "out of random error");
1041 /* XXX fix targetinfo */
1042 ALLOC(r
.u
.ntlmInitReply
.targetinfo
);
1043 if (r
.u
.ntlmInitReply
.targetinfo
== NULL
) {
1044 krb5_set_error_string(context
, "out of memory");
1049 ret
= fill_targetinfo(context
,
1050 r
.u
.ntlmInitReply
.targetname
,
1052 r
.u
.ntlmInitReply
.targetinfo
);
1054 krb5_set_error_string(context
, "out of memory");
1060 * Save data encryted in opaque for the second part of the
1061 * ntlm authentication
1063 sp
= krb5_storage_emem();
1066 krb5_set_error_string(context
, "out of memory");
1070 ret
= krb5_storage_write(sp
, r
.u
.ntlmInitReply
.challange
.data
, 8);
1073 krb5_set_error_string(context
, "storage write challange");
1076 ret
= krb5_store_uint32(sp
, r
.u
.ntlmInitReply
.flags
);
1078 krb5_clear_error_string(context
);
1082 ret
= krb5_storage_to_data(sp
, &buf
);
1084 krb5_clear_error_string(context
);
1088 ret
= get_digest_key(context
, config
, server
, &crypto
);
1092 ret
= krb5_encrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1093 buf
.data
, buf
.length
, &r
.u
.ntlmInitReply
.opaque
);
1094 krb5_data_free(&buf
);
1095 krb5_crypto_destroy(context
, crypto
);
1100 kdc_log(context
, config
, 0, "NTLM init from %s", from
);
1104 case choice_DigestReqInner_ntlmRequest
: {
1105 krb5_principal clientprincipal
;
1106 unsigned char sessionkey
[16];
1107 unsigned char challange
[8];
1112 r
.element
= choice_DigestRepInner_ntlmResponse
;
1113 r
.u
.ntlmResponse
.success
= 0;
1114 r
.u
.ntlmResponse
.flags
= 0;
1115 r
.u
.ntlmResponse
.sessionkey
= NULL
;
1116 r
.u
.ntlmResponse
.tickets
= NULL
;
1119 ret
= krb5_parse_name(context
,
1120 ireq
.u
.ntlmRequest
.username
,
1125 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
1126 HDB_F_GET_CLIENT
, NULL
, &user
);
1127 krb5_free_principal(context
, clientprincipal
);
1129 krb5_set_error_string(context
, "NTLM user %s not in database",
1130 ireq
.u
.ntlmRequest
.username
);
1134 ret
= get_digest_key(context
, config
, server
, &crypto
);
1138 ret
= krb5_decrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1139 ireq
.u
.ntlmRequest
.opaque
.data
,
1140 ireq
.u
.ntlmRequest
.opaque
.length
, &buf
);
1141 krb5_crypto_destroy(context
, crypto
);
1144 kdc_log(context
, config
, 0,
1145 "Failed to decrypt nonce from %s", from
);
1149 sp
= krb5_storage_from_data(&buf
);
1152 krb5_set_error_string(context
, "out of memory");
1156 ret
= krb5_storage_read(sp
, challange
, sizeof(challange
));
1157 if (ret
!= sizeof(challange
)) {
1158 krb5_set_error_string(context
, "NTLM storage read challange");
1162 ret
= krb5_ret_uint32(sp
, &flags
);
1164 krb5_set_error_string(context
, "NTLM storage read flags");
1167 krb5_data_free(&buf
);
1169 if ((flags
& NTLM_NEG_NTLM
) == 0) {
1171 krb5_set_error_string(context
, "NTLM not negotiated");
1175 ret
= hdb_enctype2key(context
, &user
->entry
,
1176 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
1178 krb5_set_error_string(context
, "NTLM missing arcfour key");
1182 /* check if this is NTLMv2 */
1183 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= 24) {
1184 struct ntlm_buf infotarget
, answer
;
1187 if ((config
->digests_allowed
& NTLM_V2
) == 0) {
1188 kdc_log(context
, config
, 0, "NTLM v2 not allowed");
1194 targetname
= get_ntlm_targetname(context
, client
);
1195 if (targetname
== NULL
) {
1196 krb5_set_error_string(context
, "out of memory");
1201 answer
.length
= ireq
.u
.ntlmRequest
.ntlm
.length
;
1202 answer
.data
= ireq
.u
.ntlmRequest
.ntlm
.data
;
1204 ret
= heim_ntlm_verify_ntlm2(key
->key
.keyvalue
.data
,
1205 key
->key
.keyvalue
.length
,
1206 ireq
.u
.ntlmRequest
.username
,
1215 krb5_set_error_string(context
, "NTLM v2 verify failed");
1219 /* XXX verify infotarget matches client (checksum ?) */
1221 free(infotarget
.data
);
1225 struct ntlm_buf answer
;
1229 if (flags
& NTLM_NEG_NTLM2_SESSION
) {
1230 unsigned char sessionhash
[MD5_DIGEST_LENGTH
];
1233 if ((config
->digests_allowed
& NTLM_V1_SESSION
) == 0) {
1234 kdc_log(context
, config
, 0, "NTLM v1-session not allowed");
1239 if (ireq
.u
.ntlmRequest
.lm
.length
!= 24) {
1240 krb5_set_error_string(context
, "LM hash have wrong length "
1241 "for NTLM session key");
1247 MD5_Update(&md5ctx
, challange
, sizeof(challange
));
1248 MD5_Update(&md5ctx
, ireq
.u
.ntlmRequest
.lm
.data
, 8);
1249 MD5_Final(sessionhash
, &md5ctx
);
1250 memcpy(challange
, sessionhash
, sizeof(challange
));
1252 if ((config
->digests_allowed
& NTLM_V1
) == 0) {
1253 kdc_log(context
, config
, 0, "NTLM v1 not allowed");
1258 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
1259 key
->key
.keyvalue
.length
,
1260 challange
, &answer
);
1262 krb5_set_error_string(context
, "NTLM missing arcfour key");
1266 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= answer
.length
||
1267 memcmp(ireq
.u
.ntlmRequest
.ntlm
.data
, answer
.data
, answer
.length
) != 0)
1271 krb5_set_error_string(context
, "NTLM hash mismatch");
1281 key
->key
.keyvalue
.data
, key
->key
.keyvalue
.length
);
1282 MD4_Final(sessionkey
, &ctx
);
1286 if (ireq
.u
.ntlmRequest
.sessionkey
) {
1287 unsigned char masterkey
[MD4_DIGEST_LENGTH
];
1291 if ((flags
& NTLM_NEG_KEYEX
) == 0) {
1292 krb5_set_error_string(context
,
1293 "NTLM client failed to neg key "
1294 "exchange but still sent key");
1299 len
= ireq
.u
.ntlmRequest
.sessionkey
->length
;
1300 if (len
!= sizeof(masterkey
)){
1301 krb5_set_error_string(context
,
1302 "NTLM master key wrong length: %lu",
1303 (unsigned long)len
);
1307 RC4_set_key(&rc4
, sizeof(sessionkey
), sessionkey
);
1309 RC4(&rc4
, sizeof(masterkey
),
1310 ireq
.u
.ntlmRequest
.sessionkey
->data
,
1312 memset(&rc4
, 0, sizeof(rc4
));
1314 r
.u
.ntlmResponse
.sessionkey
=
1315 malloc(sizeof(*r
.u
.ntlmResponse
.sessionkey
));
1316 if (r
.u
.ntlmResponse
.sessionkey
== NULL
) {
1317 krb5_set_error_string(context
, "out of memory");
1321 ret
= krb5_data_copy(r
.u
.ntlmResponse
.sessionkey
,
1322 masterkey
, sizeof(masterkey
));
1324 krb5_set_error_string(context
, "out of memory");
1329 r
.u
.ntlmResponse
.success
= 1;
1330 kdc_log(context
, config
, 0, "NTLM version %d successful for %s",
1331 version
, ireq
.u
.ntlmRequest
.username
);
1334 case choice_DigestReqInner_supportedMechs
:
1336 r
.element
= choice_DigestRepInner_supportedMechs
;
1337 memset(&r
.u
.supportedMechs
, 0, sizeof(r
.u
.supportedMechs
));
1339 if (config
->digests_allowed
& NTLM_V1
)
1340 r
.u
.supportedMechs
.ntlm_v1
= 1;
1341 if (config
->digests_allowed
& NTLM_V1_SESSION
)
1342 r
.u
.supportedMechs
.ntlm_v1_session
= 1;
1343 if (config
->digests_allowed
& NTLM_V2
)
1344 r
.u
.supportedMechs
.ntlm_v2
= 1;
1345 if (config
->digests_allowed
& DIGEST_MD5
)
1346 r
.u
.supportedMechs
.digest_md5
= 1;
1347 if (config
->digests_allowed
& CHAP_MD5
)
1348 r
.u
.supportedMechs
.chap_md5
= 1;
1349 if (config
->digests_allowed
& MS_CHAP_V2
)
1350 r
.u
.supportedMechs
.ms_chap_v2
= 1;
1355 krb5_set_error_string(context
, "unknown operation to digest");
1360 s
= krb5_get_error_message(context
, ret
);
1362 krb5_clear_error_string(context
);
1366 kdc_log(context
, config
, 0, "Digest failed with: %s", s
);
1368 r
.element
= choice_DigestRepInner_error
;
1369 r
.u
.error
.reason
= strdup("unknown error");
1370 krb5_free_error_string(context
, s
);
1371 if (r
.u
.error
.reason
== NULL
) {
1372 krb5_set_error_string(context
, "out of memory");
1376 r
.u
.error
.code
= EINVAL
;
1381 ASN1_MALLOC_ENCODE(DigestRepInner
, buf
.data
, buf
.length
, &r
, &size
, ret
);
1383 krb5_set_error_string(context
, "Failed to encode inner digest reply");
1386 if (size
!= buf
.length
)
1387 krb5_abortx(context
, "ASN1 internal error");
1389 krb5_auth_con_addflags(context
, ac
, KRB5_AUTH_CONTEXT_USE_SUBKEY
, NULL
);
1391 ret
= krb5_mk_rep (context
, ac
, &rep
.apRep
);
1398 ret
= krb5_auth_con_getlocalsubkey(context
, ac
, &key
);
1402 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
1403 krb5_free_keyblock (context
, key
);
1408 ret
= krb5_encrypt_EncryptedData(context
, crypto
, KRB5_KU_DIGEST_ENCRYPT
,
1409 buf
.data
, buf
.length
, 0,
1412 ASN1_MALLOC_ENCODE(DigestREP
, reply
->data
, reply
->length
, &rep
, &size
, ret
);
1414 krb5_set_error_string(context
, "Failed to encode digest reply");
1417 if (size
!= reply
->length
)
1418 krb5_abortx(context
, "ASN1 internal error");
1423 krb5_auth_con_free(context
, ac
);
1425 krb5_warn(context
, ret
, "Digest request from %s failed", from
);
1427 krb5_free_ticket(context
, ticket
);
1429 krb5_kt_close(context
, id
);
1431 krb5_crypto_destroy(context
, crypto
);
1433 krb5_storage_free(sp
);
1435 _kdc_free_ent (context
, user
);
1437 _kdc_free_ent (context
, server
);
1439 _kdc_free_ent (context
, client
);
1441 memset(password
, 0, strlen(password
));
1446 krb5_data_free(&buf
);
1447 krb5_data_free(&serverNonce
);
1448 free_DigestREP(&rep
);
1449 free_DigestRepInner(&r
);
1450 free_DigestReqInner(&ireq
);