Further polish and docs for hierarchical capaths
[heimdal.git] / kdc / digest.c
blob295189c66f2b76f647e44ba29926ea50e5805d8d
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_ex *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_ex *client)
86 char *targetname, *p;
88 targetname = strdup(krb5_principal_get_realm(context,
89 client->entry.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_ex *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->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)))
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_ex *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->entry, 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, 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_entry_ex *server = NULL, *user = NULL;
221 hdb_entry_ex *client = NULL;
222 char *client_name = NULL, *password = NULL;
223 krb5_data serverNonce;
225 if(!config->enable_digest) {
226 kdc_log(context, config, 0,
227 "Rejected digest request (disabled) from %s", from);
228 return KRB5KDC_ERR_POLICY;
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));
239 kdc_log(context, config, 0, "Digest request from %s", from);
241 ret = krb5_kt_resolve(context, "HDBGET:", &id);
242 if (ret) {
243 kdc_log(context, config, 0, "Can't open database for digest");
244 goto out;
247 ret = krb5_rd_req(context,
248 &ac,
249 &req->apReq,
250 NULL,
252 &ap_req_options,
253 &ticket);
254 if (ret)
255 goto out;
257 /* check the server principal in the ticket matches digest/R@R */
259 krb5_principal principal = NULL;
260 const char *p, *rr;
262 ret = krb5_ticket_get_server(context, ticket, &principal);
263 if (ret)
264 goto out;
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;
273 if (strcmp(p, KRB5_DIGEST_NAME) != 0) {
274 krb5_free_principal(context, principal);
275 goto out;
278 p = krb5_principal_get_comp_string(context, principal, 1);
279 if (p == NULL) {
280 krb5_free_principal(context, principal);
281 goto out;
283 rr = krb5_principal_get_realm(context, principal);
284 if (rr == NULL) {
285 krb5_free_principal(context, principal);
286 goto out;
288 if (strcmp(p, rr) != 0) {
289 krb5_free_principal(context, principal);
290 goto out;
292 krb5_clear_error_message(context);
294 ret = _kdc_db_fetch(context, config, principal,
295 HDB_F_GET_SERVER, NULL, NULL, &server);
296 if (ret)
297 goto out;
299 krb5_free_principal(context, principal);
302 /* check the client is allowed to do digest auth */
304 krb5_principal principal = NULL;
306 ret = krb5_ticket_get_client(context, ticket, &principal);
307 if (ret)
308 goto out;
310 ret = krb5_unparse_name(context, principal, &client_name);
311 if (ret) {
312 krb5_free_principal(context, principal);
313 goto out;
316 ret = _kdc_db_fetch(context, config, principal,
317 HDB_F_GET_CLIENT, NULL, NULL, &client);
318 krb5_free_principal(context, principal);
319 if (ret)
320 goto out;
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;
334 /* unpack request */
336 krb5_keyblock *key;
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;
347 ret = krb5_crypto_init(context, key, 0, &crypto);
348 krb5_free_keyblock (context, key);
349 if (ret)
350 goto out;
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;
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;
367 kdc_log(context, config, 0, "Valid digest request from %s (%s)",
368 client_name, from);
371 * Process the inner request
374 switch (ireq.element) {
375 case choice_DigestReqInner_init: {
376 unsigned char server_nonce[16], identifier;
378 RAND_pseudo_bytes(&identifier, sizeof(identifier));
379 RAND_pseudo_bytes(server_nonce, sizeof(server_nonce));
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;
386 r.element = choice_DigestRepInner_initReply;
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;
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;
401 ret = krb5_store_stringz(sp, ireq.u.init.type);
402 if (ret) {
403 krb5_clear_error_message(context);
404 goto out;
407 if (ireq.u.init.channel) {
408 char *s;
409 int aret;
411 aret = asprintf(&s, "%s-%s:%s", r.u.initReply.nonce,
412 ireq.u.init.channel->cb_type,
413 ireq.u.init.channel->cb_binding);
414 if (aret == -1 || s == NULL) {
415 ret = ENOMEM;
416 krb5_set_error_message(context, ret,
417 "Failed to allocate channel binding");
418 goto out;
420 free(r.u.initReply.nonce);
421 r.u.initReply.nonce = s;
424 ret = krb5_store_stringz(sp, r.u.initReply.nonce);
425 if (ret) {
426 krb5_clear_error_message(context);
427 goto out;
430 if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
431 int aret;
433 r.u.initReply.identifier =
434 malloc(sizeof(*r.u.initReply.identifier));
435 if (r.u.initReply.identifier == NULL) {
436 ret = ENOMEM;
437 krb5_set_error_message(context, ret, "malloc: out of memory");
438 goto out;
441 aret = asprintf(r.u.initReply.identifier, "%02X", identifier&0xff);
442 if (aret == -1 || *r.u.initReply.identifier == NULL) {
443 ret = ENOMEM;
444 krb5_set_error_message(context, ret, "malloc: out of memory");
445 goto out;
448 } else
449 r.u.initReply.identifier = NULL;
451 if (ireq.u.init.hostname) {
452 ret = krb5_store_stringz(sp, *ireq.u.init.hostname);
453 if (ret) {
454 krb5_clear_error_message(context);
455 goto out;
459 ret = krb5_storage_to_data(sp, &buf);
460 if (ret) {
461 krb5_clear_error_message(context);
462 goto out;
465 ret = get_digest_key(context, config, server, &crypto);
466 if (ret)
467 goto out;
469 ret = krb5_create_checksum(context,
470 crypto,
471 KRB5_KU_DIGEST_OPAQUE,
473 buf.data,
474 buf.length,
475 &res);
476 krb5_crypto_destroy(context, crypto);
477 crypto = NULL;
478 krb5_data_free(&buf);
479 if (ret)
480 goto out;
482 ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret);
483 free_Checksum(&res);
484 if (ret) {
485 krb5_set_error_message(context, ret, "Failed to encode "
486 "checksum in digest request");
487 goto out;
489 if (size != buf.length)
490 krb5_abortx(context, "ASN1 internal error");
492 hex_encode(buf.data, buf.length, &r.u.initReply.opaque);
493 free(buf.data);
494 krb5_data_zero(&buf);
495 if (r.u.initReply.opaque == NULL) {
496 krb5_clear_error_message(context);
497 ret = ENOMEM;
498 goto out;
501 kdc_log(context, config, 0, "Digest %s init request successful from %s",
502 ireq.u.init.type, from);
504 break;
506 case choice_DigestReqInner_digestRequest: {
507 sp = krb5_storage_emem();
508 if (sp == NULL) {
509 ret = ENOMEM;
510 krb5_set_error_message(context, ret, "malloc: out of memory");
511 goto out;
513 ret = krb5_store_stringz(sp, ireq.u.digestRequest.type);
514 if (ret) {
515 krb5_clear_error_message(context);
516 goto out;
519 krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
521 if (ireq.u.digestRequest.hostname) {
522 ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname);
523 if (ret) {
524 krb5_clear_error_message(context);
525 goto out;
529 buf.length = strlen(ireq.u.digestRequest.opaque);
530 buf.data = malloc(buf.length);
531 if (buf.data == NULL) {
532 ret = ENOMEM;
533 krb5_set_error_message(context, ret, "malloc: out of memory");
534 goto out;
537 ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
538 if (ret <= 0) {
539 ret = ENOMEM;
540 krb5_set_error_message(context, ret, "Failed to decode opaque");
541 goto out;
543 buf.length = ret;
545 ret = decode_Checksum(buf.data, buf.length, &res, NULL);
546 free(buf.data);
547 krb5_data_zero(&buf);
548 if (ret) {
549 krb5_set_error_message(context, ret,
550 "Failed to decode digest Checksum");
551 goto out;
554 ret = krb5_storage_to_data(sp, &buf);
555 if (ret) {
556 krb5_clear_error_message(context);
557 goto out;
560 serverNonce.length = strlen(ireq.u.digestRequest.serverNonce);
561 serverNonce.data = malloc(serverNonce.length);
562 if (serverNonce.data == NULL) {
563 ret = ENOMEM;
564 krb5_set_error_message(context, ret, "malloc: out of memory");
565 goto out;
569 * CHAP does the checksum of the raw nonce, but do it for all
570 * types, since we need to check the timestamp.
573 ssize_t ssize;
575 ssize = hex_decode(ireq.u.digestRequest.serverNonce,
576 serverNonce.data, serverNonce.length);
577 if (ssize <= 0) {
578 ret = ENOMEM;
579 krb5_set_error_message(context, ret, "Failed to decode serverNonce");
580 goto out;
582 serverNonce.length = ssize;
585 ret = get_digest_key(context, config, server, &crypto);
586 if (ret)
587 goto out;
589 ret = krb5_verify_checksum(context, crypto,
590 KRB5_KU_DIGEST_OPAQUE,
591 buf.data, buf.length, &res);
592 free_Checksum(&res);
593 krb5_data_free(&buf);
594 krb5_crypto_destroy(context, crypto);
595 crypto = NULL;
596 if (ret)
597 goto out;
599 /* verify time */
601 unsigned char *p = serverNonce.data;
602 uint32_t t;
604 if (serverNonce.length < 4) {
605 ret = EINVAL;
606 krb5_set_error_message(context, ret, "server nonce too short");
607 goto out;
609 t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
611 if (labs((kdc_time & 0xffffffff) - t) > context->max_skew) {
612 ret = EINVAL;
613 krb5_set_error_message(context, ret, "time screw in server nonce ");
614 goto out;
618 if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) {
619 EVP_MD_CTX *ctx;
620 unsigned char md[MD5_DIGEST_LENGTH];
621 char *mdx;
622 char idx;
624 if ((config->digests_allowed & CHAP_MD5) == 0) {
625 kdc_log(context, config, 0, "Digest CHAP MD5 not allowed");
626 goto out;
629 if (ireq.u.digestRequest.identifier == NULL) {
630 ret = EINVAL;
631 krb5_set_error_message(context, ret, "Identifier missing "
632 "from CHAP request");
633 goto out;
636 if (hex_decode(*ireq.u.digestRequest.identifier, &idx, 1) != 1) {
637 ret = EINVAL;
638 krb5_set_error_message(context, ret, "failed to decode identifier");
639 goto out;
642 ret = get_password_entry(context, config,
643 ireq.u.digestRequest.username,
644 &password);
645 if (ret)
646 goto out;
648 ctx = EVP_MD_CTX_create();
650 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
651 EVP_DigestUpdate(ctx, &idx, 1);
652 EVP_DigestUpdate(ctx, password, strlen(password));
653 EVP_DigestUpdate(ctx, serverNonce.data, serverNonce.length);
654 EVP_DigestFinal_ex(ctx, md, NULL);
656 EVP_MD_CTX_destroy(ctx);
658 hex_encode(md, sizeof(md), &mdx);
659 if (mdx == NULL) {
660 krb5_clear_error_message(context);
661 ret = ENOMEM;
662 goto out;
665 r.element = choice_DigestRepInner_response;
667 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
668 free(mdx);
669 if (ret == 0) {
670 r.u.response.success = TRUE;
671 } else {
672 kdc_log(context, config, 0,
673 "CHAP reply mismatch for %s",
674 ireq.u.digestRequest.username);
675 r.u.response.success = FALSE;
678 } else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
679 EVP_MD_CTX *ctx;
680 unsigned char md[MD5_DIGEST_LENGTH];
681 char *mdx;
682 char *A1, *A2;
684 if ((config->digests_allowed & DIGEST_MD5) == 0) {
685 kdc_log(context, config, 0, "Digest SASL MD5 not allowed");
686 goto out;
689 if (ireq.u.digestRequest.nonceCount == NULL)
690 goto out;
691 if (ireq.u.digestRequest.clientNonce == NULL)
692 goto out;
693 if (ireq.u.digestRequest.qop == NULL)
694 goto out;
695 if (ireq.u.digestRequest.realm == NULL)
696 goto out;
698 ret = get_password_entry(context, config,
699 ireq.u.digestRequest.username,
700 &password);
701 if (ret)
702 goto failed;
704 ctx = EVP_MD_CTX_create();
706 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
707 EVP_DigestUpdate(ctx, ireq.u.digestRequest.username,
708 strlen(ireq.u.digestRequest.username));
709 EVP_DigestUpdate(ctx, ":", 1);
710 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.realm,
711 strlen(*ireq.u.digestRequest.realm));
712 EVP_DigestUpdate(ctx, ":", 1);
713 EVP_DigestUpdate(ctx, password, strlen(password));
714 EVP_DigestFinal_ex(ctx, md, NULL);
716 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
717 EVP_DigestUpdate(ctx, md, sizeof(md));
718 EVP_DigestUpdate(ctx, ":", 1);
719 EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce,
720 strlen(ireq.u.digestRequest.serverNonce));
721 EVP_DigestUpdate(ctx, ":", 1);
722 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount,
723 strlen(*ireq.u.digestRequest.nonceCount));
724 if (ireq.u.digestRequest.authid) {
725 EVP_DigestUpdate(ctx, ":", 1);
726 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.authid,
727 strlen(*ireq.u.digestRequest.authid));
729 EVP_DigestFinal_ex(ctx, md, NULL);
730 hex_encode(md, sizeof(md), &A1);
731 if (A1 == NULL) {
732 ret = ENOMEM;
733 krb5_set_error_message(context, ret, "malloc: out of memory");
734 EVP_MD_CTX_destroy(ctx);
735 goto failed;
738 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
739 EVP_DigestUpdate(ctx,
740 "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
741 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.uri,
742 strlen(*ireq.u.digestRequest.uri));
744 /* conf|int */
745 if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
746 static char conf_zeros[] = ":00000000000000000000000000000000";
747 EVP_DigestUpdate(ctx, conf_zeros, sizeof(conf_zeros) - 1);
750 EVP_DigestFinal_ex(ctx, md, NULL);
752 hex_encode(md, sizeof(md), &A2);
753 if (A2 == NULL) {
754 ret = ENOMEM;
755 krb5_set_error_message(context, ret, "malloc: out of memory");
756 free(A1);
757 goto failed;
760 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
761 EVP_DigestUpdate(ctx, A1, strlen(A2));
762 EVP_DigestUpdate(ctx, ":", 1);
763 EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce,
764 strlen(ireq.u.digestRequest.serverNonce));
765 EVP_DigestUpdate(ctx, ":", 1);
766 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount,
767 strlen(*ireq.u.digestRequest.nonceCount));
768 EVP_DigestUpdate(ctx, ":", 1);
769 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.clientNonce,
770 strlen(*ireq.u.digestRequest.clientNonce));
771 EVP_DigestUpdate(ctx, ":", 1);
772 EVP_DigestUpdate(ctx, *ireq.u.digestRequest.qop,
773 strlen(*ireq.u.digestRequest.qop));
774 EVP_DigestUpdate(ctx, ":", 1);
775 EVP_DigestUpdate(ctx, A2, strlen(A2));
777 EVP_DigestFinal_ex(ctx, md, NULL);
779 EVP_MD_CTX_destroy(ctx);
781 free(A1);
782 free(A2);
784 hex_encode(md, sizeof(md), &mdx);
785 if (mdx == NULL) {
786 krb5_clear_error_message(context);
787 ret = ENOMEM;
788 goto out;
791 r.element = choice_DigestRepInner_response;
792 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
793 free(mdx);
794 if (ret == 0) {
795 r.u.response.success = TRUE;
796 } else {
797 kdc_log(context, config, 0,
798 "DIGEST-MD5 reply mismatch for %s",
799 ireq.u.digestRequest.username);
800 r.u.response.success = FALSE;
803 } else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) {
804 unsigned char md[SHA_DIGEST_LENGTH], challenge[SHA_DIGEST_LENGTH];
805 krb5_principal clientprincipal = NULL;
806 char *mdx;
807 const char *username;
808 struct ntlm_buf answer;
809 Key *key = NULL;
810 EVP_MD_CTX *ctp;
812 if ((config->digests_allowed & MS_CHAP_V2) == 0) {
813 kdc_log(context, config, 0, "MS-CHAP-V2 not allowed");
814 goto failed;
817 if (ireq.u.digestRequest.clientNonce == NULL) {
818 ret = EINVAL;
819 krb5_set_error_message(context, ret,
820 "MS-CHAP-V2 clientNonce missing");
821 goto failed;
823 if (serverNonce.length != 16) {
824 ret = EINVAL;
825 krb5_set_error_message(context, ret,
826 "MS-CHAP-V2 serverNonce wrong length");
827 goto failed;
830 /* strip of the domain component */
831 username = strchr(ireq.u.digestRequest.username, '\\');
832 if (username == NULL)
833 username = ireq.u.digestRequest.username;
834 else
835 username++;
837 ctp = EVP_MD_CTX_create();
839 /* ChallengeHash */
840 EVP_DigestInit_ex(ctp, EVP_sha1(), NULL);
842 ssize_t ssize;
843 krb5_data clientNonce;
845 clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce);
846 clientNonce.data = malloc(clientNonce.length);
847 if (clientNonce.data == NULL) {
848 ret = ENOMEM;
849 krb5_set_error_message(context, ret,
850 "malloc: out of memory");
851 EVP_MD_CTX_destroy(ctp);
852 goto out;
855 ssize = hex_decode(*ireq.u.digestRequest.clientNonce,
856 clientNonce.data, clientNonce.length);
857 if (ssize != 16) {
858 ret = ENOMEM;
859 krb5_set_error_message(context, ret,
860 "Failed to decode clientNonce");
861 EVP_MD_CTX_destroy(ctp);
862 goto out;
864 EVP_DigestUpdate(ctp, clientNonce.data, ssize);
865 free(clientNonce.data);
867 EVP_DigestUpdate(ctp, serverNonce.data, serverNonce.length);
868 EVP_DigestUpdate(ctp, username, strlen(username));
870 EVP_DigestFinal_ex(ctp, challenge, NULL);
872 EVP_MD_CTX_destroy(ctp);
874 /* NtPasswordHash */
875 ret = krb5_parse_name(context, username, &clientprincipal);
876 if (ret)
877 goto failed;
879 ret = _kdc_db_fetch(context, config, clientprincipal,
880 HDB_F_GET_CLIENT, NULL, NULL, &user);
881 krb5_free_principal(context, clientprincipal);
882 if (ret) {
883 krb5_set_error_message(context, ret,
884 "MS-CHAP-V2 user %s not in database",
885 username);
886 goto failed;
889 ret = hdb_enctype2key(context, &user->entry, NULL,
890 ETYPE_ARCFOUR_HMAC_MD5, &key);
891 if (ret) {
892 krb5_set_error_message(context, ret,
893 "MS-CHAP-V2 missing arcfour key %s",
894 username);
895 goto failed;
898 /* ChallengeResponse */
899 ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
900 key->key.keyvalue.length,
901 challenge, &answer);
902 if (ret) {
903 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
904 goto failed;
907 hex_encode(answer.data, answer.length, &mdx);
908 if (mdx == NULL) {
909 free(answer.data);
910 krb5_clear_error_message(context);
911 ret = ENOMEM;
912 goto out;
915 r.element = choice_DigestRepInner_response;
916 ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
917 if (ret == 0) {
918 r.u.response.success = TRUE;
919 } else {
920 kdc_log(context, config, 0,
921 "MS-CHAP-V2 hash mismatch for %s",
922 ireq.u.digestRequest.username);
923 r.u.response.success = FALSE;
925 free(mdx);
927 if (r.u.response.success) {
928 unsigned char hashhash[MD4_DIGEST_LENGTH];
929 EVP_MD_CTX *ctxp;
931 ctxp = EVP_MD_CTX_create();
933 /* hashhash */
935 EVP_DigestInit_ex(ctxp, EVP_md4(), NULL);
936 EVP_DigestUpdate(ctxp,
937 key->key.keyvalue.data,
938 key->key.keyvalue.length);
939 EVP_DigestFinal_ex(ctxp, hashhash, NULL);
942 /* GenerateAuthenticatorResponse */
943 EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
944 EVP_DigestUpdate(ctxp, hashhash, sizeof(hashhash));
945 EVP_DigestUpdate(ctxp, answer.data, answer.length);
946 EVP_DigestUpdate(ctxp, ms_chap_v2_magic1,
947 sizeof(ms_chap_v2_magic1));
948 EVP_DigestFinal_ex(ctxp, md, NULL);
950 EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
951 EVP_DigestUpdate(ctxp, md, sizeof(md));
952 EVP_DigestUpdate(ctxp, challenge, 8);
953 EVP_DigestUpdate(ctxp, ms_chap_v2_magic2,
954 sizeof(ms_chap_v2_magic2));
955 EVP_DigestFinal_ex(ctxp, md, NULL);
957 r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp));
958 if (r.u.response.rsp == NULL) {
959 free(answer.data);
960 krb5_clear_error_message(context);
961 EVP_MD_CTX_destroy(ctxp);
962 ret = ENOMEM;
963 goto out;
966 hex_encode(md, sizeof(md), r.u.response.rsp);
967 if (r.u.response.rsp == NULL) {
968 free(answer.data);
969 krb5_clear_error_message(context);
970 EVP_MD_CTX_destroy(ctxp);
971 ret = ENOMEM;
972 goto out;
975 /* get_master, rfc 3079 3.4 */
976 EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
977 EVP_DigestUpdate(ctxp, hashhash, 16);
978 EVP_DigestUpdate(ctxp, answer.data, answer.length);
979 EVP_DigestUpdate(ctxp, ms_rfc3079_magic1,
980 sizeof(ms_rfc3079_magic1));
981 EVP_DigestFinal_ex(ctxp, md, NULL);
983 free(answer.data);
985 EVP_MD_CTX_destroy(ctxp);
987 r.u.response.session_key =
988 calloc(1, sizeof(*r.u.response.session_key));
989 if (r.u.response.session_key == NULL) {
990 krb5_clear_error_message(context);
991 ret = ENOMEM;
992 goto out;
995 ret = krb5_data_copy(r.u.response.session_key, md, 16);
996 if (ret) {
997 krb5_clear_error_message(context);
998 goto out;
1002 } else {
1003 int aret;
1005 r.element = choice_DigestRepInner_error;
1006 aret = asprintf(&r.u.error.reason, "Unsupported digest type %s",
1007 ireq.u.digestRequest.type);
1008 if (aret == -1 || r.u.error.reason == NULL) {
1009 ret = ENOMEM;
1010 krb5_set_error_message(context, ret, "malloc: out of memory");
1011 goto out;
1013 r.u.error.code = EINVAL;
1016 kdc_log(context, config, 0, "Digest %s request successful %s",
1017 ireq.u.digestRequest.type, ireq.u.digestRequest.username);
1019 break;
1021 case choice_DigestReqInner_ntlmInit:
1023 if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) {
1024 kdc_log(context, config, 0, "NTLM not allowed");
1025 goto failed;
1028 r.element = choice_DigestRepInner_ntlmInitReply;
1030 r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE;
1032 if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) {
1033 kdc_log(context, config, 0, "NTLM client have no unicode");
1034 goto failed;
1037 if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM)
1038 r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM;
1039 else {
1040 kdc_log(context, config, 0, "NTLM client doesn't support NTLM");
1041 goto failed;
1044 r.u.ntlmInitReply.flags |=
1045 NTLM_NEG_TARGET |
1046 NTLM_TARGET_DOMAIN |
1047 NTLM_ENC_128;
1049 #define ALL \
1050 NTLM_NEG_SIGN| \
1051 NTLM_NEG_SEAL| \
1052 NTLM_NEG_ALWAYS_SIGN| \
1053 NTLM_NEG_NTLM2_SESSION| \
1054 NTLM_NEG_KEYEX
1056 r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
1058 #undef ALL
1060 r.u.ntlmInitReply.targetname =
1061 get_ntlm_targetname(context, client);
1062 if (r.u.ntlmInitReply.targetname == NULL) {
1063 ret = ENOMEM;
1064 krb5_set_error_message(context, ret, "malloc: out of memory");
1065 goto out;
1067 r.u.ntlmInitReply.challenge.data = malloc(8);
1068 if (r.u.ntlmInitReply.challenge.data == NULL) {
1069 ret = ENOMEM;
1070 krb5_set_error_message(context, ret, "malloc: out of memory");
1071 goto out;
1073 r.u.ntlmInitReply.challenge.length = 8;
1074 if (RAND_bytes(r.u.ntlmInitReply.challenge.data,
1075 r.u.ntlmInitReply.challenge.length) != 1)
1077 ret = ENOMEM;
1078 krb5_set_error_message(context, ret, "out of random error");
1079 goto out;
1081 /* XXX fix targetinfo */
1082 ALLOC(r.u.ntlmInitReply.targetinfo);
1083 if (r.u.ntlmInitReply.targetinfo == NULL) {
1084 ret = ENOMEM;
1085 krb5_set_error_message(context, ret, "malloc: out of memory");
1086 goto out;
1089 ret = fill_targetinfo(context,
1090 r.u.ntlmInitReply.targetname,
1091 client,
1092 r.u.ntlmInitReply.targetinfo);
1093 if (ret) {
1094 ret = ENOMEM;
1095 krb5_set_error_message(context, ret, "malloc: out of memory");
1096 goto out;
1100 * Save data encryted in opaque for the second part of the
1101 * ntlm authentication
1103 sp = krb5_storage_emem();
1104 if (sp == NULL) {
1105 ret = ENOMEM;
1106 krb5_set_error_message(context, ret, "malloc: out of memory");
1107 goto out;
1110 ret = krb5_storage_write(sp, r.u.ntlmInitReply.challenge.data, 8);
1111 if (ret != 8) {
1112 ret = ENOMEM;
1113 krb5_set_error_message(context, ret, "storage write challenge");
1114 goto out;
1116 ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
1117 if (ret) {
1118 krb5_clear_error_message(context);
1119 goto out;
1122 ret = krb5_storage_to_data(sp, &buf);
1123 if (ret) {
1124 krb5_clear_error_message(context);
1125 goto out;
1128 ret = get_digest_key(context, config, server, &crypto);
1129 if (ret)
1130 goto out;
1132 ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1133 buf.data, buf.length, &r.u.ntlmInitReply.opaque);
1134 krb5_data_free(&buf);
1135 krb5_crypto_destroy(context, crypto);
1136 crypto = NULL;
1137 if (ret)
1138 goto out;
1140 kdc_log(context, config, 0, "NTLM init from %s", from);
1142 break;
1144 case choice_DigestReqInner_ntlmRequest: {
1145 krb5_principal clientprincipal;
1146 unsigned char sessionkey[16];
1147 unsigned char challenge[8];
1148 uint32_t flags;
1149 Key *key = NULL;
1150 int version;
1152 r.element = choice_DigestRepInner_ntlmResponse;
1153 r.u.ntlmResponse.success = 0;
1154 r.u.ntlmResponse.flags = 0;
1155 r.u.ntlmResponse.sessionkey = NULL;
1156 r.u.ntlmResponse.tickets = NULL;
1158 /* get username */
1159 ret = krb5_parse_name(context,
1160 ireq.u.ntlmRequest.username,
1161 &clientprincipal);
1162 if (ret)
1163 goto failed;
1165 ret = _kdc_db_fetch(context, config, clientprincipal,
1166 HDB_F_GET_CLIENT, NULL, NULL, &user);
1167 krb5_free_principal(context, clientprincipal);
1168 if (ret) {
1169 krb5_set_error_message(context, ret, "NTLM user %s not in database",
1170 ireq.u.ntlmRequest.username);
1171 goto failed;
1174 ret = get_digest_key(context, config, server, &crypto);
1175 if (ret)
1176 goto failed;
1178 ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1179 ireq.u.ntlmRequest.opaque.data,
1180 ireq.u.ntlmRequest.opaque.length, &buf);
1181 krb5_crypto_destroy(context, crypto);
1182 crypto = NULL;
1183 if (ret) {
1184 kdc_log(context, config, 0,
1185 "Failed to decrypt nonce from %s", from);
1186 goto failed;
1189 sp = krb5_storage_from_data(&buf);
1190 if (sp == NULL) {
1191 ret = ENOMEM;
1192 krb5_set_error_message(context, ret, "malloc: out of memory");
1193 goto out;
1196 ret = krb5_storage_read(sp, challenge, sizeof(challenge));
1197 if (ret != sizeof(challenge)) {
1198 ret = ENOMEM;
1199 krb5_set_error_message(context, ret, "NTLM storage read challenge");
1200 goto out;
1202 ret = krb5_ret_uint32(sp, &flags);
1203 if (ret) {
1204 krb5_set_error_message(context, ret, "NTLM storage read flags");
1205 goto out;
1207 krb5_storage_free(sp);
1208 sp = NULL;
1209 krb5_data_free(&buf);
1211 if ((flags & NTLM_NEG_NTLM) == 0) {
1212 ret = EINVAL;
1213 krb5_set_error_message(context, ret, "NTLM not negotiated");
1214 goto out;
1217 ret = hdb_enctype2key(context, &user->entry, NULL,
1218 ETYPE_ARCFOUR_HMAC_MD5, &key);
1219 if (ret) {
1220 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1221 goto out;
1224 /* check if this is NTLMv2 */
1225 if (ireq.u.ntlmRequest.ntlm.length != 24) {
1226 struct ntlm_buf infotarget, answer;
1227 char *targetname;
1229 if ((config->digests_allowed & NTLM_V2) == 0) {
1230 kdc_log(context, config, 0, "NTLM v2 not allowed");
1231 goto out;
1234 version = 2;
1236 targetname = get_ntlm_targetname(context, client);
1237 if (targetname == NULL) {
1238 ret = ENOMEM;
1239 krb5_set_error_message(context, ret, "malloc: out of memory");
1240 goto out;
1243 answer.length = ireq.u.ntlmRequest.ntlm.length;
1244 answer.data = ireq.u.ntlmRequest.ntlm.data;
1246 ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
1247 key->key.keyvalue.length,
1248 ireq.u.ntlmRequest.username,
1249 targetname,
1251 challenge,
1252 &answer,
1253 &infotarget,
1254 sessionkey);
1255 free(targetname);
1256 if (ret) {
1257 krb5_set_error_message(context, ret, "NTLM v2 verify failed");
1258 goto failed;
1261 /* XXX verify infotarget matches client (checksum ?) */
1263 free(infotarget.data);
1264 /* */
1266 } else {
1267 struct ntlm_buf answer;
1269 version = 1;
1271 if (flags & NTLM_NEG_NTLM2_SESSION) {
1272 unsigned char sessionhash[MD5_DIGEST_LENGTH];
1273 EVP_MD_CTX *ctx;
1275 if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
1276 kdc_log(context, config, 0, "NTLM v1-session not allowed");
1277 ret = EINVAL;
1278 goto failed;
1281 if (ireq.u.ntlmRequest.lm.length != 24) {
1282 ret = EINVAL;
1283 krb5_set_error_message(context, ret, "LM hash have wrong length "
1284 "for NTLM session key");
1285 goto failed;
1288 ctx = EVP_MD_CTX_create();
1290 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
1292 EVP_DigestUpdate(ctx, challenge, sizeof(challenge));
1293 EVP_DigestUpdate(ctx, ireq.u.ntlmRequest.lm.data, 8);
1294 EVP_DigestFinal_ex(ctx, sessionhash, NULL);
1295 memcpy(challenge, sessionhash, sizeof(challenge));
1297 EVP_MD_CTX_destroy(ctx);
1299 } else {
1300 if ((config->digests_allowed & NTLM_V1) == 0) {
1301 kdc_log(context, config, 0, "NTLM v1 not allowed");
1302 goto failed;
1306 ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
1307 key->key.keyvalue.length,
1308 challenge, &answer);
1309 if (ret) {
1310 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1311 goto failed;
1314 if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
1315 memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
1317 free(answer.data);
1318 ret = EINVAL;
1319 krb5_set_error_message(context, ret, "NTLM hash mismatch");
1320 goto failed;
1322 free(answer.data);
1325 EVP_MD_CTX *ctx;
1327 ctx = EVP_MD_CTX_create();
1329 EVP_DigestInit_ex(ctx, EVP_md4(), NULL);
1330 EVP_DigestUpdate(ctx,
1331 key->key.keyvalue.data,
1332 key->key.keyvalue.length);
1333 EVP_DigestFinal_ex(ctx, sessionkey, NULL);
1335 EVP_MD_CTX_destroy(ctx);
1339 if (ireq.u.ntlmRequest.sessionkey) {
1340 unsigned char masterkey[MD4_DIGEST_LENGTH];
1341 EVP_CIPHER_CTX rc4;
1342 size_t len;
1344 if ((flags & NTLM_NEG_KEYEX) == 0) {
1345 ret = EINVAL;
1346 krb5_set_error_message(context, ret,
1347 "NTLM client failed to neg key "
1348 "exchange but still sent key");
1349 goto failed;
1352 len = ireq.u.ntlmRequest.sessionkey->length;
1353 if (len != sizeof(masterkey)){
1354 ret = EINVAL;
1355 krb5_set_error_message(context, ret,
1356 "NTLM master key wrong length: %lu",
1357 (unsigned long)len);
1358 goto failed;
1362 EVP_CIPHER_CTX_init(&rc4);
1363 EVP_CipherInit_ex(&rc4, EVP_rc4(), NULL, sessionkey, NULL, 1);
1364 EVP_Cipher(&rc4,
1365 masterkey, ireq.u.ntlmRequest.sessionkey->data,
1366 sizeof(masterkey));
1367 EVP_CIPHER_CTX_cleanup(&rc4);
1369 r.u.ntlmResponse.sessionkey =
1370 malloc(sizeof(*r.u.ntlmResponse.sessionkey));
1371 if (r.u.ntlmResponse.sessionkey == NULL) {
1372 ret = EINVAL;
1373 krb5_set_error_message(context, ret, "malloc: out of memory");
1374 goto out;
1377 ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
1378 masterkey, sizeof(masterkey));
1379 if (ret) {
1380 krb5_set_error_message(context, ret, "malloc: out of memory");
1381 goto out;
1385 r.u.ntlmResponse.success = 1;
1386 kdc_log(context, config, 0, "NTLM version %d successful for %s",
1387 version, ireq.u.ntlmRequest.username);
1388 break;
1390 case choice_DigestReqInner_supportedMechs:
1392 kdc_log(context, config, 0, "digest supportedMechs from %s", from);
1394 r.element = choice_DigestRepInner_supportedMechs;
1395 memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs));
1397 if (config->digests_allowed & NTLM_V1)
1398 r.u.supportedMechs.ntlm_v1 = 1;
1399 if (config->digests_allowed & NTLM_V1_SESSION)
1400 r.u.supportedMechs.ntlm_v1_session = 1;
1401 if (config->digests_allowed & NTLM_V2)
1402 r.u.supportedMechs.ntlm_v2 = 1;
1403 if (config->digests_allowed & DIGEST_MD5)
1404 r.u.supportedMechs.digest_md5 = 1;
1405 if (config->digests_allowed & CHAP_MD5)
1406 r.u.supportedMechs.chap_md5 = 1;
1407 if (config->digests_allowed & MS_CHAP_V2)
1408 r.u.supportedMechs.ms_chap_v2 = 1;
1409 break;
1411 default: {
1412 const char *s;
1413 ret = EINVAL;
1414 krb5_set_error_message(context, ret, "unknown operation to digest");
1416 failed:
1418 s = krb5_get_error_message(context, ret);
1419 if (s == NULL) {
1420 krb5_clear_error_message(context);
1421 goto out;
1424 kdc_log(context, config, 0, "Digest failed with: %s", s);
1426 r.element = choice_DigestRepInner_error;
1427 r.u.error.reason = strdup("unknown error");
1428 krb5_free_error_message(context, s);
1429 if (r.u.error.reason == NULL) {
1430 ret = ENOMEM;
1431 krb5_set_error_message(context, ret, "malloc: out of memory");
1432 goto out;
1434 r.u.error.code = EINVAL;
1435 break;
1439 ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
1440 if (ret) {
1441 krb5_set_error_message(context, ret, "Failed to encode inner digest reply");
1442 goto out;
1444 if (size != buf.length)
1445 krb5_abortx(context, "ASN1 internal error");
1447 krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
1449 ret = krb5_mk_rep (context, ac, &rep.apRep);
1450 if (ret)
1451 goto out;
1454 krb5_keyblock *key;
1456 ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
1457 if (ret)
1458 goto out;
1460 ret = krb5_crypto_init(context, key, 0, &crypto);
1461 krb5_free_keyblock (context, key);
1462 if (ret)
1463 goto out;
1466 ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
1467 buf.data, buf.length, 0,
1468 &rep.innerRep);
1470 ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
1471 if (ret) {
1472 krb5_set_error_message(context, ret, "Failed to encode digest reply");
1473 goto out;
1475 if (size != reply->length)
1476 krb5_abortx(context, "ASN1 internal error");
1479 out:
1480 if (ac)
1481 krb5_auth_con_free(context, ac);
1482 if (ret)
1483 krb5_warn(context, ret, "Digest request from %s failed", from);
1484 if (ticket)
1485 krb5_free_ticket(context, ticket);
1486 if (id)
1487 krb5_kt_close(context, id);
1488 if (crypto)
1489 krb5_crypto_destroy(context, crypto);
1490 if (sp)
1491 krb5_storage_free(sp);
1492 if (user)
1493 _kdc_free_ent (context, user);
1494 if (server)
1495 _kdc_free_ent (context, server);
1496 if (client)
1497 _kdc_free_ent (context, client);
1498 if (password) {
1499 memset(password, 0, strlen(password));
1500 free (password);
1502 if (client_name)
1503 free (client_name);
1504 krb5_data_free(&buf);
1505 krb5_data_free(&serverNonce);
1506 free_Checksum(&res);
1507 free_DigestREP(&rep);
1508 free_DigestRepInner(&r);
1509 free_DigestReqInner(&ireq);
1511 return ret;
1514 #endif /* DIGEST */