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
,
90 if (targetname
== NULL
)
93 p
= strchr(targetname
, '.');
101 static krb5_error_code
102 fill_targetinfo(krb5_context context
,
107 struct ntlm_targetinfo ti
;
113 memset(&ti
, 0, sizeof(ti
));
115 ti
.domainname
= targetname
;
116 p
= client
->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
, 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
, db
, 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
*serverdb
, *userdb
;
221 hdb_entry
*server
= NULL
, *user
= NULL
;
223 hdb_entry
*client
= NULL
;
224 char *client_name
= NULL
, *password
= NULL
;
225 krb5_data serverNonce
;
227 if(!config
->enable_digest
) {
228 kdc_log(context
, config
, 2,
229 "Rejected digest request (disabled) from %s", from
);
230 return KRB5KDC_ERR_POLICY
;
233 krb5_data_zero(&buf
);
234 krb5_data_zero(reply
);
235 krb5_data_zero(&serverNonce
);
236 memset(&ireq
, 0, sizeof(ireq
));
237 memset(&r
, 0, sizeof(r
));
238 memset(&rep
, 0, sizeof(rep
));
239 memset(&res
, 0, sizeof(res
));
241 kdc_log(context
, config
, 3, "Digest request from %s", from
);
243 ret
= krb5_kt_resolve(context
, "HDBGET:", &id
);
245 kdc_log(context
, config
, 0, "Can't open database for digest");
249 ret
= krb5_rd_req(context
,
259 /* check the server principal in the ticket matches digest/R@R */
261 krb5_principal principal
= NULL
;
264 ret
= krb5_ticket_get_server(context
, ticket
, &principal
);
269 krb5_set_error_message(context
, ret
, "Wrong digest server principal used");
270 p
= krb5_principal_get_comp_string(context
, principal
, 0);
272 krb5_free_principal(context
, principal
);
275 if (strcmp(p
, KRB5_DIGEST_NAME
) != 0) {
276 krb5_free_principal(context
, principal
);
280 p
= krb5_principal_get_comp_string(context
, principal
, 1);
282 krb5_free_principal(context
, principal
);
285 rr
= krb5_principal_get_realm(context
, principal
);
287 krb5_free_principal(context
, principal
);
290 if (strcmp(p
, rr
) != 0) {
291 krb5_free_principal(context
, principal
);
294 krb5_clear_error_message(context
);
296 ret
= _kdc_db_fetch(context
, config
, principal
,
297 HDB_F_GET_SERVER
, NULL
, &serverdb
, &server
);
301 krb5_free_principal(context
, principal
);
304 /* check the client is allowed to do digest auth */
306 krb5_principal principal
= NULL
;
308 ret
= krb5_ticket_get_client(context
, ticket
, &principal
);
312 ret
= krb5_unparse_name(context
, principal
, &client_name
);
314 krb5_free_principal(context
, principal
);
318 ret
= _kdc_db_fetch(context
, config
, principal
,
319 HDB_F_GET_CLIENT
, NULL
, &clientdb
, &client
);
320 krb5_free_principal(context
, principal
);
324 if (client
->flags
.allow_digest
== 0) {
325 kdc_log(context
, config
, 2,
326 "Client %s tried to use digest "
327 "but is not allowed to",
329 ret
= KRB5KDC_ERR_POLICY
;
330 krb5_set_error_message(context
, ret
,
331 "Client is not permitted to use digest");
340 ret
= krb5_auth_con_getremotesubkey(context
, ac
, &key
);
345 krb5_set_error_message(context
, ret
, "digest: remote subkey not found");
349 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
350 krb5_free_keyblock (context
, key
);
355 ret
= krb5_decrypt_EncryptedData(context
, crypto
, KRB5_KU_DIGEST_ENCRYPT
,
356 &req
->innerReq
, &buf
);
357 krb5_crypto_destroy(context
, crypto
);
362 ret
= decode_DigestReqInner(buf
.data
, buf
.length
, &ireq
, NULL
);
363 krb5_data_free(&buf
);
365 krb5_set_error_message(context
, ret
, "Failed to decode digest inner request");
369 kdc_log(context
, config
, 3, "Valid digest request from %s (%s)",
373 * Process the inner request
376 switch (ireq
.element
) {
377 case choice_DigestReqInner_init
: {
378 unsigned char server_nonce
[16], identifier
;
380 RAND_pseudo_bytes(&identifier
, sizeof(identifier
));
381 RAND_pseudo_bytes(server_nonce
, sizeof(server_nonce
));
383 server_nonce
[0] = kdc_time
& 0xff;
384 server_nonce
[1] = (kdc_time
>> 8) & 0xff;
385 server_nonce
[2] = (kdc_time
>> 16) & 0xff;
386 server_nonce
[3] = (kdc_time
>> 24) & 0xff;
388 r
.element
= choice_DigestRepInner_initReply
;
390 hex_encode(server_nonce
, sizeof(server_nonce
), &r
.u
.initReply
.nonce
);
391 if (r
.u
.initReply
.nonce
== NULL
) {
393 krb5_set_error_message(context
, ret
, "Failed to decode server nonce");
397 sp
= krb5_storage_emem();
400 krb5_set_error_message(context
, ret
, "malloc: out of memory");
403 ret
= krb5_store_stringz(sp
, ireq
.u
.init
.type
);
405 krb5_clear_error_message(context
);
409 if (ireq
.u
.init
.channel
) {
413 aret
= asprintf(&s
, "%s-%s:%s", r
.u
.initReply
.nonce
,
414 ireq
.u
.init
.channel
->cb_type
,
415 ireq
.u
.init
.channel
->cb_binding
);
416 if (aret
== -1 || s
== NULL
) {
418 krb5_set_error_message(context
, ret
,
419 "Failed to allocate channel binding");
422 free(r
.u
.initReply
.nonce
);
423 r
.u
.initReply
.nonce
= s
;
426 ret
= krb5_store_stringz(sp
, r
.u
.initReply
.nonce
);
428 krb5_clear_error_message(context
);
432 if (strcasecmp(ireq
.u
.init
.type
, "CHAP") == 0) {
435 r
.u
.initReply
.identifier
=
436 malloc(sizeof(*r
.u
.initReply
.identifier
));
437 if (r
.u
.initReply
.identifier
== NULL
) {
439 krb5_set_error_message(context
, ret
, "malloc: out of memory");
443 aret
= asprintf(r
.u
.initReply
.identifier
, "%02X", identifier
&0xff);
444 if (aret
== -1 || *r
.u
.initReply
.identifier
== NULL
) {
446 krb5_set_error_message(context
, ret
, "malloc: out of memory");
451 r
.u
.initReply
.identifier
= NULL
;
453 if (ireq
.u
.init
.hostname
) {
454 ret
= krb5_store_stringz(sp
, *ireq
.u
.init
.hostname
);
456 krb5_clear_error_message(context
);
461 ret
= krb5_storage_to_data(sp
, &buf
);
463 krb5_clear_error_message(context
);
467 ret
= get_digest_key(context
, config
, server
, &crypto
);
471 ret
= krb5_create_checksum(context
,
473 KRB5_KU_DIGEST_OPAQUE
,
478 krb5_crypto_destroy(context
, crypto
);
480 krb5_data_free(&buf
);
484 ASN1_MALLOC_ENCODE(Checksum
, buf
.data
, buf
.length
, &res
, &size
, ret
);
487 krb5_set_error_message(context
, ret
, "Failed to encode "
488 "checksum in digest request");
491 if (size
!= buf
.length
)
492 krb5_abortx(context
, "ASN1 internal error");
494 hex_encode(buf
.data
, buf
.length
, &r
.u
.initReply
.opaque
);
496 krb5_data_zero(&buf
);
497 if (r
.u
.initReply
.opaque
== NULL
) {
498 krb5_clear_error_message(context
);
503 kdc_log(context
, config
, 3, "Digest %s init request successful from %s",
504 ireq
.u
.init
.type
, from
);
508 case choice_DigestReqInner_digestRequest
: {
509 sp
= krb5_storage_emem();
512 krb5_set_error_message(context
, ret
, "malloc: out of memory");
515 ret
= krb5_store_stringz(sp
, ireq
.u
.digestRequest
.type
);
517 krb5_clear_error_message(context
);
521 krb5_store_stringz(sp
, ireq
.u
.digestRequest
.serverNonce
);
523 if (ireq
.u
.digestRequest
.hostname
) {
524 ret
= krb5_store_stringz(sp
, *ireq
.u
.digestRequest
.hostname
);
526 krb5_clear_error_message(context
);
531 buf
.length
= strlen(ireq
.u
.digestRequest
.opaque
);
532 buf
.data
= malloc(buf
.length
);
533 if (buf
.data
== NULL
) {
535 krb5_set_error_message(context
, ret
, "malloc: out of memory");
539 ret
= hex_decode(ireq
.u
.digestRequest
.opaque
, buf
.data
, buf
.length
);
542 krb5_set_error_message(context
, ret
, "Failed to decode opaque");
547 ret
= decode_Checksum(buf
.data
, buf
.length
, &res
, NULL
);
549 krb5_data_zero(&buf
);
551 krb5_set_error_message(context
, ret
,
552 "Failed to decode digest Checksum");
556 ret
= krb5_storage_to_data(sp
, &buf
);
558 krb5_clear_error_message(context
);
562 serverNonce
.length
= strlen(ireq
.u
.digestRequest
.serverNonce
);
563 serverNonce
.data
= malloc(serverNonce
.length
);
564 if (serverNonce
.data
== NULL
) {
566 krb5_set_error_message(context
, ret
, "malloc: out of memory");
571 * CHAP does the checksum of the raw nonce, but do it for all
572 * types, since we need to check the timestamp.
577 ssize
= hex_decode(ireq
.u
.digestRequest
.serverNonce
,
578 serverNonce
.data
, serverNonce
.length
);
581 krb5_set_error_message(context
, ret
, "Failed to decode serverNonce");
584 serverNonce
.length
= ssize
;
587 ret
= get_digest_key(context
, config
, server
, &crypto
);
591 ret
= _kdc_verify_checksum(context
, crypto
,
592 KRB5_KU_DIGEST_OPAQUE
,
595 krb5_data_free(&buf
);
596 krb5_crypto_destroy(context
, crypto
);
603 unsigned char *p
= serverNonce
.data
;
606 if (serverNonce
.length
< 4) {
608 krb5_set_error_message(context
, ret
, "server nonce too short");
611 t
= p
[0] | (p
[1] << 8) | (p
[2] << 16) | (p
[3] << 24);
613 if (labs((kdc_time
& 0xffffffff) - t
) > context
->max_skew
) {
615 krb5_set_error_message(context
, ret
, "time screw in server nonce ");
620 if (strcasecmp(ireq
.u
.digestRequest
.type
, "CHAP") == 0) {
622 unsigned char md
[MD5_DIGEST_LENGTH
];
626 if ((config
->digests_allowed
& CHAP_MD5
) == 0) {
627 kdc_log(context
, config
, 2, "Digest CHAP MD5 not allowed");
631 if (ireq
.u
.digestRequest
.identifier
== NULL
) {
633 krb5_set_error_message(context
, ret
, "Identifier missing "
634 "from CHAP request");
638 if (hex_decode(*ireq
.u
.digestRequest
.identifier
, &idx
, 1) != 1) {
640 krb5_set_error_message(context
, ret
, "failed to decode identifier");
644 ret
= get_password_entry(context
, config
,
645 ireq
.u
.digestRequest
.username
,
650 ctx
= EVP_MD_CTX_create();
652 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
653 EVP_DigestUpdate(ctx
, &idx
, 1);
654 EVP_DigestUpdate(ctx
, password
, strlen(password
));
655 EVP_DigestUpdate(ctx
, serverNonce
.data
, serverNonce
.length
);
656 EVP_DigestFinal_ex(ctx
, md
, NULL
);
658 EVP_MD_CTX_destroy(ctx
);
660 hex_encode(md
, sizeof(md
), &mdx
);
662 krb5_clear_error_message(context
);
667 r
.element
= choice_DigestRepInner_response
;
669 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
672 r
.u
.response
.success
= TRUE
;
674 kdc_log(context
, config
, 2,
675 "CHAP reply mismatch for %s",
676 ireq
.u
.digestRequest
.username
);
677 r
.u
.response
.success
= FALSE
;
680 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "SASL-DIGEST-MD5") == 0) {
682 unsigned char md
[MD5_DIGEST_LENGTH
];
686 if ((config
->digests_allowed
& DIGEST_MD5
) == 0) {
687 kdc_log(context
, config
, 2, "Digest SASL MD5 not allowed");
691 if (ireq
.u
.digestRequest
.nonceCount
== NULL
)
693 if (ireq
.u
.digestRequest
.clientNonce
== NULL
)
695 if (ireq
.u
.digestRequest
.qop
== NULL
)
697 if (ireq
.u
.digestRequest
.realm
== NULL
)
700 ret
= get_password_entry(context
, config
,
701 ireq
.u
.digestRequest
.username
,
706 ctx
= EVP_MD_CTX_create();
708 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
709 EVP_DigestUpdate(ctx
, ireq
.u
.digestRequest
.username
,
710 strlen(ireq
.u
.digestRequest
.username
));
711 EVP_DigestUpdate(ctx
, ":", 1);
712 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.realm
,
713 strlen(*ireq
.u
.digestRequest
.realm
));
714 EVP_DigestUpdate(ctx
, ":", 1);
715 EVP_DigestUpdate(ctx
, password
, strlen(password
));
716 EVP_DigestFinal_ex(ctx
, md
, NULL
);
718 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
719 EVP_DigestUpdate(ctx
, md
, sizeof(md
));
720 EVP_DigestUpdate(ctx
, ":", 1);
721 EVP_DigestUpdate(ctx
, ireq
.u
.digestRequest
.serverNonce
,
722 strlen(ireq
.u
.digestRequest
.serverNonce
));
723 EVP_DigestUpdate(ctx
, ":", 1);
724 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.nonceCount
,
725 strlen(*ireq
.u
.digestRequest
.nonceCount
));
726 if (ireq
.u
.digestRequest
.authid
) {
727 EVP_DigestUpdate(ctx
, ":", 1);
728 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.authid
,
729 strlen(*ireq
.u
.digestRequest
.authid
));
731 EVP_DigestFinal_ex(ctx
, md
, NULL
);
732 hex_encode(md
, sizeof(md
), &A1
);
735 krb5_set_error_message(context
, ret
, "malloc: out of memory");
736 EVP_MD_CTX_destroy(ctx
);
740 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
741 EVP_DigestUpdate(ctx
,
742 "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
743 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.uri
,
744 strlen(*ireq
.u
.digestRequest
.uri
));
747 if (strcmp(ireq
.u
.digestRequest
.digest
, "clear") != 0) {
748 static char conf_zeros
[] = ":00000000000000000000000000000000";
749 EVP_DigestUpdate(ctx
, conf_zeros
, sizeof(conf_zeros
) - 1);
752 EVP_DigestFinal_ex(ctx
, md
, NULL
);
754 hex_encode(md
, sizeof(md
), &A2
);
757 krb5_set_error_message(context
, ret
, "malloc: out of memory");
762 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
763 EVP_DigestUpdate(ctx
, A1
, strlen(A2
));
764 EVP_DigestUpdate(ctx
, ":", 1);
765 EVP_DigestUpdate(ctx
, ireq
.u
.digestRequest
.serverNonce
,
766 strlen(ireq
.u
.digestRequest
.serverNonce
));
767 EVP_DigestUpdate(ctx
, ":", 1);
768 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.nonceCount
,
769 strlen(*ireq
.u
.digestRequest
.nonceCount
));
770 EVP_DigestUpdate(ctx
, ":", 1);
771 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.clientNonce
,
772 strlen(*ireq
.u
.digestRequest
.clientNonce
));
773 EVP_DigestUpdate(ctx
, ":", 1);
774 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.qop
,
775 strlen(*ireq
.u
.digestRequest
.qop
));
776 EVP_DigestUpdate(ctx
, ":", 1);
777 EVP_DigestUpdate(ctx
, A2
, strlen(A2
));
779 EVP_DigestFinal_ex(ctx
, md
, NULL
);
781 EVP_MD_CTX_destroy(ctx
);
786 hex_encode(md
, sizeof(md
), &mdx
);
788 krb5_clear_error_message(context
);
793 r
.element
= choice_DigestRepInner_response
;
794 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
797 r
.u
.response
.success
= TRUE
;
799 kdc_log(context
, config
, 2,
800 "DIGEST-MD5 reply mismatch for %s",
801 ireq
.u
.digestRequest
.username
);
802 r
.u
.response
.success
= FALSE
;
805 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "MS-CHAP-V2") == 0) {
806 unsigned char md
[SHA_DIGEST_LENGTH
], challenge
[SHA_DIGEST_LENGTH
];
807 krb5_principal clientprincipal
= NULL
;
809 const char *username
;
810 struct ntlm_buf answer
;
814 if ((config
->digests_allowed
& MS_CHAP_V2
) == 0) {
815 kdc_log(context
, config
, 2, "MS-CHAP-V2 not allowed");
819 if (ireq
.u
.digestRequest
.clientNonce
== NULL
) {
821 krb5_set_error_message(context
, ret
,
822 "MS-CHAP-V2 clientNonce missing");
825 if (serverNonce
.length
!= 16) {
827 krb5_set_error_message(context
, ret
,
828 "MS-CHAP-V2 serverNonce wrong length");
832 /* strip of the domain component */
833 username
= strchr(ireq
.u
.digestRequest
.username
, '\\');
834 if (username
== NULL
)
835 username
= ireq
.u
.digestRequest
.username
;
839 ctp
= EVP_MD_CTX_create();
842 EVP_DigestInit_ex(ctp
, EVP_sha1(), NULL
);
845 krb5_data clientNonce
;
847 clientNonce
.length
= strlen(*ireq
.u
.digestRequest
.clientNonce
);
848 clientNonce
.data
= malloc(clientNonce
.length
);
849 if (clientNonce
.data
== NULL
) {
851 krb5_set_error_message(context
, ret
,
852 "malloc: out of memory");
853 EVP_MD_CTX_destroy(ctp
);
857 ssize
= hex_decode(*ireq
.u
.digestRequest
.clientNonce
,
858 clientNonce
.data
, clientNonce
.length
);
861 krb5_set_error_message(context
, ret
,
862 "Failed to decode clientNonce");
863 EVP_MD_CTX_destroy(ctp
);
866 EVP_DigestUpdate(ctp
, clientNonce
.data
, ssize
);
867 free(clientNonce
.data
);
869 EVP_DigestUpdate(ctp
, serverNonce
.data
, serverNonce
.length
);
870 EVP_DigestUpdate(ctp
, username
, strlen(username
));
872 EVP_DigestFinal_ex(ctp
, challenge
, NULL
);
874 EVP_MD_CTX_destroy(ctp
);
877 ret
= krb5_parse_name(context
, username
, &clientprincipal
);
881 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
882 HDB_F_GET_CLIENT
, NULL
, &userdb
, &user
);
883 krb5_free_principal(context
, clientprincipal
);
885 krb5_set_error_message(context
, ret
,
886 "MS-CHAP-V2 user %s not in database",
891 ret
= hdb_enctype2key(context
, user
, NULL
,
892 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
894 krb5_set_error_message(context
, ret
,
895 "MS-CHAP-V2 missing arcfour key %s",
900 /* ChallengeResponse */
901 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
902 key
->key
.keyvalue
.length
,
905 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
909 hex_encode(answer
.data
, answer
.length
, &mdx
);
912 krb5_clear_error_message(context
);
917 r
.element
= choice_DigestRepInner_response
;
918 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
920 r
.u
.response
.success
= TRUE
;
922 kdc_log(context
, config
, 2,
923 "MS-CHAP-V2 hash mismatch for %s",
924 ireq
.u
.digestRequest
.username
);
925 r
.u
.response
.success
= FALSE
;
929 if (r
.u
.response
.success
) {
930 unsigned char hashhash
[MD4_DIGEST_LENGTH
];
933 ctxp
= EVP_MD_CTX_create();
937 EVP_DigestInit_ex(ctxp
, EVP_md4(), NULL
);
938 EVP_DigestUpdate(ctxp
,
939 key
->key
.keyvalue
.data
,
940 key
->key
.keyvalue
.length
);
941 EVP_DigestFinal_ex(ctxp
, hashhash
, NULL
);
944 /* GenerateAuthenticatorResponse */
945 EVP_DigestInit_ex(ctxp
, EVP_sha1(), NULL
);
946 EVP_DigestUpdate(ctxp
, hashhash
, sizeof(hashhash
));
947 EVP_DigestUpdate(ctxp
, answer
.data
, answer
.length
);
948 EVP_DigestUpdate(ctxp
, ms_chap_v2_magic1
,
949 sizeof(ms_chap_v2_magic1
));
950 EVP_DigestFinal_ex(ctxp
, md
, NULL
);
952 EVP_DigestInit_ex(ctxp
, EVP_sha1(), NULL
);
953 EVP_DigestUpdate(ctxp
, md
, sizeof(md
));
954 EVP_DigestUpdate(ctxp
, challenge
, 8);
955 EVP_DigestUpdate(ctxp
, ms_chap_v2_magic2
,
956 sizeof(ms_chap_v2_magic2
));
957 EVP_DigestFinal_ex(ctxp
, md
, NULL
);
959 r
.u
.response
.rsp
= calloc(1, sizeof(*r
.u
.response
.rsp
));
960 if (r
.u
.response
.rsp
== NULL
) {
962 krb5_clear_error_message(context
);
963 EVP_MD_CTX_destroy(ctxp
);
968 hex_encode(md
, sizeof(md
), r
.u
.response
.rsp
);
969 if (r
.u
.response
.rsp
== NULL
) {
971 krb5_clear_error_message(context
);
972 EVP_MD_CTX_destroy(ctxp
);
977 /* get_master, rfc 3079 3.4 */
978 EVP_DigestInit_ex(ctxp
, EVP_sha1(), NULL
);
979 EVP_DigestUpdate(ctxp
, hashhash
, 16);
980 EVP_DigestUpdate(ctxp
, answer
.data
, answer
.length
);
981 EVP_DigestUpdate(ctxp
, ms_rfc3079_magic1
,
982 sizeof(ms_rfc3079_magic1
));
983 EVP_DigestFinal_ex(ctxp
, md
, NULL
);
987 EVP_MD_CTX_destroy(ctxp
);
989 r
.u
.response
.session_key
=
990 calloc(1, sizeof(*r
.u
.response
.session_key
));
991 if (r
.u
.response
.session_key
== NULL
) {
992 krb5_clear_error_message(context
);
997 ret
= krb5_data_copy(r
.u
.response
.session_key
, md
, 16);
999 krb5_clear_error_message(context
);
1007 r
.element
= choice_DigestRepInner_error
;
1008 aret
= asprintf(&r
.u
.error
.reason
, "Unsupported digest type %s",
1009 ireq
.u
.digestRequest
.type
);
1010 if (aret
== -1 || r
.u
.error
.reason
== NULL
) {
1012 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1015 r
.u
.error
.code
= EINVAL
;
1018 kdc_log(context
, config
, 3, "Digest %s request successful %s",
1019 ireq
.u
.digestRequest
.type
, ireq
.u
.digestRequest
.username
);
1023 case choice_DigestReqInner_ntlmInit
:
1025 if ((config
->digests_allowed
& (NTLM_V1
|NTLM_V1_SESSION
|NTLM_V2
)) == 0) {
1026 kdc_log(context
, config
, 2, "NTLM not allowed");
1030 r
.element
= choice_DigestRepInner_ntlmInitReply
;
1032 r
.u
.ntlmInitReply
.flags
= NTLM_NEG_UNICODE
;
1034 if ((ireq
.u
.ntlmInit
.flags
& NTLM_NEG_UNICODE
) == 0) {
1035 kdc_log(context
, config
, 2, "NTLM client have no unicode");
1039 if (ireq
.u
.ntlmInit
.flags
& NTLM_NEG_NTLM
)
1040 r
.u
.ntlmInitReply
.flags
|= NTLM_NEG_NTLM
;
1042 kdc_log(context
, config
, 2, "NTLM client doesn't support NTLM");
1046 r
.u
.ntlmInitReply
.flags
|=
1048 NTLM_TARGET_DOMAIN
|
1054 NTLM_NEG_ALWAYS_SIGN| \
1055 NTLM_NEG_NTLM2_SESSION| \
1058 r
.u
.ntlmInitReply
.flags
|= (ireq
.u
.ntlmInit
.flags
& (ALL
));
1062 r
.u
.ntlmInitReply
.targetname
=
1063 get_ntlm_targetname(context
, client
);
1064 if (r
.u
.ntlmInitReply
.targetname
== NULL
) {
1066 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1069 r
.u
.ntlmInitReply
.challenge
.data
= malloc(8);
1070 if (r
.u
.ntlmInitReply
.challenge
.data
== NULL
) {
1072 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1075 r
.u
.ntlmInitReply
.challenge
.length
= 8;
1076 if (RAND_bytes(r
.u
.ntlmInitReply
.challenge
.data
,
1077 r
.u
.ntlmInitReply
.challenge
.length
) != 1)
1080 krb5_set_error_message(context
, ret
, "out of random error");
1083 /* XXX fix targetinfo */
1084 ALLOC(r
.u
.ntlmInitReply
.targetinfo
);
1085 if (r
.u
.ntlmInitReply
.targetinfo
== NULL
) {
1087 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1091 ret
= fill_targetinfo(context
,
1092 r
.u
.ntlmInitReply
.targetname
,
1094 r
.u
.ntlmInitReply
.targetinfo
);
1097 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1102 * Save data encryted in opaque for the second part of the
1103 * ntlm authentication
1105 sp
= krb5_storage_emem();
1108 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1112 ret
= krb5_storage_write(sp
, r
.u
.ntlmInitReply
.challenge
.data
, 8);
1115 krb5_set_error_message(context
, ret
, "storage write challenge");
1118 ret
= krb5_store_uint32(sp
, r
.u
.ntlmInitReply
.flags
);
1120 krb5_clear_error_message(context
);
1124 ret
= krb5_storage_to_data(sp
, &buf
);
1126 krb5_clear_error_message(context
);
1130 ret
= get_digest_key(context
, config
, server
, &crypto
);
1134 ret
= krb5_encrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1135 buf
.data
, buf
.length
, &r
.u
.ntlmInitReply
.opaque
);
1136 krb5_data_free(&buf
);
1137 krb5_crypto_destroy(context
, crypto
);
1142 kdc_log(context
, config
, 3, "NTLM init from %s", from
);
1146 case choice_DigestReqInner_ntlmRequest
: {
1147 krb5_principal clientprincipal
;
1148 unsigned char sessionkey
[16];
1149 unsigned char challenge
[8];
1154 r
.element
= choice_DigestRepInner_ntlmResponse
;
1155 r
.u
.ntlmResponse
.success
= 0;
1156 r
.u
.ntlmResponse
.flags
= 0;
1157 r
.u
.ntlmResponse
.sessionkey
= NULL
;
1158 r
.u
.ntlmResponse
.tickets
= NULL
;
1161 ret
= krb5_parse_name(context
,
1162 ireq
.u
.ntlmRequest
.username
,
1167 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
1168 HDB_F_GET_CLIENT
, NULL
, &userdb
, &user
);
1169 krb5_free_principal(context
, clientprincipal
);
1171 krb5_set_error_message(context
, ret
, "NTLM user %s not in database",
1172 ireq
.u
.ntlmRequest
.username
);
1176 ret
= get_digest_key(context
, config
, server
, &crypto
);
1180 ret
= krb5_decrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1181 ireq
.u
.ntlmRequest
.opaque
.data
,
1182 ireq
.u
.ntlmRequest
.opaque
.length
, &buf
);
1183 krb5_crypto_destroy(context
, crypto
);
1186 kdc_log(context
, config
, 2,
1187 "Failed to decrypt nonce from %s", from
);
1191 sp
= krb5_storage_from_data(&buf
);
1194 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1198 ret
= krb5_storage_read(sp
, challenge
, sizeof(challenge
));
1199 if (ret
!= sizeof(challenge
)) {
1201 krb5_set_error_message(context
, ret
, "NTLM storage read challenge");
1204 ret
= krb5_ret_uint32(sp
, &flags
);
1206 krb5_set_error_message(context
, ret
, "NTLM storage read flags");
1209 krb5_storage_free(sp
);
1211 krb5_data_free(&buf
);
1213 if ((flags
& NTLM_NEG_NTLM
) == 0) {
1215 krb5_set_error_message(context
, ret
, "NTLM not negotiated");
1219 ret
= hdb_enctype2key(context
, user
, NULL
,
1220 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
1222 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
1226 /* check if this is NTLMv2 */
1227 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= 24) {
1228 struct ntlm_buf infotarget
, answer
;
1231 if ((config
->digests_allowed
& NTLM_V2
) == 0) {
1232 kdc_log(context
, config
, 2, "NTLM v2 not allowed");
1238 targetname
= get_ntlm_targetname(context
, client
);
1239 if (targetname
== NULL
) {
1241 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1245 answer
.length
= ireq
.u
.ntlmRequest
.ntlm
.length
;
1246 answer
.data
= ireq
.u
.ntlmRequest
.ntlm
.data
;
1248 ret
= heim_ntlm_verify_ntlm2(key
->key
.keyvalue
.data
,
1249 key
->key
.keyvalue
.length
,
1250 ireq
.u
.ntlmRequest
.username
,
1259 krb5_set_error_message(context
, ret
, "NTLM v2 verify failed");
1263 /* XXX verify infotarget matches client (checksum ?) */
1265 free(infotarget
.data
);
1269 struct ntlm_buf answer
;
1273 if (flags
& NTLM_NEG_NTLM2_SESSION
) {
1274 unsigned char sessionhash
[MD5_DIGEST_LENGTH
];
1277 if ((config
->digests_allowed
& NTLM_V1_SESSION
) == 0) {
1278 kdc_log(context
, config
, 2, "NTLM v1-session not allowed");
1283 if (ireq
.u
.ntlmRequest
.lm
.length
!= 24) {
1285 krb5_set_error_message(context
, ret
, "LM hash have wrong length "
1286 "for NTLM session key");
1290 ctx
= EVP_MD_CTX_create();
1292 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
1294 EVP_DigestUpdate(ctx
, challenge
, sizeof(challenge
));
1295 EVP_DigestUpdate(ctx
, ireq
.u
.ntlmRequest
.lm
.data
, 8);
1296 EVP_DigestFinal_ex(ctx
, sessionhash
, NULL
);
1297 memcpy(challenge
, sessionhash
, sizeof(challenge
));
1299 EVP_MD_CTX_destroy(ctx
);
1302 if ((config
->digests_allowed
& NTLM_V1
) == 0) {
1303 kdc_log(context
, config
, 2, "NTLM v1 not allowed");
1308 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
1309 key
->key
.keyvalue
.length
,
1310 challenge
, &answer
);
1312 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
1316 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= answer
.length
||
1317 ct_memcmp(ireq
.u
.ntlmRequest
.ntlm
.data
, answer
.data
, answer
.length
) != 0)
1321 krb5_set_error_message(context
, ret
, "NTLM hash mismatch");
1329 ctx
= EVP_MD_CTX_create();
1331 EVP_DigestInit_ex(ctx
, EVP_md4(), NULL
);
1332 EVP_DigestUpdate(ctx
,
1333 key
->key
.keyvalue
.data
,
1334 key
->key
.keyvalue
.length
);
1335 EVP_DigestFinal_ex(ctx
, sessionkey
, NULL
);
1337 EVP_MD_CTX_destroy(ctx
);
1341 if (ireq
.u
.ntlmRequest
.sessionkey
) {
1342 unsigned char masterkey
[MD4_DIGEST_LENGTH
];
1346 if ((flags
& NTLM_NEG_KEYEX
) == 0) {
1348 krb5_set_error_message(context
, ret
,
1349 "NTLM client failed to neg key "
1350 "exchange but still sent key");
1354 len
= ireq
.u
.ntlmRequest
.sessionkey
->length
;
1355 if (len
!= sizeof(masterkey
)){
1357 krb5_set_error_message(context
, ret
,
1358 "NTLM master key wrong length: %lu",
1359 (unsigned long)len
);
1364 EVP_CIPHER_CTX_init(&rc4
);
1365 EVP_CipherInit_ex(&rc4
, EVP_rc4(), NULL
, sessionkey
, NULL
, 1);
1367 masterkey
, ireq
.u
.ntlmRequest
.sessionkey
->data
,
1369 EVP_CIPHER_CTX_cleanup(&rc4
);
1371 r
.u
.ntlmResponse
.sessionkey
=
1372 malloc(sizeof(*r
.u
.ntlmResponse
.sessionkey
));
1373 if (r
.u
.ntlmResponse
.sessionkey
== NULL
) {
1375 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1379 ret
= krb5_data_copy(r
.u
.ntlmResponse
.sessionkey
,
1380 masterkey
, sizeof(masterkey
));
1382 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1387 r
.u
.ntlmResponse
.success
= 1;
1388 kdc_log(context
, config
, 0, "NTLM version %d successful for %s",
1389 version
, ireq
.u
.ntlmRequest
.username
);
1392 case choice_DigestReqInner_supportedMechs
:
1394 kdc_log(context
, config
, 4, "digest supportedMechs from %s", from
);
1396 r
.element
= choice_DigestRepInner_supportedMechs
;
1397 memset(&r
.u
.supportedMechs
, 0, sizeof(r
.u
.supportedMechs
));
1399 if (config
->digests_allowed
& NTLM_V1
)
1400 r
.u
.supportedMechs
.ntlm_v1
= 1;
1401 if (config
->digests_allowed
& NTLM_V1_SESSION
)
1402 r
.u
.supportedMechs
.ntlm_v1_session
= 1;
1403 if (config
->digests_allowed
& NTLM_V2
)
1404 r
.u
.supportedMechs
.ntlm_v2
= 1;
1405 if (config
->digests_allowed
& DIGEST_MD5
)
1406 r
.u
.supportedMechs
.digest_md5
= 1;
1407 if (config
->digests_allowed
& CHAP_MD5
)
1408 r
.u
.supportedMechs
.chap_md5
= 1;
1409 if (config
->digests_allowed
& MS_CHAP_V2
)
1410 r
.u
.supportedMechs
.ms_chap_v2
= 1;
1416 krb5_set_error_message(context
, ret
, "unknown operation to digest");
1420 s
= krb5_get_error_message(context
, ret
);
1422 krb5_clear_error_message(context
);
1426 kdc_log(context
, config
, 2, "Digest failed with: %s", s
);
1428 r
.element
= choice_DigestRepInner_error
;
1429 r
.u
.error
.reason
= strdup("unknown error");
1430 krb5_free_error_message(context
, s
);
1431 if (r
.u
.error
.reason
== NULL
) {
1433 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1436 r
.u
.error
.code
= EINVAL
;
1441 ASN1_MALLOC_ENCODE(DigestRepInner
, buf
.data
, buf
.length
, &r
, &size
, ret
);
1443 krb5_set_error_message(context
, ret
, "Failed to encode inner digest reply");
1446 if (size
!= buf
.length
)
1447 krb5_abortx(context
, "ASN1 internal error");
1449 krb5_auth_con_addflags(context
, ac
, KRB5_AUTH_CONTEXT_USE_SUBKEY
, NULL
);
1451 ret
= krb5_mk_rep (context
, ac
, &rep
.apRep
);
1458 ret
= krb5_auth_con_getlocalsubkey(context
, ac
, &key
);
1462 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
1463 krb5_free_keyblock (context
, key
);
1468 ret
= krb5_encrypt_EncryptedData(context
, crypto
, KRB5_KU_DIGEST_ENCRYPT
,
1469 buf
.data
, buf
.length
, 0,
1472 krb5_prepend_error_message(context
, ret
, "Failed to encrypt digest: ");
1476 ASN1_MALLOC_ENCODE(DigestREP
, reply
->data
, reply
->length
, &rep
, &size
, ret
);
1478 krb5_set_error_message(context
, ret
, "Failed to encode digest reply");
1481 if (size
!= reply
->length
)
1482 krb5_abortx(context
, "ASN1 internal error");
1487 krb5_auth_con_free(context
, ac
);
1489 krb5_warn(context
, ret
, "Digest request from %s failed", from
);
1491 krb5_free_ticket(context
, ticket
);
1493 krb5_kt_close(context
, id
);
1495 krb5_crypto_destroy(context
, crypto
);
1497 krb5_storage_free(sp
);
1499 _kdc_free_ent (context
, userdb
, user
);
1501 _kdc_free_ent (context
, serverdb
, server
);
1503 _kdc_free_ent (context
, clientdb
, client
);
1505 memset(password
, 0, strlen(password
));
1510 krb5_data_free(&buf
);
1511 krb5_data_free(&serverNonce
);
1512 free_Checksum(&res
);
1513 free_DigestREP(&rep
);
1514 free_DigestRepInner(&r
);
1515 free_DigestReqInner(&ireq
);