1 /* $NetBSD: digest.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $ */
4 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 #define MS_CHAP_V2 0x20
43 #define DIGEST_MD5 0x08
45 #define NTLM_V1_SESSION 0x02
48 const struct units _kdc_digestunits
[] = {
49 {"ms-chap-v2", 1U << 5},
50 {"chap-md5", 1U << 4},
51 {"digest-md5", 1U << 3},
53 {"ntlm-v1-session", 1U << 1},
59 static krb5_error_code
60 get_digest_key(krb5_context context
,
61 krb5_kdc_configuration
*config
,
69 ret
= _kdc_get_preferred_key(context
,
77 return krb5_crypto_init(context
, &key
->key
, 0, crypto
);
85 get_ntlm_targetname(krb5_context context
,
90 targetname
= strdup(krb5_principal_get_realm(context
,
91 client
->entry
.principal
));
92 if (targetname
== NULL
)
95 p
= strchr(targetname
, '.');
103 static krb5_error_code
104 fill_targetinfo(krb5_context context
,
106 hdb_entry_ex
*client
,
109 struct ntlm_targetinfo ti
;
115 memset(&ti
, 0, sizeof(ti
));
117 ti
.domainname
= targetname
;
118 p
= client
->entry
.principal
;
119 str
= krb5_principal_get_comp_string(context
, p
, 0);
121 (strcmp("host", str
) == 0 ||
122 strcmp("ftp", str
) == 0 ||
123 strcmp("imap", str
) == 0 ||
124 strcmp("pop", str
) == 0 ||
125 strcmp("smtp", str
)))
127 str
= krb5_principal_get_comp_string(context
, p
, 1);
128 ti
.dnsservername
= rk_UNCONST(str
);
131 ret
= heim_ntlm_encode_targetinfo(&ti
, 1, &d
);
136 data
->length
= d
.length
;
142 static const unsigned char ms_chap_v2_magic1
[39] = {
143 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
144 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
145 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
146 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
148 static const unsigned char ms_chap_v2_magic2
[41] = {
149 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
150 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
151 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
152 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
155 static const unsigned char ms_rfc3079_magic1
[27] = {
156 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
157 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
158 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
165 static krb5_error_code
166 get_password_entry(krb5_context context
,
167 krb5_kdc_configuration
*config
,
168 const char *username
,
171 krb5_principal clientprincipal
;
177 ret
= krb5_parse_name(context
, username
, &clientprincipal
);
181 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
182 HDB_F_GET_CLIENT
, NULL
, &db
, &user
);
183 krb5_free_principal(context
, clientprincipal
);
187 ret
= hdb_entry_get_password(context
, db
, &user
->entry
, password
);
188 if (ret
|| password
== NULL
) {
191 krb5_set_error_message(context
, ret
, "password missing");
193 memset(user
, 0, sizeof(*user
));
195 _kdc_free_ent (context
, user
);
204 _kdc_do_digest(krb5_context context
,
205 krb5_kdc_configuration
*config
,
206 const struct DigestREQ
*req
, krb5_data
*reply
,
207 const char *from
, struct sockaddr
*addr
)
209 krb5_error_code ret
= 0;
210 krb5_ticket
*ticket
= NULL
;
211 krb5_auth_context ac
= NULL
;
212 krb5_keytab id
= NULL
;
213 krb5_crypto crypto
= NULL
;
217 krb5_flags ap_req_options
;
220 krb5_storage
*sp
= NULL
;
222 hdb_entry_ex
*server
= NULL
, *user
= NULL
;
223 hdb_entry_ex
*client
= NULL
;
224 char *client_name
= NULL
, *password
= NULL
;
225 krb5_data serverNonce
;
227 if(!config
->enable_digest
) {
228 kdc_log(context
, config
, 0,
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
, 0, "Digest request from %s", from
);
243 ret
= krb5_kt_resolve(context
, "HDB:", &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
, NULL
, &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
, NULL
, &client
);
320 krb5_free_principal(context
, principal
);
324 if (client
->entry
.flags
.allow_digest
== 0) {
325 kdc_log(context
, config
, 0,
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
, 0, "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
) {
412 asprintf(&s
, "%s-%s:%s", r
.u
.initReply
.nonce
,
413 ireq
.u
.init
.channel
->cb_type
,
414 ireq
.u
.init
.channel
->cb_binding
);
417 krb5_set_error_message(context
, ret
,
418 "Failed to allocate channel binding");
421 free(r
.u
.initReply
.nonce
);
422 r
.u
.initReply
.nonce
= s
;
425 ret
= krb5_store_stringz(sp
, r
.u
.initReply
.nonce
);
427 krb5_clear_error_message(context
);
431 if (strcasecmp(ireq
.u
.init
.type
, "CHAP") == 0) {
432 r
.u
.initReply
.identifier
=
433 malloc(sizeof(*r
.u
.initReply
.identifier
));
434 if (r
.u
.initReply
.identifier
== NULL
) {
436 krb5_set_error_message(context
, ret
, "malloc: out of memory");
440 asprintf(r
.u
.initReply
.identifier
, "%02X", identifier
& 0xff);
441 if (*r
.u
.initReply
.identifier
== NULL
) {
443 krb5_set_error_message(context
, ret
, "malloc: out of memory");
448 r
.u
.initReply
.identifier
= NULL
;
450 if (ireq
.u
.init
.hostname
) {
451 ret
= krb5_store_stringz(sp
, *ireq
.u
.init
.hostname
);
453 krb5_clear_error_message(context
);
458 ret
= krb5_storage_to_data(sp
, &buf
);
460 krb5_clear_error_message(context
);
464 ret
= get_digest_key(context
, config
, server
, &crypto
);
468 ret
= krb5_create_checksum(context
,
470 KRB5_KU_DIGEST_OPAQUE
,
475 krb5_crypto_destroy(context
, crypto
);
477 krb5_data_free(&buf
);
481 ASN1_MALLOC_ENCODE(Checksum
, buf
.data
, buf
.length
, &res
, &size
, ret
);
484 krb5_set_error_message(context
, ret
, "Failed to encode "
485 "checksum in digest request");
488 if (size
!= buf
.length
)
489 krb5_abortx(context
, "ASN1 internal error");
491 hex_encode(buf
.data
, buf
.length
, &r
.u
.initReply
.opaque
);
493 krb5_data_zero(&buf
);
494 if (r
.u
.initReply
.opaque
== NULL
) {
495 krb5_clear_error_message(context
);
500 kdc_log(context
, config
, 0, "Digest %s init request successful from %s",
501 ireq
.u
.init
.type
, from
);
505 case choice_DigestReqInner_digestRequest
: {
506 sp
= krb5_storage_emem();
509 krb5_set_error_message(context
, ret
, "malloc: out of memory");
512 ret
= krb5_store_stringz(sp
, ireq
.u
.digestRequest
.type
);
514 krb5_clear_error_message(context
);
518 krb5_store_stringz(sp
, ireq
.u
.digestRequest
.serverNonce
);
520 if (ireq
.u
.digestRequest
.hostname
) {
521 ret
= krb5_store_stringz(sp
, *ireq
.u
.digestRequest
.hostname
);
523 krb5_clear_error_message(context
);
528 buf
.length
= strlen(ireq
.u
.digestRequest
.opaque
);
529 buf
.data
= malloc(buf
.length
);
530 if (buf
.data
== NULL
) {
532 krb5_set_error_message(context
, ret
, "malloc: out of memory");
536 ret
= hex_decode(ireq
.u
.digestRequest
.opaque
, buf
.data
, buf
.length
);
539 krb5_set_error_message(context
, ret
, "Failed to decode opaque");
544 ret
= decode_Checksum(buf
.data
, buf
.length
, &res
, NULL
);
546 krb5_data_zero(&buf
);
548 krb5_set_error_message(context
, ret
,
549 "Failed to decode digest Checksum");
553 ret
= krb5_storage_to_data(sp
, &buf
);
555 krb5_clear_error_message(context
);
559 serverNonce
.length
= strlen(ireq
.u
.digestRequest
.serverNonce
);
560 serverNonce
.data
= malloc(serverNonce
.length
);
561 if (serverNonce
.data
== NULL
) {
563 krb5_set_error_message(context
, ret
, "malloc: out of memory");
568 * CHAP does the checksum of the raw nonce, but do it for all
569 * types, since we need to check the timestamp.
574 ssize
= hex_decode(ireq
.u
.digestRequest
.serverNonce
,
575 serverNonce
.data
, serverNonce
.length
);
578 krb5_set_error_message(context
, ret
, "Failed to decode serverNonce");
581 serverNonce
.length
= ssize
;
584 ret
= get_digest_key(context
, config
, server
, &crypto
);
588 ret
= krb5_verify_checksum(context
, crypto
,
589 KRB5_KU_DIGEST_OPAQUE
,
590 buf
.data
, buf
.length
, &res
);
592 krb5_data_free(&buf
);
593 krb5_crypto_destroy(context
, crypto
);
600 unsigned char *p
= serverNonce
.data
;
603 if (serverNonce
.length
< 4) {
605 krb5_set_error_message(context
, ret
, "server nonce too short");
608 t
= p
[0] | (p
[1] << 8) | (p
[2] << 16) | (p
[3] << 24);
610 if (abs((kdc_time
& 0xffffffff) - t
) > context
->max_skew
) {
612 krb5_set_error_message(context
, ret
, "time screw in server nonce ");
617 if (strcasecmp(ireq
.u
.digestRequest
.type
, "CHAP") == 0) {
619 unsigned char md
[MD5_DIGEST_LENGTH
];
623 if ((config
->digests_allowed
& CHAP_MD5
) == 0) {
624 kdc_log(context
, config
, 0, "Digest CHAP MD5 not allowed");
628 if (ireq
.u
.digestRequest
.identifier
== NULL
) {
630 krb5_set_error_message(context
, ret
, "Identifier missing "
631 "from CHAP request");
635 if (hex_decode(*ireq
.u
.digestRequest
.identifier
, &idx
, 1) != 1) {
637 krb5_set_error_message(context
, ret
, "failed to decode identifier");
641 ret
= get_password_entry(context
, config
,
642 ireq
.u
.digestRequest
.username
,
647 ctx
= EVP_MD_CTX_create();
649 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
650 EVP_DigestUpdate(ctx
, &idx
, 1);
651 EVP_DigestUpdate(ctx
, password
, strlen(password
));
652 EVP_DigestUpdate(ctx
, serverNonce
.data
, serverNonce
.length
);
653 EVP_DigestFinal_ex(ctx
, md
, NULL
);
655 EVP_MD_CTX_destroy(ctx
);
657 hex_encode(md
, sizeof(md
), &mdx
);
659 krb5_clear_error_message(context
);
664 r
.element
= choice_DigestRepInner_response
;
666 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
669 r
.u
.response
.success
= TRUE
;
671 kdc_log(context
, config
, 0,
672 "CHAP reply mismatch for %s",
673 ireq
.u
.digestRequest
.username
);
674 r
.u
.response
.success
= FALSE
;
677 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "SASL-DIGEST-MD5") == 0) {
679 unsigned char md
[MD5_DIGEST_LENGTH
];
683 if ((config
->digests_allowed
& DIGEST_MD5
) == 0) {
684 kdc_log(context
, config
, 0, "Digest SASL MD5 not allowed");
688 if (ireq
.u
.digestRequest
.nonceCount
== NULL
)
690 if (ireq
.u
.digestRequest
.clientNonce
== NULL
)
692 if (ireq
.u
.digestRequest
.qop
== NULL
)
694 if (ireq
.u
.digestRequest
.realm
== NULL
)
697 ret
= get_password_entry(context
, config
,
698 ireq
.u
.digestRequest
.username
,
703 ctx
= EVP_MD_CTX_create();
705 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
706 EVP_DigestUpdate(ctx
, ireq
.u
.digestRequest
.username
,
707 strlen(ireq
.u
.digestRequest
.username
));
708 EVP_DigestUpdate(ctx
, ":", 1);
709 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.realm
,
710 strlen(*ireq
.u
.digestRequest
.realm
));
711 EVP_DigestUpdate(ctx
, ":", 1);
712 EVP_DigestUpdate(ctx
, password
, strlen(password
));
713 EVP_DigestFinal_ex(ctx
, md
, NULL
);
715 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
716 EVP_DigestUpdate(ctx
, md
, sizeof(md
));
717 EVP_DigestUpdate(ctx
, ":", 1);
718 EVP_DigestUpdate(ctx
, ireq
.u
.digestRequest
.serverNonce
,
719 strlen(ireq
.u
.digestRequest
.serverNonce
));
720 EVP_DigestUpdate(ctx
, ":", 1);
721 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.nonceCount
,
722 strlen(*ireq
.u
.digestRequest
.nonceCount
));
723 if (ireq
.u
.digestRequest
.authid
) {
724 EVP_DigestUpdate(ctx
, ":", 1);
725 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.authid
,
726 strlen(*ireq
.u
.digestRequest
.authid
));
728 EVP_DigestFinal_ex(ctx
, md
, NULL
);
729 hex_encode(md
, sizeof(md
), &A1
);
732 krb5_set_error_message(context
, ret
, "malloc: out of memory");
733 EVP_MD_CTX_destroy(ctx
);
737 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
738 EVP_DigestUpdate(ctx
,
739 "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
740 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.uri
,
741 strlen(*ireq
.u
.digestRequest
.uri
));
744 if (strcmp(ireq
.u
.digestRequest
.digest
, "clear") != 0) {
745 static char conf_zeros
[] = ":00000000000000000000000000000000";
746 EVP_DigestUpdate(ctx
, conf_zeros
, sizeof(conf_zeros
) - 1);
749 EVP_DigestFinal_ex(ctx
, md
, NULL
);
751 hex_encode(md
, sizeof(md
), &A2
);
754 krb5_set_error_message(context
, ret
, "malloc: out of memory");
759 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
760 EVP_DigestUpdate(ctx
, A1
, strlen(A2
));
761 EVP_DigestUpdate(ctx
, ":", 1);
762 EVP_DigestUpdate(ctx
, ireq
.u
.digestRequest
.serverNonce
,
763 strlen(ireq
.u
.digestRequest
.serverNonce
));
764 EVP_DigestUpdate(ctx
, ":", 1);
765 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.nonceCount
,
766 strlen(*ireq
.u
.digestRequest
.nonceCount
));
767 EVP_DigestUpdate(ctx
, ":", 1);
768 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.clientNonce
,
769 strlen(*ireq
.u
.digestRequest
.clientNonce
));
770 EVP_DigestUpdate(ctx
, ":", 1);
771 EVP_DigestUpdate(ctx
, *ireq
.u
.digestRequest
.qop
,
772 strlen(*ireq
.u
.digestRequest
.qop
));
773 EVP_DigestUpdate(ctx
, ":", 1);
774 EVP_DigestUpdate(ctx
, A2
, strlen(A2
));
776 EVP_DigestFinal_ex(ctx
, md
, NULL
);
778 EVP_MD_CTX_destroy(ctx
);
783 hex_encode(md
, sizeof(md
), &mdx
);
785 krb5_clear_error_message(context
);
790 r
.element
= choice_DigestRepInner_response
;
791 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
794 r
.u
.response
.success
= TRUE
;
796 kdc_log(context
, config
, 0,
797 "DIGEST-MD5 reply mismatch for %s",
798 ireq
.u
.digestRequest
.username
);
799 r
.u
.response
.success
= FALSE
;
802 } else if (strcasecmp(ireq
.u
.digestRequest
.type
, "MS-CHAP-V2") == 0) {
803 unsigned char md
[SHA_DIGEST_LENGTH
], challange
[SHA_DIGEST_LENGTH
];
804 krb5_principal clientprincipal
= NULL
;
806 const char *username
;
807 struct ntlm_buf answer
;
811 if ((config
->digests_allowed
& MS_CHAP_V2
) == 0) {
812 kdc_log(context
, config
, 0, "MS-CHAP-V2 not allowed");
816 if (ireq
.u
.digestRequest
.clientNonce
== NULL
) {
818 krb5_set_error_message(context
, ret
,
819 "MS-CHAP-V2 clientNonce missing");
822 if (serverNonce
.length
!= 16) {
824 krb5_set_error_message(context
, ret
,
825 "MS-CHAP-V2 serverNonce wrong length");
829 /* strip of the domain component */
830 username
= strchr(ireq
.u
.digestRequest
.username
, '\\');
831 if (username
== NULL
)
832 username
= ireq
.u
.digestRequest
.username
;
836 ctp
= EVP_MD_CTX_create();
839 EVP_DigestInit_ex(ctp
, EVP_sha1(), NULL
);
842 krb5_data clientNonce
;
844 clientNonce
.length
= strlen(*ireq
.u
.digestRequest
.clientNonce
);
845 clientNonce
.data
= malloc(clientNonce
.length
);
846 if (clientNonce
.data
== NULL
) {
848 krb5_set_error_message(context
, ret
,
849 "malloc: out of memory");
850 EVP_MD_CTX_destroy(ctp
);
854 ssize
= hex_decode(*ireq
.u
.digestRequest
.clientNonce
,
855 clientNonce
.data
, clientNonce
.length
);
858 krb5_set_error_message(context
, ret
,
859 "Failed to decode clientNonce");
860 EVP_MD_CTX_destroy(ctp
);
863 EVP_DigestUpdate(ctp
, clientNonce
.data
, ssize
);
864 free(clientNonce
.data
);
866 EVP_DigestUpdate(ctp
, serverNonce
.data
, serverNonce
.length
);
867 EVP_DigestUpdate(ctp
, username
, strlen(username
));
869 EVP_DigestFinal_ex(ctp
, challange
, NULL
);
871 EVP_MD_CTX_destroy(ctp
);
874 ret
= krb5_parse_name(context
, username
, &clientprincipal
);
878 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
879 HDB_F_GET_CLIENT
, NULL
, NULL
, &user
);
880 krb5_free_principal(context
, clientprincipal
);
882 krb5_set_error_message(context
, ret
,
883 "MS-CHAP-V2 user %s not in database",
888 ret
= hdb_enctype2key(context
, &user
->entry
,
889 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
891 krb5_set_error_message(context
, ret
,
892 "MS-CHAP-V2 missing arcfour key %s",
897 /* ChallengeResponse */
898 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
899 key
->key
.keyvalue
.length
,
902 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
906 hex_encode(answer
.data
, answer
.length
, &mdx
);
909 krb5_clear_error_message(context
);
914 r
.element
= choice_DigestRepInner_response
;
915 ret
= strcasecmp(mdx
, ireq
.u
.digestRequest
.responseData
);
917 r
.u
.response
.success
= TRUE
;
919 kdc_log(context
, config
, 0,
920 "MS-CHAP-V2 hash mismatch for %s",
921 ireq
.u
.digestRequest
.username
);
922 r
.u
.response
.success
= FALSE
;
926 if (r
.u
.response
.success
) {
927 unsigned char hashhash
[MD4_DIGEST_LENGTH
];
930 ctxp
= EVP_MD_CTX_create();
934 EVP_DigestInit_ex(ctxp
, EVP_md4(), NULL
);
935 EVP_DigestUpdate(ctxp
,
936 key
->key
.keyvalue
.data
,
937 key
->key
.keyvalue
.length
);
938 EVP_DigestFinal_ex(ctxp
, hashhash
, NULL
);
941 /* GenerateAuthenticatorResponse */
942 EVP_DigestInit_ex(ctxp
, EVP_sha1(), NULL
);
943 EVP_DigestUpdate(ctxp
, hashhash
, sizeof(hashhash
));
944 EVP_DigestUpdate(ctxp
, answer
.data
, answer
.length
);
945 EVP_DigestUpdate(ctxp
, ms_chap_v2_magic1
,
946 sizeof(ms_chap_v2_magic1
));
947 EVP_DigestFinal_ex(ctxp
, md
, NULL
);
949 EVP_DigestInit_ex(ctxp
, EVP_sha1(), NULL
);
950 EVP_DigestUpdate(ctxp
, md
, sizeof(md
));
951 EVP_DigestUpdate(ctxp
, challange
, 8);
952 EVP_DigestUpdate(ctxp
, ms_chap_v2_magic2
,
953 sizeof(ms_chap_v2_magic2
));
954 EVP_DigestFinal_ex(ctxp
, md
, NULL
);
956 r
.u
.response
.rsp
= calloc(1, sizeof(*r
.u
.response
.rsp
));
957 if (r
.u
.response
.rsp
== NULL
) {
959 krb5_clear_error_message(context
);
960 EVP_MD_CTX_destroy(ctxp
);
965 hex_encode(md
, sizeof(md
), r
.u
.response
.rsp
);
966 if (r
.u
.response
.rsp
== NULL
) {
968 krb5_clear_error_message(context
);
969 EVP_MD_CTX_destroy(ctxp
);
974 /* get_master, rfc 3079 3.4 */
975 EVP_DigestInit_ex(ctxp
, EVP_sha1(), NULL
);
976 EVP_DigestUpdate(ctxp
, hashhash
, 16);
977 EVP_DigestUpdate(ctxp
, answer
.data
, answer
.length
);
978 EVP_DigestUpdate(ctxp
, ms_rfc3079_magic1
,
979 sizeof(ms_rfc3079_magic1
));
980 EVP_DigestFinal_ex(ctxp
, md
, NULL
);
984 EVP_MD_CTX_destroy(ctxp
);
986 r
.u
.response
.session_key
=
987 calloc(1, sizeof(*r
.u
.response
.session_key
));
988 if (r
.u
.response
.session_key
== NULL
) {
989 krb5_clear_error_message(context
);
994 ret
= krb5_data_copy(r
.u
.response
.session_key
, md
, 16);
996 krb5_clear_error_message(context
);
1002 r
.element
= choice_DigestRepInner_error
;
1003 asprintf(&r
.u
.error
.reason
, "Unsupported digest type %s",
1004 ireq
.u
.digestRequest
.type
);
1005 if (r
.u
.error
.reason
== NULL
) {
1007 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1010 r
.u
.error
.code
= EINVAL
;
1013 kdc_log(context
, config
, 0, "Digest %s request successful %s",
1014 ireq
.u
.digestRequest
.type
, ireq
.u
.digestRequest
.username
);
1018 case choice_DigestReqInner_ntlmInit
:
1020 if ((config
->digests_allowed
& (NTLM_V1
|NTLM_V1_SESSION
|NTLM_V2
)) == 0) {
1021 kdc_log(context
, config
, 0, "NTLM not allowed");
1025 r
.element
= choice_DigestRepInner_ntlmInitReply
;
1027 r
.u
.ntlmInitReply
.flags
= NTLM_NEG_UNICODE
;
1029 if ((ireq
.u
.ntlmInit
.flags
& NTLM_NEG_UNICODE
) == 0) {
1030 kdc_log(context
, config
, 0, "NTLM client have no unicode");
1034 if (ireq
.u
.ntlmInit
.flags
& NTLM_NEG_NTLM
)
1035 r
.u
.ntlmInitReply
.flags
|= NTLM_NEG_NTLM
;
1037 kdc_log(context
, config
, 0, "NTLM client doesn't support NTLM");
1041 r
.u
.ntlmInitReply
.flags
|=
1043 NTLM_TARGET_DOMAIN
|
1049 NTLM_NEG_ALWAYS_SIGN| \
1050 NTLM_NEG_NTLM2_SESSION| \
1053 r
.u
.ntlmInitReply
.flags
|= (ireq
.u
.ntlmInit
.flags
& (ALL
));
1057 r
.u
.ntlmInitReply
.targetname
=
1058 get_ntlm_targetname(context
, client
);
1059 if (r
.u
.ntlmInitReply
.targetname
== NULL
) {
1061 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1064 r
.u
.ntlmInitReply
.challange
.data
= malloc(8);
1065 if (r
.u
.ntlmInitReply
.challange
.data
== NULL
) {
1067 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1070 r
.u
.ntlmInitReply
.challange
.length
= 8;
1071 if (RAND_bytes(r
.u
.ntlmInitReply
.challange
.data
,
1072 r
.u
.ntlmInitReply
.challange
.length
) != 1)
1075 krb5_set_error_message(context
, ret
, "out of random error");
1078 /* XXX fix targetinfo */
1079 ALLOC(r
.u
.ntlmInitReply
.targetinfo
);
1080 if (r
.u
.ntlmInitReply
.targetinfo
== NULL
) {
1082 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1086 ret
= fill_targetinfo(context
,
1087 r
.u
.ntlmInitReply
.targetname
,
1089 r
.u
.ntlmInitReply
.targetinfo
);
1092 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1097 * Save data encryted in opaque for the second part of the
1098 * ntlm authentication
1100 sp
= krb5_storage_emem();
1103 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1107 ret
= krb5_storage_write(sp
, r
.u
.ntlmInitReply
.challange
.data
, 8);
1110 krb5_set_error_message(context
, ret
, "storage write challange");
1113 ret
= krb5_store_uint32(sp
, r
.u
.ntlmInitReply
.flags
);
1115 krb5_clear_error_message(context
);
1119 ret
= krb5_storage_to_data(sp
, &buf
);
1121 krb5_clear_error_message(context
);
1125 ret
= get_digest_key(context
, config
, server
, &crypto
);
1129 ret
= krb5_encrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1130 buf
.data
, buf
.length
, &r
.u
.ntlmInitReply
.opaque
);
1131 krb5_data_free(&buf
);
1132 krb5_crypto_destroy(context
, crypto
);
1137 kdc_log(context
, config
, 0, "NTLM init from %s", from
);
1141 case choice_DigestReqInner_ntlmRequest
: {
1142 krb5_principal clientprincipal
;
1143 unsigned char sessionkey
[16];
1144 unsigned char challange
[8];
1149 r
.element
= choice_DigestRepInner_ntlmResponse
;
1150 r
.u
.ntlmResponse
.success
= 0;
1151 r
.u
.ntlmResponse
.flags
= 0;
1152 r
.u
.ntlmResponse
.sessionkey
= NULL
;
1153 r
.u
.ntlmResponse
.tickets
= NULL
;
1156 ret
= krb5_parse_name(context
,
1157 ireq
.u
.ntlmRequest
.username
,
1162 ret
= _kdc_db_fetch(context
, config
, clientprincipal
,
1163 HDB_F_GET_CLIENT
, NULL
, NULL
, &user
);
1164 krb5_free_principal(context
, clientprincipal
);
1166 krb5_set_error_message(context
, ret
, "NTLM user %s not in database",
1167 ireq
.u
.ntlmRequest
.username
);
1171 ret
= get_digest_key(context
, config
, server
, &crypto
);
1175 ret
= krb5_decrypt(context
, crypto
, KRB5_KU_DIGEST_OPAQUE
,
1176 ireq
.u
.ntlmRequest
.opaque
.data
,
1177 ireq
.u
.ntlmRequest
.opaque
.length
, &buf
);
1178 krb5_crypto_destroy(context
, crypto
);
1181 kdc_log(context
, config
, 0,
1182 "Failed to decrypt nonce from %s", from
);
1186 sp
= krb5_storage_from_data(&buf
);
1189 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1193 ret
= krb5_storage_read(sp
, challange
, sizeof(challange
));
1194 if (ret
!= sizeof(challange
)) {
1196 krb5_set_error_message(context
, ret
, "NTLM storage read challange");
1199 ret
= krb5_ret_uint32(sp
, &flags
);
1201 krb5_set_error_message(context
, ret
, "NTLM storage read flags");
1204 krb5_storage_free(sp
);
1206 krb5_data_free(&buf
);
1208 if ((flags
& NTLM_NEG_NTLM
) == 0) {
1210 krb5_set_error_message(context
, ret
, "NTLM not negotiated");
1214 ret
= hdb_enctype2key(context
, &user
->entry
,
1215 ETYPE_ARCFOUR_HMAC_MD5
, &key
);
1217 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
1221 /* check if this is NTLMv2 */
1222 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= 24) {
1223 struct ntlm_buf infotarget
, answer
;
1226 if ((config
->digests_allowed
& NTLM_V2
) == 0) {
1227 kdc_log(context
, config
, 0, "NTLM v2 not allowed");
1233 targetname
= get_ntlm_targetname(context
, client
);
1234 if (targetname
== NULL
) {
1236 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1240 answer
.length
= ireq
.u
.ntlmRequest
.ntlm
.length
;
1241 answer
.data
= ireq
.u
.ntlmRequest
.ntlm
.data
;
1243 ret
= heim_ntlm_verify_ntlm2(key
->key
.keyvalue
.data
,
1244 key
->key
.keyvalue
.length
,
1245 ireq
.u
.ntlmRequest
.username
,
1254 krb5_set_error_message(context
, ret
, "NTLM v2 verify failed");
1258 /* XXX verify infotarget matches client (checksum ?) */
1260 free(infotarget
.data
);
1264 struct ntlm_buf answer
;
1268 if (flags
& NTLM_NEG_NTLM2_SESSION
) {
1269 unsigned char sessionhash
[MD5_DIGEST_LENGTH
];
1272 if ((config
->digests_allowed
& NTLM_V1_SESSION
) == 0) {
1273 kdc_log(context
, config
, 0, "NTLM v1-session not allowed");
1278 if (ireq
.u
.ntlmRequest
.lm
.length
!= 24) {
1280 krb5_set_error_message(context
, ret
, "LM hash have wrong length "
1281 "for NTLM session key");
1285 ctx
= EVP_MD_CTX_create();
1287 EVP_DigestInit_ex(ctx
, EVP_md5(), NULL
);
1289 EVP_DigestUpdate(ctx
, challange
, sizeof(challange
));
1290 EVP_DigestUpdate(ctx
, ireq
.u
.ntlmRequest
.lm
.data
, 8);
1291 EVP_DigestFinal_ex(ctx
, sessionhash
, NULL
);
1292 memcpy(challange
, sessionhash
, sizeof(challange
));
1294 EVP_MD_CTX_destroy(ctx
);
1297 if ((config
->digests_allowed
& NTLM_V1
) == 0) {
1298 kdc_log(context
, config
, 0, "NTLM v1 not allowed");
1303 ret
= heim_ntlm_calculate_ntlm1(key
->key
.keyvalue
.data
,
1304 key
->key
.keyvalue
.length
,
1305 challange
, &answer
);
1307 krb5_set_error_message(context
, ret
, "NTLM missing arcfour key");
1311 if (ireq
.u
.ntlmRequest
.ntlm
.length
!= answer
.length
||
1312 memcmp(ireq
.u
.ntlmRequest
.ntlm
.data
, answer
.data
, answer
.length
) != 0)
1316 krb5_set_error_message(context
, ret
, "NTLM hash mismatch");
1324 ctx
= EVP_MD_CTX_create();
1326 EVP_DigestInit_ex(ctx
, EVP_md4(), NULL
);
1327 EVP_DigestUpdate(ctx
,
1328 key
->key
.keyvalue
.data
,
1329 key
->key
.keyvalue
.length
);
1330 EVP_DigestFinal_ex(ctx
, sessionkey
, NULL
);
1332 EVP_MD_CTX_destroy(ctx
);
1336 if (ireq
.u
.ntlmRequest
.sessionkey
) {
1337 unsigned char masterkey
[MD4_DIGEST_LENGTH
];
1341 if ((flags
& NTLM_NEG_KEYEX
) == 0) {
1343 krb5_set_error_message(context
, ret
,
1344 "NTLM client failed to neg key "
1345 "exchange but still sent key");
1349 len
= ireq
.u
.ntlmRequest
.sessionkey
->length
;
1350 if (len
!= sizeof(masterkey
)){
1352 krb5_set_error_message(context
, ret
,
1353 "NTLM master key wrong length: %lu",
1354 (unsigned long)len
);
1359 EVP_CIPHER_CTX_init(&rc4
);
1360 EVP_CipherInit_ex(&rc4
, EVP_rc4(), NULL
, sessionkey
, NULL
, 1);
1362 masterkey
, ireq
.u
.ntlmRequest
.sessionkey
->data
,
1364 EVP_CIPHER_CTX_cleanup(&rc4
);
1366 r
.u
.ntlmResponse
.sessionkey
=
1367 malloc(sizeof(*r
.u
.ntlmResponse
.sessionkey
));
1368 if (r
.u
.ntlmResponse
.sessionkey
== NULL
) {
1370 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1374 ret
= krb5_data_copy(r
.u
.ntlmResponse
.sessionkey
,
1375 masterkey
, sizeof(masterkey
));
1377 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1382 r
.u
.ntlmResponse
.success
= 1;
1383 kdc_log(context
, config
, 0, "NTLM version %d successful for %s",
1384 version
, ireq
.u
.ntlmRequest
.username
);
1387 case choice_DigestReqInner_supportedMechs
:
1389 kdc_log(context
, config
, 0, "digest supportedMechs from %s", from
);
1391 r
.element
= choice_DigestRepInner_supportedMechs
;
1392 memset(&r
.u
.supportedMechs
, 0, sizeof(r
.u
.supportedMechs
));
1394 if (config
->digests_allowed
& NTLM_V1
)
1395 r
.u
.supportedMechs
.ntlm_v1
= 1;
1396 if (config
->digests_allowed
& NTLM_V1_SESSION
)
1397 r
.u
.supportedMechs
.ntlm_v1_session
= 1;
1398 if (config
->digests_allowed
& NTLM_V2
)
1399 r
.u
.supportedMechs
.ntlm_v2
= 1;
1400 if (config
->digests_allowed
& DIGEST_MD5
)
1401 r
.u
.supportedMechs
.digest_md5
= 1;
1402 if (config
->digests_allowed
& CHAP_MD5
)
1403 r
.u
.supportedMechs
.chap_md5
= 1;
1404 if (config
->digests_allowed
& MS_CHAP_V2
)
1405 r
.u
.supportedMechs
.ms_chap_v2
= 1;
1411 krb5_set_error_message(context
, ret
, "unknown operation to digest");
1415 s
= krb5_get_error_message(context
, ret
);
1417 krb5_clear_error_message(context
);
1421 kdc_log(context
, config
, 0, "Digest failed with: %s", s
);
1423 r
.element
= choice_DigestRepInner_error
;
1424 r
.u
.error
.reason
= strdup("unknown error");
1425 krb5_free_error_message(context
, s
);
1426 if (r
.u
.error
.reason
== NULL
) {
1428 krb5_set_error_message(context
, ret
, "malloc: out of memory");
1431 r
.u
.error
.code
= EINVAL
;
1436 ASN1_MALLOC_ENCODE(DigestRepInner
, buf
.data
, buf
.length
, &r
, &size
, ret
);
1438 krb5_set_error_message(context
, ret
, "Failed to encode inner digest reply");
1441 if (size
!= buf
.length
)
1442 krb5_abortx(context
, "ASN1 internal error");
1444 krb5_auth_con_addflags(context
, ac
, KRB5_AUTH_CONTEXT_USE_SUBKEY
, NULL
);
1446 ret
= krb5_mk_rep (context
, ac
, &rep
.apRep
);
1453 ret
= krb5_auth_con_getlocalsubkey(context
, ac
, &key
);
1457 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
1458 krb5_free_keyblock (context
, key
);
1463 ret
= krb5_encrypt_EncryptedData(context
, crypto
, KRB5_KU_DIGEST_ENCRYPT
,
1464 buf
.data
, buf
.length
, 0,
1467 ASN1_MALLOC_ENCODE(DigestREP
, reply
->data
, reply
->length
, &rep
, &size
, ret
);
1469 krb5_set_error_message(context
, ret
, "Failed to encode digest reply");
1472 if (size
!= reply
->length
)
1473 krb5_abortx(context
, "ASN1 internal error");
1478 krb5_auth_con_free(context
, ac
);
1480 krb5_warn(context
, ret
, "Digest request from %s failed", from
);
1482 krb5_free_ticket(context
, ticket
);
1484 krb5_kt_close(context
, id
);
1486 krb5_crypto_destroy(context
, crypto
);
1488 krb5_storage_free(sp
);
1490 _kdc_free_ent (context
, user
);
1492 _kdc_free_ent (context
, server
);
1494 _kdc_free_ent (context
, client
);
1496 memset(password
, 0, strlen(password
));
1501 krb5_data_free(&buf
);
1502 krb5_data_free(&serverNonce
);
1503 free_Checksum(&res
);
1504 free_DigestREP(&rep
);
1505 free_DigestRepInner(&r
);
1506 free_DigestReqInner(&ireq
);