source: branches/samba-3.5.x/source4/heimdal/kdc/digest.c

Last change on this file was 414, checked in by Herwig Bauernfeind, 16 years ago

Samba 3.5.0: Initial import

File size: 37.5 KB
Line 
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
46const 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
57static krb5_error_code
58get_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
82static char *
83get_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
101static krb5_error_code
102fill_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
140static 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};
146static 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};
153static 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
163static krb5_error_code
164get_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, &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
201krb5_error_code
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, *r;
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 r = krb5_principal_get_realm(context, principal);
284 if (r == NULL) {
285 krb5_free_principal(context, principal);
286 goto out;
287 }
288 if (strcmp(p, r) != 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, &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, &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_pseudo_bytes(&identifier, sizeof(identifier));
379 RAND_pseudo_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 MD5_CTX ctx;
617 unsigned char md[MD5_DIGEST_LENGTH];
618 char *mdx;
619 char id;
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, &id, 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 MD5_Init(&ctx);
646 MD5_Update(&ctx, &id, 1);
647 MD5_Update(&ctx, password, strlen(password));
648 MD5_Update(&ctx, serverNonce.data, serverNonce.length);
649 MD5_Final(md, &ctx);
650
651 hex_encode(md, sizeof(md), &mdx);
652 if (mdx == NULL) {
653 krb5_clear_error_message(context);
654 ret = ENOMEM;
655 goto out;
656 }
657
658 r.element = choice_DigestRepInner_response;
659
660 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
661 free(mdx);
662 if (ret == 0) {
663 r.u.response.success = TRUE;
664 } else {
665 kdc_log(context, config, 0,
666 "CHAP reply mismatch for %s",
667 ireq.u.digestRequest.username);
668 r.u.response.success = FALSE;
669 }
670
671 } else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
672 MD5_CTX ctx;
673 unsigned char md[MD5_DIGEST_LENGTH];
674 char *mdx;
675 char *A1, *A2;
676
677 if ((config->digests_allowed & DIGEST_MD5) == 0) {
678 kdc_log(context, config, 0, "Digest SASL MD5 not allowed");
679 goto out;
680 }
681
682 if (ireq.u.digestRequest.nonceCount == NULL)
683 goto out;
684 if (ireq.u.digestRequest.clientNonce == NULL)
685 goto out;
686 if (ireq.u.digestRequest.qop == NULL)
687 goto out;
688 if (ireq.u.digestRequest.realm == NULL)
689 goto out;
690
691 ret = get_password_entry(context, config,
692 ireq.u.digestRequest.username,
693 &password);
694 if (ret)
695 goto failed;
696
697 MD5_Init(&ctx);
698 MD5_Update(&ctx, ireq.u.digestRequest.username,
699 strlen(ireq.u.digestRequest.username));
700 MD5_Update(&ctx, ":", 1);
701 MD5_Update(&ctx, *ireq.u.digestRequest.realm,
702 strlen(*ireq.u.digestRequest.realm));
703 MD5_Update(&ctx, ":", 1);
704 MD5_Update(&ctx, password, strlen(password));
705 MD5_Final(md, &ctx);
706
707 MD5_Init(&ctx);
708 MD5_Update(&ctx, md, sizeof(md));
709 MD5_Update(&ctx, ":", 1);
710 MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
711 strlen(ireq.u.digestRequest.serverNonce));
712 MD5_Update(&ctx, ":", 1);
713 MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
714 strlen(*ireq.u.digestRequest.nonceCount));
715 if (ireq.u.digestRequest.authid) {
716 MD5_Update(&ctx, ":", 1);
717 MD5_Update(&ctx, *ireq.u.digestRequest.authid,
718 strlen(*ireq.u.digestRequest.authid));
719 }
720 MD5_Final(md, &ctx);
721 hex_encode(md, sizeof(md), &A1);
722 if (A1 == NULL) {
723 ret = ENOMEM;
724 krb5_set_error_message(context, ret, "malloc: out of memory");
725 goto failed;
726 }
727
728 MD5_Init(&ctx);
729 MD5_Update(&ctx, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
730 MD5_Update(&ctx, *ireq.u.digestRequest.uri,
731 strlen(*ireq.u.digestRequest.uri));
732
733 /* conf|int */
734 if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
735 static char conf_zeros[] = ":00000000000000000000000000000000";
736 MD5_Update(&ctx, conf_zeros, sizeof(conf_zeros) - 1);
737 }
738
739 MD5_Final(md, &ctx);
740 hex_encode(md, sizeof(md), &A2);
741 if (A2 == NULL) {
742 ret = ENOMEM;
743 krb5_set_error_message(context, ret, "malloc: out of memory");
744 free(A1);
745 goto failed;
746 }
747
748 MD5_Init(&ctx);
749 MD5_Update(&ctx, A1, strlen(A2));
750 MD5_Update(&ctx, ":", 1);
751 MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
752 strlen(ireq.u.digestRequest.serverNonce));
753 MD5_Update(&ctx, ":", 1);
754 MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
755 strlen(*ireq.u.digestRequest.nonceCount));
756 MD5_Update(&ctx, ":", 1);
757 MD5_Update(&ctx, *ireq.u.digestRequest.clientNonce,
758 strlen(*ireq.u.digestRequest.clientNonce));
759 MD5_Update(&ctx, ":", 1);
760 MD5_Update(&ctx, *ireq.u.digestRequest.qop,
761 strlen(*ireq.u.digestRequest.qop));
762 MD5_Update(&ctx, ":", 1);
763 MD5_Update(&ctx, A2, strlen(A2));
764
765 MD5_Final(md, &ctx);
766
767 free(A1);
768 free(A2);
769
770 hex_encode(md, sizeof(md), &mdx);
771 if (mdx == NULL) {
772 krb5_clear_error_message(context);
773 ret = ENOMEM;
774 goto out;
775 }
776
777 r.element = choice_DigestRepInner_response;
778 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
779 free(mdx);
780 if (ret == 0) {
781 r.u.response.success = TRUE;
782 } else {
783 kdc_log(context, config, 0,
784 "DIGEST-MD5 reply mismatch for %s",
785 ireq.u.digestRequest.username);
786 r.u.response.success = FALSE;
787 }
788
789 } else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) {
790 unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH];
791 krb5_principal clientprincipal = NULL;
792 char *mdx;
793 const char *username;
794 struct ntlm_buf answer;
795 Key *key = NULL;
796 SHA_CTX ctx;
797
798 if ((config->digests_allowed & MS_CHAP_V2) == 0) {
799 kdc_log(context, config, 0, "MS-CHAP-V2 not allowed");
800 goto failed;
801 }
802
803 if (ireq.u.digestRequest.clientNonce == NULL) {
804 ret = EINVAL;
805 krb5_set_error_message(context, ret,
806 "MS-CHAP-V2 clientNonce missing");
807 goto failed;
808 }
809 if (serverNonce.length != 16) {
810 ret = EINVAL;
811 krb5_set_error_message(context, ret,
812 "MS-CHAP-V2 serverNonce wrong length");
813 goto failed;
814 }
815
816 /* strip of the domain component */
817 username = strchr(ireq.u.digestRequest.username, '\\');
818 if (username == NULL)
819 username = ireq.u.digestRequest.username;
820 else
821 username++;
822
823 /* ChallangeHash */
824 SHA1_Init(&ctx);
825 {
826 ssize_t ssize;
827 krb5_data clientNonce;
828
829 clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce);
830 clientNonce.data = malloc(clientNonce.length);
831 if (clientNonce.data == NULL) {
832 ret = ENOMEM;
833 krb5_set_error_message(context, ret, "malloc: out of memory");
834 goto out;
835 }
836
837 ssize = hex_decode(*ireq.u.digestRequest.clientNonce,
838 clientNonce.data, clientNonce.length);
839 if (ssize != 16) {
840 ret = ENOMEM;
841 krb5_set_error_message(context, ret,
842 "Failed to decode clientNonce");
843 goto out;
844 }
845 SHA1_Update(&ctx, clientNonce.data, ssize);
846 free(clientNonce.data);
847 }
848 SHA1_Update(&ctx, serverNonce.data, serverNonce.length);
849 SHA1_Update(&ctx, username, strlen(username));
850 SHA1_Final(challange, &ctx);
851
852 /* NtPasswordHash */
853 ret = krb5_parse_name(context, username, &clientprincipal);
854 if (ret)
855 goto failed;
856
857 ret = _kdc_db_fetch(context, config, clientprincipal,
858 HDB_F_GET_CLIENT, NULL, &user);
859 krb5_free_principal(context, clientprincipal);
860 if (ret) {
861 krb5_set_error_message(context, ret,
862 "MS-CHAP-V2 user %s not in database",
863 username);
864 goto failed;
865 }
866
867 ret = hdb_enctype2key(context, &user->entry,
868 ETYPE_ARCFOUR_HMAC_MD5, &key);
869 if (ret) {
870 krb5_set_error_message(context, ret,
871 "MS-CHAP-V2 missing arcfour key %s",
872 username);
873 goto failed;
874 }
875
876 /* ChallengeResponse */
877 ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
878 key->key.keyvalue.length,
879 challange, &answer);
880 if (ret) {
881 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
882 goto failed;
883 }
884
885 hex_encode(answer.data, answer.length, &mdx);
886 if (mdx == NULL) {
887 free(answer.data);
888 krb5_clear_error_message(context);
889 ret = ENOMEM;
890 goto out;
891 }
892
893 r.element = choice_DigestRepInner_response;
894 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
895 if (ret == 0) {
896 r.u.response.success = TRUE;
897 } else {
898 kdc_log(context, config, 0,
899 "MS-CHAP-V2 hash mismatch for %s",
900 ireq.u.digestRequest.username);
901 r.u.response.success = FALSE;
902 }
903 free(mdx);
904
905 if (r.u.response.success) {
906 unsigned char hashhash[MD4_DIGEST_LENGTH];
907
908 /* hashhash */
909 {
910 MD4_CTX hctx;
911
912 MD4_Init(&hctx);
913 MD4_Update(&hctx, key->key.keyvalue.data,
914 key->key.keyvalue.length);
915 MD4_Final(hashhash, &hctx);
916 }
917
918 /* GenerateAuthenticatorResponse */
919 SHA1_Init(&ctx);
920 SHA1_Update(&ctx, hashhash, sizeof(hashhash));
921 SHA1_Update(&ctx, answer.data, answer.length);
922 SHA1_Update(&ctx, ms_chap_v2_magic1,sizeof(ms_chap_v2_magic1));
923 SHA1_Final(md, &ctx);
924
925 SHA1_Init(&ctx);
926 SHA1_Update(&ctx, md, sizeof(md));
927 SHA1_Update(&ctx, challange, 8);
928 SHA1_Update(&ctx, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2));
929 SHA1_Final(md, &ctx);
930
931 r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp));
932 if (r.u.response.rsp == NULL) {
933 free(answer.data);
934 krb5_clear_error_message(context);
935 ret = ENOMEM;
936 goto out;
937 }
938
939 hex_encode(md, sizeof(md), r.u.response.rsp);
940 if (r.u.response.rsp == NULL) {
941 free(answer.data);
942 krb5_clear_error_message(context);
943 ret = ENOMEM;
944 goto out;
945 }
946
947 /* get_master, rfc 3079 3.4 */
948 SHA1_Init(&ctx);
949 SHA1_Update(&ctx, hashhash, 16); /* md4(hash) */
950 SHA1_Update(&ctx, answer.data, answer.length);
951 SHA1_Update(&ctx, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1));
952 SHA1_Final(md, &ctx);
953
954 free(answer.data);
955
956 r.u.response.session_key =
957 calloc(1, sizeof(*r.u.response.session_key));
958 if (r.u.response.session_key == NULL) {
959 krb5_clear_error_message(context);
960 ret = ENOMEM;
961 goto out;
962 }
963
964 ret = krb5_data_copy(r.u.response.session_key, md, 16);
965 if (ret) {
966 krb5_clear_error_message(context);
967 goto out;
968 }
969 }
970
971 } else {
972 r.element = choice_DigestRepInner_error;
973 asprintf(&r.u.error.reason, "Unsupported digest type %s",
974 ireq.u.digestRequest.type);
975 if (r.u.error.reason == NULL) {
976 ret = ENOMEM;
977 krb5_set_error_message(context, ret, "malloc: out of memory");
978 goto out;
979 }
980 r.u.error.code = EINVAL;
981 }
982
983 kdc_log(context, config, 0, "Digest %s request successful %s",
984 ireq.u.digestRequest.type, ireq.u.digestRequest.username);
985
986 break;
987 }
988 case choice_DigestReqInner_ntlmInit:
989
990 if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) {
991 kdc_log(context, config, 0, "NTLM not allowed");
992 goto failed;
993 }
994
995 r.element = choice_DigestRepInner_ntlmInitReply;
996
997 r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE;
998
999 if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) {
1000 kdc_log(context, config, 0, "NTLM client have no unicode");
1001 goto failed;
1002 }
1003
1004 if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM)
1005 r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM;
1006 else {
1007 kdc_log(context, config, 0, "NTLM client doesn't support NTLM");
1008 goto failed;
1009 }
1010
1011 r.u.ntlmInitReply.flags |=
1012 NTLM_NEG_TARGET |
1013 NTLM_TARGET_DOMAIN |
1014 NTLM_ENC_128;
1015
1016#define ALL \
1017 NTLM_NEG_SIGN| \
1018 NTLM_NEG_SEAL| \
1019 NTLM_NEG_ALWAYS_SIGN| \
1020 NTLM_NEG_NTLM2_SESSION| \
1021 NTLM_NEG_KEYEX
1022
1023 r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
1024
1025#undef ALL
1026
1027 r.u.ntlmInitReply.targetname =
1028 get_ntlm_targetname(context, client);
1029 if (r.u.ntlmInitReply.targetname == NULL) {
1030 ret = ENOMEM;
1031 krb5_set_error_message(context, ret, "malloc: out of memory");
1032 goto out;
1033 }
1034 r.u.ntlmInitReply.challange.data = malloc(8);
1035 if (r.u.ntlmInitReply.challange.data == NULL) {
1036 ret = ENOMEM;
1037 krb5_set_error_message(context, ret, "malloc: out of memory");
1038 goto out;
1039 }
1040 r.u.ntlmInitReply.challange.length = 8;
1041 if (RAND_bytes(r.u.ntlmInitReply.challange.data,
1042 r.u.ntlmInitReply.challange.length) != 1)
1043 {
1044 ret = ENOMEM;
1045 krb5_set_error_message(context, ret, "out of random error");
1046 goto out;
1047 }
1048 /* XXX fix targetinfo */
1049 ALLOC(r.u.ntlmInitReply.targetinfo);
1050 if (r.u.ntlmInitReply.targetinfo == NULL) {
1051 ret = ENOMEM;
1052 krb5_set_error_message(context, ret, "malloc: out of memory");
1053 goto out;
1054 }
1055
1056 ret = fill_targetinfo(context,
1057 r.u.ntlmInitReply.targetname,
1058 client,
1059 r.u.ntlmInitReply.targetinfo);
1060 if (ret) {
1061 ret = ENOMEM;
1062 krb5_set_error_message(context, ret, "malloc: out of memory");
1063 goto out;
1064 }
1065
1066 /*
1067 * Save data encryted in opaque for the second part of the
1068 * ntlm authentication
1069 */
1070 sp = krb5_storage_emem();
1071 if (sp == NULL) {
1072 ret = ENOMEM;
1073 krb5_set_error_message(context, ret, "malloc: out of memory");
1074 goto out;
1075 }
1076
1077 ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8);
1078 if (ret != 8) {
1079 ret = ENOMEM;
1080 krb5_set_error_message(context, ret, "storage write challange");
1081 goto out;
1082 }
1083 ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
1084 if (ret) {
1085 krb5_clear_error_message(context);
1086 goto out;
1087 }
1088
1089 ret = krb5_storage_to_data(sp, &buf);
1090 if (ret) {
1091 krb5_clear_error_message(context);
1092 goto out;
1093 }
1094
1095 ret = get_digest_key(context, config, server, &crypto);
1096 if (ret)
1097 goto out;
1098
1099 ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1100 buf.data, buf.length, &r.u.ntlmInitReply.opaque);
1101 krb5_data_free(&buf);
1102 krb5_crypto_destroy(context, crypto);
1103 crypto = NULL;
1104 if (ret)
1105 goto out;
1106
1107 kdc_log(context, config, 0, "NTLM init from %s", from);
1108
1109 break;
1110
1111 case choice_DigestReqInner_ntlmRequest: {
1112 krb5_principal clientprincipal;
1113 unsigned char sessionkey[16];
1114 unsigned char challange[8];
1115 uint32_t flags;
1116 Key *key = NULL;
1117 int version;
1118
1119 r.element = choice_DigestRepInner_ntlmResponse;
1120 r.u.ntlmResponse.success = 0;
1121 r.u.ntlmResponse.flags = 0;
1122 r.u.ntlmResponse.sessionkey = NULL;
1123 r.u.ntlmResponse.tickets = NULL;
1124
1125 /* get username */
1126 ret = krb5_parse_name(context,
1127 ireq.u.ntlmRequest.username,
1128 &clientprincipal);
1129 if (ret)
1130 goto failed;
1131
1132 ret = _kdc_db_fetch(context, config, clientprincipal,
1133 HDB_F_GET_CLIENT, NULL, &user);
1134 krb5_free_principal(context, clientprincipal);
1135 if (ret) {
1136 krb5_set_error_message(context, ret, "NTLM user %s not in database",
1137 ireq.u.ntlmRequest.username);
1138 goto failed;
1139 }
1140
1141 ret = get_digest_key(context, config, server, &crypto);
1142 if (ret)
1143 goto failed;
1144
1145 ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1146 ireq.u.ntlmRequest.opaque.data,
1147 ireq.u.ntlmRequest.opaque.length, &buf);
1148 krb5_crypto_destroy(context, crypto);
1149 crypto = NULL;
1150 if (ret) {
1151 kdc_log(context, config, 0,
1152 "Failed to decrypt nonce from %s", from);
1153 goto failed;
1154 }
1155
1156 sp = krb5_storage_from_data(&buf);
1157 if (sp == NULL) {
1158 ret = ENOMEM;
1159 krb5_set_error_message(context, ret, "malloc: out of memory");
1160 goto out;
1161 }
1162
1163 ret = krb5_storage_read(sp, challange, sizeof(challange));
1164 if (ret != sizeof(challange)) {
1165 ret = ENOMEM;
1166 krb5_set_error_message(context, ret, "NTLM storage read challange");
1167 goto out;
1168 }
1169 ret = krb5_ret_uint32(sp, &flags);
1170 if (ret) {
1171 krb5_set_error_message(context, ret, "NTLM storage read flags");
1172 goto out;
1173 }
1174 krb5_storage_free(sp);
1175 sp = NULL;
1176 krb5_data_free(&buf);
1177
1178 if ((flags & NTLM_NEG_NTLM) == 0) {
1179 ret = EINVAL;
1180 krb5_set_error_message(context, ret, "NTLM not negotiated");
1181 goto out;
1182 }
1183
1184 ret = hdb_enctype2key(context, &user->entry,
1185 ETYPE_ARCFOUR_HMAC_MD5, &key);
1186 if (ret) {
1187 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1188 goto out;
1189 }
1190
1191 /* check if this is NTLMv2 */
1192 if (ireq.u.ntlmRequest.ntlm.length != 24) {
1193 struct ntlm_buf infotarget, answer;
1194 char *targetname;
1195
1196 if ((config->digests_allowed & NTLM_V2) == 0) {
1197 kdc_log(context, config, 0, "NTLM v2 not allowed");
1198 goto out;
1199 }
1200
1201 version = 2;
1202
1203 targetname = get_ntlm_targetname(context, client);
1204 if (targetname == NULL) {
1205 ret = ENOMEM;
1206 krb5_set_error_message(context, ret, "malloc: out of memory");
1207 goto out;
1208 }
1209
1210 answer.length = ireq.u.ntlmRequest.ntlm.length;
1211 answer.data = ireq.u.ntlmRequest.ntlm.data;
1212
1213 ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
1214 key->key.keyvalue.length,
1215 ireq.u.ntlmRequest.username,
1216 targetname,
1217 0,
1218 challange,
1219 &answer,
1220 &infotarget,
1221 sessionkey);
1222 free(targetname);
1223 if (ret) {
1224 krb5_set_error_message(context, ret, "NTLM v2 verify failed");
1225 goto failed;
1226 }
1227
1228 /* XXX verify infotarget matches client (checksum ?) */
1229
1230 free(infotarget.data);
1231 /* */
1232
1233 } else {
1234 struct ntlm_buf answer;
1235
1236 version = 1;
1237
1238 if (flags & NTLM_NEG_NTLM2_SESSION) {
1239 unsigned char sessionhash[MD5_DIGEST_LENGTH];
1240 MD5_CTX md5ctx;
1241
1242 if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
1243 kdc_log(context, config, 0, "NTLM v1-session not allowed");
1244 ret = EINVAL;
1245 goto failed;
1246 }
1247
1248 if (ireq.u.ntlmRequest.lm.length != 24) {
1249 ret = EINVAL;
1250 krb5_set_error_message(context, ret, "LM hash have wrong length "
1251 "for NTLM session key");
1252 goto failed;
1253 }
1254
1255 MD5_Init(&md5ctx);
1256 MD5_Update(&md5ctx, challange, sizeof(challange));
1257 MD5_Update(&md5ctx, ireq.u.ntlmRequest.lm.data, 8);
1258 MD5_Final(sessionhash, &md5ctx);
1259 memcpy(challange, sessionhash, sizeof(challange));
1260 } else {
1261 if ((config->digests_allowed & NTLM_V1) == 0) {
1262 kdc_log(context, config, 0, "NTLM v1 not allowed");
1263 goto failed;
1264 }
1265 }
1266
1267 ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
1268 key->key.keyvalue.length,
1269 challange, &answer);
1270 if (ret) {
1271 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1272 goto failed;
1273 }
1274
1275 if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
1276 memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
1277 {
1278 free(answer.data);
1279 ret = EINVAL;
1280 krb5_set_error_message(context, ret, "NTLM hash mismatch");
1281 goto failed;
1282 }
1283 free(answer.data);
1284
1285 {
1286 MD4_CTX ctx;
1287
1288 MD4_Init(&ctx);
1289 MD4_Update(&ctx,
1290 key->key.keyvalue.data, key->key.keyvalue.length);
1291 MD4_Final(sessionkey, &ctx);
1292 }
1293 }
1294
1295 if (ireq.u.ntlmRequest.sessionkey) {
1296 unsigned char masterkey[MD4_DIGEST_LENGTH];
1297 RC4_KEY rc4;
1298 size_t len;
1299
1300 if ((flags & NTLM_NEG_KEYEX) == 0) {
1301 ret = EINVAL;
1302 krb5_set_error_message(context, ret,
1303 "NTLM client failed to neg key "
1304 "exchange but still sent key");
1305 goto failed;
1306 }
1307
1308 len = ireq.u.ntlmRequest.sessionkey->length;
1309 if (len != sizeof(masterkey)){
1310 ret = EINVAL;
1311 krb5_set_error_message(context, ret,
1312 "NTLM master key wrong length: %lu",
1313 (unsigned long)len);
1314 goto failed;
1315 }
1316
1317 RC4_set_key(&rc4, sizeof(sessionkey), sessionkey);
1318
1319 RC4(&rc4, sizeof(masterkey),
1320 ireq.u.ntlmRequest.sessionkey->data,
1321 masterkey);
1322 memset(&rc4, 0, sizeof(rc4));
1323
1324 r.u.ntlmResponse.sessionkey =
1325 malloc(sizeof(*r.u.ntlmResponse.sessionkey));
1326 if (r.u.ntlmResponse.sessionkey == NULL) {
1327 ret = EINVAL;
1328 krb5_set_error_message(context, ret, "malloc: out of memory");
1329 goto out;
1330 }
1331
1332 ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
1333 masterkey, sizeof(masterkey));
1334 if (ret) {
1335 krb5_set_error_message(context, ret, "malloc: out of memory");
1336 goto out;
1337 }
1338 }
1339
1340 r.u.ntlmResponse.success = 1;
1341 kdc_log(context, config, 0, "NTLM version %d successful for %s",
1342 version, ireq.u.ntlmRequest.username);
1343 break;
1344 }
1345 case choice_DigestReqInner_supportedMechs:
1346
1347 kdc_log(context, config, 0, "digest supportedMechs from %s", from);
1348
1349 r.element = choice_DigestRepInner_supportedMechs;
1350 memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs));
1351
1352 if (config->digests_allowed & NTLM_V1)
1353 r.u.supportedMechs.ntlm_v1 = 1;
1354 if (config->digests_allowed & NTLM_V1_SESSION)
1355 r.u.supportedMechs.ntlm_v1_session = 1;
1356 if (config->digests_allowed & NTLM_V2)
1357 r.u.supportedMechs.ntlm_v2 = 1;
1358 if (config->digests_allowed & DIGEST_MD5)
1359 r.u.supportedMechs.digest_md5 = 1;
1360 if (config->digests_allowed & CHAP_MD5)
1361 r.u.supportedMechs.chap_md5 = 1;
1362 if (config->digests_allowed & MS_CHAP_V2)
1363 r.u.supportedMechs.ms_chap_v2 = 1;
1364 break;
1365
1366 default: {
1367 const char *s;
1368 ret = EINVAL;
1369 krb5_set_error_message(context, ret, "unknown operation to digest");
1370
1371 failed:
1372
1373 s = krb5_get_error_message(context, ret);
1374 if (s == NULL) {
1375 krb5_clear_error_message(context);
1376 goto out;
1377 }
1378
1379 kdc_log(context, config, 0, "Digest failed with: %s", s);
1380
1381 r.element = choice_DigestRepInner_error;
1382 r.u.error.reason = strdup("unknown error");
1383 krb5_free_error_message(context, s);
1384 if (r.u.error.reason == NULL) {
1385 ret = ENOMEM;
1386 krb5_set_error_message(context, ret, "malloc: out of memory");
1387 goto out;
1388 }
1389 r.u.error.code = EINVAL;
1390 break;
1391 }
1392 }
1393
1394 ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
1395 if (ret) {
1396 krb5_set_error_message(context, ret, "Failed to encode inner digest reply");
1397 goto out;
1398 }
1399 if (size != buf.length)
1400 krb5_abortx(context, "ASN1 internal error");
1401
1402 krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
1403
1404 ret = krb5_mk_rep (context, ac, &rep.apRep);
1405 if (ret)
1406 goto out;
1407
1408 {
1409 krb5_keyblock *key;
1410
1411 ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
1412 if (ret)
1413 goto out;
1414
1415 ret = krb5_crypto_init(context, key, 0, &crypto);
1416 krb5_free_keyblock (context, key);
1417 if (ret)
1418 goto out;
1419 }
1420
1421 ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
1422 buf.data, buf.length, 0,
1423 &rep.innerRep);
1424
1425 ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
1426 if (ret) {
1427 krb5_set_error_message(context, ret, "Failed to encode digest reply");
1428 goto out;
1429 }
1430 if (size != reply->length)
1431 krb5_abortx(context, "ASN1 internal error");
1432
1433
1434 out:
1435 if (ac)
1436 krb5_auth_con_free(context, ac);
1437 if (ret)
1438 krb5_warn(context, ret, "Digest request from %s failed", from);
1439 if (ticket)
1440 krb5_free_ticket(context, ticket);
1441 if (id)
1442 krb5_kt_close(context, id);
1443 if (crypto)
1444 krb5_crypto_destroy(context, crypto);
1445 if (sp)
1446 krb5_storage_free(sp);
1447 if (user)
1448 _kdc_free_ent (context, user);
1449 if (server)
1450 _kdc_free_ent (context, server);
1451 if (client)
1452 _kdc_free_ent (context, client);
1453 if (password) {
1454 memset(password, 0, strlen(password));
1455 free (password);
1456 }
1457 if (client_name)
1458 free (client_name);
1459 krb5_data_free(&buf);
1460 krb5_data_free(&serverNonce);
1461 free_Checksum(&res);
1462 free_DigestREP(&rep);
1463 free_DigestRepInner(&r);
1464 free_DigestReqInner(&ireq);
1465
1466 return ret;
1467}
1468
1469#endif /* DIGEST */
Note: See TracBrowser for help on using the repository browser.