etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / kdc / digest.c
blob31eed38dc9a7b37ba696ac2e7271a48c2ddf78d3
1 /* $NetBSD: digest.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $ */
3 /*
4 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "kdc_locl.h"
37 #include <krb5/hex.h>
39 #ifdef DIGEST
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
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}
59 static krb5_error_code
60 get_digest_key(krb5_context context,
61 krb5_kdc_configuration *config,
62 hdb_entry_ex *server,
63 krb5_crypto *crypto)
65 krb5_error_code ret;
66 krb5_enctype enctype;
67 Key *key;
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);
84 static char *
85 get_ntlm_targetname(krb5_context context,
86 hdb_entry_ex *client)
88 char *targetname, *p;
90 targetname = strdup(krb5_principal_get_realm(context,
91 client->entry.principal));
92 if (targetname == NULL)
93 return NULL;
95 p = strchr(targetname, '.');
96 if (p)
97 *p = '\0';
99 strupr(targetname);
100 return targetname;
103 static krb5_error_code
104 fill_targetinfo(krb5_context context,
105 char *targetname,
106 hdb_entry_ex *client,
107 krb5_data *data)
109 struct ntlm_targetinfo ti;
110 krb5_error_code ret;
111 struct ntlm_buf d;
112 krb5_principal p;
113 const char *str;
115 memset(&ti, 0, sizeof(ti));
117 ti.domainname = targetname;
118 p = client->entry.principal;
119 str = krb5_principal_get_comp_string(context, p, 0);
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)))
127 str = krb5_principal_get_comp_string(context, p, 1);
128 ti.dnsservername = rk_UNCONST(str);
131 ret = heim_ntlm_encode_targetinfo(&ti, 1, &d);
132 if (ret)
133 return ret;
135 data->data = d.data;
136 data->length = d.length;
138 return 0;
142 static const unsigned char ms_chap_v2_magic1[39] = {
143 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
144 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
145 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
146 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
148 static const unsigned char ms_chap_v2_magic2[41] = {
149 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
150 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
151 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
152 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
153 0x6E
155 static const unsigned char ms_rfc3079_magic1[27] = {
156 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
157 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
158 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
165 static krb5_error_code
166 get_password_entry(krb5_context context,
167 krb5_kdc_configuration *config,
168 const char *username,
169 char **password)
171 krb5_principal clientprincipal;
172 krb5_error_code ret;
173 hdb_entry_ex *user;
174 HDB *db;
176 /* get username */
177 ret = krb5_parse_name(context, username, &clientprincipal);
178 if (ret)
179 return ret;
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;
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");
193 memset(user, 0, sizeof(*user));
195 _kdc_free_ent (context, user);
196 return ret;
203 krb5_error_code
204 _kdc_do_digest(krb5_context context,
205 krb5_kdc_configuration *config,
206 const struct DigestREQ *req, krb5_data *reply,
207 const char *from, struct sockaddr *addr)
209 krb5_error_code ret = 0;
210 krb5_ticket *ticket = NULL;
211 krb5_auth_context ac = NULL;
212 krb5_keytab id = NULL;
213 krb5_crypto crypto = NULL;
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;
227 if(!config->enable_digest) {
228 kdc_log(context, config, 0,
229 "Rejected digest request (disabled) from %s", from);
230 return KRB5KDC_ERR_POLICY;
233 krb5_data_zero(&buf);
234 krb5_data_zero(reply);
235 krb5_data_zero(&serverNonce);
236 memset(&ireq, 0, sizeof(ireq));
237 memset(&r, 0, sizeof(r));
238 memset(&rep, 0, sizeof(rep));
239 memset(&res, 0, sizeof(res));
241 kdc_log(context, config, 0, "Digest request from %s", from);
243 ret = krb5_kt_resolve(context, "HDB:", &id);
244 if (ret) {
245 kdc_log(context, config, 0, "Can't open database for digest");
246 goto out;
249 ret = krb5_rd_req(context,
250 &ac,
251 &req->apReq,
252 NULL,
254 &ap_req_options,
255 &ticket);
256 if (ret)
257 goto out;
259 /* check the server principal in the ticket matches digest/R@R */
261 krb5_principal principal = NULL;
262 const char *p, *rr;
264 ret = krb5_ticket_get_server(context, ticket, &principal);
265 if (ret)
266 goto out;
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;
275 if (strcmp(p, KRB5_DIGEST_NAME) != 0) {
276 krb5_free_principal(context, principal);
277 goto out;
280 p = krb5_principal_get_comp_string(context, principal, 1);
281 if (p == NULL) {
282 krb5_free_principal(context, principal);
283 goto out;
285 rr = krb5_principal_get_realm(context, principal);
286 if (rr == NULL) {
287 krb5_free_principal(context, principal);
288 goto out;
290 if (strcmp(p, rr) != 0) {
291 krb5_free_principal(context, principal);
292 goto out;
294 krb5_clear_error_message(context);
296 ret = _kdc_db_fetch(context, config, principal,
297 HDB_F_GET_SERVER, NULL, NULL, &server);
298 if (ret)
299 goto out;
301 krb5_free_principal(context, principal);
304 /* check the client is allowed to do digest auth */
306 krb5_principal principal = NULL;
308 ret = krb5_ticket_get_client(context, ticket, &principal);
309 if (ret)
310 goto out;
312 ret = krb5_unparse_name(context, principal, &client_name);
313 if (ret) {
314 krb5_free_principal(context, principal);
315 goto out;
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;
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;
336 /* unpack request */
338 krb5_keyblock *key;
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;
349 ret = krb5_crypto_init(context, key, 0, &crypto);
350 krb5_free_keyblock (context, key);
351 if (ret)
352 goto out;
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;
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;
369 kdc_log(context, config, 0, "Valid digest request from %s (%s)",
370 client_name, from);
373 * Process the inner request
376 switch (ireq.element) {
377 case choice_DigestReqInner_init: {
378 unsigned char server_nonce[16], identifier;
380 RAND_pseudo_bytes(&identifier, sizeof(identifier));
381 RAND_pseudo_bytes(server_nonce, sizeof(server_nonce));
383 server_nonce[0] = kdc_time & 0xff;
384 server_nonce[1] = (kdc_time >> 8) & 0xff;
385 server_nonce[2] = (kdc_time >> 16) & 0xff;
386 server_nonce[3] = (kdc_time >> 24) & 0xff;
388 r.element = choice_DigestRepInner_initReply;
390 hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce);
391 if (r.u.initReply.nonce == NULL) {
392 ret = ENOMEM;
393 krb5_set_error_message(context, ret, "Failed to decode server nonce");
394 goto out;
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;
403 ret = krb5_store_stringz(sp, ireq.u.init.type);
404 if (ret) {
405 krb5_clear_error_message(context);
406 goto out;
409 if (ireq.u.init.channel) {
410 char *s;
412 asprintf(&s, "%s-%s:%s", r.u.initReply.nonce,
413 ireq.u.init.channel->cb_type,
414 ireq.u.init.channel->cb_binding);
415 if (s == NULL) {
416 ret = ENOMEM;
417 krb5_set_error_message(context, ret,
418 "Failed to allocate channel binding");
419 goto out;
421 free(r.u.initReply.nonce);
422 r.u.initReply.nonce = s;
425 ret = krb5_store_stringz(sp, r.u.initReply.nonce);
426 if (ret) {
427 krb5_clear_error_message(context);
428 goto out;
431 if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
432 r.u.initReply.identifier =
433 malloc(sizeof(*r.u.initReply.identifier));
434 if (r.u.initReply.identifier == NULL) {
435 ret = ENOMEM;
436 krb5_set_error_message(context, ret, "malloc: out of memory");
437 goto out;
440 asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff);
441 if (*r.u.initReply.identifier == NULL) {
442 ret = ENOMEM;
443 krb5_set_error_message(context, ret, "malloc: out of memory");
444 goto out;
447 } else
448 r.u.initReply.identifier = NULL;
450 if (ireq.u.init.hostname) {
451 ret = krb5_store_stringz(sp, *ireq.u.init.hostname);
452 if (ret) {
453 krb5_clear_error_message(context);
454 goto out;
458 ret = krb5_storage_to_data(sp, &buf);
459 if (ret) {
460 krb5_clear_error_message(context);
461 goto out;
464 ret = get_digest_key(context, config, server, &crypto);
465 if (ret)
466 goto out;
468 ret = krb5_create_checksum(context,
469 crypto,
470 KRB5_KU_DIGEST_OPAQUE,
472 buf.data,
473 buf.length,
474 &res);
475 krb5_crypto_destroy(context, crypto);
476 crypto = NULL;
477 krb5_data_free(&buf);
478 if (ret)
479 goto out;
481 ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret);
482 free_Checksum(&res);
483 if (ret) {
484 krb5_set_error_message(context, ret, "Failed to encode "
485 "checksum in digest request");
486 goto out;
488 if (size != buf.length)
489 krb5_abortx(context, "ASN1 internal error");
491 hex_encode(buf.data, buf.length, &r.u.initReply.opaque);
492 free(buf.data);
493 krb5_data_zero(&buf);
494 if (r.u.initReply.opaque == NULL) {
495 krb5_clear_error_message(context);
496 ret = ENOMEM;
497 goto out;
500 kdc_log(context, config, 0, "Digest %s init request successful from %s",
501 ireq.u.init.type, from);
503 break;
505 case choice_DigestReqInner_digestRequest: {
506 sp = krb5_storage_emem();
507 if (sp == NULL) {
508 ret = ENOMEM;
509 krb5_set_error_message(context, ret, "malloc: out of memory");
510 goto out;
512 ret = krb5_store_stringz(sp, ireq.u.digestRequest.type);
513 if (ret) {
514 krb5_clear_error_message(context);
515 goto out;
518 krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
520 if (ireq.u.digestRequest.hostname) {
521 ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname);
522 if (ret) {
523 krb5_clear_error_message(context);
524 goto out;
528 buf.length = strlen(ireq.u.digestRequest.opaque);
529 buf.data = malloc(buf.length);
530 if (buf.data == NULL) {
531 ret = ENOMEM;
532 krb5_set_error_message(context, ret, "malloc: out of memory");
533 goto out;
536 ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
537 if (ret <= 0) {
538 ret = ENOMEM;
539 krb5_set_error_message(context, ret, "Failed to decode opaque");
540 goto out;
542 buf.length = ret;
544 ret = decode_Checksum(buf.data, buf.length, &res, NULL);
545 free(buf.data);
546 krb5_data_zero(&buf);
547 if (ret) {
548 krb5_set_error_message(context, ret,
549 "Failed to decode digest Checksum");
550 goto out;
553 ret = krb5_storage_to_data(sp, &buf);
554 if (ret) {
555 krb5_clear_error_message(context);
556 goto out;
559 serverNonce.length = strlen(ireq.u.digestRequest.serverNonce);
560 serverNonce.data = malloc(serverNonce.length);
561 if (serverNonce.data == NULL) {
562 ret = ENOMEM;
563 krb5_set_error_message(context, ret, "malloc: out of memory");
564 goto out;
568 * CHAP does the checksum of the raw nonce, but do it for all
569 * types, since we need to check the timestamp.
572 ssize_t ssize;
574 ssize = hex_decode(ireq.u.digestRequest.serverNonce,
575 serverNonce.data, serverNonce.length);
576 if (ssize <= 0) {
577 ret = ENOMEM;
578 krb5_set_error_message(context, ret, "Failed to decode serverNonce");
579 goto out;
581 serverNonce.length = ssize;
584 ret = get_digest_key(context, config, server, &crypto);
585 if (ret)
586 goto out;
588 ret = krb5_verify_checksum(context, crypto,
589 KRB5_KU_DIGEST_OPAQUE,
590 buf.data, buf.length, &res);
591 free_Checksum(&res);
592 krb5_data_free(&buf);
593 krb5_crypto_destroy(context, crypto);
594 crypto = NULL;
595 if (ret)
596 goto out;
598 /* verify time */
600 unsigned char *p = serverNonce.data;
601 uint32_t t;
603 if (serverNonce.length < 4) {
604 ret = EINVAL;
605 krb5_set_error_message(context, ret, "server nonce too short");
606 goto out;
608 t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
610 if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) {
611 ret = EINVAL;
612 krb5_set_error_message(context, ret, "time screw in server nonce ");
613 goto out;
617 if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) {
618 EVP_MD_CTX *ctx;
619 unsigned char md[MD5_DIGEST_LENGTH];
620 char *mdx;
621 char idx;
623 if ((config->digests_allowed & CHAP_MD5) == 0) {
624 kdc_log(context, config, 0, "Digest CHAP MD5 not allowed");
625 goto out;
628 if (ireq.u.digestRequest.identifier == NULL) {
629 ret = EINVAL;
630 krb5_set_error_message(context, ret, "Identifier missing "
631 "from CHAP request");
632 goto out;
635 if (hex_decode(*ireq.u.digestRequest.identifier, &idx, 1) != 1) {
636 ret = EINVAL;
637 krb5_set_error_message(context, ret, "failed to decode identifier");
638 goto out;
641 ret = get_password_entry(context, config,
642 ireq.u.digestRequest.username,
643 &password);
644 if (ret)
645 goto out;
647 ctx = EVP_MD_CTX_create();
649 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
650 EVP_DigestUpdate(ctx, &idx, 1);
651 EVP_DigestUpdate(ctx, password, strlen(password));
652 EVP_DigestUpdate(ctx, serverNonce.data, serverNonce.length);
653 EVP_DigestFinal_ex(ctx, md, NULL);
655 EVP_MD_CTX_destroy(ctx);
657 hex_encode(md, sizeof(md), &mdx);
658 if (mdx == NULL) {
659 krb5_clear_error_message(context);
660 ret = ENOMEM;
661 goto out;
664 r.element = choice_DigestRepInner_response;
666 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
667 free(mdx);
668 if (ret == 0) {
669 r.u.response.success = TRUE;
670 } else {
671 kdc_log(context, config, 0,
672 "CHAP reply mismatch for %s",
673 ireq.u.digestRequest.username);
674 r.u.response.success = FALSE;
677 } else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
678 EVP_MD_CTX *ctx;
679 unsigned char md[MD5_DIGEST_LENGTH];
680 char *mdx;
681 char *A1, *A2;
683 if ((config->digests_allowed & DIGEST_MD5) == 0) {
684 kdc_log(context, config, 0, "Digest SASL MD5 not allowed");
685 goto out;
688 if (ireq.u.digestRequest.nonceCount == NULL)
689 goto out;
690 if (ireq.u.digestRequest.clientNonce == NULL)
691 goto out;
692 if (ireq.u.digestRequest.qop == NULL)
693 goto out;
694 if (ireq.u.digestRequest.realm == NULL)
695 goto out;
697 ret = get_password_entry(context, config,
698 ireq.u.digestRequest.username,
699 &password);
700 if (ret)
701 goto failed;
703 ctx = EVP_MD_CTX_create();
705 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
706 EVP_DigestUpdate(ctx, ireq.u.digestRequest.username,
707 strlen(ireq.u.digestRequest.username));
708 EVP_DigestUpdate(ctx, ":", 1);
709 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.realm,
710 strlen(*ireq.u.digestRequest.realm));
711 EVP_DigestUpdate(ctx, ":", 1);
712 EVP_DigestUpdate(ctx, password, strlen(password));
713 EVP_DigestFinal_ex(ctx, md, NULL);
715 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
716 EVP_DigestUpdate(ctx, md, sizeof(md));
717 EVP_DigestUpdate(ctx, ":", 1);
718 EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce,
719 strlen(ireq.u.digestRequest.serverNonce));
720 EVP_DigestUpdate(ctx, ":", 1);
721 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount,
722 strlen(*ireq.u.digestRequest.nonceCount));
723 if (ireq.u.digestRequest.authid) {
724 EVP_DigestUpdate(ctx, ":", 1);
725 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.authid,
726 strlen(*ireq.u.digestRequest.authid));
728 EVP_DigestFinal_ex(ctx, md, NULL);
729 hex_encode(md, sizeof(md), &A1);
730 if (A1 == NULL) {
731 ret = ENOMEM;
732 krb5_set_error_message(context, ret, "malloc: out of memory");
733 EVP_MD_CTX_destroy(ctx);
734 goto failed;
737 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
738 EVP_DigestUpdate(ctx,
739 "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
740 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.uri,
741 strlen(*ireq.u.digestRequest.uri));
743 /* conf|int */
744 if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
745 static char conf_zeros[] = ":00000000000000000000000000000000";
746 EVP_DigestUpdate(ctx, conf_zeros, sizeof(conf_zeros) - 1);
749 EVP_DigestFinal_ex(ctx, md, NULL);
751 hex_encode(md, sizeof(md), &A2);
752 if (A2 == NULL) {
753 ret = ENOMEM;
754 krb5_set_error_message(context, ret, "malloc: out of memory");
755 free(A1);
756 goto failed;
759 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
760 EVP_DigestUpdate(ctx, A1, strlen(A2));
761 EVP_DigestUpdate(ctx, ":", 1);
762 EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce,
763 strlen(ireq.u.digestRequest.serverNonce));
764 EVP_DigestUpdate(ctx, ":", 1);
765 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount,
766 strlen(*ireq.u.digestRequest.nonceCount));
767 EVP_DigestUpdate(ctx, ":", 1);
768 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.clientNonce,
769 strlen(*ireq.u.digestRequest.clientNonce));
770 EVP_DigestUpdate(ctx, ":", 1);
771 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.qop,
772 strlen(*ireq.u.digestRequest.qop));
773 EVP_DigestUpdate(ctx, ":", 1);
774 EVP_DigestUpdate(ctx, A2, strlen(A2));
776 EVP_DigestFinal_ex(ctx, md, NULL);
778 EVP_MD_CTX_destroy(ctx);
780 free(A1);
781 free(A2);
783 hex_encode(md, sizeof(md), &mdx);
784 if (mdx == NULL) {
785 krb5_clear_error_message(context);
786 ret = ENOMEM;
787 goto out;
790 r.element = choice_DigestRepInner_response;
791 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
792 free(mdx);
793 if (ret == 0) {
794 r.u.response.success = TRUE;
795 } else {
796 kdc_log(context, config, 0,
797 "DIGEST-MD5 reply mismatch for %s",
798 ireq.u.digestRequest.username);
799 r.u.response.success = FALSE;
802 } else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) {
803 unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH];
804 krb5_principal clientprincipal = NULL;
805 char *mdx;
806 const char *username;
807 struct ntlm_buf answer;
808 Key *key = NULL;
809 EVP_MD_CTX *ctp;
811 if ((config->digests_allowed & MS_CHAP_V2) == 0) {
812 kdc_log(context, config, 0, "MS-CHAP-V2 not allowed");
813 goto failed;
816 if (ireq.u.digestRequest.clientNonce == NULL) {
817 ret = EINVAL;
818 krb5_set_error_message(context, ret,
819 "MS-CHAP-V2 clientNonce missing");
820 goto failed;
822 if (serverNonce.length != 16) {
823 ret = EINVAL;
824 krb5_set_error_message(context, ret,
825 "MS-CHAP-V2 serverNonce wrong length");
826 goto failed;
829 /* strip of the domain component */
830 username = strchr(ireq.u.digestRequest.username, '\\');
831 if (username == NULL)
832 username = ireq.u.digestRequest.username;
833 else
834 username++;
836 ctp = EVP_MD_CTX_create();
838 /* ChallangeHash */
839 EVP_DigestInit_ex(ctp, EVP_sha1(), NULL);
841 ssize_t ssize;
842 krb5_data clientNonce;
844 clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce);
845 clientNonce.data = malloc(clientNonce.length);
846 if (clientNonce.data == NULL) {
847 ret = ENOMEM;
848 krb5_set_error_message(context, ret,
849 "malloc: out of memory");
850 EVP_MD_CTX_destroy(ctp);
851 goto out;
854 ssize = hex_decode(*ireq.u.digestRequest.clientNonce,
855 clientNonce.data, clientNonce.length);
856 if (ssize != 16) {
857 ret = ENOMEM;
858 krb5_set_error_message(context, ret,
859 "Failed to decode clientNonce");
860 EVP_MD_CTX_destroy(ctp);
861 goto out;
863 EVP_DigestUpdate(ctp, clientNonce.data, ssize);
864 free(clientNonce.data);
866 EVP_DigestUpdate(ctp, serverNonce.data, serverNonce.length);
867 EVP_DigestUpdate(ctp, username, strlen(username));
869 EVP_DigestFinal_ex(ctp, challange, NULL);
871 EVP_MD_CTX_destroy(ctp);
873 /* NtPasswordHash */
874 ret = krb5_parse_name(context, username, &clientprincipal);
875 if (ret)
876 goto failed;
878 ret = _kdc_db_fetch(context, config, clientprincipal,
879 HDB_F_GET_CLIENT, NULL, NULL, &user);
880 krb5_free_principal(context, clientprincipal);
881 if (ret) {
882 krb5_set_error_message(context, ret,
883 "MS-CHAP-V2 user %s not in database",
884 username);
885 goto failed;
888 ret = hdb_enctype2key(context, &user->entry,
889 ETYPE_ARCFOUR_HMAC_MD5, &key);
890 if (ret) {
891 krb5_set_error_message(context, ret,
892 "MS-CHAP-V2 missing arcfour key %s",
893 username);
894 goto failed;
897 /* ChallengeResponse */
898 ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
899 key->key.keyvalue.length,
900 challange, &answer);
901 if (ret) {
902 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
903 goto failed;
906 hex_encode(answer.data, answer.length, &mdx);
907 if (mdx == NULL) {
908 free(answer.data);
909 krb5_clear_error_message(context);
910 ret = ENOMEM;
911 goto out;
914 r.element = choice_DigestRepInner_response;
915 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
916 if (ret == 0) {
917 r.u.response.success = TRUE;
918 } else {
919 kdc_log(context, config, 0,
920 "MS-CHAP-V2 hash mismatch for %s",
921 ireq.u.digestRequest.username);
922 r.u.response.success = FALSE;
924 free(mdx);
926 if (r.u.response.success) {
927 unsigned char hashhash[MD4_DIGEST_LENGTH];
928 EVP_MD_CTX *ctxp;
930 ctxp = EVP_MD_CTX_create();
932 /* hashhash */
934 EVP_DigestInit_ex(ctxp, EVP_md4(), NULL);
935 EVP_DigestUpdate(ctxp,
936 key->key.keyvalue.data,
937 key->key.keyvalue.length);
938 EVP_DigestFinal_ex(ctxp, hashhash, NULL);
941 /* GenerateAuthenticatorResponse */
942 EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
943 EVP_DigestUpdate(ctxp, hashhash, sizeof(hashhash));
944 EVP_DigestUpdate(ctxp, answer.data, answer.length);
945 EVP_DigestUpdate(ctxp, ms_chap_v2_magic1,
946 sizeof(ms_chap_v2_magic1));
947 EVP_DigestFinal_ex(ctxp, md, NULL);
949 EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
950 EVP_DigestUpdate(ctxp, md, sizeof(md));
951 EVP_DigestUpdate(ctxp, challange, 8);
952 EVP_DigestUpdate(ctxp, ms_chap_v2_magic2,
953 sizeof(ms_chap_v2_magic2));
954 EVP_DigestFinal_ex(ctxp, md, NULL);
956 r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp));
957 if (r.u.response.rsp == NULL) {
958 free(answer.data);
959 krb5_clear_error_message(context);
960 EVP_MD_CTX_destroy(ctxp);
961 ret = ENOMEM;
962 goto out;
965 hex_encode(md, sizeof(md), r.u.response.rsp);
966 if (r.u.response.rsp == NULL) {
967 free(answer.data);
968 krb5_clear_error_message(context);
969 EVP_MD_CTX_destroy(ctxp);
970 ret = ENOMEM;
971 goto out;
974 /* get_master, rfc 3079 3.4 */
975 EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
976 EVP_DigestUpdate(ctxp, hashhash, 16);
977 EVP_DigestUpdate(ctxp, answer.data, answer.length);
978 EVP_DigestUpdate(ctxp, ms_rfc3079_magic1,
979 sizeof(ms_rfc3079_magic1));
980 EVP_DigestFinal_ex(ctxp, md, NULL);
982 free(answer.data);
984 EVP_MD_CTX_destroy(ctxp);
986 r.u.response.session_key =
987 calloc(1, sizeof(*r.u.response.session_key));
988 if (r.u.response.session_key == NULL) {
989 krb5_clear_error_message(context);
990 ret = ENOMEM;
991 goto out;
994 ret = krb5_data_copy(r.u.response.session_key, md, 16);
995 if (ret) {
996 krb5_clear_error_message(context);
997 goto out;
1001 } else {
1002 r.element = choice_DigestRepInner_error;
1003 asprintf(&r.u.error.reason, "Unsupported digest type %s",
1004 ireq.u.digestRequest.type);
1005 if (r.u.error.reason == NULL) {
1006 ret = ENOMEM;
1007 krb5_set_error_message(context, ret, "malloc: out of memory");
1008 goto out;
1010 r.u.error.code = EINVAL;
1013 kdc_log(context, config, 0, "Digest %s request successful %s",
1014 ireq.u.digestRequest.type, ireq.u.digestRequest.username);
1016 break;
1018 case choice_DigestReqInner_ntlmInit:
1020 if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) {
1021 kdc_log(context, config, 0, "NTLM not allowed");
1022 goto failed;
1025 r.element = choice_DigestRepInner_ntlmInitReply;
1027 r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE;
1029 if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) {
1030 kdc_log(context, config, 0, "NTLM client have no unicode");
1031 goto failed;
1034 if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM)
1035 r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM;
1036 else {
1037 kdc_log(context, config, 0, "NTLM client doesn't support NTLM");
1038 goto failed;
1041 r.u.ntlmInitReply.flags |=
1042 NTLM_NEG_TARGET |
1043 NTLM_TARGET_DOMAIN |
1044 NTLM_ENC_128;
1046 #define ALL \
1047 NTLM_NEG_SIGN| \
1048 NTLM_NEG_SEAL| \
1049 NTLM_NEG_ALWAYS_SIGN| \
1050 NTLM_NEG_NTLM2_SESSION| \
1051 NTLM_NEG_KEYEX
1053 r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
1055 #undef ALL
1057 r.u.ntlmInitReply.targetname =
1058 get_ntlm_targetname(context, client);
1059 if (r.u.ntlmInitReply.targetname == NULL) {
1060 ret = ENOMEM;
1061 krb5_set_error_message(context, ret, "malloc: out of memory");
1062 goto out;
1064 r.u.ntlmInitReply.challange.data = malloc(8);
1065 if (r.u.ntlmInitReply.challange.data == NULL) {
1066 ret = ENOMEM;
1067 krb5_set_error_message(context, ret, "malloc: out of memory");
1068 goto out;
1070 r.u.ntlmInitReply.challange.length = 8;
1071 if (RAND_bytes(r.u.ntlmInitReply.challange.data,
1072 r.u.ntlmInitReply.challange.length) != 1)
1074 ret = ENOMEM;
1075 krb5_set_error_message(context, ret, "out of random error");
1076 goto out;
1078 /* XXX fix targetinfo */
1079 ALLOC(r.u.ntlmInitReply.targetinfo);
1080 if (r.u.ntlmInitReply.targetinfo == NULL) {
1081 ret = ENOMEM;
1082 krb5_set_error_message(context, ret, "malloc: out of memory");
1083 goto out;
1086 ret = fill_targetinfo(context,
1087 r.u.ntlmInitReply.targetname,
1088 client,
1089 r.u.ntlmInitReply.targetinfo);
1090 if (ret) {
1091 ret = ENOMEM;
1092 krb5_set_error_message(context, ret, "malloc: out of memory");
1093 goto out;
1097 * Save data encryted in opaque for the second part of the
1098 * ntlm authentication
1100 sp = krb5_storage_emem();
1101 if (sp == NULL) {
1102 ret = ENOMEM;
1103 krb5_set_error_message(context, ret, "malloc: out of memory");
1104 goto out;
1107 ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8);
1108 if (ret != 8) {
1109 ret = ENOMEM;
1110 krb5_set_error_message(context, ret, "storage write challange");
1111 goto out;
1113 ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
1114 if (ret) {
1115 krb5_clear_error_message(context);
1116 goto out;
1119 ret = krb5_storage_to_data(sp, &buf);
1120 if (ret) {
1121 krb5_clear_error_message(context);
1122 goto out;
1125 ret = get_digest_key(context, config, server, &crypto);
1126 if (ret)
1127 goto out;
1129 ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1130 buf.data, buf.length, &r.u.ntlmInitReply.opaque);
1131 krb5_data_free(&buf);
1132 krb5_crypto_destroy(context, crypto);
1133 crypto = NULL;
1134 if (ret)
1135 goto out;
1137 kdc_log(context, config, 0, "NTLM init from %s", from);
1139 break;
1141 case choice_DigestReqInner_ntlmRequest: {
1142 krb5_principal clientprincipal;
1143 unsigned char sessionkey[16];
1144 unsigned char challange[8];
1145 uint32_t flags;
1146 Key *key = NULL;
1147 int version;
1149 r.element = choice_DigestRepInner_ntlmResponse;
1150 r.u.ntlmResponse.success = 0;
1151 r.u.ntlmResponse.flags = 0;
1152 r.u.ntlmResponse.sessionkey = NULL;
1153 r.u.ntlmResponse.tickets = NULL;
1155 /* get username */
1156 ret = krb5_parse_name(context,
1157 ireq.u.ntlmRequest.username,
1158 &clientprincipal);
1159 if (ret)
1160 goto failed;
1162 ret = _kdc_db_fetch(context, config, clientprincipal,
1163 HDB_F_GET_CLIENT, NULL, NULL, &user);
1164 krb5_free_principal(context, clientprincipal);
1165 if (ret) {
1166 krb5_set_error_message(context, ret, "NTLM user %s not in database",
1167 ireq.u.ntlmRequest.username);
1168 goto failed;
1171 ret = get_digest_key(context, config, server, &crypto);
1172 if (ret)
1173 goto failed;
1175 ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1176 ireq.u.ntlmRequest.opaque.data,
1177 ireq.u.ntlmRequest.opaque.length, &buf);
1178 krb5_crypto_destroy(context, crypto);
1179 crypto = NULL;
1180 if (ret) {
1181 kdc_log(context, config, 0,
1182 "Failed to decrypt nonce from %s", from);
1183 goto failed;
1186 sp = krb5_storage_from_data(&buf);
1187 if (sp == NULL) {
1188 ret = ENOMEM;
1189 krb5_set_error_message(context, ret, "malloc: out of memory");
1190 goto out;
1193 ret = krb5_storage_read(sp, challange, sizeof(challange));
1194 if (ret != sizeof(challange)) {
1195 ret = ENOMEM;
1196 krb5_set_error_message(context, ret, "NTLM storage read challange");
1197 goto out;
1199 ret = krb5_ret_uint32(sp, &flags);
1200 if (ret) {
1201 krb5_set_error_message(context, ret, "NTLM storage read flags");
1202 goto out;
1204 krb5_storage_free(sp);
1205 sp = NULL;
1206 krb5_data_free(&buf);
1208 if ((flags & NTLM_NEG_NTLM) == 0) {
1209 ret = EINVAL;
1210 krb5_set_error_message(context, ret, "NTLM not negotiated");
1211 goto out;
1214 ret = hdb_enctype2key(context, &user->entry,
1215 ETYPE_ARCFOUR_HMAC_MD5, &key);
1216 if (ret) {
1217 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1218 goto out;
1221 /* check if this is NTLMv2 */
1222 if (ireq.u.ntlmRequest.ntlm.length != 24) {
1223 struct ntlm_buf infotarget, answer;
1224 char *targetname;
1226 if ((config->digests_allowed & NTLM_V2) == 0) {
1227 kdc_log(context, config, 0, "NTLM v2 not allowed");
1228 goto out;
1231 version = 2;
1233 targetname = get_ntlm_targetname(context, client);
1234 if (targetname == NULL) {
1235 ret = ENOMEM;
1236 krb5_set_error_message(context, ret, "malloc: out of memory");
1237 goto out;
1240 answer.length = ireq.u.ntlmRequest.ntlm.length;
1241 answer.data = ireq.u.ntlmRequest.ntlm.data;
1243 ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
1244 key->key.keyvalue.length,
1245 ireq.u.ntlmRequest.username,
1246 targetname,
1248 challange,
1249 &answer,
1250 &infotarget,
1251 sessionkey);
1252 free(targetname);
1253 if (ret) {
1254 krb5_set_error_message(context, ret, "NTLM v2 verify failed");
1255 goto failed;
1258 /* XXX verify infotarget matches client (checksum ?) */
1260 free(infotarget.data);
1261 /* */
1263 } else {
1264 struct ntlm_buf answer;
1266 version = 1;
1268 if (flags & NTLM_NEG_NTLM2_SESSION) {
1269 unsigned char sessionhash[MD5_DIGEST_LENGTH];
1270 EVP_MD_CTX *ctx;
1272 if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
1273 kdc_log(context, config, 0, "NTLM v1-session not allowed");
1274 ret = EINVAL;
1275 goto failed;
1278 if (ireq.u.ntlmRequest.lm.length != 24) {
1279 ret = EINVAL;
1280 krb5_set_error_message(context, ret, "LM hash have wrong length "
1281 "for NTLM session key");
1282 goto failed;
1285 ctx = EVP_MD_CTX_create();
1287 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
1289 EVP_DigestUpdate(ctx, challange, sizeof(challange));
1290 EVP_DigestUpdate(ctx, ireq.u.ntlmRequest.lm.data, 8);
1291 EVP_DigestFinal_ex(ctx, sessionhash, NULL);
1292 memcpy(challange, sessionhash, sizeof(challange));
1294 EVP_MD_CTX_destroy(ctx);
1296 } else {
1297 if ((config->digests_allowed & NTLM_V1) == 0) {
1298 kdc_log(context, config, 0, "NTLM v1 not allowed");
1299 goto failed;
1303 ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
1304 key->key.keyvalue.length,
1305 challange, &answer);
1306 if (ret) {
1307 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1308 goto failed;
1311 if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
1312 memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
1314 free(answer.data);
1315 ret = EINVAL;
1316 krb5_set_error_message(context, ret, "NTLM hash mismatch");
1317 goto failed;
1319 free(answer.data);
1322 EVP_MD_CTX *ctx;
1324 ctx = EVP_MD_CTX_create();
1326 EVP_DigestInit_ex(ctx, EVP_md4(), NULL);
1327 EVP_DigestUpdate(ctx,
1328 key->key.keyvalue.data,
1329 key->key.keyvalue.length);
1330 EVP_DigestFinal_ex(ctx, sessionkey, NULL);
1332 EVP_MD_CTX_destroy(ctx);
1336 if (ireq.u.ntlmRequest.sessionkey) {
1337 unsigned char masterkey[MD4_DIGEST_LENGTH];
1338 EVP_CIPHER_CTX rc4;
1339 size_t len;
1341 if ((flags & NTLM_NEG_KEYEX) == 0) {
1342 ret = EINVAL;
1343 krb5_set_error_message(context, ret,
1344 "NTLM client failed to neg key "
1345 "exchange but still sent key");
1346 goto failed;
1349 len = ireq.u.ntlmRequest.sessionkey->length;
1350 if (len != sizeof(masterkey)){
1351 ret = EINVAL;
1352 krb5_set_error_message(context, ret,
1353 "NTLM master key wrong length: %lu",
1354 (unsigned long)len);
1355 goto failed;
1359 EVP_CIPHER_CTX_init(&rc4);
1360 EVP_CipherInit_ex(&rc4, EVP_rc4(), NULL, sessionkey, NULL, 1);
1361 EVP_Cipher(&rc4,
1362 masterkey, ireq.u.ntlmRequest.sessionkey->data,
1363 sizeof(masterkey));
1364 EVP_CIPHER_CTX_cleanup(&rc4);
1366 r.u.ntlmResponse.sessionkey =
1367 malloc(sizeof(*r.u.ntlmResponse.sessionkey));
1368 if (r.u.ntlmResponse.sessionkey == NULL) {
1369 ret = EINVAL;
1370 krb5_set_error_message(context, ret, "malloc: out of memory");
1371 goto out;
1374 ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
1375 masterkey, sizeof(masterkey));
1376 if (ret) {
1377 krb5_set_error_message(context, ret, "malloc: out of memory");
1378 goto out;
1382 r.u.ntlmResponse.success = 1;
1383 kdc_log(context, config, 0, "NTLM version %d successful for %s",
1384 version, ireq.u.ntlmRequest.username);
1385 break;
1387 case choice_DigestReqInner_supportedMechs:
1389 kdc_log(context, config, 0, "digest supportedMechs from %s", from);
1391 r.element = choice_DigestRepInner_supportedMechs;
1392 memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs));
1394 if (config->digests_allowed & NTLM_V1)
1395 r.u.supportedMechs.ntlm_v1 = 1;
1396 if (config->digests_allowed & NTLM_V1_SESSION)
1397 r.u.supportedMechs.ntlm_v1_session = 1;
1398 if (config->digests_allowed & NTLM_V2)
1399 r.u.supportedMechs.ntlm_v2 = 1;
1400 if (config->digests_allowed & DIGEST_MD5)
1401 r.u.supportedMechs.digest_md5 = 1;
1402 if (config->digests_allowed & CHAP_MD5)
1403 r.u.supportedMechs.chap_md5 = 1;
1404 if (config->digests_allowed & MS_CHAP_V2)
1405 r.u.supportedMechs.ms_chap_v2 = 1;
1406 break;
1408 default: {
1409 const char *s;
1410 ret = EINVAL;
1411 krb5_set_error_message(context, ret, "unknown operation to digest");
1413 failed:
1415 s = krb5_get_error_message(context, ret);
1416 if (s == NULL) {
1417 krb5_clear_error_message(context);
1418 goto out;
1421 kdc_log(context, config, 0, "Digest failed with: %s", s);
1423 r.element = choice_DigestRepInner_error;
1424 r.u.error.reason = strdup("unknown error");
1425 krb5_free_error_message(context, s);
1426 if (r.u.error.reason == NULL) {
1427 ret = ENOMEM;
1428 krb5_set_error_message(context, ret, "malloc: out of memory");
1429 goto out;
1431 r.u.error.code = EINVAL;
1432 break;
1436 ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
1437 if (ret) {
1438 krb5_set_error_message(context, ret, "Failed to encode inner digest reply");
1439 goto out;
1441 if (size != buf.length)
1442 krb5_abortx(context, "ASN1 internal error");
1444 krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
1446 ret = krb5_mk_rep (context, ac, &rep.apRep);
1447 if (ret)
1448 goto out;
1451 krb5_keyblock *key;
1453 ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
1454 if (ret)
1455 goto out;
1457 ret = krb5_crypto_init(context, key, 0, &crypto);
1458 krb5_free_keyblock (context, key);
1459 if (ret)
1460 goto out;
1463 ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
1464 buf.data, buf.length, 0,
1465 &rep.innerRep);
1467 ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
1468 if (ret) {
1469 krb5_set_error_message(context, ret, "Failed to encode digest reply");
1470 goto out;
1472 if (size != reply->length)
1473 krb5_abortx(context, "ASN1 internal error");
1476 out:
1477 if (ac)
1478 krb5_auth_con_free(context, ac);
1479 if (ret)
1480 krb5_warn(context, ret, "Digest request from %s failed", from);
1481 if (ticket)
1482 krb5_free_ticket(context, ticket);
1483 if (id)
1484 krb5_kt_close(context, id);
1485 if (crypto)
1486 krb5_crypto_destroy(context, crypto);
1487 if (sp)
1488 krb5_storage_free(sp);
1489 if (user)
1490 _kdc_free_ent (context, user);
1491 if (server)
1492 _kdc_free_ent (context, server);
1493 if (client)
1494 _kdc_free_ent (context, client);
1495 if (password) {
1496 memset(password, 0, strlen(password));
1497 free (password);
1499 if (client_name)
1500 free (client_name);
1501 krb5_data_free(&buf);
1502 krb5_data_free(&serverNonce);
1503 free_Checksum(&res);
1504 free_DigestREP(&rep);
1505 free_DigestRepInner(&r);
1506 free_DigestReqInner(&ireq);
1508 return ret;
1511 #endif /* DIGEST */