lib/kadm5: find_db_spec do not leak 'info'
[heimdal.git] / kdc / digest.c
blob092b4a75a4be36234d09601537aa0bc3bcaaed12
1 /*
2 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "kdc_locl.h"
35 #include <hex.h>
37 #ifdef DIGEST
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
46 const struct units _kdc_digestunits[] = {
47 {"ms-chap-v2", 1U << 5},
48 {"chap-md5", 1U << 4},
49 {"digest-md5", 1U << 3},
50 {"ntlm-v2", 1U << 2},
51 {"ntlm-v1-session", 1U << 1},
52 {"ntlm-v1", 1U << 0},
53 {NULL, 0}
57 static krb5_error_code
58 get_digest_key(krb5_context context,
59 krb5_kdc_configuration *config,
60 hdb_entry *server,
61 krb5_crypto *crypto)
63 krb5_error_code ret;
64 krb5_enctype enctype;
65 Key *key;
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);
82 static char *
83 get_ntlm_targetname(krb5_context context,
84 hdb_entry *client)
86 char *targetname, *p;
88 targetname = strdup(krb5_principal_get_realm(context,
89 client->principal));
90 if (targetname == NULL)
91 return NULL;
93 p = strchr(targetname, '.');
94 if (p)
95 *p = '\0';
97 strupr(targetname);
98 return targetname;
101 static krb5_error_code
102 fill_targetinfo(krb5_context context,
103 char *targetname,
104 hdb_entry *client,
105 krb5_data *data)
107 struct ntlm_targetinfo ti;
108 krb5_error_code ret;
109 struct ntlm_buf d;
110 krb5_principal p;
111 const char *str;
113 memset(&ti, 0, sizeof(ti));
115 ti.domainname = targetname;
116 p = client->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)))
125 str = krb5_principal_get_comp_string(context, p, 1);
126 ti.dnsservername = rk_UNCONST(str);
129 ret = heim_ntlm_encode_targetinfo(&ti, 1, &d);
130 if (ret)
131 return ret;
133 data->data = d.data;
134 data->length = d.length;
136 return 0;
140 static const unsigned char ms_chap_v2_magic1[39] = {
141 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
142 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
143 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
144 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
146 static const unsigned char ms_chap_v2_magic2[41] = {
147 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
148 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
149 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
150 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
151 0x6E
153 static const unsigned char ms_rfc3079_magic1[27] = {
154 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
155 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
156 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
163 static krb5_error_code
164 get_password_entry(krb5_context context,
165 krb5_kdc_configuration *config,
166 const char *username,
167 char **password)
169 krb5_principal clientprincipal;
170 krb5_error_code ret;
171 hdb_entry *user;
172 HDB *db;
174 /* get username */
175 ret = krb5_parse_name(context, username, &clientprincipal);
176 if (ret)
177 return ret;
179 ret = _kdc_db_fetch(context, config, clientprincipal,
180 HDB_F_GET_CLIENT, NULL, &db, &user);
181 krb5_free_principal(context, clientprincipal);
182 if (ret)
183 return ret;
185 ret = hdb_entry_get_password(context, db, user, password);
186 if (ret || password == NULL) {
187 if (ret == 0) {
188 ret = EINVAL;
189 krb5_set_error_message(context, ret, "password missing");
191 memset(user, 0, sizeof(*user));
193 _kdc_free_ent (context, db, user);
194 return ret;
201 krb5_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)
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 *serverdb, *userdb;
221 hdb_entry *server = NULL, *user = NULL;
222 HDB *clientdb;
223 hdb_entry *client = NULL;
224 char *client_name = NULL, *password = NULL;
225 krb5_data serverNonce;
227 if(!config->enable_digest) {
228 kdc_log(context, config, 2,
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, 3, "Digest request from %s", from);
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;
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, &serverdb, &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, &clientdb, &client);
320 krb5_free_principal(context, principal);
321 if (ret)
322 goto out;
324 if (client->flags.allow_digest == 0) {
325 kdc_log(context, config, 2,
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, 3, "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;
411 int aret;
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;
422 free(r.u.initReply.nonce);
423 r.u.initReply.nonce = s;
426 ret = krb5_store_stringz(sp, r.u.initReply.nonce);
427 if (ret) {
428 krb5_clear_error_message(context);
429 goto out;
432 if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
433 int aret;
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;
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;
450 } else
451 r.u.initReply.identifier = NULL;
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;
461 ret = krb5_storage_to_data(sp, &buf);
462 if (ret) {
463 krb5_clear_error_message(context);
464 goto out;
467 ret = get_digest_key(context, config, server, &crypto);
468 if (ret)
469 goto out;
471 ret = krb5_create_checksum(context,
472 crypto,
473 KRB5_KU_DIGEST_OPAQUE,
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;
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;
491 if (size != buf.length)
492 krb5_abortx(context, "ASN1 internal error");
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;
503 kdc_log(context, config, 3, "Digest %s init request successful from %s",
504 ireq.u.init.type, from);
506 break;
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;
515 ret = krb5_store_stringz(sp, ireq.u.digestRequest.type);
516 if (ret) {
517 krb5_clear_error_message(context);
518 goto out;
521 krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
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;
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;
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;
545 buf.length = ret;
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;
556 ret = krb5_storage_to_data(sp, &buf);
557 if (ret) {
558 krb5_clear_error_message(context);
559 goto out;
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;
571 * CHAP does the checksum of the raw nonce, but do it for all
572 * types, since we need to check the timestamp.
575 ssize_t ssize;
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;
584 serverNonce.length = ssize;
587 ret = get_digest_key(context, config, server, &crypto);
588 if (ret)
589 goto out;
591 ret = _kdc_verify_checksum(context, crypto,
592 KRB5_KU_DIGEST_OPAQUE,
593 &buf, &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;
601 /* verify time */
603 unsigned char *p = serverNonce.data;
604 uint32_t t;
606 if (serverNonce.length < 4) {
607 ret = EINVAL;
608 krb5_set_error_message(context, ret, "server nonce too short");
609 goto out;
611 t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
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;
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;
626 if ((config->digests_allowed & CHAP_MD5) == 0) {
627 kdc_log(context, config, 2, "Digest CHAP MD5 not allowed");
628 goto out;
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;
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;
644 ret = get_password_entry(context, config,
645 ireq.u.digestRequest.username,
646 &password);
647 if (ret)
648 goto out;
650 ctx = EVP_MD_CTX_create();
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);
658 EVP_MD_CTX_destroy(ctx);
660 hex_encode(md, sizeof(md), &mdx);
661 if (mdx == NULL) {
662 krb5_clear_error_message(context);
663 ret = ENOMEM;
664 goto out;
667 r.element = choice_DigestRepInner_response;
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, 2,
675 "CHAP reply mismatch for %s",
676 ireq.u.digestRequest.username);
677 r.u.response.success = FALSE;
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;
686 if ((config->digests_allowed & DIGEST_MD5) == 0) {
687 kdc_log(context, config, 2, "Digest SASL MD5 not allowed");
688 goto out;
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;
700 ret = get_password_entry(context, config,
701 ireq.u.digestRequest.username,
702 &password);
703 if (ret)
704 goto failed;
706 ctx = EVP_MD_CTX_create();
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);
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));
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;
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));
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);
752 EVP_DigestFinal_ex(ctx, md, NULL);
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;
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));
779 EVP_DigestFinal_ex(ctx, md, NULL);
781 EVP_MD_CTX_destroy(ctx);
783 free(A1);
784 free(A2);
786 hex_encode(md, sizeof(md), &mdx);
787 if (mdx == NULL) {
788 krb5_clear_error_message(context);
789 ret = ENOMEM;
790 goto out;
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, 2,
800 "DIGEST-MD5 reply mismatch for %s",
801 ireq.u.digestRequest.username);
802 r.u.response.success = FALSE;
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;
814 if ((config->digests_allowed & MS_CHAP_V2) == 0) {
815 kdc_log(context, config, 2, "MS-CHAP-V2 not allowed");
816 goto failed;
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;
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;
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++;
839 ctp = EVP_MD_CTX_create();
841 /* ChallengeHash */
842 EVP_DigestInit_ex(ctp, EVP_sha1(), NULL);
844 ssize_t ssize;
845 krb5_data clientNonce;
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;
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;
866 EVP_DigestUpdate(ctp, clientNonce.data, ssize);
867 free(clientNonce.data);
869 EVP_DigestUpdate(ctp, serverNonce.data, serverNonce.length);
870 EVP_DigestUpdate(ctp, username, strlen(username));
872 EVP_DigestFinal_ex(ctp, challenge, NULL);
874 EVP_MD_CTX_destroy(ctp);
876 /* NtPasswordHash */
877 ret = krb5_parse_name(context, username, &clientprincipal);
878 if (ret)
879 goto failed;
881 ret = _kdc_db_fetch(context, config, clientprincipal,
882 HDB_F_GET_CLIENT, NULL, &userdb, &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;
891 ret = hdb_enctype2key(context, user, 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;
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;
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;
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, 2,
923 "MS-CHAP-V2 hash mismatch for %s",
924 ireq.u.digestRequest.username);
925 r.u.response.success = FALSE;
927 free(mdx);
929 if (r.u.response.success) {
930 unsigned char hashhash[MD4_DIGEST_LENGTH];
931 EVP_MD_CTX *ctxp;
933 ctxp = EVP_MD_CTX_create();
935 /* hashhash */
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);
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);
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);
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;
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;
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);
985 free(answer.data);
987 EVP_MD_CTX_destroy(ctxp);
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;
997 ret = krb5_data_copy(r.u.response.session_key, md, 16);
998 if (ret) {
999 krb5_clear_error_message(context);
1000 goto out;
1004 } else {
1005 int aret;
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;
1015 r.u.error.code = EINVAL;
1018 kdc_log(context, config, 3, "Digest %s request successful %s",
1019 ireq.u.digestRequest.type, ireq.u.digestRequest.username);
1021 break;
1023 case choice_DigestReqInner_ntlmInit:
1025 if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) {
1026 kdc_log(context, config, 2, "NTLM not allowed");
1027 goto failed;
1030 r.element = choice_DigestRepInner_ntlmInitReply;
1032 r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE;
1034 if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) {
1035 kdc_log(context, config, 2, "NTLM client have no unicode");
1036 goto failed;
1039 if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM)
1040 r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM;
1041 else {
1042 kdc_log(context, config, 2, "NTLM client doesn't support NTLM");
1043 goto failed;
1046 r.u.ntlmInitReply.flags |=
1047 NTLM_NEG_TARGET |
1048 NTLM_TARGET_DOMAIN |
1049 NTLM_ENC_128;
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
1058 r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
1060 #undef ALL
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;
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;
1075 r.u.ntlmInitReply.challenge.length = 8;
1076 if (RAND_bytes(r.u.ntlmInitReply.challenge.data,
1077 r.u.ntlmInitReply.challenge.length) != 1)
1079 ret = ENOMEM;
1080 krb5_set_error_message(context, ret, "out of random error");
1081 goto out;
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;
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;
1102 * Save data encryted in opaque for the second part of the
1103 * ntlm authentication
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;
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;
1118 ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
1119 if (ret) {
1120 krb5_clear_error_message(context);
1121 goto out;
1124 ret = krb5_storage_to_data(sp, &buf);
1125 if (ret) {
1126 krb5_clear_error_message(context);
1127 goto out;
1130 ret = get_digest_key(context, config, server, &crypto);
1131 if (ret)
1132 goto out;
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;
1142 kdc_log(context, config, 3, "NTLM init from %s", from);
1144 break;
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;
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;
1160 /* get username */
1161 ret = krb5_parse_name(context,
1162 ireq.u.ntlmRequest.username,
1163 &clientprincipal);
1164 if (ret)
1165 goto failed;
1167 ret = _kdc_db_fetch(context, config, clientprincipal,
1168 HDB_F_GET_CLIENT, NULL, &userdb, &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;
1176 ret = get_digest_key(context, config, server, &crypto);
1177 if (ret)
1178 goto failed;
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, 2,
1187 "Failed to decrypt nonce from %s", from);
1188 goto failed;
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;
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;
1204 ret = krb5_ret_uint32(sp, &flags);
1205 if (ret) {
1206 krb5_set_error_message(context, ret, "NTLM storage read flags");
1207 goto out;
1209 krb5_storage_free(sp);
1210 sp = NULL;
1211 krb5_data_free(&buf);
1213 if ((flags & NTLM_NEG_NTLM) == 0) {
1214 ret = EINVAL;
1215 krb5_set_error_message(context, ret, "NTLM not negotiated");
1216 goto out;
1219 ret = hdb_enctype2key(context, user, NULL,
1220 ETYPE_ARCFOUR_HMAC_MD5, &key);
1221 if (ret) {
1222 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1223 goto out;
1226 /* check if this is NTLMv2 */
1227 if (ireq.u.ntlmRequest.ntlm.length != 24) {
1228 struct ntlm_buf infotarget, answer;
1229 char *targetname;
1231 if ((config->digests_allowed & NTLM_V2) == 0) {
1232 kdc_log(context, config, 2, "NTLM v2 not allowed");
1233 goto out;
1236 version = 2;
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;
1245 answer.length = ireq.u.ntlmRequest.ntlm.length;
1246 answer.data = ireq.u.ntlmRequest.ntlm.data;
1248 ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
1249 key->key.keyvalue.length,
1250 ireq.u.ntlmRequest.username,
1251 targetname,
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;
1263 /* XXX verify infotarget matches client (checksum ?) */
1265 free(infotarget.data);
1266 /* */
1268 } else {
1269 struct ntlm_buf answer;
1271 version = 1;
1273 if (flags & NTLM_NEG_NTLM2_SESSION) {
1274 unsigned char sessionhash[MD5_DIGEST_LENGTH];
1275 EVP_MD_CTX *ctx;
1277 if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
1278 kdc_log(context, config, 2, "NTLM v1-session not allowed");
1279 ret = EINVAL;
1280 goto failed;
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;
1290 ctx = EVP_MD_CTX_create();
1292 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
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));
1299 EVP_MD_CTX_destroy(ctx);
1301 } else {
1302 if ((config->digests_allowed & NTLM_V1) == 0) {
1303 kdc_log(context, config, 2, "NTLM v1 not allowed");
1304 goto failed;
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;
1316 if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
1317 memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
1319 free(answer.data);
1320 ret = EINVAL;
1321 krb5_set_error_message(context, ret, "NTLM hash mismatch");
1322 goto failed;
1324 free(answer.data);
1327 EVP_MD_CTX *ctx;
1329 ctx = EVP_MD_CTX_create();
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);
1337 EVP_MD_CTX_destroy(ctx);
1341 if (ireq.u.ntlmRequest.sessionkey) {
1342 unsigned char masterkey[MD4_DIGEST_LENGTH];
1343 EVP_CIPHER_CTX rc4;
1344 size_t len;
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;
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;
1364 EVP_CIPHER_CTX_init(&rc4);
1365 EVP_CipherInit_ex(&rc4, EVP_rc4(), NULL, sessionkey, NULL, 1);
1366 EVP_Cipher(&rc4,
1367 masterkey, ireq.u.ntlmRequest.sessionkey->data,
1368 sizeof(masterkey));
1369 EVP_CIPHER_CTX_cleanup(&rc4);
1371 r.u.ntlmResponse.sessionkey =
1372 malloc(sizeof(*r.u.ntlmResponse.sessionkey));
1373 if (r.u.ntlmResponse.sessionkey == NULL) {
1374 ret = EINVAL;
1375 krb5_set_error_message(context, ret, "malloc: out of memory");
1376 goto out;
1379 ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
1380 masterkey, sizeof(masterkey));
1381 if (ret) {
1382 krb5_set_error_message(context, ret, "malloc: out of memory");
1383 goto out;
1387 r.u.ntlmResponse.success = 1;
1388 kdc_log(context, config, 0, "NTLM version %d successful for %s",
1389 version, ireq.u.ntlmRequest.username);
1390 break;
1392 case choice_DigestReqInner_supportedMechs:
1394 kdc_log(context, config, 4, "digest supportedMechs from %s", from);
1396 r.element = choice_DigestRepInner_supportedMechs;
1397 memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs));
1399 if (config->digests_allowed & NTLM_V1)
1400 r.u.supportedMechs.ntlm_v1 = 1;
1401 if (config->digests_allowed & NTLM_V1_SESSION)
1402 r.u.supportedMechs.ntlm_v1_session = 1;
1403 if (config->digests_allowed & NTLM_V2)
1404 r.u.supportedMechs.ntlm_v2 = 1;
1405 if (config->digests_allowed & DIGEST_MD5)
1406 r.u.supportedMechs.digest_md5 = 1;
1407 if (config->digests_allowed & CHAP_MD5)
1408 r.u.supportedMechs.chap_md5 = 1;
1409 if (config->digests_allowed & MS_CHAP_V2)
1410 r.u.supportedMechs.ms_chap_v2 = 1;
1411 break;
1413 default: {
1414 const char *s;
1415 ret = EINVAL;
1416 krb5_set_error_message(context, ret, "unknown operation to digest");
1418 failed:
1420 s = krb5_get_error_message(context, ret);
1421 if (s == NULL) {
1422 krb5_clear_error_message(context);
1423 goto out;
1426 kdc_log(context, config, 2, "Digest failed with: %s", s);
1428 r.element = choice_DigestRepInner_error;
1429 r.u.error.reason = strdup("unknown error");
1430 krb5_free_error_message(context, s);
1431 if (r.u.error.reason == NULL) {
1432 ret = ENOMEM;
1433 krb5_set_error_message(context, ret, "malloc: out of memory");
1434 goto out;
1436 r.u.error.code = EINVAL;
1437 break;
1441 ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
1442 if (ret) {
1443 krb5_set_error_message(context, ret, "Failed to encode inner digest reply");
1444 goto out;
1446 if (size != buf.length)
1447 krb5_abortx(context, "ASN1 internal error");
1449 krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
1451 ret = krb5_mk_rep (context, ac, &rep.apRep);
1452 if (ret)
1453 goto out;
1456 krb5_keyblock *key;
1458 ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
1459 if (ret)
1460 goto out;
1462 ret = krb5_crypto_init(context, key, 0, &crypto);
1463 krb5_free_keyblock (context, key);
1464 if (ret)
1465 goto out;
1468 ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
1469 buf.data, buf.length, 0,
1470 &rep.innerRep);
1471 if (ret) {
1472 krb5_prepend_error_message(context, ret, "Failed to encrypt digest: ");
1473 goto out;
1476 ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
1477 if (ret) {
1478 krb5_set_error_message(context, ret, "Failed to encode digest reply");
1479 goto out;
1481 if (size != reply->length)
1482 krb5_abortx(context, "ASN1 internal error");
1485 out:
1486 if (ac)
1487 krb5_auth_con_free(context, ac);
1488 if (ret)
1489 krb5_warn(context, ret, "Digest request from %s failed", from);
1490 if (ticket)
1491 krb5_free_ticket(context, ticket);
1492 if (id)
1493 krb5_kt_close(context, id);
1494 if (crypto)
1495 krb5_crypto_destroy(context, crypto);
1496 if (sp)
1497 krb5_storage_free(sp);
1498 if (user)
1499 _kdc_free_ent (context, userdb, user);
1500 if (server)
1501 _kdc_free_ent (context, serverdb, server);
1502 if (client)
1503 _kdc_free_ent (context, clientdb, client);
1504 if (password) {
1505 memset(password, 0, strlen(password));
1506 free (password);
1508 if (client_name)
1509 free (client_name);
1510 krb5_data_free(&buf);
1511 krb5_data_free(&serverNonce);
1512 free_Checksum(&res);
1513 free_DigestREP(&rep);
1514 free_DigestRepInner(&r);
1515 free_DigestReqInner(&ireq);
1517 return ret;
1520 #endif /* DIGEST */