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
, NULL
, &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
, "HDBGET:", &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 rr
= krb5_principal_get_realm(context
, principal
);
285 krb5_free_principal(context
, principal
);
288 if (strcmp(p
, rr
) != 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
, 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
, 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
) {
411 aret
= asprintf(&s
, "%s-%s:%s", r
.u
.initReply
.nonce
,
412 ireq
.u
.init
.channel
->cb_type
,
413 ireq
.u
.init
.channel
->cb_binding
);
414 if (aret
== -1 || s
== NULL
) {
416 krb5_set_error_message(context
, ret
,
417 "Failed to allocate channel binding");
420 free(r
.u
.initReply
.nonce
);
421 r
.u
.initReply
.nonce
= s
;
424 ret
= krb5_store_stringz(sp
, r
.u
.initReply
.nonce
);
426 krb5_clear_error_message(context
);
430 if (strcasecmp(ireq
.u
.init
.type
, "CHAP") == 0) {
433 r
.u
.initReply
.identifier
=
434 malloc(sizeof(*r
.u
.initReply
.identifier
));
435 if (r
.u
.initReply
.identifier
== NULL
) {
437 krb5_set_error_message(context
, ret
, "malloc: out of memory");
441 aret
= asprintf(r
.u
.initReply
.identifier
, "%02X", identifier
&0xff);
442 if (aret
== -1 || *r
.u
.initReply
.identifier
== NULL
) {
444 krb5_set_error_message(context
, ret
, "malloc: out of memory");
449 r
.u
.initReply
.identifier
= NULL
;
451 if (ireq
.u
.init
.hostname
) {
452 ret
= krb5_store_stringz(sp
, *ireq
.u
.init
.hostname
);
454 krb5_clear_error_message(context
);
459 ret
= krb5_storage_to_data(sp
, &buf
);
461 krb5_clear_error_message(context
);
465 ret
= get_digest_key(context
, config
, server
, &crypto
);
469 ret
= krb5_create_checksum(context
,
471 KRB5_KU_DIGEST_OPAQUE
,
476 krb5_crypto_destroy(context
, crypto
);
478 krb5_data_free(&buf
);
482 ASN1_MALLOC_ENCODE(Checksum
, buf
.data
, buf
.length
, &res
, &size
, ret
);
485 krb5_set_error_message(context
, ret
, "Failed to encode "
486 "checksum in digest request");
489 if (size
!= buf
.length
)
490 krb5_abortx(context
, "ASN1 internal error");
492 hex_encode(buf
.data
, buf
.length
, &r
.u
.initReply
.opaque
);
494 krb5_data_zero(&buf
);
495 if (r
.u
.initReply
.opaque
== NULL
) {
496 krb5_clear_error_message(context
);
501 kdc_log(context
, config
, 0, "Digest %s init request successful from %s",
502 ireq
.u
.init
.type
, from
);
506 case choice_DigestReqInner_digestRequest
: {
507 sp
= krb5_storage_emem();
510 krb5_set_error_message(context
, ret
, "malloc: out of memory");
513 ret
= krb5_store_stringz(sp
, ireq
.u
.digestRequest
.type
);
515 krb5_clear_error_message(context
);
519 krb5_store_stringz(sp
, ireq
.u
.digestRequest
.serverNonce
);
521 if (ireq
.u
.digestRequest
.hostname
) {
522 ret
= krb5_store_stringz(sp
, *ireq
.u
.digestRequest
.hostname
);
524 krb5_clear_error_message(context
);
529 buf
.length
= strlen(ireq
.u
.digestRequest
.opaque
);
530 buf
.data
= malloc(buf
.length
);
531 if (buf
.data
== NULL
) {
533 krb5_set_error_message(context
, ret
, "malloc: out of memory");
537 ret
= hex_decode(ireq
.u
.digestRequest
.opaque
, buf
.data
, buf
.length
);
540 krb5_set_error_message(context
, ret
, "Failed to decode opaque");
545 ret
= decode_Checksum(buf
.data
, buf
.length
, &res
, NULL
);
547 krb5_data_zero(&buf
);
549 krb5_set_error_message(context
, ret
,
550 "Failed to decode digest Checksum");
554 ret
= krb5_storage_to_data(sp
, &buf
);
556 krb5_clear_error_message(context
);
560 serverNonce
.length
= strlen(ireq
.u
.digestRequest
.serverNonce
);
561 serverNonce
.data
= malloc(serverNonce
.length
);
562 if (serverNonce
.data
== NULL
) {
564 krb5_set_error_message(context
, ret
, "malloc: out of memory");
569 * CHAP does the checksum of the raw nonce, but do it for all
570 * types, since we need to check the timestamp.
575 ssize
= hex_decode(ireq
.u
.digestRequest
.serverNonce
,
576 serverNonce
.data
, serverNonce
.length
);
579 krb5_set_error_message(context
, ret
, "Failed to decode serverNonce");
582 serverNonce
.length
= ssize
;
585 ret
= get_digest_key(context
, config
, server
, &crypto
);
589 ret
= krb5_verify_checksum(context
, crypto
,
590 KRB5_KU_DIGEST_OPAQUE
,
591 buf
.data
, buf
.length
, &res
);
593 krb5_data_free(&buf
);
594 krb5_crypto_destroy(context
, crypto
);
601 unsigned char *p
= serverNonce
.data
;
604 if (serverNonce
.length
< 4) {
606 krb5_set_error_message(context
, ret
, "server nonce too short");
609 t
= p
[0] | (p
[1] << 8) | (p
[2] << 16) | (p
[3] << 24);
611 if (labs((kdc_time
& 0xffffffff) - t
) > context
->max_skew
) {
613 krb5_set_error_message(context
, ret
, "time screw in server nonce ");
618 if (strcasecmp(ireq
.u
.digestRequest
.type
, "CHAP") == 0) {
620 unsigned char md
[MD5_DIGEST_LENGTH
];
624 if ((config
->digests_allowed
& CHAP_MD5
) == 0) {
625 kdc_log(context
, config
, 0, "Digest CHAP MD5 not allowed");
629 if (ireq
.u
.digestRequest
.identifier
== NULL
) {
631 krb5_set_error_message(context
, ret
, "Identifier missing "
632 "from CHAP request");
636 if (hex_decode(*ireq
.u
.digestRequest
.identifier
, &idx
, 1) != 1) {
638 krb5_set_error_message(context
, ret
, "failed to decode identifier");
642 ret
= get_password_entry(context
, config
,
643 ireq
.u
.digestRequest
.username
,
648 ctx
= EVP_MD_CTX_create();
650 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
651 EVP_DigestUpdate(ctx
, &idx
, 1);
652 EVP_DigestUpdate(ctx
, password
, strlen(password
));
653 EVP_DigestUpdate(ctx
, serverNonce
.data
, serverNonce
.length
);
654 EVP_DigestFinal_ex(ctx
, md
, NULL
);
656 EVP_MD_CTX_destroy(ctx
);
658 hex_encode(md
, sizeof(md
), &mdx
);
660 krb5_clear_error_message(context
);
665 r
.element
= choice_DigestRepInner_response
;
667 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
670 r
.u
.response
.success
= TRUE
;
672 kdc_log(context
, config
, 0,
673 "CHAP reply mismatch for %s",
674 ireq
.u
.digestRequest
.username
);
675 r
.u
.response
.success
= FALSE
;
678 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "SASL-DIGEST-MD5") == 0) {
680 unsigned char md
[MD5_DIGEST_LENGTH
];
684 if ((config
->digests_allowed
& DIGEST_MD5
) == 0) {
685 kdc_log(context
, config
, 0, "Digest SASL MD5 not allowed");
689 if (ireq
.u
.digestRequest
.nonceCount
== NULL
)
691 if (ireq
.u
.digestRequest
.clientNonce
== NULL
)
693 if (ireq
.u
.digestRequest
.qop
== NULL
)
695 if (ireq
.u
.digestRequest
.realm
== NULL
)
698 ret
= get_password_entry(context
, config
,
699 ireq
.u
.digestRequest
.username
,
704 ctx
= EVP_MD_CTX_create();
706 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
707 EVP_DigestUpdate(ctx
, ireq
.u
.digestRequest
.username
,
708 strlen(ireq
.u
.digestRequest
.username
));
709 EVP_DigestUpdate(ctx
, ":", 1);
710 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.realm
,
711 strlen(*ireq
.u
.digestRequest
.realm
));
712 EVP_DigestUpdate(ctx
, ":", 1);
713 EVP_DigestUpdate(ctx
, password
, strlen(password
));
714 EVP_DigestFinal_ex(ctx
, md
, NULL
);
716 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
717 EVP_DigestUpdate(ctx
, md
, sizeof(md
));
718 EVP_DigestUpdate(ctx
, ":", 1);
719 EVP_DigestUpdate(ctx
, ireq
.u
.digestRequest
.serverNonce
,
720 strlen(ireq
.u
.digestRequest
.serverNonce
));
721 EVP_DigestUpdate(ctx
, ":", 1);
722 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.nonceCount
,
723 strlen(*ireq
.u
.digestRequest
.nonceCount
));
724 if (ireq
.u
.digestRequest
.authid
) {
725 EVP_DigestUpdate(ctx
, ":", 1);
726 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.authid
,
727 strlen(*ireq
.u
.digestRequest
.authid
));
729 EVP_DigestFinal_ex(ctx
, md
, NULL
);
730 hex_encode(md
, sizeof(md
), &A1
);
733 krb5_set_error_message(context
, ret
, "malloc: out of memory");
734 EVP_MD_CTX_destroy(ctx
);
738 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
739 EVP_DigestUpdate(ctx
,
740 "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
741 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.uri
,
742 strlen(*ireq
.u
.digestRequest
.uri
));
745 if (strcmp(ireq
.u
.digestRequest
.digest
, "clear") != 0) {
746 static char conf_zeros
[] = ":00000000000000000000000000000000";
747 EVP_DigestUpdate(ctx
, conf_zeros
, sizeof(conf_zeros
) - 1);
750 EVP_DigestFinal_ex(ctx
, md
, NULL
);
752 hex_encode(md
, sizeof(md
), &A2
);
755 krb5_set_error_message(context
, ret
, "malloc: out of memory");
760 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
761 EVP_DigestUpdate(ctx
, A1
, strlen(A2
));
762 EVP_DigestUpdate(ctx
, ":", 1);
763 EVP_DigestUpdate(ctx
, ireq
.u
.digestRequest
.serverNonce
,
764 strlen(ireq
.u
.digestRequest
.serverNonce
));
765 EVP_DigestUpdate(ctx
, ":", 1);
766 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.nonceCount
,
767 strlen(*ireq
.u
.digestRequest
.nonceCount
));
768 EVP_DigestUpdate(ctx
, ":", 1);
769 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.clientNonce
,
770 strlen(*ireq
.u
.digestRequest
.clientNonce
));
771 EVP_DigestUpdate(ctx
, ":", 1);
772 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.qop
,
773 strlen(*ireq
.u
.digestRequest
.qop
));
774 EVP_DigestUpdate(ctx
, ":", 1);
775 EVP_DigestUpdate(ctx
, A2
, strlen(A2
));
777 EVP_DigestFinal_ex(ctx
, md
, NULL
);
779 EVP_MD_CTX_destroy(ctx
);
784 hex_encode(md
, sizeof(md
), &mdx
);
786 krb5_clear_error_message(context
);
791 r
.element
= choice_DigestRepInner_response
;
792 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
795 r
.u
.response
.success
= TRUE
;
797 kdc_log(context
, config
, 0,
798 "DIGEST-MD5 reply mismatch for %s",
799 ireq
.u
.digestRequest
.username
);
800 r
.u
.response
.success
= FALSE
;
803 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "MS-CHAP-V2") == 0) {
804 unsigned char md
[SHA_DIGEST_LENGTH
], challenge
[SHA_DIGEST_LENGTH
];
805 krb5_principal clientprincipal
= NULL
;
807 const char *username
;
808 struct ntlm_buf answer
;
812 if ((config
->digests_allowed
& MS_CHAP_V2
) == 0) {
813 kdc_log(context
, config
, 0, "MS-CHAP-V2 not allowed");
817 if (ireq
.u
.digestRequest
.clientNonce
== NULL
) {
819 krb5_set_error_message(context
, ret
,
820 "MS-CHAP-V2 clientNonce missing");
823 if (serverNonce
.length
!= 16) {
825 krb5_set_error_message(context
, ret
,
826 "MS-CHAP-V2 serverNonce wrong length");
830 /* strip of the domain component */
831 username
= strchr(ireq
.u
.digestRequest
.username
, '\\');
832 if (username
== NULL
)
833 username
= ireq
.u
.digestRequest
.username
;
837 ctp
= EVP_MD_CTX_create();
840 EVP_DigestInit_ex(ctp
, EVP_sha1(), NULL
);
843 krb5_data clientNonce
;
845 clientNonce
.length
= strlen(*ireq
.u
.digestRequest
.clientNonce
);
846 clientNonce
.data
= malloc(clientNonce
.length
);
847 if (clientNonce
.data
== NULL
) {
849 krb5_set_error_message(context
, ret
,
850 "malloc: out of memory");
851 EVP_MD_CTX_destroy(ctp
);
855 ssize
= hex_decode(*ireq
.u
.digestRequest
.clientNonce
,
856 clientNonce
.data
, clientNonce
.length
);
859 krb5_set_error_message(context
, ret
,
860 "Failed to decode clientNonce");
861 EVP_MD_CTX_destroy(ctp
);
864 EVP_DigestUpdate(ctp
, clientNonce
.data
, ssize
);
865 free(clientNonce
.data
);
867 EVP_DigestUpdate(ctp
, serverNonce
.data
, serverNonce
.length
);
868 EVP_DigestUpdate(ctp
, username
, strlen(username
));
870 EVP_DigestFinal_ex(ctp
, challenge
, NULL
);
872 EVP_MD_CTX_destroy(ctp
);
875 ret
= krb5_parse_name(context
, username
, &clientprincipal
);
879 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
880 HDB_F_GET_CLIENT
, NULL
, NULL
, &user
);
881 krb5_free_principal(context
, clientprincipal
);
883 krb5_set_error_message(context
, ret
,
884 "MS-CHAP-V2 user %s not in database",
889 ret
= hdb_enctype2key(context
, &user
->entry
, NULL
,
890 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
892 krb5_set_error_message(context
, ret
,
893 "MS-CHAP-V2 missing arcfour key %s",
898 /* ChallengeResponse */
899 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
900 key
->key
.keyvalue
.length
,
903 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
907 hex_encode(answer
.data
, answer
.length
, &mdx
);
910 krb5_clear_error_message(context
);
915 r
.element
= choice_DigestRepInner_response
;
916 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
918 r
.u
.response
.success
= TRUE
;
920 kdc_log(context
, config
, 0,
921 "MS-CHAP-V2 hash mismatch for %s",
922 ireq
.u
.digestRequest
.username
);
923 r
.u
.response
.success
= FALSE
;
927 if (r
.u
.response
.success
) {
928 unsigned char hashhash
[MD4_DIGEST_LENGTH
];
931 ctxp
= EVP_MD_CTX_create();
935 EVP_DigestInit_ex(ctxp
, EVP_md4(), NULL
);
936 EVP_DigestUpdate(ctxp
,
937 key
->key
.keyvalue
.data
,
938 key
->key
.keyvalue
.length
);
939 EVP_DigestFinal_ex(ctxp
, hashhash
, NULL
);
942 /* GenerateAuthenticatorResponse */
943 EVP_DigestInit_ex(ctxp
, EVP_sha1(), NULL
);
944 EVP_DigestUpdate(ctxp
, hashhash
, sizeof(hashhash
));
945 EVP_DigestUpdate(ctxp
, answer
.data
, answer
.length
);
946 EVP_DigestUpdate(ctxp
, ms_chap_v2_magic1
,
947 sizeof(ms_chap_v2_magic1
));
948 EVP_DigestFinal_ex(ctxp
, md
, NULL
);
950 EVP_DigestInit_ex(ctxp
, EVP_sha1(), NULL
);
951 EVP_DigestUpdate(ctxp
, md
, sizeof(md
));
952 EVP_DigestUpdate(ctxp
, challenge
, 8);
953 EVP_DigestUpdate(ctxp
, ms_chap_v2_magic2
,
954 sizeof(ms_chap_v2_magic2
));
955 EVP_DigestFinal_ex(ctxp
, md
, NULL
);
957 r
.u
.response
.rsp
= calloc(1, sizeof(*r
.u
.response
.rsp
));
958 if (r
.u
.response
.rsp
== NULL
) {
960 krb5_clear_error_message(context
);
961 EVP_MD_CTX_destroy(ctxp
);
966 hex_encode(md
, sizeof(md
), r
.u
.response
.rsp
);
967 if (r
.u
.response
.rsp
== NULL
) {
969 krb5_clear_error_message(context
);
970 EVP_MD_CTX_destroy(ctxp
);
975 /* get_master, rfc 3079 3.4 */
976 EVP_DigestInit_ex(ctxp
, EVP_sha1(), NULL
);
977 EVP_DigestUpdate(ctxp
, hashhash
, 16);
978 EVP_DigestUpdate(ctxp
, answer
.data
, answer
.length
);
979 EVP_DigestUpdate(ctxp
, ms_rfc3079_magic1
,
980 sizeof(ms_rfc3079_magic1
));
981 EVP_DigestFinal_ex(ctxp
, md
, NULL
);
985 EVP_MD_CTX_destroy(ctxp
);
987 r
.u
.response
.session_key
=
988 calloc(1, sizeof(*r
.u
.response
.session_key
));
989 if (r
.u
.response
.session_key
== NULL
) {
990 krb5_clear_error_message(context
);
995 ret
= krb5_data_copy(r
.u
.response
.session_key
, md
, 16);
997 krb5_clear_error_message(context
);
1005 r
.element
= choice_DigestRepInner_error
;
1006 aret
= asprintf(&r
.u
.error
.reason
, "Unsupported digest type %s",
1007 ireq
.u
.digestRequest
.type
);
1008 if (aret
== -1 || r
.u
.error
.reason
== NULL
) {
1010 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1013 r
.u
.error
.code
= EINVAL
;
1016 kdc_log(context
, config
, 0, "Digest %s request successful %s",
1017 ireq
.u
.digestRequest
.type
, ireq
.u
.digestRequest
.username
);
1021 case choice_DigestReqInner_ntlmInit
:
1023 if ((config
->digests_allowed
& (NTLM_V1
|NTLM_V1_SESSION
|NTLM_V2
)) == 0) {
1024 kdc_log(context
, config
, 0, "NTLM not allowed");
1028 r
.element
= choice_DigestRepInner_ntlmInitReply
;
1030 r
.u
.ntlmInitReply
.flags
= NTLM_NEG_UNICODE
;
1032 if ((ireq
.u
.ntlmInit
.flags
& NTLM_NEG_UNICODE
) == 0) {
1033 kdc_log(context
, config
, 0, "NTLM client have no unicode");
1037 if (ireq
.u
.ntlmInit
.flags
& NTLM_NEG_NTLM
)
1038 r
.u
.ntlmInitReply
.flags
|= NTLM_NEG_NTLM
;
1040 kdc_log(context
, config
, 0, "NTLM client doesn't support NTLM");
1044 r
.u
.ntlmInitReply
.flags
|=
1046 NTLM_TARGET_DOMAIN
|
1052 NTLM_NEG_ALWAYS_SIGN| \
1053 NTLM_NEG_NTLM2_SESSION| \
1056 r
.u
.ntlmInitReply
.flags
|= (ireq
.u
.ntlmInit
.flags
& (ALL
));
1060 r
.u
.ntlmInitReply
.targetname
=
1061 get_ntlm_targetname(context
, client
);
1062 if (r
.u
.ntlmInitReply
.targetname
== NULL
) {
1064 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1067 r
.u
.ntlmInitReply
.challenge
.data
= malloc(8);
1068 if (r
.u
.ntlmInitReply
.challenge
.data
== NULL
) {
1070 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1073 r
.u
.ntlmInitReply
.challenge
.length
= 8;
1074 if (RAND_bytes(r
.u
.ntlmInitReply
.challenge
.data
,
1075 r
.u
.ntlmInitReply
.challenge
.length
) != 1)
1078 krb5_set_error_message(context
, ret
, "out of random error");
1081 /* XXX fix targetinfo */
1082 ALLOC(r
.u
.ntlmInitReply
.targetinfo
);
1083 if (r
.u
.ntlmInitReply
.targetinfo
== NULL
) {
1085 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1089 ret
= fill_targetinfo(context
,
1090 r
.u
.ntlmInitReply
.targetname
,
1092 r
.u
.ntlmInitReply
.targetinfo
);
1095 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1100 * Save data encryted in opaque for the second part of the
1101 * ntlm authentication
1103 sp
= krb5_storage_emem();
1106 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1110 ret
= krb5_storage_write(sp
, r
.u
.ntlmInitReply
.challenge
.data
, 8);
1113 krb5_set_error_message(context
, ret
, "storage write challenge");
1116 ret
= krb5_store_uint32(sp
, r
.u
.ntlmInitReply
.flags
);
1118 krb5_clear_error_message(context
);
1122 ret
= krb5_storage_to_data(sp
, &buf
);
1124 krb5_clear_error_message(context
);
1128 ret
= get_digest_key(context
, config
, server
, &crypto
);
1132 ret
= krb5_encrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1133 buf
.data
, buf
.length
, &r
.u
.ntlmInitReply
.opaque
);
1134 krb5_data_free(&buf
);
1135 krb5_crypto_destroy(context
, crypto
);
1140 kdc_log(context
, config
, 0, "NTLM init from %s", from
);
1144 case choice_DigestReqInner_ntlmRequest
: {
1145 krb5_principal clientprincipal
;
1146 unsigned char sessionkey
[16];
1147 unsigned char challenge
[8];
1152 r
.element
= choice_DigestRepInner_ntlmResponse
;
1153 r
.u
.ntlmResponse
.success
= 0;
1154 r
.u
.ntlmResponse
.flags
= 0;
1155 r
.u
.ntlmResponse
.sessionkey
= NULL
;
1156 r
.u
.ntlmResponse
.tickets
= NULL
;
1159 ret
= krb5_parse_name(context
,
1160 ireq
.u
.ntlmRequest
.username
,
1165 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
1166 HDB_F_GET_CLIENT
, NULL
, NULL
, &user
);
1167 krb5_free_principal(context
, clientprincipal
);
1169 krb5_set_error_message(context
, ret
, "NTLM user %s not in database",
1170 ireq
.u
.ntlmRequest
.username
);
1174 ret
= get_digest_key(context
, config
, server
, &crypto
);
1178 ret
= krb5_decrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1179 ireq
.u
.ntlmRequest
.opaque
.data
,
1180 ireq
.u
.ntlmRequest
.opaque
.length
, &buf
);
1181 krb5_crypto_destroy(context
, crypto
);
1184 kdc_log(context
, config
, 0,
1185 "Failed to decrypt nonce from %s", from
);
1189 sp
= krb5_storage_from_data(&buf
);
1192 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1196 ret
= krb5_storage_read(sp
, challenge
, sizeof(challenge
));
1197 if (ret
!= sizeof(challenge
)) {
1199 krb5_set_error_message(context
, ret
, "NTLM storage read challenge");
1202 ret
= krb5_ret_uint32(sp
, &flags
);
1204 krb5_set_error_message(context
, ret
, "NTLM storage read flags");
1207 krb5_storage_free(sp
);
1209 krb5_data_free(&buf
);
1211 if ((flags
& NTLM_NEG_NTLM
) == 0) {
1213 krb5_set_error_message(context
, ret
, "NTLM not negotiated");
1217 ret
= hdb_enctype2key(context
, &user
->entry
, NULL
,
1218 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
1220 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
1224 /* check if this is NTLMv2 */
1225 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= 24) {
1226 struct ntlm_buf infotarget
, answer
;
1229 if ((config
->digests_allowed
& NTLM_V2
) == 0) {
1230 kdc_log(context
, config
, 0, "NTLM v2 not allowed");
1236 targetname
= get_ntlm_targetname(context
, client
);
1237 if (targetname
== NULL
) {
1239 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1243 answer
.length
= ireq
.u
.ntlmRequest
.ntlm
.length
;
1244 answer
.data
= ireq
.u
.ntlmRequest
.ntlm
.data
;
1246 ret
= heim_ntlm_verify_ntlm2(key
->key
.keyvalue
.data
,
1247 key
->key
.keyvalue
.length
,
1248 ireq
.u
.ntlmRequest
.username
,
1257 krb5_set_error_message(context
, ret
, "NTLM v2 verify failed");
1261 /* XXX verify infotarget matches client (checksum ?) */
1263 free(infotarget
.data
);
1267 struct ntlm_buf answer
;
1271 if (flags
& NTLM_NEG_NTLM2_SESSION
) {
1272 unsigned char sessionhash
[MD5_DIGEST_LENGTH
];
1275 if ((config
->digests_allowed
& NTLM_V1_SESSION
) == 0) {
1276 kdc_log(context
, config
, 0, "NTLM v1-session not allowed");
1281 if (ireq
.u
.ntlmRequest
.lm
.length
!= 24) {
1283 krb5_set_error_message(context
, ret
, "LM hash have wrong length "
1284 "for NTLM session key");
1288 ctx
= EVP_MD_CTX_create();
1290 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
1292 EVP_DigestUpdate(ctx
, challenge
, sizeof(challenge
));
1293 EVP_DigestUpdate(ctx
, ireq
.u
.ntlmRequest
.lm
.data
, 8);
1294 EVP_DigestFinal_ex(ctx
, sessionhash
, NULL
);
1295 memcpy(challenge
, sessionhash
, sizeof(challenge
));
1297 EVP_MD_CTX_destroy(ctx
);
1300 if ((config
->digests_allowed
& NTLM_V1
) == 0) {
1301 kdc_log(context
, config
, 0, "NTLM v1 not allowed");
1306 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
1307 key
->key
.keyvalue
.length
,
1308 challenge
, &answer
);
1310 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
1314 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= answer
.length
||
1315 memcmp(ireq
.u
.ntlmRequest
.ntlm
.data
, answer
.data
, answer
.length
) != 0)
1319 krb5_set_error_message(context
, ret
, "NTLM hash mismatch");
1327 ctx
= EVP_MD_CTX_create();
1329 EVP_DigestInit_ex(ctx
, EVP_md4(), NULL
);
1330 EVP_DigestUpdate(ctx
,
1331 key
->key
.keyvalue
.data
,
1332 key
->key
.keyvalue
.length
);
1333 EVP_DigestFinal_ex(ctx
, sessionkey
, NULL
);
1335 EVP_MD_CTX_destroy(ctx
);
1339 if (ireq
.u
.ntlmRequest
.sessionkey
) {
1340 unsigned char masterkey
[MD4_DIGEST_LENGTH
];
1344 if ((flags
& NTLM_NEG_KEYEX
) == 0) {
1346 krb5_set_error_message(context
, ret
,
1347 "NTLM client failed to neg key "
1348 "exchange but still sent key");
1352 len
= ireq
.u
.ntlmRequest
.sessionkey
->length
;
1353 if (len
!= sizeof(masterkey
)){
1355 krb5_set_error_message(context
, ret
,
1356 "NTLM master key wrong length: %lu",
1357 (unsigned long)len
);
1362 EVP_CIPHER_CTX_init(&rc4
);
1363 EVP_CipherInit_ex(&rc4
, EVP_rc4(), NULL
, sessionkey
, NULL
, 1);
1365 masterkey
, ireq
.u
.ntlmRequest
.sessionkey
->data
,
1367 EVP_CIPHER_CTX_cleanup(&rc4
);
1369 r
.u
.ntlmResponse
.sessionkey
=
1370 malloc(sizeof(*r
.u
.ntlmResponse
.sessionkey
));
1371 if (r
.u
.ntlmResponse
.sessionkey
== NULL
) {
1373 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1377 ret
= krb5_data_copy(r
.u
.ntlmResponse
.sessionkey
,
1378 masterkey
, sizeof(masterkey
));
1380 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1385 r
.u
.ntlmResponse
.success
= 1;
1386 kdc_log(context
, config
, 0, "NTLM version %d successful for %s",
1387 version
, ireq
.u
.ntlmRequest
.username
);
1390 case choice_DigestReqInner_supportedMechs
:
1392 kdc_log(context
, config
, 0, "digest supportedMechs from %s", from
);
1394 r
.element
= choice_DigestRepInner_supportedMechs
;
1395 memset(&r
.u
.supportedMechs
, 0, sizeof(r
.u
.supportedMechs
));
1397 if (config
->digests_allowed
& NTLM_V1
)
1398 r
.u
.supportedMechs
.ntlm_v1
= 1;
1399 if (config
->digests_allowed
& NTLM_V1_SESSION
)
1400 r
.u
.supportedMechs
.ntlm_v1_session
= 1;
1401 if (config
->digests_allowed
& NTLM_V2
)
1402 r
.u
.supportedMechs
.ntlm_v2
= 1;
1403 if (config
->digests_allowed
& DIGEST_MD5
)
1404 r
.u
.supportedMechs
.digest_md5
= 1;
1405 if (config
->digests_allowed
& CHAP_MD5
)
1406 r
.u
.supportedMechs
.chap_md5
= 1;
1407 if (config
->digests_allowed
& MS_CHAP_V2
)
1408 r
.u
.supportedMechs
.ms_chap_v2
= 1;
1414 krb5_set_error_message(context
, ret
, "unknown operation to digest");
1418 s
= krb5_get_error_message(context
, ret
);
1420 krb5_clear_error_message(context
);
1424 kdc_log(context
, config
, 0, "Digest failed with: %s", s
);
1426 r
.element
= choice_DigestRepInner_error
;
1427 r
.u
.error
.reason
= strdup("unknown error");
1428 krb5_free_error_message(context
, s
);
1429 if (r
.u
.error
.reason
== NULL
) {
1431 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1434 r
.u
.error
.code
= EINVAL
;
1439 ASN1_MALLOC_ENCODE(DigestRepInner
, buf
.data
, buf
.length
, &r
, &size
, ret
);
1441 krb5_set_error_message(context
, ret
, "Failed to encode inner digest reply");
1444 if (size
!= buf
.length
)
1445 krb5_abortx(context
, "ASN1 internal error");
1447 krb5_auth_con_addflags(context
, ac
, KRB5_AUTH_CONTEXT_USE_SUBKEY
, NULL
);
1449 ret
= krb5_mk_rep (context
, ac
, &rep
.apRep
);
1456 ret
= krb5_auth_con_getlocalsubkey(context
, ac
, &key
);
1460 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
1461 krb5_free_keyblock (context
, key
);
1466 ret
= krb5_encrypt_EncryptedData(context
, crypto
, KRB5_KU_DIGEST_ENCRYPT
,
1467 buf
.data
, buf
.length
, 0,
1470 ASN1_MALLOC_ENCODE(DigestREP
, reply
->data
, reply
->length
, &rep
, &size
, ret
);
1472 krb5_set_error_message(context
, ret
, "Failed to encode digest reply");
1475 if (size
!= reply
->length
)
1476 krb5_abortx(context
, "ASN1 internal error");
1481 krb5_auth_con_free(context
, ac
);
1483 krb5_warn(context
, ret
, "Digest request from %s failed", from
);
1485 krb5_free_ticket(context
, ticket
);
1487 krb5_kt_close(context
, id
);
1489 krb5_crypto_destroy(context
, crypto
);
1491 krb5_storage_free(sp
);
1493 _kdc_free_ent (context
, user
);
1495 _kdc_free_ent (context
, server
);
1497 _kdc_free_ent (context
, client
);
1499 memset(password
, 0, strlen(password
));
1504 krb5_data_free(&buf
);
1505 krb5_data_free(&serverNonce
);
1506 free_Checksum(&res
);
1507 free_DigestREP(&rep
);
1508 free_DigestRepInner(&r
);
1509 free_DigestReqInner(&ireq
);