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