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