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