1 /*
2 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
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.
16 *
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.
20 *
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
31 * SUCH DAMAGE.
32 */
33
34 #include "kdc_locl.h"
35 #include <hex.h>
36
37 #ifdef DIGEST
38
39 #define MS_CHAP_V2 0x20
40 #define CHAP_MD5 0x10
41 #define DIGEST_MD5 0x08
42 #define NTLM_V2 0x04
43 #define NTLM_V1_SESSION 0x02
44 #define NTLM_V1 0x01
45
46 const struct units _kdc_digestunits[] = {
47 {"ms-chap-v2", 1U << 5},
48 {"chap-md5", 1U << 4},
49 {"digest-md5", 1U << 3},
50 {"ntlm-v2", 1U << 2},
51 {"ntlm-v1-session", 1U << 1},
52 {"ntlm-v1", 1U << 0},
53 {NULL, 0}
54 };
55
56
57 static krb5_error_code
get_digest_key(krb5_context context,krb5_kdc_configuration * config,hdb_entry_ex * server,krb5_crypto * crypto)58 get_digest_key(krb5_context context,
59 krb5_kdc_configuration *config,
60 hdb_entry_ex *server,
61 krb5_crypto *crypto)
62 {
63 krb5_error_code ret;
64 krb5_enctype enctype;
65 Key *key;
66
67 ret = _kdc_get_preferred_key(context,
68 config,
69 server,
70 "digest-service",
71 &enctype,
72 &key);
73 if (ret)
74 return ret;
75 return krb5_crypto_init(context, &key->key, 0, crypto);
76 }
77
78 /*
79 *
80 */
81
82 static char *
get_ntlm_targetname(krb5_context context,hdb_entry_ex * client)83 get_ntlm_targetname(krb5_context context,
84 hdb_entry_ex *client)
85 {
86 char *targetname, *p;
87
88 targetname = strdup(krb5_principal_get_realm(context,
89 client->entry.principal));
90 if (targetname == NULL)
91 return NULL;
92
93 p = strchr(targetname, '.');
94 if (p)
95 *p = '\0';
96
97 strupr(targetname);
98 return targetname;
99 }
100
101 static krb5_error_code
fill_targetinfo(krb5_context context,char * targetname,hdb_entry_ex * client,krb5_data * data)102 fill_targetinfo(krb5_context context,
103 char *targetname,
104 hdb_entry_ex *client,
105 krb5_data *data)
106 {
107 struct ntlm_targetinfo ti;
108 krb5_error_code ret;
109 struct ntlm_buf d;
110 krb5_principal p;
111 const char *str;
112
113 memset(&ti, 0, sizeof(ti));
114
115 ti.domainname = targetname;
116 p = client->entry.principal;
117 str = krb5_principal_get_comp_string(context, p, 0);
118 if (str != NULL &&
119 (strcmp("host", str) == 0 ||
120 strcmp("ftp", str) == 0 ||
121 strcmp("imap", str) == 0 ||
122 strcmp("pop", str) == 0 ||
123 strcmp("smtp", str)))
124 {
125 str = krb5_principal_get_comp_string(context, p, 1);
126 ti.dnsservername = rk_UNCONST(str);
127 }
128
129 ret = heim_ntlm_encode_targetinfo(&ti, 1, &d);
130 if (ret)
131 return ret;
132
133 data->data = d.data;
134 data->length = d.length;
135
136 return 0;
137 }
138
139
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
145 };
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,
151 0x6E
152 };
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
157 };
158
159 /*
160 *
161 */
162
163 static krb5_error_code
get_password_entry(krb5_context context,krb5_kdc_configuration * config,const char * username,char ** password)164 get_password_entry(krb5_context context,
165 krb5_kdc_configuration *config,
166 const char *username,
167 char **password)
168 {
169 krb5_principal clientprincipal;
170 krb5_error_code ret;
171 hdb_entry_ex *user;
172 HDB *db;
173
174 /* get username */
175 ret = krb5_parse_name(context, username, &clientprincipal);
176 if (ret)
177 return ret;
178
179 ret = _kdc_db_fetch(context, config, clientprincipal,
180 HDB_F_GET_CLIENT, NULL, &db, &user);
181 krb5_free_principal(context, clientprincipal);
182 if (ret)
183 return ret;
184
185 ret = hdb_entry_get_password(context, db, &user->entry, password);
186 if (ret || password == NULL) {
187 if (ret == 0) {
188 ret = EINVAL;
189 krb5_set_error_message(context, ret, "password missing");
190 }
191 memset(user, 0, sizeof(*user));
192 }
193 _kdc_free_ent (context, user);
194 return ret;
195 }
196
197 /*
198 *
199 */
200
201 krb5_error_code
_kdc_do_digest(krb5_context context,krb5_kdc_configuration * config,const struct DigestREQ * req,krb5_data * reply,const char * from,struct sockaddr * addr)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)
206 {
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;
212 DigestReqInner ireq;
213 DigestRepInner r;
214 DigestREP rep;
215 krb5_flags ap_req_options;
216 krb5_data buf;
217 size_t size;
218 krb5_storage *sp = NULL;
219 Checksum res;
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;
224
225 if(!config->enable_digest) {
226 kdc_log(context, config, 0,
227 "Rejected digest request (disabled) from %s", from);
228 return KRB5KDC_ERR_POLICY;
229 }
230
231 krb5_data_zero(&buf);
232 krb5_data_zero(reply);
233 krb5_data_zero(&serverNonce);
234 memset(&ireq, 0, sizeof(ireq));
235 memset(&r, 0, sizeof(r));
236 memset(&rep, 0, sizeof(rep));
237 memset(&res, 0, sizeof(res));
238
239 kdc_log(context, config, 0, "Digest request from %s", from);
240
241 ret = krb5_kt_resolve(context, "HDB:", &id);
242 if (ret) {
243 kdc_log(context, config, 0, "Can't open database for digest");
244 goto out;
245 }
246
247 ret = krb5_rd_req(context,
248 &ac,
249 &req->apReq,
250 NULL,
251 id,
252 &ap_req_options,
253 &ticket);
254 if (ret)
255 goto out;
256
257 /* check the server principal in the ticket matches digest/R@R */
258 {
259 krb5_principal principal = NULL;
260 const char *p, *rr;
261
262 ret = krb5_ticket_get_server(context, ticket, &principal);
263 if (ret)
264 goto out;
265
266 ret = EINVAL;
267 krb5_set_error_message(context, ret, "Wrong digest server principal used");
268 p = krb5_principal_get_comp_string(context, principal, 0);
269 if (p == NULL) {
270 krb5_free_principal(context, principal);
271 goto out;
272 }
273 if (strcmp(p, KRB5_DIGEST_NAME) != 0) {
274 krb5_free_principal(context, principal);
275 goto out;
276 }
277
278 p = krb5_principal_get_comp_string(context, principal, 1);
279 if (p == NULL) {
280 krb5_free_principal(context, principal);
281 goto out;
282 }
283 rr = krb5_principal_get_realm(context, principal);
284 if (rr == NULL) {
285 krb5_free_principal(context, principal);
286 goto out;
287 }
288 if (strcmp(p, rr) != 0) {
289 krb5_free_principal(context, principal);
290 goto out;
291 }
292 krb5_clear_error_message(context);
293
294 ret = _kdc_db_fetch(context, config, principal,
295 HDB_F_GET_SERVER, NULL, NULL, &server);
296 if (ret)
297 goto out;
298
299 krb5_free_principal(context, principal);
300 }
301
302 /* check the client is allowed to do digest auth */
303 {
304 krb5_principal principal = NULL;
305
306 ret = krb5_ticket_get_client(context, ticket, &principal);
307 if (ret)
308 goto out;
309
310 ret = krb5_unparse_name(context, principal, &client_name);
311 if (ret) {
312 krb5_free_principal(context, principal);
313 goto out;
314 }
315
316 ret = _kdc_db_fetch(context, config, principal,
317 HDB_F_GET_CLIENT, NULL, NULL, &client);
318 krb5_free_principal(context, principal);
319 if (ret)
320 goto out;
321
322 if (client->entry.flags.allow_digest == 0) {
323 kdc_log(context, config, 0,
324 "Client %s tried to use digest "
325 "but is not allowed to",
326 client_name);
327 ret = KRB5KDC_ERR_POLICY;
328 krb5_set_error_message(context, ret,
329 "Client is not permitted to use digest");
330 goto out;
331 }
332 }
333
334 /* unpack request */
335 {
336 krb5_keyblock *key;
337
338 ret = krb5_auth_con_getremotesubkey(context, ac, &key);
339 if (ret)
340 goto out;
341 if (key == NULL) {
342 ret = EINVAL;
343 krb5_set_error_message(context, ret, "digest: remote subkey not found");
344 goto out;
345 }
346
347 ret = krb5_crypto_init(context, key, 0, &crypto);
348 krb5_free_keyblock (context, key);
349 if (ret)
350 goto out;
351 }
352
353 ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
354 &req->innerReq, &buf);
355 krb5_crypto_destroy(context, crypto);
356 crypto = NULL;
357 if (ret)
358 goto out;
359
360 ret = decode_DigestReqInner(buf.data, buf.length, &ireq, NULL);
361 krb5_data_free(&buf);
362 if (ret) {
363 krb5_set_error_message(context, ret, "Failed to decode digest inner request");
364 goto out;
365 }
366
367 kdc_log(context, config, 0, "Valid digest request from %s (%s)",
368 client_name, from);
369
370 /*
371 * Process the inner request
372 */
373
374 switch (ireq.element) {
375 case choice_DigestReqInner_init: {
376 unsigned char server_nonce[16], identifier;
377
378 RAND_bytes(&identifier, sizeof(identifier));
379 RAND_bytes(server_nonce, sizeof(server_nonce));
380
381 server_nonce[0] = kdc_time & 0xff;
382 server_nonce[1] = (kdc_time >> 8) & 0xff;
383 server_nonce[2] = (kdc_time >> 16) & 0xff;
384 server_nonce[3] = (kdc_time >> 24) & 0xff;
385
386 r.element = choice_DigestRepInner_initReply;
387
388 hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce);
389 if (r.u.initReply.nonce == NULL) {
390 ret = ENOMEM;
391 krb5_set_error_message(context, ret, "Failed to decode server nonce");
392 goto out;
393 }
394
395 sp = krb5_storage_emem();
396 if (sp == NULL) {
397 ret = ENOMEM;
398 krb5_set_error_message(context, ret, "malloc: out of memory");
399 goto out;
400 }
401 ret = krb5_store_stringz(sp, ireq.u.init.type);
402 if (ret) {
403 krb5_clear_error_message(context);
404 goto out;
405 }
406
407 if (ireq.u.init.channel) {
408 char *s;
409
410 asprintf(&s, "%s-%s:%s", r.u.initReply.nonce,
411 ireq.u.init.channel->cb_type,
412 ireq.u.init.channel->cb_binding);
413 if (s == NULL) {
414 ret = ENOMEM;
415 krb5_set_error_message(context, ret,
416 "Failed to allocate channel binding");
417 goto out;
418 }
419 free(r.u.initReply.nonce);
420 r.u.initReply.nonce = s;
421 }
422
423 ret = krb5_store_stringz(sp, r.u.initReply.nonce);
424 if (ret) {
425 krb5_clear_error_message(context);
426 goto out;
427 }
428
429 if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
430 r.u.initReply.identifier =
431 malloc(sizeof(*r.u.initReply.identifier));
432 if (r.u.initReply.identifier == NULL) {
433 ret = ENOMEM;
434 krb5_set_error_message(context, ret, "malloc: out of memory");
435 goto out;
436 }
437
438 asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff);
439 if (*r.u.initReply.identifier == NULL) {
440 ret = ENOMEM;
441 krb5_set_error_message(context, ret, "malloc: out of memory");
442 goto out;
443 }
444
445 } else
446 r.u.initReply.identifier = NULL;
447
448 if (ireq.u.init.hostname) {
449 ret = krb5_store_stringz(sp, *ireq.u.init.hostname);
450 if (ret) {
451 krb5_clear_error_message(context);
452 goto out;
453 }
454 }
455
456 ret = krb5_storage_to_data(sp, &buf);
457 if (ret) {
458 krb5_clear_error_message(context);
459 goto out;
460 }
461
462 ret = get_digest_key(context, config, server, &crypto);
463 if (ret)
464 goto out;
465
466 ret = krb5_create_checksum(context,
467 crypto,
468 KRB5_KU_DIGEST_OPAQUE,
469 0,
470 buf.data,
471 buf.length,
472 &res);
473 krb5_crypto_destroy(context, crypto);
474 crypto = NULL;
475 krb5_data_free(&buf);
476 if (ret)
477 goto out;
478
479 ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret);
480 free_Checksum(&res);
481 if (ret) {
482 krb5_set_error_message(context, ret, "Failed to encode "
483 "checksum in digest request");
484 goto out;
485 }
486 if (size != buf.length)
487 krb5_abortx(context, "ASN1 internal error");
488
489 hex_encode(buf.data, buf.length, &r.u.initReply.opaque);
490 free(buf.data);
491 krb5_data_zero(&buf);
492 if (r.u.initReply.opaque == NULL) {
493 krb5_clear_error_message(context);
494 ret = ENOMEM;
495 goto out;
496 }
497
498 kdc_log(context, config, 0, "Digest %s init request successful from %s",
499 ireq.u.init.type, from);
500
501 break;
502 }
503 case choice_DigestReqInner_digestRequest: {
504 sp = krb5_storage_emem();
505 if (sp == NULL) {
506 ret = ENOMEM;
507 krb5_set_error_message(context, ret, "malloc: out of memory");
508 goto out;
509 }
510 ret = krb5_store_stringz(sp, ireq.u.digestRequest.type);
511 if (ret) {
512 krb5_clear_error_message(context);
513 goto out;
514 }
515
516 krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
517
518 if (ireq.u.digestRequest.hostname) {
519 ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname);
520 if (ret) {
521 krb5_clear_error_message(context);
522 goto out;
523 }
524 }
525
526 buf.length = strlen(ireq.u.digestRequest.opaque);
527 buf.data = malloc(buf.length);
528 if (buf.data == NULL) {
529 ret = ENOMEM;
530 krb5_set_error_message(context, ret, "malloc: out of memory");
531 goto out;
532 }
533
534 ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
535 if (ret <= 0) {
536 ret = ENOMEM;
537 krb5_set_error_message(context, ret, "Failed to decode opaque");
538 goto out;
539 }
540 buf.length = ret;
541
542 ret = decode_Checksum(buf.data, buf.length, &res, NULL);
543 free(buf.data);
544 krb5_data_zero(&buf);
545 if (ret) {
546 krb5_set_error_message(context, ret,
547 "Failed to decode digest Checksum");
548 goto out;
549 }
550
551 ret = krb5_storage_to_data(sp, &buf);
552 if (ret) {
553 krb5_clear_error_message(context);
554 goto out;
555 }
556
557 serverNonce.length = strlen(ireq.u.digestRequest.serverNonce);
558 serverNonce.data = malloc(serverNonce.length);
559 if (serverNonce.data == NULL) {
560 ret = ENOMEM;
561 krb5_set_error_message(context, ret, "malloc: out of memory");
562 goto out;
563 }
564
565 /*
566 * CHAP does the checksum of the raw nonce, but do it for all
567 * types, since we need to check the timestamp.
568 */
569 {
570 ssize_t ssize;
571
572 ssize = hex_decode(ireq.u.digestRequest.serverNonce,
573 serverNonce.data, serverNonce.length);
574 if (ssize <= 0) {
575 ret = ENOMEM;
576 krb5_set_error_message(context, ret, "Failed to decode serverNonce");
577 goto out;
578 }
579 serverNonce.length = ssize;
580 }
581
582 ret = get_digest_key(context, config, server, &crypto);
583 if (ret)
584 goto out;
585
586 ret = krb5_verify_checksum(context, crypto,
587 KRB5_KU_DIGEST_OPAQUE,
588 buf.data, buf.length, &res);
589 free_Checksum(&res);
590 krb5_data_free(&buf);
591 krb5_crypto_destroy(context, crypto);
592 crypto = NULL;
593 if (ret)
594 goto out;
595
596 /* verify time */
597 {
598 unsigned char *p = serverNonce.data;
599 uint32_t t;
600
601 if (serverNonce.length < 4) {
602 ret = EINVAL;
603 krb5_set_error_message(context, ret, "server nonce too short");
604 goto out;
605 }
606 t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
607
608 if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) {
609 ret = EINVAL;
610 krb5_set_error_message(context, ret, "time screw in server nonce ");
611 goto out;
612 }
613 }
614
615 if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) {
616 EVP_MD_CTX *ctx;
617 unsigned char md[MD5_DIGEST_LENGTH];
618 char *mdx;
619 char idx;
620
621 if ((config->digests_allowed & CHAP_MD5) == 0) {
622 kdc_log(context, config, 0, "Digest CHAP MD5 not allowed");
623 goto out;
624 }
625
626 if (ireq.u.digestRequest.identifier == NULL) {
627 ret = EINVAL;
628 krb5_set_error_message(context, ret, "Identifier missing "
629 "from CHAP request");
630 goto out;
631 }
632
633 if (hex_decode(*ireq.u.digestRequest.identifier, &idx, 1) != 1) {
634 ret = EINVAL;
635 krb5_set_error_message(context, ret, "failed to decode identifier");
636 goto out;
637 }
638
639 ret = get_password_entry(context, config,
640 ireq.u.digestRequest.username,
641 &password);
642 if (ret)
643 goto out;
644
645 ctx = EVP_MD_CTX_create();
646
647 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
648 EVP_DigestUpdate(ctx, &idx, 1);
649 EVP_DigestUpdate(ctx, password, strlen(password));
650 EVP_DigestUpdate(ctx, serverNonce.data, serverNonce.length);
651 EVP_DigestFinal_ex(ctx, md, NULL);
652
653 EVP_MD_CTX_destroy(ctx);
654
655 hex_encode(md, sizeof(md), &mdx);
656 if (mdx == NULL) {
657 krb5_clear_error_message(context);
658 ret = ENOMEM;
659 goto out;
660 }
661
662 r.element = choice_DigestRepInner_response;
663
664 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
665 free(mdx);
666 if (ret == 0) {
667 r.u.response.success = TRUE;
668 } else {
669 kdc_log(context, config, 0,
670 "CHAP reply mismatch for %s",
671 ireq.u.digestRequest.username);
672 r.u.response.success = FALSE;
673 }
674
675 } else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
676 EVP_MD_CTX *ctx;
677 unsigned char md[MD5_DIGEST_LENGTH];
678 char *mdx;
679 char *A1, *A2;
680
681 if ((config->digests_allowed & DIGEST_MD5) == 0) {
682 kdc_log(context, config, 0, "Digest SASL MD5 not allowed");
683 goto out;
684 }
685
686 if (ireq.u.digestRequest.nonceCount == NULL)
687 goto out;
688 if (ireq.u.digestRequest.clientNonce == NULL)
689 goto out;
690 if (ireq.u.digestRequest.qop == NULL)
691 goto out;
692 if (ireq.u.digestRequest.realm == NULL)
693 goto out;
694
695 ret = get_password_entry(context, config,
696 ireq.u.digestRequest.username,
697 &password);
698 if (ret)
699 goto failed;
700
701 ctx = EVP_MD_CTX_create();
702
703 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
704 EVP_DigestUpdate(ctx, ireq.u.digestRequest.username,
705 strlen(ireq.u.digestRequest.username));
706 EVP_DigestUpdate(ctx, ":", 1);
707 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.realm,
708 strlen(*ireq.u.digestRequest.realm));
709 EVP_DigestUpdate(ctx, ":", 1);
710 EVP_DigestUpdate(ctx, password, strlen(password));
711 EVP_DigestFinal_ex(ctx, md, NULL);
712
713 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
714 EVP_DigestUpdate(ctx, md, sizeof(md));
715 EVP_DigestUpdate(ctx, ":", 1);
716 EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce,
717 strlen(ireq.u.digestRequest.serverNonce));
718 EVP_DigestUpdate(ctx, ":", 1);
719 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount,
720 strlen(*ireq.u.digestRequest.nonceCount));
721 if (ireq.u.digestRequest.authid) {
722 EVP_DigestUpdate(ctx, ":", 1);
723 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.authid,
724 strlen(*ireq.u.digestRequest.authid));
725 }
726 EVP_DigestFinal_ex(ctx, md, NULL);
727 hex_encode(md, sizeof(md), &A1);
728 if (A1 == NULL) {
729 ret = ENOMEM;
730 krb5_set_error_message(context, ret, "malloc: out of memory");
731 EVP_MD_CTX_destroy(ctx);
732 goto failed;
733 }
734
735 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
736 EVP_DigestUpdate(ctx,
737 "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
738 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.uri,
739 strlen(*ireq.u.digestRequest.uri));
740
741 /* conf|int */
742 if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
743 static char conf_zeros[] = ":00000000000000000000000000000000";
744 EVP_DigestUpdate(ctx, conf_zeros, sizeof(conf_zeros) - 1);
745 }
746
747 EVP_DigestFinal_ex(ctx, md, NULL);
748
749 hex_encode(md, sizeof(md), &A2);
750 if (A2 == NULL) {
751 ret = ENOMEM;
752 krb5_set_error_message(context, ret, "malloc: out of memory");
753 free(A1);
754 goto failed;
755 }
756
757 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
758 EVP_DigestUpdate(ctx, A1, strlen(A2));
759 EVP_DigestUpdate(ctx, ":", 1);
760 EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce,
761 strlen(ireq.u.digestRequest.serverNonce));
762 EVP_DigestUpdate(ctx, ":", 1);
763 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount,
764 strlen(*ireq.u.digestRequest.nonceCount));
765 EVP_DigestUpdate(ctx, ":", 1);
766 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.clientNonce,
767 strlen(*ireq.u.digestRequest.clientNonce));
768 EVP_DigestUpdate(ctx, ":", 1);
769 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.qop,
770 strlen(*ireq.u.digestRequest.qop));
771 EVP_DigestUpdate(ctx, ":", 1);
772 EVP_DigestUpdate(ctx, A2, strlen(A2));
773
774 EVP_DigestFinal_ex(ctx, md, NULL);
775
776 EVP_MD_CTX_destroy(ctx);
777
778 free(A1);
779 free(A2);
780
781 hex_encode(md, sizeof(md), &mdx);
782 if (mdx == NULL) {
783 krb5_clear_error_message(context);
784 ret = ENOMEM;
785 goto out;
786 }
787
788 r.element = choice_DigestRepInner_response;
789 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
790 free(mdx);
791 if (ret == 0) {
792 r.u.response.success = TRUE;
793 } else {
794 kdc_log(context, config, 0,
795 "DIGEST-MD5 reply mismatch for %s",
796 ireq.u.digestRequest.username);
797 r.u.response.success = FALSE;
798 }
799
800 } else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) {
801 unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH];
802 krb5_principal clientprincipal = NULL;
803 char *mdx;
804 const char *username;
805 struct ntlm_buf answer;
806 Key *key = NULL;
807 EVP_MD_CTX *ctp;
808
809 if ((config->digests_allowed & MS_CHAP_V2) == 0) {
810 kdc_log(context, config, 0, "MS-CHAP-V2 not allowed");
811 goto failed;
812 }
813
814 if (ireq.u.digestRequest.clientNonce == NULL) {
815 ret = EINVAL;
816 krb5_set_error_message(context, ret,
817 "MS-CHAP-V2 clientNonce missing");
818 goto failed;
819 }
820 if (serverNonce.length != 16) {
821 ret = EINVAL;
822 krb5_set_error_message(context, ret,
823 "MS-CHAP-V2 serverNonce wrong length");
824 goto failed;
825 }
826
827 /* strip of the domain component */
828 username = strchr(ireq.u.digestRequest.username, '\\');
829 if (username == NULL)
830 username = ireq.u.digestRequest.username;
831 else
832 username++;
833
834 ctp = EVP_MD_CTX_create();
835
836 /* ChallangeHash */
837 EVP_DigestInit_ex(ctp, EVP_sha1(), NULL);
838 {
839 ssize_t ssize;
840 krb5_data clientNonce;
841
842 clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce);
843 clientNonce.data = malloc(clientNonce.length);
844 if (clientNonce.data == NULL) {
845 ret = ENOMEM;
846 krb5_set_error_message(context, ret,
847 "malloc: out of memory");
848 EVP_MD_CTX_destroy(ctp);
849 goto out;
850 }
851
852 ssize = hex_decode(*ireq.u.digestRequest.clientNonce,
853 clientNonce.data, clientNonce.length);
854 if (ssize != 16) {
855 ret = ENOMEM;
856 krb5_set_error_message(context, ret,
857 "Failed to decode clientNonce");
858 EVP_MD_CTX_destroy(ctp);
859 goto out;
860 }
861 EVP_DigestUpdate(ctp, clientNonce.data, ssize);
862 free(clientNonce.data);
863 }
864 EVP_DigestUpdate(ctp, serverNonce.data, serverNonce.length);
865 EVP_DigestUpdate(ctp, username, strlen(username));
866
867 EVP_DigestFinal_ex(ctp, challange, NULL);
868
869 EVP_MD_CTX_destroy(ctp);
870
871 /* NtPasswordHash */
872 ret = krb5_parse_name(context, username, &clientprincipal);
873 if (ret)
874 goto failed;
875
876 ret = _kdc_db_fetch(context, config, clientprincipal,
877 HDB_F_GET_CLIENT, NULL, NULL, &user);
878 krb5_free_principal(context, clientprincipal);
879 if (ret) {
880 krb5_set_error_message(context, ret,
881 "MS-CHAP-V2 user %s not in database",
882 username);
883 goto failed;
884 }
885
886 ret = hdb_enctype2key(context, &user->entry,
887 ETYPE_ARCFOUR_HMAC_MD5, &key);
888 if (ret) {
889 krb5_set_error_message(context, ret,
890 "MS-CHAP-V2 missing arcfour key %s",
891 username);
892 goto failed;
893 }
894
895 /* ChallengeResponse */
896 ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
897 key->key.keyvalue.length,
898 challange, &answer);
899 if (ret) {
900 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
901 goto failed;
902 }
903
904 hex_encode(answer.data, answer.length, &mdx);
905 if (mdx == NULL) {
906 free(answer.data);
907 krb5_clear_error_message(context);
908 ret = ENOMEM;
909 goto out;
910 }
911
912 r.element = choice_DigestRepInner_response;
913 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
914 if (ret == 0) {
915 r.u.response.success = TRUE;
916 } else {
917 kdc_log(context, config, 0,
918 "MS-CHAP-V2 hash mismatch for %s",
919 ireq.u.digestRequest.username);
920 r.u.response.success = FALSE;
921 }
922 free(mdx);
923
924 if (r.u.response.success) {
925 unsigned char hashhash[MD4_DIGEST_LENGTH];
926 EVP_MD_CTX *ctxp;
927
928 ctxp = EVP_MD_CTX_create();
929
930 /* hashhash */
931 {
932 EVP_DigestInit_ex(ctxp, EVP_md4(), NULL);
933 EVP_DigestUpdate(ctxp,
934 key->key.keyvalue.data,
935 key->key.keyvalue.length);
936 EVP_DigestFinal_ex(ctxp, hashhash, NULL);
937 }
938
939 /* GenerateAuthenticatorResponse */
940 EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
941 EVP_DigestUpdate(ctxp, hashhash, sizeof(hashhash));
942 EVP_DigestUpdate(ctxp, answer.data, answer.length);
943 EVP_DigestUpdate(ctxp, ms_chap_v2_magic1,
944 sizeof(ms_chap_v2_magic1));
945 EVP_DigestFinal_ex(ctxp, md, NULL);
946
947 EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
948 EVP_DigestUpdate(ctxp, md, sizeof(md));
949 EVP_DigestUpdate(ctxp, challange, 8);
950 EVP_DigestUpdate(ctxp, ms_chap_v2_magic2,
951 sizeof(ms_chap_v2_magic2));
952 EVP_DigestFinal_ex(ctxp, md, NULL);
953
954 r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp));
955 if (r.u.response.rsp == NULL) {
956 free(answer.data);
957 krb5_clear_error_message(context);
958 EVP_MD_CTX_destroy(ctxp);
959 ret = ENOMEM;
960 goto out;
961 }
962
963 hex_encode(md, sizeof(md), r.u.response.rsp);
964 if (r.u.response.rsp == NULL) {
965 free(answer.data);
966 krb5_clear_error_message(context);
967 EVP_MD_CTX_destroy(ctxp);
968 ret = ENOMEM;
969 goto out;
970 }
971
972 /* get_master, rfc 3079 3.4 */
973 EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
974 EVP_DigestUpdate(ctxp, hashhash, 16);
975 EVP_DigestUpdate(ctxp, answer.data, answer.length);
976 EVP_DigestUpdate(ctxp, ms_rfc3079_magic1,
977 sizeof(ms_rfc3079_magic1));
978 EVP_DigestFinal_ex(ctxp, md, NULL);
979
980 free(answer.data);
981
982 EVP_MD_CTX_destroy(ctxp);
983
984 r.u.response.session_key =
985 calloc(1, sizeof(*r.u.response.session_key));
986 if (r.u.response.session_key == NULL) {
987 krb5_clear_error_message(context);
988 ret = ENOMEM;
989 goto out;
990 }
991
992 ret = krb5_data_copy(r.u.response.session_key, md, 16);
993 if (ret) {
994 krb5_clear_error_message(context);
995 goto out;
996 }
997 }
998
999 } else {
1000 r.element = choice_DigestRepInner_error;
1001 asprintf(&r.u.error.reason, "Unsupported digest type %s",
1002 ireq.u.digestRequest.type);
1003 if (r.u.error.reason == NULL) {
1004 ret = ENOMEM;
1005 krb5_set_error_message(context, ret, "malloc: out of memory");
1006 goto out;
1007 }
1008 r.u.error.code = EINVAL;
1009 }
1010
1011 kdc_log(context, config, 0, "Digest %s request successful %s",
1012 ireq.u.digestRequest.type, ireq.u.digestRequest.username);
1013
1014 break;
1015 }
1016 case choice_DigestReqInner_ntlmInit:
1017
1018 if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) {
1019 kdc_log(context, config, 0, "NTLM not allowed");
1020 goto failed;
1021 }
1022
1023 r.element = choice_DigestRepInner_ntlmInitReply;
1024
1025 r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE;
1026
1027 if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) {
1028 kdc_log(context, config, 0, "NTLM client have no unicode");
1029 goto failed;
1030 }
1031
1032 if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM)
1033 r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM;
1034 else {
1035 kdc_log(context, config, 0, "NTLM client doesn't support NTLM");
1036 goto failed;
1037 }
1038
1039 r.u.ntlmInitReply.flags |=
1040 NTLM_NEG_TARGET |
1041 NTLM_TARGET_DOMAIN |
1042 NTLM_ENC_128;
1043
1044 #define ALL \
1045 NTLM_NEG_SIGN| \
1046 NTLM_NEG_SEAL| \
1047 NTLM_NEG_ALWAYS_SIGN| \
1048 NTLM_NEG_NTLM2_SESSION| \
1049 NTLM_NEG_KEYEX
1050
1051 r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
1052
1053 #undef ALL
1054
1055 r.u.ntlmInitReply.targetname =
1056 get_ntlm_targetname(context, client);
1057 if (r.u.ntlmInitReply.targetname == NULL) {
1058 ret = ENOMEM;
1059 krb5_set_error_message(context, ret, "malloc: out of memory");
1060 goto out;
1061 }
1062 r.u.ntlmInitReply.challange.data = malloc(8);
1063 if (r.u.ntlmInitReply.challange.data == NULL) {
1064 ret = ENOMEM;
1065 krb5_set_error_message(context, ret, "malloc: out of memory");
1066 goto out;
1067 }
1068 r.u.ntlmInitReply.challange.length = 8;
1069 if (RAND_bytes(r.u.ntlmInitReply.challange.data,
1070 r.u.ntlmInitReply.challange.length) != 1)
1071 {
1072 ret = ENOMEM;
1073 krb5_set_error_message(context, ret, "out of random error");
1074 goto out;
1075 }
1076 /* XXX fix targetinfo */
1077 ALLOC(r.u.ntlmInitReply.targetinfo);
1078 if (r.u.ntlmInitReply.targetinfo == NULL) {
1079 ret = ENOMEM;
1080 krb5_set_error_message(context, ret, "malloc: out of memory");
1081 goto out;
1082 }
1083
1084 ret = fill_targetinfo(context,
1085 r.u.ntlmInitReply.targetname,
1086 client,
1087 r.u.ntlmInitReply.targetinfo);
1088 if (ret) {
1089 ret = ENOMEM;
1090 krb5_set_error_message(context, ret, "malloc: out of memory");
1091 goto out;
1092 }
1093
1094 /*
1095 * Save data encryted in opaque for the second part of the
1096 * ntlm authentication
1097 */
1098 sp = krb5_storage_emem();
1099 if (sp == NULL) {
1100 ret = ENOMEM;
1101 krb5_set_error_message(context, ret, "malloc: out of memory");
1102 goto out;
1103 }
1104
1105 ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8);
1106 if (ret != 8) {
1107 ret = ENOMEM;
1108 krb5_set_error_message(context, ret, "storage write challange");
1109 goto out;
1110 }
1111 ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
1112 if (ret) {
1113 krb5_clear_error_message(context);
1114 goto out;
1115 }
1116
1117 ret = krb5_storage_to_data(sp, &buf);
1118 if (ret) {
1119 krb5_clear_error_message(context);
1120 goto out;
1121 }
1122
1123 ret = get_digest_key(context, config, server, &crypto);
1124 if (ret)
1125 goto out;
1126
1127 ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1128 buf.data, buf.length, &r.u.ntlmInitReply.opaque);
1129 krb5_data_free(&buf);
1130 krb5_crypto_destroy(context, crypto);
1131 crypto = NULL;
1132 if (ret)
1133 goto out;
1134
1135 kdc_log(context, config, 0, "NTLM init from %s", from);
1136
1137 break;
1138
1139 case choice_DigestReqInner_ntlmRequest: {
1140 krb5_principal clientprincipal;
1141 unsigned char sessionkey[16];
1142 unsigned char challange[8];
1143 uint32_t flags;
1144 Key *key = NULL;
1145 int version;
1146
1147 r.element = choice_DigestRepInner_ntlmResponse;
1148 r.u.ntlmResponse.success = 0;
1149 r.u.ntlmResponse.flags = 0;
1150 r.u.ntlmResponse.sessionkey = NULL;
1151 r.u.ntlmResponse.tickets = NULL;
1152
1153 /* get username */
1154 ret = krb5_parse_name(context,
1155 ireq.u.ntlmRequest.username,
1156 &clientprincipal);
1157 if (ret)
1158 goto failed;
1159
1160 ret = _kdc_db_fetch(context, config, clientprincipal,
1161 HDB_F_GET_CLIENT, NULL, NULL, &user);
1162 krb5_free_principal(context, clientprincipal);
1163 if (ret) {
1164 krb5_set_error_message(context, ret, "NTLM user %s not in database",
1165 ireq.u.ntlmRequest.username);
1166 goto failed;
1167 }
1168
1169 ret = get_digest_key(context, config, server, &crypto);
1170 if (ret)
1171 goto failed;
1172
1173 ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1174 ireq.u.ntlmRequest.opaque.data,
1175 ireq.u.ntlmRequest.opaque.length, &buf);
1176 krb5_crypto_destroy(context, crypto);
1177 crypto = NULL;
1178 if (ret) {
1179 kdc_log(context, config, 0,
1180 "Failed to decrypt nonce from %s", from);
1181 goto failed;
1182 }
1183
1184 sp = krb5_storage_from_data(&buf);
1185 if (sp == NULL) {
1186 ret = ENOMEM;
1187 krb5_set_error_message(context, ret, "malloc: out of memory");
1188 goto out;
1189 }
1190
1191 ret = krb5_storage_read(sp, challange, sizeof(challange));
1192 if (ret != sizeof(challange)) {
1193 ret = ENOMEM;
1194 krb5_set_error_message(context, ret, "NTLM storage read challange");
1195 goto out;
1196 }
1197 ret = krb5_ret_uint32(sp, &flags);
1198 if (ret) {
1199 krb5_set_error_message(context, ret, "NTLM storage read flags");
1200 goto out;
1201 }
1202 krb5_storage_free(sp);
1203 sp = NULL;
1204 krb5_data_free(&buf);
1205
1206 if ((flags & NTLM_NEG_NTLM) == 0) {
1207 ret = EINVAL;
1208 krb5_set_error_message(context, ret, "NTLM not negotiated");
1209 goto out;
1210 }
1211
1212 ret = hdb_enctype2key(context, &user->entry,
1213 ETYPE_ARCFOUR_HMAC_MD5, &key);
1214 if (ret) {
1215 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1216 goto out;
1217 }
1218
1219 /* check if this is NTLMv2 */
1220 if (ireq.u.ntlmRequest.ntlm.length != 24) {
1221 struct ntlm_buf infotarget, answer;
1222 char *targetname;
1223
1224 if ((config->digests_allowed & NTLM_V2) == 0) {
1225 kdc_log(context, config, 0, "NTLM v2 not allowed");
1226 goto out;
1227 }
1228
1229 version = 2;
1230
1231 targetname = get_ntlm_targetname(context, client);
1232 if (targetname == NULL) {
1233 ret = ENOMEM;
1234 krb5_set_error_message(context, ret, "malloc: out of memory");
1235 goto out;
1236 }
1237
1238 answer.length = ireq.u.ntlmRequest.ntlm.length;
1239 answer.data = ireq.u.ntlmRequest.ntlm.data;
1240
1241 ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
1242 key->key.keyvalue.length,
1243 ireq.u.ntlmRequest.username,
1244 targetname,
1245 0,
1246 challange,
1247 &answer,
1248 &infotarget,
1249 sessionkey);
1250 free(targetname);
1251 if (ret) {
1252 krb5_set_error_message(context, ret, "NTLM v2 verify failed");
1253 goto failed;
1254 }
1255
1256 /* XXX verify infotarget matches client (checksum ?) */
1257
1258 free(infotarget.data);
1259 /* */
1260
1261 } else {
1262 struct ntlm_buf answer;
1263
1264 version = 1;
1265
1266 if (flags & NTLM_NEG_NTLM2_SESSION) {
1267 unsigned char sessionhash[MD5_DIGEST_LENGTH];
1268 EVP_MD_CTX *ctx;
1269
1270 if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
1271 kdc_log(context, config, 0, "NTLM v1-session not allowed");
1272 ret = EINVAL;
1273 goto failed;
1274 }
1275
1276 if (ireq.u.ntlmRequest.lm.length != 24) {
1277 ret = EINVAL;
1278 krb5_set_error_message(context, ret, "LM hash have wrong length "
1279 "for NTLM session key");
1280 goto failed;
1281 }
1282
1283 ctx = EVP_MD_CTX_create();
1284
1285 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
1286
1287 EVP_DigestUpdate(ctx, challange, sizeof(challange));
1288 EVP_DigestUpdate(ctx, ireq.u.ntlmRequest.lm.data, 8);
1289 EVP_DigestFinal_ex(ctx, sessionhash, NULL);
1290 memcpy(challange, sessionhash, sizeof(challange));
1291
1292 EVP_MD_CTX_destroy(ctx);
1293
1294 } else {
1295 if ((config->digests_allowed & NTLM_V1) == 0) {
1296 kdc_log(context, config, 0, "NTLM v1 not allowed");
1297 goto failed;
1298 }
1299 }
1300
1301 ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
1302 key->key.keyvalue.length,
1303 challange, &answer);
1304 if (ret) {
1305 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1306 goto failed;
1307 }
1308
1309 if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
1310 memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
1311 {
1312 free(answer.data);
1313 ret = EINVAL;
1314 krb5_set_error_message(context, ret, "NTLM hash mismatch");
1315 goto failed;
1316 }
1317 free(answer.data);
1318
1319 {
1320 EVP_MD_CTX *ctx;
1321
1322 ctx = EVP_MD_CTX_create();
1323
1324 EVP_DigestInit_ex(ctx, EVP_md4(), NULL);
1325 EVP_DigestUpdate(ctx,
1326 key->key.keyvalue.data,
1327 key->key.keyvalue.length);
1328 EVP_DigestFinal_ex(ctx, sessionkey, NULL);
1329
1330 EVP_MD_CTX_destroy(ctx);
1331 }
1332 }
1333
1334 if (ireq.u.ntlmRequest.sessionkey) {
1335 unsigned char masterkey[MD4_DIGEST_LENGTH];
1336 EVP_CIPHER_CTX *rc4;
1337 size_t len;
1338
1339 if ((flags & NTLM_NEG_KEYEX) == 0) {
1340 ret = EINVAL;
1341 krb5_set_error_message(context, ret,
1342 "NTLM client failed to neg key "
1343 "exchange but still sent key");
1344 goto failed;
1345 }
1346
1347 len = ireq.u.ntlmRequest.sessionkey->length;
1348 if (len != sizeof(masterkey)){
1349 ret = EINVAL;
1350 krb5_set_error_message(context, ret,
1351 "NTLM master key wrong length: %lu",
1352 (unsigned long)len);
1353 goto failed;
1354 }
1355
1356
1357 rc4 = EVP_CIPHER_CTX_new();
1358 if (rc4 == NULL) {
1359 ret = ENOMEM;
1360 krb5_set_error_message(context, ret,
1361 "NTLM failed to malloc cipher context");
1362 goto failed;
1363 }
1364 EVP_CipherInit_ex(rc4, EVP_rc4(), NULL, sessionkey, NULL, 1);
1365 EVP_Cipher(rc4,
1366 masterkey, ireq.u.ntlmRequest.sessionkey->data,
1367 sizeof(masterkey));
1368 EVP_CIPHER_CTX_free(rc4);
1369
1370 r.u.ntlmResponse.sessionkey =
1371 malloc(sizeof(*r.u.ntlmResponse.sessionkey));
1372 if (r.u.ntlmResponse.sessionkey == NULL) {
1373 ret = EINVAL;
1374 krb5_set_error_message(context, ret, "malloc: out of memory");
1375 goto out;
1376 }
1377
1378 ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
1379 masterkey, sizeof(masterkey));
1380 if (ret) {
1381 krb5_set_error_message(context, ret, "malloc: out of memory");
1382 goto out;
1383 }
1384 }
1385
1386 r.u.ntlmResponse.success = 1;
1387 kdc_log(context, config, 0, "NTLM version %d successful for %s",
1388 version, ireq.u.ntlmRequest.username);
1389 break;
1390 }
1391 case choice_DigestReqInner_supportedMechs:
1392
1393 kdc_log(context, config, 0, "digest supportedMechs from %s", from);
1394
1395 r.element = choice_DigestRepInner_supportedMechs;
1396 memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs));
1397
1398 if (config->digests_allowed & NTLM_V1)
1399 r.u.supportedMechs.ntlm_v1 = 1;
1400 if (config->digests_allowed & NTLM_V1_SESSION)
1401 r.u.supportedMechs.ntlm_v1_session = 1;
1402 if (config->digests_allowed & NTLM_V2)
1403 r.u.supportedMechs.ntlm_v2 = 1;
1404 if (config->digests_allowed & DIGEST_MD5)
1405 r.u.supportedMechs.digest_md5 = 1;
1406 if (config->digests_allowed & CHAP_MD5)
1407 r.u.supportedMechs.chap_md5 = 1;
1408 if (config->digests_allowed & MS_CHAP_V2)
1409 r.u.supportedMechs.ms_chap_v2 = 1;
1410 break;
1411
1412 default: {
1413 const char *s;
1414 ret = EINVAL;
1415 krb5_set_error_message(context, ret, "unknown operation to digest");
1416
1417 failed:
1418
1419 s = krb5_get_error_message(context, ret);
1420 if (s == NULL) {
1421 krb5_clear_error_message(context);
1422 goto out;
1423 }
1424
1425 kdc_log(context, config, 0, "Digest failed with: %s", s);
1426
1427 r.element = choice_DigestRepInner_error;
1428 r.u.error.reason = strdup("unknown error");
1429 krb5_free_error_message(context, s);
1430 if (r.u.error.reason == NULL) {
1431 ret = ENOMEM;
1432 krb5_set_error_message(context, ret, "malloc: out of memory");
1433 goto out;
1434 }
1435 r.u.error.code = EINVAL;
1436 break;
1437 }
1438 }
1439
1440 ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
1441 if (ret) {
1442 krb5_set_error_message(context, ret, "Failed to encode inner digest reply");
1443 goto out;
1444 }
1445 if (size != buf.length)
1446 krb5_abortx(context, "ASN1 internal error");
1447
1448 krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
1449
1450 ret = krb5_mk_rep (context, ac, &rep.apRep);
1451 if (ret)
1452 goto out;
1453
1454 {
1455 krb5_keyblock *key;
1456
1457 ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
1458 if (ret)
1459 goto out;
1460
1461 ret = krb5_crypto_init(context, key, 0, &crypto);
1462 krb5_free_keyblock (context, key);
1463 if (ret)
1464 goto out;
1465 }
1466
1467 ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
1468 buf.data, buf.length, 0,
1469 &rep.innerRep);
1470 if (ret) {
1471 krb5_prepend_error_message(context, ret, "Failed to encrypt digest: ");
1472 goto out;
1473 }
1474
1475 ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
1476 if (ret) {
1477 krb5_set_error_message(context, ret, "Failed to encode digest reply");
1478 goto out;
1479 }
1480 if (size != reply->length)
1481 krb5_abortx(context, "ASN1 internal error");
1482
1483
1484 out:
1485 if (ac)
1486 krb5_auth_con_free(context, ac);
1487 if (ret)
1488 krb5_warn(context, ret, "Digest request from %s failed", from);
1489 if (ticket)
1490 krb5_free_ticket(context, ticket);
1491 if (id)
1492 krb5_kt_close(context, id);
1493 if (crypto)
1494 krb5_crypto_destroy(context, crypto);
1495 if (sp)
1496 krb5_storage_free(sp);
1497 if (user)
1498 _kdc_free_ent (context, user);
1499 if (server)
1500 _kdc_free_ent (context, server);
1501 if (client)
1502 _kdc_free_ent (context, client);
1503 if (password) {
1504 memset(password, 0, strlen(password));
1505 free (password);
1506 }
1507 if (client_name)
1508 free (client_name);
1509 krb5_data_free(&buf);
1510 krb5_data_free(&serverNonce);
1511 free_Checksum(&res);
1512 free_DigestREP(&rep);
1513 free_DigestRepInner(&r);
1514 free_DigestReqInner(&ireq);
1515
1516 return ret;
1517 }
1518
1519 #endif /* DIGEST */
1520