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 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));
238
kdc_log(context, config, 0, "Digest request from %s", from);
240
ret = krb5_kt_resolve(context, "HDB:", &id);
242
kdc_log(context, config, 0, "Can't open database for digest");
246
ret = krb5_rd_req(context,
256
/* check the server principal in the ticket matches digest/R@R */
258
krb5_principal principal = NULL;
261
ret = krb5_ticket_get_server(context, ticket, &principal);
266
krb5_set_error_message(context, ret, "Wrong digest server principal used");
267
p = krb5_principal_get_comp_string(context, principal, 0);
269
krb5_free_principal(context, principal);
272
if (strcmp(p, KRB5_DIGEST_NAME) != 0) {
273
krb5_free_principal(context, principal);
277
p = krb5_principal_get_comp_string(context, principal, 1);
279
krb5_free_principal(context, principal);
282
r = krb5_principal_get_realm(context, principal);
284
krb5_free_principal(context, principal);
287
if (strcmp(p, r) != 0) {
288
krb5_free_principal(context, principal);
291
krb5_clear_error_message(context);
293
ret = _kdc_db_fetch(context, config, principal,
294
HDB_F_GET_SERVER, NULL, &server);
298
krb5_free_principal(context, principal);
301
/* check the client is allowed to do digest auth */
303
krb5_principal principal = NULL;
305
ret = krb5_ticket_get_client(context, ticket, &principal);
309
ret = krb5_unparse_name(context, principal, &client_name);
311
krb5_free_principal(context, principal);
315
ret = _kdc_db_fetch(context, config, principal,
316
HDB_F_GET_CLIENT, NULL, &client);
317
krb5_free_principal(context, principal);
321
if (client->entry.flags.allow_digest == 0) {
322
kdc_log(context, config, 0,
323
"Client %s tried to use digest "
324
"but is not allowed to",
326
ret = KRB5KDC_ERR_POLICY;
327
krb5_set_error_message(context, ret,
328
"Client is not permitted to use digest");
337
ret = krb5_auth_con_getremotesubkey(context, ac, &key);
342
krb5_set_error_message(context, ret, "digest: remote subkey not found");
346
ret = krb5_crypto_init(context, key, 0, &crypto);
347
krb5_free_keyblock (context, key);
352
ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
353
&req->innerReq, &buf);
354
krb5_crypto_destroy(context, crypto);
359
ret = decode_DigestReqInner(buf.data, buf.length, &ireq, NULL);
360
krb5_data_free(&buf);
362
krb5_set_error_message(context, ret, "Failed to decode digest inner request");
366
kdc_log(context, config, 0, "Valid digest request from %s (%s)",
370
* Process the inner request
373
switch (ireq.element) {
374
case choice_DigestReqInner_init: {
375
unsigned char server_nonce[16], identifier;
377
RAND_pseudo_bytes(&identifier, sizeof(identifier));
378
RAND_pseudo_bytes(server_nonce, sizeof(server_nonce));
380
server_nonce[0] = kdc_time & 0xff;
381
server_nonce[1] = (kdc_time >> 8) & 0xff;
382
server_nonce[2] = (kdc_time >> 16) & 0xff;
383
server_nonce[3] = (kdc_time >> 24) & 0xff;
385
r.element = choice_DigestRepInner_initReply;
387
hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce);
388
if (r.u.initReply.nonce == NULL) {
390
krb5_set_error_message(context, ret, "Failed to decode server nonce");
394
sp = krb5_storage_emem();
397
krb5_set_error_message(context, ret, "malloc: out of memory");
400
ret = krb5_store_stringz(sp, ireq.u.init.type);
402
krb5_clear_error_message(context);
406
if (ireq.u.init.channel) {
409
asprintf(&s, "%s-%s:%s", r.u.initReply.nonce,
410
ireq.u.init.channel->cb_type,
411
ireq.u.init.channel->cb_binding);
414
krb5_set_error_message(context, ret,
415
"Failed to allocate channel binding");
418
free(r.u.initReply.nonce);
419
r.u.initReply.nonce = s;
422
ret = krb5_store_stringz(sp, r.u.initReply.nonce);
424
krb5_clear_error_message(context);
428
if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
429
r.u.initReply.identifier =
430
malloc(sizeof(*r.u.initReply.identifier));
431
if (r.u.initReply.identifier == NULL) {
433
krb5_set_error_message(context, ret, "malloc: out of memory");
437
asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff);
438
if (*r.u.initReply.identifier == NULL) {
440
krb5_set_error_message(context, ret, "malloc: out of memory");
445
r.u.initReply.identifier = NULL;
447
if (ireq.u.init.hostname) {
448
ret = krb5_store_stringz(sp, *ireq.u.init.hostname);
450
krb5_clear_error_message(context);
455
ret = krb5_storage_to_data(sp, &buf);
457
krb5_clear_error_message(context);
461
ret = get_digest_key(context, config, server, &crypto);
465
ret = krb5_create_checksum(context,
467
KRB5_KU_DIGEST_OPAQUE,
472
krb5_crypto_destroy(context, crypto);
474
krb5_data_free(&buf);
478
ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret);
481
krb5_set_error_message(context, ret, "Failed to encode "
482
"checksum in digest request");
485
if (size != buf.length)
486
krb5_abortx(context, "ASN1 internal error");
488
hex_encode(buf.data, buf.length, &r.u.initReply.opaque);
490
if (r.u.initReply.opaque == NULL) {
491
krb5_clear_error_message(context);
496
kdc_log(context, config, 0, "Digest %s init request successful from %s",
497
ireq.u.init.type, from);
501
case choice_DigestReqInner_digestRequest: {
502
sp = krb5_storage_emem();
505
krb5_set_error_message(context, ret, "malloc: out of memory");
508
ret = krb5_store_stringz(sp, ireq.u.digestRequest.type);
510
krb5_clear_error_message(context);
514
krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
516
if (ireq.u.digestRequest.hostname) {
517
ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname);
519
krb5_clear_error_message(context);
524
buf.length = strlen(ireq.u.digestRequest.opaque);
525
buf.data = malloc(buf.length);
526
if (buf.data == NULL) {
528
krb5_set_error_message(context, ret, "malloc: out of memory");
532
ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
535
krb5_set_error_message(context, ret, "Failed to decode opaque");
540
ret = decode_Checksum(buf.data, buf.length, &res, NULL);
543
krb5_set_error_message(context, ret, "Failed to decode digest Checksum");
547
ret = krb5_storage_to_data(sp, &buf);
549
krb5_clear_error_message(context);
553
serverNonce.length = strlen(ireq.u.digestRequest.serverNonce);
554
serverNonce.data = malloc(serverNonce.length);
555
if (serverNonce.data == NULL) {
557
krb5_set_error_message(context, ret, "malloc: out of memory");
562
* CHAP does the checksum of the raw nonce, but do it for all
563
* types, since we need to check the timestamp.
568
ssize = hex_decode(ireq.u.digestRequest.serverNonce,
569
serverNonce.data, serverNonce.length);
572
krb5_set_error_message(context, ret, "Failed to decode serverNonce");
575
serverNonce.length = ssize;
578
ret = get_digest_key(context, config, server, &crypto);
582
ret = krb5_verify_checksum(context, crypto,
583
KRB5_KU_DIGEST_OPAQUE,
584
buf.data, buf.length, &res);
585
krb5_crypto_destroy(context, crypto);
592
unsigned char *p = serverNonce.data;
595
if (serverNonce.length < 4) {
597
krb5_set_error_message(context, ret, "server nonce too short");
600
t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
602
if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) {
604
krb5_set_error_message(context, ret, "time screw in server nonce ");
609
if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) {
611
unsigned char md[MD5_DIGEST_LENGTH];
615
if ((config->digests_allowed & CHAP_MD5) == 0) {
616
kdc_log(context, config, 0, "Digest CHAP MD5 not allowed");
620
if (ireq.u.digestRequest.identifier == NULL) {
622
krb5_set_error_message(context, ret, "Identifier missing "
623
"from CHAP request");
627
if (hex_decode(*ireq.u.digestRequest.identifier, &id, 1) != 1) {
629
krb5_set_error_message(context, ret, "failed to decode identifier");
633
ret = get_password_entry(context, config,
634
ireq.u.digestRequest.username,
640
MD5_Update(&ctx, &id, 1);
641
MD5_Update(&ctx, password, strlen(password));
642
MD5_Update(&ctx, serverNonce.data, serverNonce.length);
645
hex_encode(md, sizeof(md), &mdx);
647
krb5_clear_error_message(context);
652
r.element = choice_DigestRepInner_response;
654
ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
657
r.u.response.success = TRUE;
659
kdc_log(context, config, 0,
660
"CHAP reply mismatch for %s",
661
ireq.u.digestRequest.username);
662
r.u.response.success = FALSE;
665
} else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
667
unsigned char md[MD5_DIGEST_LENGTH];
671
if ((config->digests_allowed & DIGEST_MD5) == 0) {
672
kdc_log(context, config, 0, "Digest SASL MD5 not allowed");
676
if (ireq.u.digestRequest.nonceCount == NULL)
678
if (ireq.u.digestRequest.clientNonce == NULL)
680
if (ireq.u.digestRequest.qop == NULL)
682
if (ireq.u.digestRequest.realm == NULL)
685
ret = get_password_entry(context, config,
686
ireq.u.digestRequest.username,
692
MD5_Update(&ctx, ireq.u.digestRequest.username,
693
strlen(ireq.u.digestRequest.username));
694
MD5_Update(&ctx, ":", 1);
695
MD5_Update(&ctx, *ireq.u.digestRequest.realm,
696
strlen(*ireq.u.digestRequest.realm));
697
MD5_Update(&ctx, ":", 1);
698
MD5_Update(&ctx, password, strlen(password));
702
MD5_Update(&ctx, md, sizeof(md));
703
MD5_Update(&ctx, ":", 1);
704
MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
705
strlen(ireq.u.digestRequest.serverNonce));
706
MD5_Update(&ctx, ":", 1);
707
MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
708
strlen(*ireq.u.digestRequest.nonceCount));
709
if (ireq.u.digestRequest.authid) {
710
MD5_Update(&ctx, ":", 1);
711
MD5_Update(&ctx, *ireq.u.digestRequest.authid,
712
strlen(*ireq.u.digestRequest.authid));
715
hex_encode(md, sizeof(md), &A1);
718
krb5_set_error_message(context, ret, "malloc: out of memory");
723
MD5_Update(&ctx, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
724
MD5_Update(&ctx, *ireq.u.digestRequest.uri,
725
strlen(*ireq.u.digestRequest.uri));
728
if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
729
static char conf_zeros[] = ":00000000000000000000000000000000";
730
MD5_Update(&ctx, conf_zeros, sizeof(conf_zeros) - 1);
734
hex_encode(md, sizeof(md), &A2);
737
krb5_set_error_message(context, ret, "malloc: out of memory");
743
MD5_Update(&ctx, A1, strlen(A2));
744
MD5_Update(&ctx, ":", 1);
745
MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
746
strlen(ireq.u.digestRequest.serverNonce));
747
MD5_Update(&ctx, ":", 1);
748
MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
749
strlen(*ireq.u.digestRequest.nonceCount));
750
MD5_Update(&ctx, ":", 1);
751
MD5_Update(&ctx, *ireq.u.digestRequest.clientNonce,
752
strlen(*ireq.u.digestRequest.clientNonce));
753
MD5_Update(&ctx, ":", 1);
754
MD5_Update(&ctx, *ireq.u.digestRequest.qop,
755
strlen(*ireq.u.digestRequest.qop));
756
MD5_Update(&ctx, ":", 1);
757
MD5_Update(&ctx, A2, strlen(A2));
764
hex_encode(md, sizeof(md), &mdx);
766
krb5_clear_error_message(context);
771
r.element = choice_DigestRepInner_response;
772
ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
775
r.u.response.success = TRUE;
777
kdc_log(context, config, 0,
778
"DIGEST-MD5 reply mismatch for %s",
779
ireq.u.digestRequest.username);
780
r.u.response.success = FALSE;
783
} else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) {
784
unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH];
785
krb5_principal clientprincipal = NULL;
787
const char *username;
788
struct ntlm_buf answer;
792
if ((config->digests_allowed & MS_CHAP_V2) == 0) {
793
kdc_log(context, config, 0, "MS-CHAP-V2 not allowed");
797
if (ireq.u.digestRequest.clientNonce == NULL) {
799
krb5_set_error_message(context, ret,
800
"MS-CHAP-V2 clientNonce missing");
803
if (serverNonce.length != 16) {
805
krb5_set_error_message(context, ret,
806
"MS-CHAP-V2 serverNonce wrong length");
810
/* strip of the domain component */
811
username = strchr(ireq.u.digestRequest.username, '\\');
812
if (username == NULL)
813
username = ireq.u.digestRequest.username;
821
krb5_data clientNonce;
823
clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce);
824
clientNonce.data = malloc(clientNonce.length);
825
if (clientNonce.data == NULL) {
827
krb5_set_error_message(context, ret, "malloc: out of memory");
831
ssize = hex_decode(*ireq.u.digestRequest.clientNonce,
832
clientNonce.data, clientNonce.length);
835
krb5_set_error_message(context, ret,
836
"Failed to decode clientNonce");
839
SHA1_Update(&ctx, clientNonce.data, ssize);
840
free(clientNonce.data);
842
SHA1_Update(&ctx, serverNonce.data, serverNonce.length);
843
SHA1_Update(&ctx, username, strlen(username));
844
SHA1_Final(challange, &ctx);
847
ret = krb5_parse_name(context, username, &clientprincipal);
851
ret = _kdc_db_fetch(context, config, clientprincipal,
852
HDB_F_GET_CLIENT, NULL, &user);
853
krb5_free_principal(context, clientprincipal);
855
krb5_set_error_message(context, ret,
856
"MS-CHAP-V2 user %s not in database",
861
ret = hdb_enctype2key(context, &user->entry,
862
ETYPE_ARCFOUR_HMAC_MD5, &key);
864
krb5_set_error_message(context, ret,
865
"MS-CHAP-V2 missing arcfour key %s",
870
/* ChallengeResponse */
871
ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
872
key->key.keyvalue.length,
875
krb5_set_error_message(context, ret, "NTLM missing arcfour key");
879
hex_encode(answer.data, answer.length, &mdx);
882
krb5_clear_error_message(context);
887
r.element = choice_DigestRepInner_response;
888
ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
890
r.u.response.success = TRUE;
892
kdc_log(context, config, 0,
893
"MS-CHAP-V2 hash mismatch for %s",
894
ireq.u.digestRequest.username);
895
r.u.response.success = FALSE;
899
if (r.u.response.success) {
900
unsigned char hashhash[MD4_DIGEST_LENGTH];
907
MD4_Update(&hctx, key->key.keyvalue.data,
908
key->key.keyvalue.length);
909
MD4_Final(hashhash, &hctx);
912
/* GenerateAuthenticatorResponse */
914
SHA1_Update(&ctx, hashhash, sizeof(hashhash));
915
SHA1_Update(&ctx, answer.data, answer.length);
916
SHA1_Update(&ctx, ms_chap_v2_magic1,sizeof(ms_chap_v2_magic1));
917
SHA1_Final(md, &ctx);
920
SHA1_Update(&ctx, md, sizeof(md));
921
SHA1_Update(&ctx, challange, 8);
922
SHA1_Update(&ctx, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2));
923
SHA1_Final(md, &ctx);
925
r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp));
926
if (r.u.response.rsp == NULL) {
928
krb5_clear_error_message(context);
933
hex_encode(md, sizeof(md), r.u.response.rsp);
934
if (r.u.response.rsp == NULL) {
936
krb5_clear_error_message(context);
941
/* get_master, rfc 3079 3.4 */
943
SHA1_Update(&ctx, hashhash, 16); /* md4(hash) */
944
SHA1_Update(&ctx, answer.data, answer.length);
945
SHA1_Update(&ctx, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1));
946
SHA1_Final(md, &ctx);
950
r.u.response.session_key =
951
calloc(1, sizeof(*r.u.response.session_key));
952
if (r.u.response.session_key == NULL) {
953
krb5_clear_error_message(context);
958
ret = krb5_data_copy(r.u.response.session_key, md, 16);
960
krb5_clear_error_message(context);
966
r.element = choice_DigestRepInner_error;
967
asprintf(&r.u.error.reason, "Unsupported digest type %s",
968
ireq.u.digestRequest.type);
969
if (r.u.error.reason == NULL) {
971
krb5_set_error_message(context, ret, "malloc: out of memory");
974
r.u.error.code = EINVAL;
977
kdc_log(context, config, 0, "Digest %s request successful %s",
978
ireq.u.digestRequest.type, ireq.u.digestRequest.username);
982
case choice_DigestReqInner_ntlmInit:
984
if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) {
985
kdc_log(context, config, 0, "NTLM not allowed");
989
r.element = choice_DigestRepInner_ntlmInitReply;
991
r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE;
993
if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) {
994
kdc_log(context, config, 0, "NTLM client have no unicode");
998
if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM)
999
r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM;
1001
kdc_log(context, config, 0, "NTLM client doesn't support NTLM");
1005
r.u.ntlmInitReply.flags |=
1007
NTLM_TARGET_DOMAIN |
1013
NTLM_NEG_ALWAYS_SIGN| \
1014
NTLM_NEG_NTLM2_SESSION| \
1017
r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
1021
r.u.ntlmInitReply.targetname =
1022
get_ntlm_targetname(context, client);
1023
if (r.u.ntlmInitReply.targetname == NULL) {
1025
krb5_set_error_message(context, ret, "malloc: out of memory");
1028
r.u.ntlmInitReply.challange.data = malloc(8);
1029
if (r.u.ntlmInitReply.challange.data == NULL) {
1031
krb5_set_error_message(context, ret, "malloc: out of memory");
1034
r.u.ntlmInitReply.challange.length = 8;
1035
if (RAND_bytes(r.u.ntlmInitReply.challange.data,
1036
r.u.ntlmInitReply.challange.length) != 1)
1039
krb5_set_error_message(context, ret, "out of random error");
1042
/* XXX fix targetinfo */
1043
ALLOC(r.u.ntlmInitReply.targetinfo);
1044
if (r.u.ntlmInitReply.targetinfo == NULL) {
1046
krb5_set_error_message(context, ret, "malloc: out of memory");
1050
ret = fill_targetinfo(context,
1051
r.u.ntlmInitReply.targetname,
1053
r.u.ntlmInitReply.targetinfo);
1056
krb5_set_error_message(context, ret, "malloc: out of memory");
1061
* Save data encryted in opaque for the second part of the
1062
* ntlm authentication
1064
sp = krb5_storage_emem();
1067
krb5_set_error_message(context, ret, "malloc: out of memory");
1071
ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8);
1074
krb5_set_error_message(context, ret, "storage write challange");
1077
ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
1079
krb5_clear_error_message(context);
1083
ret = krb5_storage_to_data(sp, &buf);
1085
krb5_clear_error_message(context);
1089
ret = get_digest_key(context, config, server, &crypto);
1093
ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1094
buf.data, buf.length, &r.u.ntlmInitReply.opaque);
1095
krb5_data_free(&buf);
1096
krb5_crypto_destroy(context, crypto);
1101
kdc_log(context, config, 0, "NTLM init from %s", from);
1105
case choice_DigestReqInner_ntlmRequest: {
1106
krb5_principal clientprincipal;
1107
unsigned char sessionkey[16];
1108
unsigned char challange[8];
1113
r.element = choice_DigestRepInner_ntlmResponse;
1114
r.u.ntlmResponse.success = 0;
1115
r.u.ntlmResponse.flags = 0;
1116
r.u.ntlmResponse.sessionkey = NULL;
1117
r.u.ntlmResponse.tickets = NULL;
1120
ret = krb5_parse_name(context,
1121
ireq.u.ntlmRequest.username,
1126
ret = _kdc_db_fetch(context, config, clientprincipal,
1127
HDB_F_GET_CLIENT, NULL, &user);
1128
krb5_free_principal(context, clientprincipal);
1130
krb5_set_error_message(context, ret, "NTLM user %s not in database",
1131
ireq.u.ntlmRequest.username);
1135
ret = get_digest_key(context, config, server, &crypto);
1139
ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1140
ireq.u.ntlmRequest.opaque.data,
1141
ireq.u.ntlmRequest.opaque.length, &buf);
1142
krb5_crypto_destroy(context, crypto);
1145
kdc_log(context, config, 0,
1146
"Failed to decrypt nonce from %s", from);
1150
sp = krb5_storage_from_data(&buf);
1153
krb5_set_error_message(context, ret, "malloc: out of memory");
1157
ret = krb5_storage_read(sp, challange, sizeof(challange));
1158
if (ret != sizeof(challange)) {
1160
krb5_set_error_message(context, ret, "NTLM storage read challange");
1163
ret = krb5_ret_uint32(sp, &flags);
1165
krb5_set_error_message(context, ret, "NTLM storage read flags");
1168
krb5_data_free(&buf);
1170
if ((flags & NTLM_NEG_NTLM) == 0) {
1172
krb5_set_error_message(context, ret, "NTLM not negotiated");
1176
ret = hdb_enctype2key(context, &user->entry,
1177
ETYPE_ARCFOUR_HMAC_MD5, &key);
1179
krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1183
/* check if this is NTLMv2 */
1184
if (ireq.u.ntlmRequest.ntlm.length != 24) {
1185
struct ntlm_buf infotarget, answer;
1188
if ((config->digests_allowed & NTLM_V2) == 0) {
1189
kdc_log(context, config, 0, "NTLM v2 not allowed");
1195
targetname = get_ntlm_targetname(context, client);
1196
if (targetname == NULL) {
1198
krb5_set_error_message(context, ret, "malloc: out of memory");
1202
answer.length = ireq.u.ntlmRequest.ntlm.length;
1203
answer.data = ireq.u.ntlmRequest.ntlm.data;
1205
ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
1206
key->key.keyvalue.length,
1207
ireq.u.ntlmRequest.username,
1216
krb5_set_error_message(context, ret, "NTLM v2 verify failed");
1220
/* XXX verify infotarget matches client (checksum ?) */
1222
free(infotarget.data);
1226
struct ntlm_buf answer;
1230
if (flags & NTLM_NEG_NTLM2_SESSION) {
1231
unsigned char sessionhash[MD5_DIGEST_LENGTH];
1234
if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
1235
kdc_log(context, config, 0, "NTLM v1-session not allowed");
1240
if (ireq.u.ntlmRequest.lm.length != 24) {
1242
krb5_set_error_message(context, ret, "LM hash have wrong length "
1243
"for NTLM session key");
1248
MD5_Update(&md5ctx, challange, sizeof(challange));
1249
MD5_Update(&md5ctx, ireq.u.ntlmRequest.lm.data, 8);
1250
MD5_Final(sessionhash, &md5ctx);
1251
memcpy(challange, sessionhash, sizeof(challange));
1253
if ((config->digests_allowed & NTLM_V1) == 0) {
1254
kdc_log(context, config, 0, "NTLM v1 not allowed");
1259
ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
1260
key->key.keyvalue.length,
1261
challange, &answer);
1263
krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1267
if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
1268
memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
1272
krb5_set_error_message(context, ret, "NTLM hash mismatch");
1282
key->key.keyvalue.data, key->key.keyvalue.length);
1283
MD4_Final(sessionkey, &ctx);
1287
if (ireq.u.ntlmRequest.sessionkey) {
1288
unsigned char masterkey[MD4_DIGEST_LENGTH];
1292
if ((flags & NTLM_NEG_KEYEX) == 0) {
1294
krb5_set_error_message(context, ret,
1295
"NTLM client failed to neg key "
1296
"exchange but still sent key");
1300
len = ireq.u.ntlmRequest.sessionkey->length;
1301
if (len != sizeof(masterkey)){
1303
krb5_set_error_message(context, ret,
1304
"NTLM master key wrong length: %lu",
1305
(unsigned long)len);
1309
RC4_set_key(&rc4, sizeof(sessionkey), sessionkey);
1311
RC4(&rc4, sizeof(masterkey),
1312
ireq.u.ntlmRequest.sessionkey->data,
1314
memset(&rc4, 0, sizeof(rc4));
1316
r.u.ntlmResponse.sessionkey =
1317
malloc(sizeof(*r.u.ntlmResponse.sessionkey));
1318
if (r.u.ntlmResponse.sessionkey == NULL) {
1320
krb5_set_error_message(context, ret, "malloc: out of memory");
1324
ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
1325
masterkey, sizeof(masterkey));
1327
krb5_set_error_message(context, ret, "malloc: out of memory");
1332
r.u.ntlmResponse.success = 1;
1333
kdc_log(context, config, 0, "NTLM version %d successful for %s",
1334
version, ireq.u.ntlmRequest.username);
1337
case choice_DigestReqInner_supportedMechs:
1339
kdc_log(context, config, 0, "digest supportedMechs from %s", from);
1341
r.element = choice_DigestRepInner_supportedMechs;
1342
memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs));
1344
if (config->digests_allowed & NTLM_V1)
1345
r.u.supportedMechs.ntlm_v1 = 1;
1346
if (config->digests_allowed & NTLM_V1_SESSION)
1347
r.u.supportedMechs.ntlm_v1_session = 1;
1348
if (config->digests_allowed & NTLM_V2)
1349
r.u.supportedMechs.ntlm_v2 = 1;
1350
if (config->digests_allowed & DIGEST_MD5)
1351
r.u.supportedMechs.digest_md5 = 1;
1352
if (config->digests_allowed & CHAP_MD5)
1353
r.u.supportedMechs.chap_md5 = 1;
1354
if (config->digests_allowed & MS_CHAP_V2)
1355
r.u.supportedMechs.ms_chap_v2 = 1;
1361
krb5_set_error_message(context, ret, "unknown operation to digest");
1365
s = krb5_get_error_message(context, ret);
1367
krb5_clear_error_message(context);
1371
kdc_log(context, config, 0, "Digest failed with: %s", s);
1373
r.element = choice_DigestRepInner_error;
1374
r.u.error.reason = strdup("unknown error");
1375
krb5_free_error_message(context, s);
1376
if (r.u.error.reason == NULL) {
1378
krb5_set_error_message(context, ret, "malloc: out of memory");
1381
r.u.error.code = EINVAL;
1386
ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
1388
krb5_set_error_message(context, ret, "Failed to encode inner digest reply");
1391
if (size != buf.length)
1392
krb5_abortx(context, "ASN1 internal error");
1394
krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
1396
ret = krb5_mk_rep (context, ac, &rep.apRep);
1403
ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
1407
ret = krb5_crypto_init(context, key, 0, &crypto);
1408
krb5_free_keyblock (context, key);
1413
ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
1414
buf.data, buf.length, 0,
1417
ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
1419
krb5_set_error_message(context, ret, "Failed to encode digest reply");
1422
if (size != reply->length)
1423
krb5_abortx(context, "ASN1 internal error");
1428
krb5_auth_con_free(context, ac);
1430
krb5_warn(context, ret, "Digest request from %s failed", from);
1432
krb5_free_ticket(context, ticket);
1434
krb5_kt_close(context, id);
1436
krb5_crypto_destroy(context, crypto);
1438
krb5_storage_free(sp);
1440
_kdc_free_ent (context, user);
1442
_kdc_free_ent (context, server);
1444
_kdc_free_ent (context, client);
1446
memset(password, 0, strlen(password));
1451
krb5_data_free(&buf);
1452
krb5_data_free(&serverNonce);
1453
free_DigestREP(&rep);
1454
free_DigestRepInner(&r);
1455
free_DigestReqInner(&ireq);