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
,
645 ctx
= EVP_MD_CTX_create();
647 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
648 EVP_DigestUpdate(ctx
, &id
, 1);
649 EVP_DigestUpdate(ctx
, password
, strlen(password
));
650 EVP_DigestUpdate(ctx
, serverNonce
.data
, serverNonce
.length
);
651 EVP_DigestFinal_ex(ctx
, md
, NULL
);
653 EVP_MD_CTX_destroy(ctx
);
655 hex_encode(md
, sizeof(md
), &mdx
);
657 krb5_clear_error_message(context
);
662 r
.element
= choice_DigestRepInner_response
;
664 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
667 r
.u
.response
.success
= TRUE
;
669 kdc_log(context
, config
, 0,
670 "CHAP reply mismatch for %s",
671 ireq
.u
.digestRequest
.username
);
672 r
.u
.response
.success
= FALSE
;
675 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "SASL-DIGEST-MD5") == 0) {
677 unsigned char md
[MD5_DIGEST_LENGTH
];
681 if ((config
->digests_allowed
& DIGEST_MD5
) == 0) {
682 kdc_log(context
, config
, 0, "Digest SASL MD5 not allowed");
686 if (ireq
.u
.digestRequest
.nonceCount
== NULL
)
688 if (ireq
.u
.digestRequest
.clientNonce
== NULL
)
690 if (ireq
.u
.digestRequest
.qop
== NULL
)
692 if (ireq
.u
.digestRequest
.realm
== NULL
)
695 ret
= get_password_entry(context
, config
,
696 ireq
.u
.digestRequest
.username
,
701 ctx
= EVP_MD_CTX_create();
703 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
704 EVP_DigestUpdate(ctx
, ireq
.u
.digestRequest
.username
,
705 strlen(ireq
.u
.digestRequest
.username
));
706 EVP_DigestUpdate(ctx
, ":", 1);
707 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.realm
,
708 strlen(*ireq
.u
.digestRequest
.realm
));
709 EVP_DigestUpdate(ctx
, ":", 1);
710 EVP_DigestUpdate(ctx
, password
, strlen(password
));
711 EVP_DigestFinal_ex(ctx
, md
, NULL
);
713 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
714 EVP_DigestUpdate(ctx
, md
, sizeof(md
));
715 EVP_DigestUpdate(ctx
, ":", 1);
716 EVP_DigestUpdate(ctx
, ireq
.u
.digestRequest
.serverNonce
,
717 strlen(ireq
.u
.digestRequest
.serverNonce
));
718 EVP_DigestUpdate(ctx
, ":", 1);
719 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.nonceCount
,
720 strlen(*ireq
.u
.digestRequest
.nonceCount
));
721 if (ireq
.u
.digestRequest
.authid
) {
722 EVP_DigestUpdate(ctx
, ":", 1);
723 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.authid
,
724 strlen(*ireq
.u
.digestRequest
.authid
));
726 EVP_DigestFinal_ex(ctx
, md
, NULL
);
727 hex_encode(md
, sizeof(md
), &A1
);
730 krb5_set_error_message(context
, ret
, "malloc: out of memory");
731 EVP_MD_CTX_destroy(ctx
);
735 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
736 EVP_DigestUpdate(ctx
,
737 "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
738 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.uri
,
739 strlen(*ireq
.u
.digestRequest
.uri
));
742 if (strcmp(ireq
.u
.digestRequest
.digest
, "clear") != 0) {
743 static char conf_zeros
[] = ":00000000000000000000000000000000";
744 EVP_DigestUpdate(ctx
, conf_zeros
, sizeof(conf_zeros
) - 1);
747 EVP_DigestFinal_ex(ctx
, md
, NULL
);
749 hex_encode(md
, sizeof(md
), &A2
);
752 krb5_set_error_message(context
, ret
, "malloc: out of memory");
757 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
758 EVP_DigestUpdate(ctx
, A1
, strlen(A2
));
759 EVP_DigestUpdate(ctx
, ":", 1);
760 EVP_DigestUpdate(ctx
, ireq
.u
.digestRequest
.serverNonce
,
761 strlen(ireq
.u
.digestRequest
.serverNonce
));
762 EVP_DigestUpdate(ctx
, ":", 1);
763 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.nonceCount
,
764 strlen(*ireq
.u
.digestRequest
.nonceCount
));
765 EVP_DigestUpdate(ctx
, ":", 1);
766 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.clientNonce
,
767 strlen(*ireq
.u
.digestRequest
.clientNonce
));
768 EVP_DigestUpdate(ctx
, ":", 1);
769 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.qop
,
770 strlen(*ireq
.u
.digestRequest
.qop
));
771 EVP_DigestUpdate(ctx
, ":", 1);
772 EVP_DigestUpdate(ctx
, A2
, strlen(A2
));
774 EVP_DigestFinal_ex(ctx
, md
, NULL
);
776 EVP_MD_CTX_destroy(ctx
);
781 hex_encode(md
, sizeof(md
), &mdx
);
783 krb5_clear_error_message(context
);
788 r
.element
= choice_DigestRepInner_response
;
789 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
792 r
.u
.response
.success
= TRUE
;
794 kdc_log(context
, config
, 0,
795 "DIGEST-MD5 reply mismatch for %s",
796 ireq
.u
.digestRequest
.username
);
797 r
.u
.response
.success
= FALSE
;
800 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "MS-CHAP-V2") == 0) {
801 unsigned char md
[SHA_DIGEST_LENGTH
], challange
[SHA_DIGEST_LENGTH
];
802 krb5_principal clientprincipal
= NULL
;
804 const char *username
;
805 struct ntlm_buf answer
;
809 if ((config
->digests_allowed
& MS_CHAP_V2
) == 0) {
810 kdc_log(context
, config
, 0, "MS-CHAP-V2 not allowed");
814 if (ireq
.u
.digestRequest
.clientNonce
== NULL
) {
816 krb5_set_error_message(context
, ret
,
817 "MS-CHAP-V2 clientNonce missing");
820 if (serverNonce
.length
!= 16) {
822 krb5_set_error_message(context
, ret
,
823 "MS-CHAP-V2 serverNonce wrong length");
827 /* strip of the domain component */
828 username
= strchr(ireq
.u
.digestRequest
.username
, '\\');
829 if (username
== NULL
)
830 username
= ireq
.u
.digestRequest
.username
;
834 ctx
= EVP_MD_CTX_create();
837 EVP_DigestInit_ex(ctx
, EVP_sha1(), NULL
);
840 krb5_data clientNonce
;
842 clientNonce
.length
= strlen(*ireq
.u
.digestRequest
.clientNonce
);
843 clientNonce
.data
= malloc(clientNonce
.length
);
844 if (clientNonce
.data
== NULL
) {
846 krb5_set_error_message(context
, ret
,
847 "malloc: out of memory");
848 EVP_MD_CTX_destroy(ctx
);
852 ssize
= hex_decode(*ireq
.u
.digestRequest
.clientNonce
,
853 clientNonce
.data
, clientNonce
.length
);
856 krb5_set_error_message(context
, ret
,
857 "Failed to decode clientNonce");
858 EVP_MD_CTX_destroy(ctx
);
861 EVP_DigestUpdate(ctx
, clientNonce
.data
, ssize
);
862 free(clientNonce
.data
);
864 EVP_DigestUpdate(ctx
, serverNonce
.data
, serverNonce
.length
);
865 EVP_DigestUpdate(ctx
, username
, strlen(username
));
867 EVP_DigestFinal_ex(ctx
, challange
, NULL
);
869 EVP_MD_CTX_destroy(ctx
);
872 ret
= krb5_parse_name(context
, username
, &clientprincipal
);
876 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
877 HDB_F_GET_CLIENT
, NULL
, &user
);
878 krb5_free_principal(context
, clientprincipal
);
880 krb5_set_error_message(context
, ret
,
881 "MS-CHAP-V2 user %s not in database",
886 ret
= hdb_enctype2key(context
, &user
->entry
,
887 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
889 krb5_set_error_message(context
, ret
,
890 "MS-CHAP-V2 missing arcfour key %s",
895 /* ChallengeResponse */
896 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
897 key
->key
.keyvalue
.length
,
900 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
904 hex_encode(answer
.data
, answer
.length
, &mdx
);
907 krb5_clear_error_message(context
);
912 r
.element
= choice_DigestRepInner_response
;
913 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
915 r
.u
.response
.success
= TRUE
;
917 kdc_log(context
, config
, 0,
918 "MS-CHAP-V2 hash mismatch for %s",
919 ireq
.u
.digestRequest
.username
);
920 r
.u
.response
.success
= FALSE
;
924 if (r
.u
.response
.success
) {
925 unsigned char hashhash
[MD4_DIGEST_LENGTH
];
928 ctx
= EVP_MD_CTX_create();
932 EVP_DigestInit_ex(ctx
, EVP_md4(), NULL
);
933 EVP_DigestUpdate(ctx
,
934 key
->key
.keyvalue
.data
,
935 key
->key
.keyvalue
.length
);
936 EVP_DigestFinal_ex(ctx
, hashhash
, NULL
);
939 /* GenerateAuthenticatorResponse */
940 EVP_DigestInit_ex(ctx
, EVP_sha1(), NULL
);
941 EVP_DigestUpdate(ctx
, hashhash
, sizeof(hashhash
));
942 EVP_DigestUpdate(ctx
, answer
.data
, answer
.length
);
943 EVP_DigestUpdate(ctx
, ms_chap_v2_magic1
,
944 sizeof(ms_chap_v2_magic1
));
945 EVP_DigestFinal_ex(ctx
, md
, NULL
);
947 EVP_DigestInit_ex(ctx
, EVP_sha1(), NULL
);
948 EVP_DigestUpdate(ctx
, md
, sizeof(md
));
949 EVP_DigestUpdate(ctx
, challange
, 8);
950 EVP_DigestUpdate(ctx
, ms_chap_v2_magic2
,
951 sizeof(ms_chap_v2_magic2
));
952 EVP_DigestFinal_ex(ctx
, md
, NULL
);
954 r
.u
.response
.rsp
= calloc(1, sizeof(*r
.u
.response
.rsp
));
955 if (r
.u
.response
.rsp
== NULL
) {
957 krb5_clear_error_message(context
);
958 EVP_MD_CTX_destroy(ctx
);
963 hex_encode(md
, sizeof(md
), r
.u
.response
.rsp
);
964 if (r
.u
.response
.rsp
== NULL
) {
966 krb5_clear_error_message(context
);
967 EVP_MD_CTX_destroy(ctx
);
972 /* get_master, rfc 3079 3.4 */
973 EVP_DigestInit_ex(ctx
, EVP_sha1(), NULL
);
974 EVP_DigestUpdate(ctx
, hashhash
, 16);
975 EVP_DigestUpdate(ctx
, answer
.data
, answer
.length
);
976 EVP_DigestUpdate(ctx
, ms_rfc3079_magic1
,
977 sizeof(ms_rfc3079_magic1
));
978 EVP_DigestFinal_ex(ctx
, md
, NULL
);
982 EVP_MD_CTX_destroy(ctx
);
984 r
.u
.response
.session_key
=
985 calloc(1, sizeof(*r
.u
.response
.session_key
));
986 if (r
.u
.response
.session_key
== NULL
) {
987 krb5_clear_error_message(context
);
992 ret
= krb5_data_copy(r
.u
.response
.session_key
, md
, 16);
994 krb5_clear_error_message(context
);
1000 r
.element
= choice_DigestRepInner_error
;
1001 asprintf(&r
.u
.error
.reason
, "Unsupported digest type %s",
1002 ireq
.u
.digestRequest
.type
);
1003 if (r
.u
.error
.reason
== NULL
) {
1005 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1008 r
.u
.error
.code
= EINVAL
;
1011 kdc_log(context
, config
, 0, "Digest %s request successful %s",
1012 ireq
.u
.digestRequest
.type
, ireq
.u
.digestRequest
.username
);
1016 case choice_DigestReqInner_ntlmInit
:
1018 if ((config
->digests_allowed
& (NTLM_V1
|NTLM_V1_SESSION
|NTLM_V2
)) == 0) {
1019 kdc_log(context
, config
, 0, "NTLM not allowed");
1023 r
.element
= choice_DigestRepInner_ntlmInitReply
;
1025 r
.u
.ntlmInitReply
.flags
= NTLM_NEG_UNICODE
;
1027 if ((ireq
.u
.ntlmInit
.flags
& NTLM_NEG_UNICODE
) == 0) {
1028 kdc_log(context
, config
, 0, "NTLM client have no unicode");
1032 if (ireq
.u
.ntlmInit
.flags
& NTLM_NEG_NTLM
)
1033 r
.u
.ntlmInitReply
.flags
|= NTLM_NEG_NTLM
;
1035 kdc_log(context
, config
, 0, "NTLM client doesn't support NTLM");
1039 r
.u
.ntlmInitReply
.flags
|=
1041 NTLM_TARGET_DOMAIN
|
1047 NTLM_NEG_ALWAYS_SIGN| \
1048 NTLM_NEG_NTLM2_SESSION| \
1051 r
.u
.ntlmInitReply
.flags
|= (ireq
.u
.ntlmInit
.flags
& (ALL
));
1055 r
.u
.ntlmInitReply
.targetname
=
1056 get_ntlm_targetname(context
, client
);
1057 if (r
.u
.ntlmInitReply
.targetname
== NULL
) {
1059 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1062 r
.u
.ntlmInitReply
.challange
.data
= malloc(8);
1063 if (r
.u
.ntlmInitReply
.challange
.data
== NULL
) {
1065 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1068 r
.u
.ntlmInitReply
.challange
.length
= 8;
1069 if (RAND_bytes(r
.u
.ntlmInitReply
.challange
.data
,
1070 r
.u
.ntlmInitReply
.challange
.length
) != 1)
1073 krb5_set_error_message(context
, ret
, "out of random error");
1076 /* XXX fix targetinfo */
1077 ALLOC(r
.u
.ntlmInitReply
.targetinfo
);
1078 if (r
.u
.ntlmInitReply
.targetinfo
== NULL
) {
1080 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1084 ret
= fill_targetinfo(context
,
1085 r
.u
.ntlmInitReply
.targetname
,
1087 r
.u
.ntlmInitReply
.targetinfo
);
1090 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1095 * Save data encryted in opaque for the second part of the
1096 * ntlm authentication
1098 sp
= krb5_storage_emem();
1101 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1105 ret
= krb5_storage_write(sp
, r
.u
.ntlmInitReply
.challange
.data
, 8);
1108 krb5_set_error_message(context
, ret
, "storage write challange");
1111 ret
= krb5_store_uint32(sp
, r
.u
.ntlmInitReply
.flags
);
1113 krb5_clear_error_message(context
);
1117 ret
= krb5_storage_to_data(sp
, &buf
);
1119 krb5_clear_error_message(context
);
1123 ret
= get_digest_key(context
, config
, server
, &crypto
);
1127 ret
= krb5_encrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1128 buf
.data
, buf
.length
, &r
.u
.ntlmInitReply
.opaque
);
1129 krb5_data_free(&buf
);
1130 krb5_crypto_destroy(context
, crypto
);
1135 kdc_log(context
, config
, 0, "NTLM init from %s", from
);
1139 case choice_DigestReqInner_ntlmRequest
: {
1140 krb5_principal clientprincipal
;
1141 unsigned char sessionkey
[16];
1142 unsigned char challange
[8];
1147 r
.element
= choice_DigestRepInner_ntlmResponse
;
1148 r
.u
.ntlmResponse
.success
= 0;
1149 r
.u
.ntlmResponse
.flags
= 0;
1150 r
.u
.ntlmResponse
.sessionkey
= NULL
;
1151 r
.u
.ntlmResponse
.tickets
= NULL
;
1154 ret
= krb5_parse_name(context
,
1155 ireq
.u
.ntlmRequest
.username
,
1160 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
1161 HDB_F_GET_CLIENT
, NULL
, &user
);
1162 krb5_free_principal(context
, clientprincipal
);
1164 krb5_set_error_message(context
, ret
, "NTLM user %s not in database",
1165 ireq
.u
.ntlmRequest
.username
);
1169 ret
= get_digest_key(context
, config
, server
, &crypto
);
1173 ret
= krb5_decrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1174 ireq
.u
.ntlmRequest
.opaque
.data
,
1175 ireq
.u
.ntlmRequest
.opaque
.length
, &buf
);
1176 krb5_crypto_destroy(context
, crypto
);
1179 kdc_log(context
, config
, 0,
1180 "Failed to decrypt nonce from %s", from
);
1184 sp
= krb5_storage_from_data(&buf
);
1187 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1191 ret
= krb5_storage_read(sp
, challange
, sizeof(challange
));
1192 if (ret
!= sizeof(challange
)) {
1194 krb5_set_error_message(context
, ret
, "NTLM storage read challange");
1197 ret
= krb5_ret_uint32(sp
, &flags
);
1199 krb5_set_error_message(context
, ret
, "NTLM storage read flags");
1202 krb5_storage_free(sp
);
1204 krb5_data_free(&buf
);
1206 if ((flags
& NTLM_NEG_NTLM
) == 0) {
1208 krb5_set_error_message(context
, ret
, "NTLM not negotiated");
1212 ret
= hdb_enctype2key(context
, &user
->entry
,
1213 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
1215 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
1219 /* check if this is NTLMv2 */
1220 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= 24) {
1221 struct ntlm_buf infotarget
, answer
;
1224 if ((config
->digests_allowed
& NTLM_V2
) == 0) {
1225 kdc_log(context
, config
, 0, "NTLM v2 not allowed");
1231 targetname
= get_ntlm_targetname(context
, client
);
1232 if (targetname
== NULL
) {
1234 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1238 answer
.length
= ireq
.u
.ntlmRequest
.ntlm
.length
;
1239 answer
.data
= ireq
.u
.ntlmRequest
.ntlm
.data
;
1241 ret
= heim_ntlm_verify_ntlm2(key
->key
.keyvalue
.data
,
1242 key
->key
.keyvalue
.length
,
1243 ireq
.u
.ntlmRequest
.username
,
1252 krb5_set_error_message(context
, ret
, "NTLM v2 verify failed");
1256 /* XXX verify infotarget matches client (checksum ?) */
1258 free(infotarget
.data
);
1262 struct ntlm_buf answer
;
1266 if (flags
& NTLM_NEG_NTLM2_SESSION
) {
1267 unsigned char sessionhash
[MD5_DIGEST_LENGTH
];
1270 if ((config
->digests_allowed
& NTLM_V1_SESSION
) == 0) {
1271 kdc_log(context
, config
, 0, "NTLM v1-session not allowed");
1276 if (ireq
.u
.ntlmRequest
.lm
.length
!= 24) {
1278 krb5_set_error_message(context
, ret
, "LM hash have wrong length "
1279 "for NTLM session key");
1283 ctx
= EVP_MD_CTX_create();
1285 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
1287 EVP_DigestUpdate(ctx
, challange
, sizeof(challange
));
1288 EVP_DigestUpdate(ctx
, ireq
.u
.ntlmRequest
.lm
.data
, 8);
1289 EVP_DigestFinal_ex(ctx
, sessionhash
, NULL
);
1290 memcpy(challange
, sessionhash
, sizeof(challange
));
1292 EVP_MD_CTX_destroy(ctx
);
1295 if ((config
->digests_allowed
& NTLM_V1
) == 0) {
1296 kdc_log(context
, config
, 0, "NTLM v1 not allowed");
1301 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
1302 key
->key
.keyvalue
.length
,
1303 challange
, &answer
);
1305 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
1309 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= answer
.length
||
1310 memcmp(ireq
.u
.ntlmRequest
.ntlm
.data
, answer
.data
, answer
.length
) != 0)
1314 krb5_set_error_message(context
, ret
, "NTLM hash mismatch");
1322 ctx
= EVP_MD_CTX_create();
1324 EVP_DigestInit_ex(ctx
, EVP_md4(), NULL
);
1325 EVP_DigestUpdate(ctx
,
1326 key
->key
.keyvalue
.data
,
1327 key
->key
.keyvalue
.length
);
1328 EVP_DigestFinal_ex(ctx
, sessionkey
, NULL
);
1330 EVP_MD_CTX_destroy(ctx
);
1334 if (ireq
.u
.ntlmRequest
.sessionkey
) {
1335 unsigned char masterkey
[MD4_DIGEST_LENGTH
];
1339 if ((flags
& NTLM_NEG_KEYEX
) == 0) {
1341 krb5_set_error_message(context
, ret
,
1342 "NTLM client failed to neg key "
1343 "exchange but still sent key");
1347 len
= ireq
.u
.ntlmRequest
.sessionkey
->length
;
1348 if (len
!= sizeof(masterkey
)){
1350 krb5_set_error_message(context
, ret
,
1351 "NTLM master key wrong length: %lu",
1352 (unsigned long)len
);
1357 EVP_CIPHER_CTX_init(&rc4
);
1358 EVP_CipherInit_ex(&rc4
, EVP_rc4(), NULL
, sessionkey
, NULL
, 1);
1360 masterkey
, ireq
.u
.ntlmRequest
.sessionkey
->data
,
1362 EVP_CIPHER_CTX_cleanup(&rc4
);
1364 r
.u
.ntlmResponse
.sessionkey
=
1365 malloc(sizeof(*r
.u
.ntlmResponse
.sessionkey
));
1366 if (r
.u
.ntlmResponse
.sessionkey
== NULL
) {
1368 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1372 ret
= krb5_data_copy(r
.u
.ntlmResponse
.sessionkey
,
1373 masterkey
, sizeof(masterkey
));
1375 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1380 r
.u
.ntlmResponse
.success
= 1;
1381 kdc_log(context
, config
, 0, "NTLM version %d successful for %s",
1382 version
, ireq
.u
.ntlmRequest
.username
);
1385 case choice_DigestReqInner_supportedMechs
:
1387 kdc_log(context
, config
, 0, "digest supportedMechs from %s", from
);
1389 r
.element
= choice_DigestRepInner_supportedMechs
;
1390 memset(&r
.u
.supportedMechs
, 0, sizeof(r
.u
.supportedMechs
));
1392 if (config
->digests_allowed
& NTLM_V1
)
1393 r
.u
.supportedMechs
.ntlm_v1
= 1;
1394 if (config
->digests_allowed
& NTLM_V1_SESSION
)
1395 r
.u
.supportedMechs
.ntlm_v1_session
= 1;
1396 if (config
->digests_allowed
& NTLM_V2
)
1397 r
.u
.supportedMechs
.ntlm_v2
= 1;
1398 if (config
->digests_allowed
& DIGEST_MD5
)
1399 r
.u
.supportedMechs
.digest_md5
= 1;
1400 if (config
->digests_allowed
& CHAP_MD5
)
1401 r
.u
.supportedMechs
.chap_md5
= 1;
1402 if (config
->digests_allowed
& MS_CHAP_V2
)
1403 r
.u
.supportedMechs
.ms_chap_v2
= 1;
1409 krb5_set_error_message(context
, ret
, "unknown operation to digest");
1413 s
= krb5_get_error_message(context
, ret
);
1415 krb5_clear_error_message(context
);
1419 kdc_log(context
, config
, 0, "Digest failed with: %s", s
);
1421 r
.element
= choice_DigestRepInner_error
;
1422 r
.u
.error
.reason
= strdup("unknown error");
1423 krb5_free_error_message(context
, s
);
1424 if (r
.u
.error
.reason
== NULL
) {
1426 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1429 r
.u
.error
.code
= EINVAL
;
1434 ASN1_MALLOC_ENCODE(DigestRepInner
, buf
.data
, buf
.length
, &r
, &size
, ret
);
1436 krb5_set_error_message(context
, ret
, "Failed to encode inner digest reply");
1439 if (size
!= buf
.length
)
1440 krb5_abortx(context
, "ASN1 internal error");
1442 krb5_auth_con_addflags(context
, ac
, KRB5_AUTH_CONTEXT_USE_SUBKEY
, NULL
);
1444 ret
= krb5_mk_rep (context
, ac
, &rep
.apRep
);
1451 ret
= krb5_auth_con_getlocalsubkey(context
, ac
, &key
);
1455 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
1456 krb5_free_keyblock (context
, key
);
1461 ret
= krb5_encrypt_EncryptedData(context
, crypto
, KRB5_KU_DIGEST_ENCRYPT
,
1462 buf
.data
, buf
.length
, 0,
1465 ASN1_MALLOC_ENCODE(DigestREP
, reply
->data
, reply
->length
, &rep
, &size
, ret
);
1467 krb5_set_error_message(context
, ret
, "Failed to encode digest reply");
1470 if (size
!= reply
->length
)
1471 krb5_abortx(context
, "ASN1 internal error");
1476 krb5_auth_con_free(context
, ac
);
1478 krb5_warn(context
, ret
, "Digest request from %s failed", from
);
1480 krb5_free_ticket(context
, ticket
);
1482 krb5_kt_close(context
, id
);
1484 krb5_crypto_destroy(context
, crypto
);
1486 krb5_storage_free(sp
);
1488 _kdc_free_ent (context
, user
);
1490 _kdc_free_ent (context
, server
);
1492 _kdc_free_ent (context
, client
);
1494 memset(password
, 0, strlen(password
));
1499 krb5_data_free(&buf
);
1500 krb5_data_free(&serverNonce
);
1501 free_Checksum(&res
);
1502 free_DigestREP(&rep
);
1503 free_DigestRepInner(&r
);
1504 free_DigestReqInner(&ireq
);