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_message(context
, ret
, "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 struct 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
));
237 memset(&res
, 0, sizeof(res
));
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_message(context
, ret
, "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_message(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 ret
= KRB5KDC_ERR_POLICY
;
328 krb5_set_error_message(context
, ret
,
329 "Client is not permitted to use digest");
338 ret
= krb5_auth_con_getremotesubkey(context
, ac
, &key
);
343 krb5_set_error_message(context
, ret
, "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_message(context
, ret
, "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
) {
391 krb5_set_error_message(context
, ret
, "Failed to decode server nonce");
395 sp
= krb5_storage_emem();
398 krb5_set_error_message(context
, ret
, "malloc: out of memory");
401 ret
= krb5_store_stringz(sp
, ireq
.u
.init
.type
);
403 krb5_clear_error_message(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
);
415 krb5_set_error_message(context
, ret
,
416 "Failed to allocate channel binding");
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_message(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
) {
434 krb5_set_error_message(context
, ret
, "malloc: out of memory");
438 asprintf(r
.u
.initReply
.identifier
, "%02X", identifier
& 0xff);
439 if (*r
.u
.initReply
.identifier
== NULL
) {
441 krb5_set_error_message(context
, ret
, "malloc: 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_message(context
);
456 ret
= krb5_storage_to_data(sp
, &buf
);
458 krb5_clear_error_message(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_message(context
, ret
, "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
);
491 krb5_data_zero(&buf
);
492 if (r
.u
.initReply
.opaque
== NULL
) {
493 krb5_clear_error_message(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_message(context
, ret
, "malloc: out of memory");
510 ret
= krb5_store_stringz(sp
, ireq
.u
.digestRequest
.type
);
512 krb5_clear_error_message(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_message(context
);
526 buf
.length
= strlen(ireq
.u
.digestRequest
.opaque
);
527 buf
.data
= malloc(buf
.length
);
528 if (buf
.data
== NULL
) {
530 krb5_set_error_message(context
, ret
, "malloc: out of memory");
534 ret
= hex_decode(ireq
.u
.digestRequest
.opaque
, buf
.data
, buf
.length
);
537 krb5_set_error_message(context
, ret
, "Failed to decode opaque");
542 ret
= decode_Checksum(buf
.data
, buf
.length
, &res
, NULL
);
544 krb5_data_zero(&buf
);
546 krb5_set_error_message(context
, ret
,
547 "Failed to decode digest Checksum");
551 ret
= krb5_storage_to_data(sp
, &buf
);
553 krb5_clear_error_message(context
);
557 serverNonce
.length
= strlen(ireq
.u
.digestRequest
.serverNonce
);
558 serverNonce
.data
= malloc(serverNonce
.length
);
559 if (serverNonce
.data
== NULL
) {
561 krb5_set_error_message(context
, ret
, "malloc: out of memory");
566 * CHAP does the checksum of the raw nonce, but do it for all
567 * types, since we need to check the timestamp.
572 ssize
= hex_decode(ireq
.u
.digestRequest
.serverNonce
,
573 serverNonce
.data
, serverNonce
.length
);
576 krb5_set_error_message(context
, ret
, "Failed to decode serverNonce");
579 serverNonce
.length
= ssize
;
582 ret
= get_digest_key(context
, config
, server
, &crypto
);
586 ret
= krb5_verify_checksum(context
, crypto
,
587 KRB5_KU_DIGEST_OPAQUE
,
588 buf
.data
, buf
.length
, &res
);
590 krb5_data_free(&buf
);
591 krb5_crypto_destroy(context
, crypto
);
598 unsigned char *p
= serverNonce
.data
;
601 if (serverNonce
.length
< 4) {
603 krb5_set_error_message(context
, ret
, "server nonce too short");
606 t
= p
[0] | (p
[1] << 8) | (p
[2] << 16) | (p
[3] << 24);
608 if (abs((kdc_time
& 0xffffffff) - t
) > context
->max_skew
) {
610 krb5_set_error_message(context
, ret
, "time screw in server nonce ");
615 if (strcasecmp(ireq
.u
.digestRequest
.type
, "CHAP") == 0) {
617 unsigned char md
[MD5_DIGEST_LENGTH
];
621 if ((config
->digests_allowed
& CHAP_MD5
) == 0) {
622 kdc_log(context
, config
, 0, "Digest CHAP MD5 not allowed");
626 if (ireq
.u
.digestRequest
.identifier
== NULL
) {
628 krb5_set_error_message(context
, ret
, "Identifier missing "
629 "from CHAP request");
633 if (hex_decode(*ireq
.u
.digestRequest
.identifier
, &id
, 1) != 1) {
635 krb5_set_error_message(context
, ret
, "failed to decode identifier");
639 ret
= get_password_entry(context
, config
,
640 ireq
.u
.digestRequest
.username
,
646 MD5_Update(&ctx
, &id
, 1);
647 MD5_Update(&ctx
, password
, strlen(password
));
648 MD5_Update(&ctx
, serverNonce
.data
, serverNonce
.length
);
651 hex_encode(md
, sizeof(md
), &mdx
);
653 krb5_clear_error_message(context
);
658 r
.element
= choice_DigestRepInner_response
;
660 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
663 r
.u
.response
.success
= TRUE
;
665 kdc_log(context
, config
, 0,
666 "CHAP reply mismatch for %s",
667 ireq
.u
.digestRequest
.username
);
668 r
.u
.response
.success
= FALSE
;
671 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "SASL-DIGEST-MD5") == 0) {
673 unsigned char md
[MD5_DIGEST_LENGTH
];
677 if ((config
->digests_allowed
& DIGEST_MD5
) == 0) {
678 kdc_log(context
, config
, 0, "Digest SASL MD5 not allowed");
682 if (ireq
.u
.digestRequest
.nonceCount
== NULL
)
684 if (ireq
.u
.digestRequest
.clientNonce
== NULL
)
686 if (ireq
.u
.digestRequest
.qop
== NULL
)
688 if (ireq
.u
.digestRequest
.realm
== NULL
)
691 ret
= get_password_entry(context
, config
,
692 ireq
.u
.digestRequest
.username
,
698 MD5_Update(&ctx
, ireq
.u
.digestRequest
.username
,
699 strlen(ireq
.u
.digestRequest
.username
));
700 MD5_Update(&ctx
, ":", 1);
701 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.realm
,
702 strlen(*ireq
.u
.digestRequest
.realm
));
703 MD5_Update(&ctx
, ":", 1);
704 MD5_Update(&ctx
, password
, strlen(password
));
708 MD5_Update(&ctx
, md
, sizeof(md
));
709 MD5_Update(&ctx
, ":", 1);
710 MD5_Update(&ctx
, ireq
.u
.digestRequest
.serverNonce
,
711 strlen(ireq
.u
.digestRequest
.serverNonce
));
712 MD5_Update(&ctx
, ":", 1);
713 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.nonceCount
,
714 strlen(*ireq
.u
.digestRequest
.nonceCount
));
715 if (ireq
.u
.digestRequest
.authid
) {
716 MD5_Update(&ctx
, ":", 1);
717 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.authid
,
718 strlen(*ireq
.u
.digestRequest
.authid
));
721 hex_encode(md
, sizeof(md
), &A1
);
724 krb5_set_error_message(context
, ret
, "malloc: out of memory");
729 MD5_Update(&ctx
, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
730 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.uri
,
731 strlen(*ireq
.u
.digestRequest
.uri
));
734 if (strcmp(ireq
.u
.digestRequest
.digest
, "clear") != 0) {
735 static char conf_zeros
[] = ":00000000000000000000000000000000";
736 MD5_Update(&ctx
, conf_zeros
, sizeof(conf_zeros
) - 1);
740 hex_encode(md
, sizeof(md
), &A2
);
743 krb5_set_error_message(context
, ret
, "malloc: out of memory");
749 MD5_Update(&ctx
, A1
, strlen(A2
));
750 MD5_Update(&ctx
, ":", 1);
751 MD5_Update(&ctx
, ireq
.u
.digestRequest
.serverNonce
,
752 strlen(ireq
.u
.digestRequest
.serverNonce
));
753 MD5_Update(&ctx
, ":", 1);
754 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.nonceCount
,
755 strlen(*ireq
.u
.digestRequest
.nonceCount
));
756 MD5_Update(&ctx
, ":", 1);
757 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.clientNonce
,
758 strlen(*ireq
.u
.digestRequest
.clientNonce
));
759 MD5_Update(&ctx
, ":", 1);
760 MD5_Update(&ctx
, *ireq
.u
.digestRequest
.qop
,
761 strlen(*ireq
.u
.digestRequest
.qop
));
762 MD5_Update(&ctx
, ":", 1);
763 MD5_Update(&ctx
, A2
, strlen(A2
));
770 hex_encode(md
, sizeof(md
), &mdx
);
772 krb5_clear_error_message(context
);
777 r
.element
= choice_DigestRepInner_response
;
778 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
781 r
.u
.response
.success
= TRUE
;
783 kdc_log(context
, config
, 0,
784 "DIGEST-MD5 reply mismatch for %s",
785 ireq
.u
.digestRequest
.username
);
786 r
.u
.response
.success
= FALSE
;
789 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "MS-CHAP-V2") == 0) {
790 unsigned char md
[SHA_DIGEST_LENGTH
], challange
[SHA_DIGEST_LENGTH
];
791 krb5_principal clientprincipal
= NULL
;
793 const char *username
;
794 struct ntlm_buf answer
;
798 if ((config
->digests_allowed
& MS_CHAP_V2
) == 0) {
799 kdc_log(context
, config
, 0, "MS-CHAP-V2 not allowed");
803 if (ireq
.u
.digestRequest
.clientNonce
== NULL
) {
805 krb5_set_error_message(context
, ret
,
806 "MS-CHAP-V2 clientNonce missing");
809 if (serverNonce
.length
!= 16) {
811 krb5_set_error_message(context
, ret
,
812 "MS-CHAP-V2 serverNonce wrong length");
816 /* strip of the domain component */
817 username
= strchr(ireq
.u
.digestRequest
.username
, '\\');
818 if (username
== NULL
)
819 username
= ireq
.u
.digestRequest
.username
;
827 krb5_data clientNonce
;
829 clientNonce
.length
= strlen(*ireq
.u
.digestRequest
.clientNonce
);
830 clientNonce
.data
= malloc(clientNonce
.length
);
831 if (clientNonce
.data
== NULL
) {
833 krb5_set_error_message(context
, ret
, "malloc: out of memory");
837 ssize
= hex_decode(*ireq
.u
.digestRequest
.clientNonce
,
838 clientNonce
.data
, clientNonce
.length
);
841 krb5_set_error_message(context
, ret
,
842 "Failed to decode clientNonce");
845 SHA1_Update(&ctx
, clientNonce
.data
, ssize
);
846 free(clientNonce
.data
);
848 SHA1_Update(&ctx
, serverNonce
.data
, serverNonce
.length
);
849 SHA1_Update(&ctx
, username
, strlen(username
));
850 SHA1_Final(challange
, &ctx
);
853 ret
= krb5_parse_name(context
, username
, &clientprincipal
);
857 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
858 HDB_F_GET_CLIENT
, NULL
, &user
);
859 krb5_free_principal(context
, clientprincipal
);
861 krb5_set_error_message(context
, ret
,
862 "MS-CHAP-V2 user %s not in database",
867 ret
= hdb_enctype2key(context
, &user
->entry
,
868 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
870 krb5_set_error_message(context
, ret
,
871 "MS-CHAP-V2 missing arcfour key %s",
876 /* ChallengeResponse */
877 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
878 key
->key
.keyvalue
.length
,
881 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
885 hex_encode(answer
.data
, answer
.length
, &mdx
);
888 krb5_clear_error_message(context
);
893 r
.element
= choice_DigestRepInner_response
;
894 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
896 r
.u
.response
.success
= TRUE
;
898 kdc_log(context
, config
, 0,
899 "MS-CHAP-V2 hash mismatch for %s",
900 ireq
.u
.digestRequest
.username
);
901 r
.u
.response
.success
= FALSE
;
905 if (r
.u
.response
.success
) {
906 unsigned char hashhash
[MD4_DIGEST_LENGTH
];
913 MD4_Update(&hctx
, key
->key
.keyvalue
.data
,
914 key
->key
.keyvalue
.length
);
915 MD4_Final(hashhash
, &hctx
);
918 /* GenerateAuthenticatorResponse */
920 SHA1_Update(&ctx
, hashhash
, sizeof(hashhash
));
921 SHA1_Update(&ctx
, answer
.data
, answer
.length
);
922 SHA1_Update(&ctx
, ms_chap_v2_magic1
,sizeof(ms_chap_v2_magic1
));
923 SHA1_Final(md
, &ctx
);
926 SHA1_Update(&ctx
, md
, sizeof(md
));
927 SHA1_Update(&ctx
, challange
, 8);
928 SHA1_Update(&ctx
, ms_chap_v2_magic2
, sizeof(ms_chap_v2_magic2
));
929 SHA1_Final(md
, &ctx
);
931 r
.u
.response
.rsp
= calloc(1, sizeof(*r
.u
.response
.rsp
));
932 if (r
.u
.response
.rsp
== NULL
) {
934 krb5_clear_error_message(context
);
939 hex_encode(md
, sizeof(md
), r
.u
.response
.rsp
);
940 if (r
.u
.response
.rsp
== NULL
) {
942 krb5_clear_error_message(context
);
947 /* get_master, rfc 3079 3.4 */
949 SHA1_Update(&ctx
, hashhash
, 16); /* md4(hash) */
950 SHA1_Update(&ctx
, answer
.data
, answer
.length
);
951 SHA1_Update(&ctx
, ms_rfc3079_magic1
, sizeof(ms_rfc3079_magic1
));
952 SHA1_Final(md
, &ctx
);
956 r
.u
.response
.session_key
=
957 calloc(1, sizeof(*r
.u
.response
.session_key
));
958 if (r
.u
.response
.session_key
== NULL
) {
959 krb5_clear_error_message(context
);
964 ret
= krb5_data_copy(r
.u
.response
.session_key
, md
, 16);
966 krb5_clear_error_message(context
);
972 r
.element
= choice_DigestRepInner_error
;
973 asprintf(&r
.u
.error
.reason
, "Unsupported digest type %s",
974 ireq
.u
.digestRequest
.type
);
975 if (r
.u
.error
.reason
== NULL
) {
977 krb5_set_error_message(context
, ret
, "malloc: out of memory");
980 r
.u
.error
.code
= EINVAL
;
983 kdc_log(context
, config
, 0, "Digest %s request successful %s",
984 ireq
.u
.digestRequest
.type
, ireq
.u
.digestRequest
.username
);
988 case choice_DigestReqInner_ntlmInit
:
990 if ((config
->digests_allowed
& (NTLM_V1
|NTLM_V1_SESSION
|NTLM_V2
)) == 0) {
991 kdc_log(context
, config
, 0, "NTLM not allowed");
995 r
.element
= choice_DigestRepInner_ntlmInitReply
;
997 r
.u
.ntlmInitReply
.flags
= NTLM_NEG_UNICODE
;
999 if ((ireq
.u
.ntlmInit
.flags
& NTLM_NEG_UNICODE
) == 0) {
1000 kdc_log(context
, config
, 0, "NTLM client have no unicode");
1004 if (ireq
.u
.ntlmInit
.flags
& NTLM_NEG_NTLM
)
1005 r
.u
.ntlmInitReply
.flags
|= NTLM_NEG_NTLM
;
1007 kdc_log(context
, config
, 0, "NTLM client doesn't support NTLM");
1011 r
.u
.ntlmInitReply
.flags
|=
1013 NTLM_TARGET_DOMAIN
|
1019 NTLM_NEG_ALWAYS_SIGN| \
1020 NTLM_NEG_NTLM2_SESSION| \
1023 r
.u
.ntlmInitReply
.flags
|= (ireq
.u
.ntlmInit
.flags
& (ALL
));
1027 r
.u
.ntlmInitReply
.targetname
=
1028 get_ntlm_targetname(context
, client
);
1029 if (r
.u
.ntlmInitReply
.targetname
== NULL
) {
1031 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1034 r
.u
.ntlmInitReply
.challange
.data
= malloc(8);
1035 if (r
.u
.ntlmInitReply
.challange
.data
== NULL
) {
1037 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1040 r
.u
.ntlmInitReply
.challange
.length
= 8;
1041 if (RAND_bytes(r
.u
.ntlmInitReply
.challange
.data
,
1042 r
.u
.ntlmInitReply
.challange
.length
) != 1)
1045 krb5_set_error_message(context
, ret
, "out of random error");
1048 /* XXX fix targetinfo */
1049 ALLOC(r
.u
.ntlmInitReply
.targetinfo
);
1050 if (r
.u
.ntlmInitReply
.targetinfo
== NULL
) {
1052 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1056 ret
= fill_targetinfo(context
,
1057 r
.u
.ntlmInitReply
.targetname
,
1059 r
.u
.ntlmInitReply
.targetinfo
);
1062 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1067 * Save data encryted in opaque for the second part of the
1068 * ntlm authentication
1070 sp
= krb5_storage_emem();
1073 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1077 ret
= krb5_storage_write(sp
, r
.u
.ntlmInitReply
.challange
.data
, 8);
1080 krb5_set_error_message(context
, ret
, "storage write challange");
1083 ret
= krb5_store_uint32(sp
, r
.u
.ntlmInitReply
.flags
);
1085 krb5_clear_error_message(context
);
1089 ret
= krb5_storage_to_data(sp
, &buf
);
1091 krb5_clear_error_message(context
);
1095 ret
= get_digest_key(context
, config
, server
, &crypto
);
1099 ret
= krb5_encrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1100 buf
.data
, buf
.length
, &r
.u
.ntlmInitReply
.opaque
);
1101 krb5_data_free(&buf
);
1102 krb5_crypto_destroy(context
, crypto
);
1107 kdc_log(context
, config
, 0, "NTLM init from %s", from
);
1111 case choice_DigestReqInner_ntlmRequest
: {
1112 krb5_principal clientprincipal
;
1113 unsigned char sessionkey
[16];
1114 unsigned char challange
[8];
1119 r
.element
= choice_DigestRepInner_ntlmResponse
;
1120 r
.u
.ntlmResponse
.success
= 0;
1121 r
.u
.ntlmResponse
.flags
= 0;
1122 r
.u
.ntlmResponse
.sessionkey
= NULL
;
1123 r
.u
.ntlmResponse
.tickets
= NULL
;
1126 ret
= krb5_parse_name(context
,
1127 ireq
.u
.ntlmRequest
.username
,
1132 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
1133 HDB_F_GET_CLIENT
, NULL
, &user
);
1134 krb5_free_principal(context
, clientprincipal
);
1136 krb5_set_error_message(context
, ret
, "NTLM user %s not in database",
1137 ireq
.u
.ntlmRequest
.username
);
1141 ret
= get_digest_key(context
, config
, server
, &crypto
);
1145 ret
= krb5_decrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1146 ireq
.u
.ntlmRequest
.opaque
.data
,
1147 ireq
.u
.ntlmRequest
.opaque
.length
, &buf
);
1148 krb5_crypto_destroy(context
, crypto
);
1151 kdc_log(context
, config
, 0,
1152 "Failed to decrypt nonce from %s", from
);
1156 sp
= krb5_storage_from_data(&buf
);
1159 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1163 ret
= krb5_storage_read(sp
, challange
, sizeof(challange
));
1164 if (ret
!= sizeof(challange
)) {
1166 krb5_set_error_message(context
, ret
, "NTLM storage read challange");
1169 ret
= krb5_ret_uint32(sp
, &flags
);
1171 krb5_set_error_message(context
, ret
, "NTLM storage read flags");
1174 krb5_storage_free(sp
);
1176 krb5_data_free(&buf
);
1178 if ((flags
& NTLM_NEG_NTLM
) == 0) {
1180 krb5_set_error_message(context
, ret
, "NTLM not negotiated");
1184 ret
= hdb_enctype2key(context
, &user
->entry
,
1185 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
1187 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
1191 /* check if this is NTLMv2 */
1192 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= 24) {
1193 struct ntlm_buf infotarget
, answer
;
1196 if ((config
->digests_allowed
& NTLM_V2
) == 0) {
1197 kdc_log(context
, config
, 0, "NTLM v2 not allowed");
1203 targetname
= get_ntlm_targetname(context
, client
);
1204 if (targetname
== NULL
) {
1206 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1210 answer
.length
= ireq
.u
.ntlmRequest
.ntlm
.length
;
1211 answer
.data
= ireq
.u
.ntlmRequest
.ntlm
.data
;
1213 ret
= heim_ntlm_verify_ntlm2(key
->key
.keyvalue
.data
,
1214 key
->key
.keyvalue
.length
,
1215 ireq
.u
.ntlmRequest
.username
,
1224 krb5_set_error_message(context
, ret
, "NTLM v2 verify failed");
1228 /* XXX verify infotarget matches client (checksum ?) */
1230 free(infotarget
.data
);
1234 struct ntlm_buf answer
;
1238 if (flags
& NTLM_NEG_NTLM2_SESSION
) {
1239 unsigned char sessionhash
[MD5_DIGEST_LENGTH
];
1242 if ((config
->digests_allowed
& NTLM_V1_SESSION
) == 0) {
1243 kdc_log(context
, config
, 0, "NTLM v1-session not allowed");
1248 if (ireq
.u
.ntlmRequest
.lm
.length
!= 24) {
1250 krb5_set_error_message(context
, ret
, "LM hash have wrong length "
1251 "for NTLM session key");
1256 MD5_Update(&md5ctx
, challange
, sizeof(challange
));
1257 MD5_Update(&md5ctx
, ireq
.u
.ntlmRequest
.lm
.data
, 8);
1258 MD5_Final(sessionhash
, &md5ctx
);
1259 memcpy(challange
, sessionhash
, sizeof(challange
));
1261 if ((config
->digests_allowed
& NTLM_V1
) == 0) {
1262 kdc_log(context
, config
, 0, "NTLM v1 not allowed");
1267 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
1268 key
->key
.keyvalue
.length
,
1269 challange
, &answer
);
1271 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
1275 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= answer
.length
||
1276 memcmp(ireq
.u
.ntlmRequest
.ntlm
.data
, answer
.data
, answer
.length
) != 0)
1280 krb5_set_error_message(context
, ret
, "NTLM hash mismatch");
1290 key
->key
.keyvalue
.data
, key
->key
.keyvalue
.length
);
1291 MD4_Final(sessionkey
, &ctx
);
1295 if (ireq
.u
.ntlmRequest
.sessionkey
) {
1296 unsigned char masterkey
[MD4_DIGEST_LENGTH
];
1300 if ((flags
& NTLM_NEG_KEYEX
) == 0) {
1302 krb5_set_error_message(context
, ret
,
1303 "NTLM client failed to neg key "
1304 "exchange but still sent key");
1308 len
= ireq
.u
.ntlmRequest
.sessionkey
->length
;
1309 if (len
!= sizeof(masterkey
)){
1311 krb5_set_error_message(context
, ret
,
1312 "NTLM master key wrong length: %lu",
1313 (unsigned long)len
);
1317 RC4_set_key(&rc4
, sizeof(sessionkey
), sessionkey
);
1319 RC4(&rc4
, sizeof(masterkey
),
1320 ireq
.u
.ntlmRequest
.sessionkey
->data
,
1322 memset(&rc4
, 0, sizeof(rc4
));
1324 r
.u
.ntlmResponse
.sessionkey
=
1325 malloc(sizeof(*r
.u
.ntlmResponse
.sessionkey
));
1326 if (r
.u
.ntlmResponse
.sessionkey
== NULL
) {
1328 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1332 ret
= krb5_data_copy(r
.u
.ntlmResponse
.sessionkey
,
1333 masterkey
, sizeof(masterkey
));
1335 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1340 r
.u
.ntlmResponse
.success
= 1;
1341 kdc_log(context
, config
, 0, "NTLM version %d successful for %s",
1342 version
, ireq
.u
.ntlmRequest
.username
);
1345 case choice_DigestReqInner_supportedMechs
:
1347 kdc_log(context
, config
, 0, "digest supportedMechs from %s", from
);
1349 r
.element
= choice_DigestRepInner_supportedMechs
;
1350 memset(&r
.u
.supportedMechs
, 0, sizeof(r
.u
.supportedMechs
));
1352 if (config
->digests_allowed
& NTLM_V1
)
1353 r
.u
.supportedMechs
.ntlm_v1
= 1;
1354 if (config
->digests_allowed
& NTLM_V1_SESSION
)
1355 r
.u
.supportedMechs
.ntlm_v1_session
= 1;
1356 if (config
->digests_allowed
& NTLM_V2
)
1357 r
.u
.supportedMechs
.ntlm_v2
= 1;
1358 if (config
->digests_allowed
& DIGEST_MD5
)
1359 r
.u
.supportedMechs
.digest_md5
= 1;
1360 if (config
->digests_allowed
& CHAP_MD5
)
1361 r
.u
.supportedMechs
.chap_md5
= 1;
1362 if (config
->digests_allowed
& MS_CHAP_V2
)
1363 r
.u
.supportedMechs
.ms_chap_v2
= 1;
1369 krb5_set_error_message(context
, ret
, "unknown operation to digest");
1373 s
= krb5_get_error_message(context
, ret
);
1375 krb5_clear_error_message(context
);
1379 kdc_log(context
, config
, 0, "Digest failed with: %s", s
);
1381 r
.element
= choice_DigestRepInner_error
;
1382 r
.u
.error
.reason
= strdup("unknown error");
1383 krb5_free_error_message(context
, s
);
1384 if (r
.u
.error
.reason
== NULL
) {
1386 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1389 r
.u
.error
.code
= EINVAL
;
1394 ASN1_MALLOC_ENCODE(DigestRepInner
, buf
.data
, buf
.length
, &r
, &size
, ret
);
1396 krb5_set_error_message(context
, ret
, "Failed to encode inner digest reply");
1399 if (size
!= buf
.length
)
1400 krb5_abortx(context
, "ASN1 internal error");
1402 krb5_auth_con_addflags(context
, ac
, KRB5_AUTH_CONTEXT_USE_SUBKEY
, NULL
);
1404 ret
= krb5_mk_rep (context
, ac
, &rep
.apRep
);
1411 ret
= krb5_auth_con_getlocalsubkey(context
, ac
, &key
);
1415 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
1416 krb5_free_keyblock (context
, key
);
1421 ret
= krb5_encrypt_EncryptedData(context
, crypto
, KRB5_KU_DIGEST_ENCRYPT
,
1422 buf
.data
, buf
.length
, 0,
1425 ASN1_MALLOC_ENCODE(DigestREP
, reply
->data
, reply
->length
, &rep
, &size
, ret
);
1427 krb5_set_error_message(context
, ret
, "Failed to encode digest reply");
1430 if (size
!= reply
->length
)
1431 krb5_abortx(context
, "ASN1 internal error");
1436 krb5_auth_con_free(context
, ac
);
1438 krb5_warn(context
, ret
, "Digest request from %s failed", from
);
1440 krb5_free_ticket(context
, ticket
);
1442 krb5_kt_close(context
, id
);
1444 krb5_crypto_destroy(context
, crypto
);
1446 krb5_storage_free(sp
);
1448 _kdc_free_ent (context
, user
);
1450 _kdc_free_ent (context
, server
);
1452 _kdc_free_ent (context
, client
);
1454 memset(password
, 0, strlen(password
));
1459 krb5_data_free(&buf
);
1460 krb5_data_free(&serverNonce
);
1461 free_Checksum(&res
);
1462 free_DigestREP(&rep
);
1463 free_DigestRepInner(&r
);
1464 free_DigestReqInner(&ireq
);