r26540: Revert my previous commit after concerns raised by Andrew.
[jra/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 RCSID("$Id: digest.c 21606 2007-07-17 07:03:25Z lha $");
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, &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_string(context, "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 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
238     kdc_log(context, config, 0, "Digest request from %s", from);
239
240     ret = krb5_kt_resolve(context, "HDB:", &id);
241     if (ret) {
242         kdc_log(context, config, 0, "Can't open database for digest");
243         goto out;
244     }
245
246     ret = krb5_rd_req(context, 
247                       &ac,
248                       &req->apReq,
249                       NULL,
250                       id,
251                       &ap_req_options,
252                       &ticket);
253     if (ret)
254         goto out;
255
256     /* check the server principal in the ticket matches digest/R@R */
257     {
258         krb5_principal principal = NULL;
259         const char *p, *r;
260
261         ret = krb5_ticket_get_server(context, ticket, &principal);
262         if (ret)
263             goto out;
264
265         ret = EINVAL;
266         krb5_set_error_string(context, "Wrong digest server principal used");
267         p = krb5_principal_get_comp_string(context, principal, 0);
268         if (p == NULL) {
269             krb5_free_principal(context, principal);
270             goto out;
271         }
272         if (strcmp(p, KRB5_DIGEST_NAME) != 0) {
273             krb5_free_principal(context, principal);
274             goto out;
275         }
276
277         p = krb5_principal_get_comp_string(context, principal, 1);
278         if (p == NULL) {
279             krb5_free_principal(context, principal);
280             goto out;
281         }
282         r = krb5_principal_get_realm(context, principal);
283         if (r == NULL) {
284             krb5_free_principal(context, principal);
285             goto out;
286         }
287         if (strcmp(p, r) != 0) {
288             krb5_free_principal(context, principal);
289             goto out;
290         }
291         krb5_clear_error_string(context);
292
293         ret = _kdc_db_fetch(context, config, principal,
294                             HDB_F_GET_SERVER, NULL, &server);
295         if (ret)
296             goto out;
297
298         krb5_free_principal(context, principal);
299     }
300
301     /* check the client is allowed to do digest auth */
302     {
303         krb5_principal principal = NULL;
304
305         ret = krb5_ticket_get_client(context, ticket, &principal);
306         if (ret)
307             goto out;
308
309         ret = krb5_unparse_name(context, principal, &client_name);
310         if (ret) {
311             krb5_free_principal(context, principal);
312             goto out;
313         }
314
315         ret = _kdc_db_fetch(context, config, principal,
316                             HDB_F_GET_CLIENT, NULL, &client);
317         krb5_free_principal(context, principal);
318         if (ret)
319             goto out;
320
321         if (client->entry.flags.allow_digest == 0) {
322             kdc_log(context, config, 0, 
323                     "Client %s tried to use digest "
324                     "but is not allowed to", 
325                     client_name);
326             krb5_set_error_string(context, 
327                                   "Client is not permitted to use digest");
328             ret = KRB5KDC_ERR_POLICY;
329             goto out;
330         }
331     }
332
333     /* unpack request */
334     {
335         krb5_keyblock *key;
336
337         ret = krb5_auth_con_getremotesubkey(context, ac, &key);
338         if (ret)
339             goto out;
340         if (key == NULL) {
341             krb5_set_error_string(context, "digest: remote subkey not found");
342             ret = EINVAL;
343             goto out;
344         }
345
346         ret = krb5_crypto_init(context, key, 0, &crypto);
347         krb5_free_keyblock (context, key);
348         if (ret)
349             goto out;
350     }
351
352     ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
353                                      &req->innerReq, &buf);
354     krb5_crypto_destroy(context, crypto);
355     crypto = NULL;
356     if (ret)
357         goto out;
358            
359     ret = decode_DigestReqInner(buf.data, buf.length, &ireq, NULL);
360     krb5_data_free(&buf);
361     if (ret) {
362         krb5_set_error_string(context, "Failed to decode digest inner request");
363         goto out;
364     }
365
366     kdc_log(context, config, 0, "Valid digest request from %s (%s)", 
367             client_name, from);
368
369     /*
370      * Process the inner request
371      */
372
373     switch (ireq.element) {
374     case choice_DigestReqInner_init: {
375         unsigned char server_nonce[16], identifier;
376
377         RAND_pseudo_bytes(&identifier, sizeof(identifier));
378         RAND_pseudo_bytes(server_nonce, sizeof(server_nonce));
379
380         server_nonce[0] = kdc_time & 0xff;
381         server_nonce[1] = (kdc_time >> 8) & 0xff;
382         server_nonce[2] = (kdc_time >> 16) & 0xff;
383         server_nonce[3] = (kdc_time >> 24) & 0xff;
384
385         r.element = choice_DigestRepInner_initReply;
386
387         hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce);
388         if (r.u.initReply.nonce == NULL) {
389             krb5_set_error_string(context, "Failed to decode server nonce");
390             ret = ENOMEM;
391             goto out;
392         }
393
394         sp = krb5_storage_emem();
395         if (sp == NULL) {
396             ret = ENOMEM;
397             krb5_set_error_string(context, "out of memory");
398             goto out;
399         }
400         ret = krb5_store_stringz(sp, ireq.u.init.type);
401         if (ret) {
402             krb5_clear_error_string(context);
403             goto out;
404         }
405
406         if (ireq.u.init.channel) {
407             char *s;
408
409             asprintf(&s, "%s-%s:%s", r.u.initReply.nonce,
410                      ireq.u.init.channel->cb_type,
411                      ireq.u.init.channel->cb_binding);
412             if (s == NULL) {
413                 krb5_set_error_string(context, "Failed to allocate "
414                                       "channel binding");
415                 ret = ENOMEM;
416                 goto out;
417             }
418             free(r.u.initReply.nonce);
419             r.u.initReply.nonce = s;
420         }
421         
422         ret = krb5_store_stringz(sp, r.u.initReply.nonce);
423         if (ret) {
424             krb5_clear_error_string(context);
425             goto out;
426         }
427
428         if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
429             r.u.initReply.identifier = 
430                 malloc(sizeof(*r.u.initReply.identifier));
431             if (r.u.initReply.identifier == NULL) {
432                 krb5_set_error_string(context, "out of memory");
433                 ret = ENOMEM;
434                 goto out;
435             }
436
437             asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff);
438             if (*r.u.initReply.identifier == NULL) {
439                 krb5_set_error_string(context, "out of memory");
440                 ret = ENOMEM;
441                 goto out;
442             }
443
444         } else
445             r.u.initReply.identifier = NULL;
446
447         if (ireq.u.init.hostname) {
448             ret = krb5_store_stringz(sp, *ireq.u.init.hostname);
449             if (ret) {
450                 krb5_clear_error_string(context);
451                 goto out;
452             }
453         }
454
455         ret = krb5_storage_to_data(sp, &buf);
456         if (ret) {
457             krb5_clear_error_string(context);
458             goto out;
459         }
460
461         ret = get_digest_key(context, config, server, &crypto);
462         if (ret)
463             goto out;
464
465         ret = krb5_create_checksum(context,
466                                    crypto,
467                                    KRB5_KU_DIGEST_OPAQUE,
468                                    0,
469                                    buf.data,
470                                    buf.length,
471                                    &res);
472         krb5_crypto_destroy(context, crypto);
473         crypto = NULL;
474         krb5_data_free(&buf);
475         if (ret)
476             goto out;
477         
478         ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret);
479         free_Checksum(&res);
480         if (ret) {
481             krb5_set_error_string(context, "Failed to encode "
482                                   "checksum in digest request");
483             goto out;
484         }
485         if (size != buf.length)
486             krb5_abortx(context, "ASN1 internal error");
487
488         hex_encode(buf.data, buf.length, &r.u.initReply.opaque);
489         free(buf.data);
490         if (r.u.initReply.opaque == NULL) {
491             krb5_clear_error_string(context);
492             ret = ENOMEM;
493             goto out;
494         }
495
496         kdc_log(context, config, 0, "Digest %s init request successful from %s",
497                 ireq.u.init.type, from);
498
499         break;
500     }
501     case choice_DigestReqInner_digestRequest: {
502         sp = krb5_storage_emem();
503         if (sp == NULL) {
504             ret = ENOMEM;
505             krb5_set_error_string(context, "out of memory");
506             goto out;
507         }
508         ret = krb5_store_stringz(sp, ireq.u.digestRequest.type);
509         if (ret) {
510             krb5_clear_error_string(context);
511             goto out;
512         }
513
514         krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
515
516         if (ireq.u.digestRequest.hostname) {
517             ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname);
518             if (ret) {
519                 krb5_clear_error_string(context);
520                 goto out;
521             }
522         }
523
524         buf.length = strlen(ireq.u.digestRequest.opaque);
525         buf.data = malloc(buf.length);
526         if (buf.data == NULL) {
527             krb5_set_error_string(context, "out of memory");
528             ret = ENOMEM;
529             goto out;
530         }
531
532         ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
533         if (ret <= 0) {
534             krb5_set_error_string(context, "Failed to decode opaque");
535             ret = ENOMEM;
536             goto out;
537         }
538         buf.length = ret;
539
540         ret = decode_Checksum(buf.data, buf.length, &res, NULL);
541         free(buf.data);
542         if (ret) {
543             krb5_set_error_string(context, "Failed to decode digest Checksum");
544             goto out;
545         }
546         
547         ret = krb5_storage_to_data(sp, &buf);
548         if (ret) {
549             krb5_clear_error_string(context);
550             goto out;
551         }
552
553         serverNonce.length = strlen(ireq.u.digestRequest.serverNonce);
554         serverNonce.data = malloc(serverNonce.length);
555         if (serverNonce.data == NULL) {
556             krb5_set_error_string(context, "out of memory");
557             ret = ENOMEM;
558             goto out;
559         }
560             
561         /*
562          * CHAP does the checksum of the raw nonce, but do it for all
563          * types, since we need to check the timestamp.
564          */
565         {
566             ssize_t ssize;
567             
568             ssize = hex_decode(ireq.u.digestRequest.serverNonce, 
569                                serverNonce.data, serverNonce.length);
570             if (ssize <= 0) {
571                 krb5_set_error_string(context, "Failed to decode serverNonce");
572                 ret = ENOMEM;
573                 goto out;
574             }
575             serverNonce.length = ssize;
576         }
577
578         ret = get_digest_key(context, config, server, &crypto);
579         if (ret)
580             goto out;
581
582         ret = krb5_verify_checksum(context, crypto, 
583                                    KRB5_KU_DIGEST_OPAQUE,
584                                    buf.data, buf.length, &res);
585         krb5_crypto_destroy(context, crypto);
586         crypto = NULL;
587         if (ret)
588             goto out;
589
590         /* verify time */
591         {
592             unsigned char *p = serverNonce.data;
593             uint32_t t;
594             
595             if (serverNonce.length < 4) {
596                 krb5_set_error_string(context, "server nonce too short");
597                 ret = EINVAL;
598                 goto out;
599             }
600             t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
601
602             if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) {
603                 krb5_set_error_string(context, "time screw in server nonce ");
604                 ret = EINVAL;
605                 goto out;
606             }
607         }
608
609         if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) {
610             MD5_CTX ctx;
611             unsigned char md[MD5_DIGEST_LENGTH];
612             char *mdx;
613             char id;
614
615             if ((config->digests_allowed & CHAP_MD5) == 0) {
616                 kdc_log(context, config, 0, "Digest CHAP MD5 not allowed");
617                 goto out;
618             }
619
620             if (ireq.u.digestRequest.identifier == NULL) {
621                 krb5_set_error_string(context, "Identifier missing "
622                                       "from CHAP request");
623                 ret = EINVAL;
624                 goto out;
625             }
626             
627             if (hex_decode(*ireq.u.digestRequest.identifier, &id, 1) != 1) {
628                 krb5_set_error_string(context, "failed to decode identifier");
629                 ret = EINVAL;
630                 goto out;
631             }
632             
633             ret = get_password_entry(context, config, 
634                                      ireq.u.digestRequest.username,
635                                      &password);
636             if (ret)
637                 goto out;
638
639             MD5_Init(&ctx);
640             MD5_Update(&ctx, &id, 1);
641             MD5_Update(&ctx, password, strlen(password));
642             MD5_Update(&ctx, serverNonce.data, serverNonce.length);
643             MD5_Final(md, &ctx);
644
645             hex_encode(md, sizeof(md), &mdx);
646             if (mdx == NULL) {
647                 krb5_clear_error_string(context);
648                 ret = ENOMEM;
649                 goto out;
650             }
651
652             r.element = choice_DigestRepInner_response;
653
654             ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
655             free(mdx);
656             if (ret == 0) {
657                 r.u.response.success = TRUE;
658             } else {
659                 kdc_log(context, config, 0, 
660                         "CHAP reply mismatch for %s",
661                         ireq.u.digestRequest.username);
662                 r.u.response.success = FALSE;
663             }
664
665         } else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
666             MD5_CTX ctx;
667             unsigned char md[MD5_DIGEST_LENGTH];
668             char *mdx;
669             char *A1, *A2;
670
671             if ((config->digests_allowed & DIGEST_MD5) == 0) {
672                 kdc_log(context, config, 0, "Digest SASL MD5 not allowed");
673                 goto out;
674             }
675
676             if (ireq.u.digestRequest.nonceCount == NULL) 
677                 goto out;
678             if (ireq.u.digestRequest.clientNonce == NULL) 
679                 goto out;
680             if (ireq.u.digestRequest.qop == NULL) 
681                 goto out;
682             if (ireq.u.digestRequest.realm == NULL) 
683                 goto out;
684             
685             ret = get_password_entry(context, config, 
686                                      ireq.u.digestRequest.username,
687                                      &password);
688             if (ret)
689                 goto failed;
690
691             MD5_Init(&ctx);
692             MD5_Update(&ctx, ireq.u.digestRequest.username,
693                        strlen(ireq.u.digestRequest.username));
694             MD5_Update(&ctx, ":", 1);
695             MD5_Update(&ctx, *ireq.u.digestRequest.realm,
696                        strlen(*ireq.u.digestRequest.realm));
697             MD5_Update(&ctx, ":", 1);
698             MD5_Update(&ctx, password, strlen(password));
699             MD5_Final(md, &ctx);
700             
701             MD5_Init(&ctx);
702             MD5_Update(&ctx, md, sizeof(md));
703             MD5_Update(&ctx, ":", 1);
704             MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
705                        strlen(ireq.u.digestRequest.serverNonce));
706             MD5_Update(&ctx, ":", 1);
707             MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
708                        strlen(*ireq.u.digestRequest.nonceCount));
709             if (ireq.u.digestRequest.authid) {
710                 MD5_Update(&ctx, ":", 1);
711                 MD5_Update(&ctx, *ireq.u.digestRequest.authid,
712                            strlen(*ireq.u.digestRequest.authid));
713             }
714             MD5_Final(md, &ctx);
715             hex_encode(md, sizeof(md), &A1);
716             if (A1 == NULL) {
717                 krb5_set_error_string(context, "out of memory");
718                 ret = ENOMEM;
719                 goto failed;
720             }
721             
722             MD5_Init(&ctx);
723             MD5_Update(&ctx, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
724             MD5_Update(&ctx, *ireq.u.digestRequest.uri,
725                        strlen(*ireq.u.digestRequest.uri));
726         
727             /* conf|int */
728             if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
729                 static char conf_zeros[] = ":00000000000000000000000000000000";
730                 MD5_Update(&ctx, conf_zeros, sizeof(conf_zeros) - 1);
731             }
732             
733             MD5_Final(md, &ctx);
734             hex_encode(md, sizeof(md), &A2);
735             if (A2 == NULL) {
736                 krb5_set_error_string(context, "out of memory");
737                 ret = ENOMEM;
738                 free(A1);
739                 goto failed;
740             }
741
742             MD5_Init(&ctx);
743             MD5_Update(&ctx, A1, strlen(A2));
744             MD5_Update(&ctx, ":", 1);
745             MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
746                        strlen(ireq.u.digestRequest.serverNonce));
747             MD5_Update(&ctx, ":", 1);
748             MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
749                        strlen(*ireq.u.digestRequest.nonceCount));
750             MD5_Update(&ctx, ":", 1);
751             MD5_Update(&ctx, *ireq.u.digestRequest.clientNonce,
752                        strlen(*ireq.u.digestRequest.clientNonce));
753             MD5_Update(&ctx, ":", 1);
754             MD5_Update(&ctx, *ireq.u.digestRequest.qop,
755                        strlen(*ireq.u.digestRequest.qop));
756             MD5_Update(&ctx, ":", 1);
757             MD5_Update(&ctx, A2, strlen(A2));
758
759             MD5_Final(md, &ctx);
760
761             free(A1);
762             free(A2);
763
764             hex_encode(md, sizeof(md), &mdx);
765             if (mdx == NULL) {
766                 krb5_clear_error_string(context);
767                 ret = ENOMEM;
768                 goto out;
769             }
770
771             r.element = choice_DigestRepInner_response;
772             ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
773             free(mdx);
774             if (ret == 0) {
775                 r.u.response.success = TRUE;
776             } else {
777                 kdc_log(context, config, 0, 
778                         "DIGEST-MD5 reply mismatch for %s",
779                         ireq.u.digestRequest.username);
780                 r.u.response.success = FALSE;
781             }
782
783         } else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) {
784             unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH];
785             krb5_principal clientprincipal = NULL;
786             char *mdx;
787             const char *username;
788             struct ntlm_buf answer;
789             Key *key = NULL;
790             SHA_CTX ctx;
791
792             if ((config->digests_allowed & MS_CHAP_V2) == 0) {
793                 kdc_log(context, config, 0, "MS-CHAP-V2 not allowed");
794                 goto failed;
795             }
796
797             if (ireq.u.digestRequest.clientNonce == NULL)  {
798                 krb5_set_error_string(context, 
799                                       "MS-CHAP-V2 clientNonce missing");
800                 ret = EINVAL;
801                 goto failed;
802             }       
803             if (serverNonce.length != 16) {
804                 krb5_set_error_string(context, 
805                                       "MS-CHAP-V2 serverNonce wrong length");
806                 ret = EINVAL;
807                 goto failed;
808             }
809
810             /* strip of the domain component */
811             username = strchr(ireq.u.digestRequest.username, '\\');
812             if (username == NULL)
813                 username = ireq.u.digestRequest.username;
814             else
815                 username++;
816
817             /* ChallangeHash */
818             SHA1_Init(&ctx);
819             {
820                 ssize_t ssize;
821                 krb5_data clientNonce;
822                 
823                 clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce);
824                 clientNonce.data = malloc(clientNonce.length);
825                 if (clientNonce.data == NULL) {
826                     ret = ENOMEM;
827                     krb5_set_error_string(context, "out of memory");
828                     goto out;
829                 }
830
831                 ssize = hex_decode(*ireq.u.digestRequest.clientNonce, 
832                                    clientNonce.data, clientNonce.length);
833                 if (ssize != 16) {
834                     krb5_set_error_string(context, 
835                                           "Failed to decode clientNonce");
836                     ret = ENOMEM;
837                     goto out;
838                 }
839                 SHA1_Update(&ctx, clientNonce.data, ssize);
840                 free(clientNonce.data);
841             }
842             SHA1_Update(&ctx, serverNonce.data, serverNonce.length);
843             SHA1_Update(&ctx, username, strlen(username));
844             SHA1_Final(challange, &ctx);
845
846             /* NtPasswordHash */
847             ret = krb5_parse_name(context, username, &clientprincipal);
848             if (ret)
849                 goto failed;
850             
851             ret = _kdc_db_fetch(context, config, clientprincipal,
852                                 HDB_F_GET_CLIENT, NULL, &user);
853             krb5_free_principal(context, clientprincipal);
854             if (ret) {
855                 krb5_set_error_string(context, 
856                                       "MS-CHAP-V2 user %s not in database",
857                                       username);
858                 goto failed;
859             }
860
861             ret = hdb_enctype2key(context, &user->entry, 
862                                   ETYPE_ARCFOUR_HMAC_MD5, &key);
863             if (ret) {
864                 krb5_set_error_string(context, 
865                                       "MS-CHAP-V2 missing arcfour key %s",
866                                       username);
867                 goto failed;
868             }
869
870             /* ChallengeResponse */
871             ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
872                                             key->key.keyvalue.length,
873                                             challange, &answer);
874             if (ret) {
875                 krb5_set_error_string(context, "NTLM missing arcfour key");
876                 goto failed;
877             }
878             
879             hex_encode(answer.data, answer.length, &mdx);
880             if (mdx == NULL) {
881                 free(answer.data);
882                 krb5_clear_error_string(context);
883                 ret = ENOMEM;
884                 goto out;
885             }
886
887             r.element = choice_DigestRepInner_response;
888             ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
889             if (ret == 0) {
890                 r.u.response.success = TRUE;
891             } else {
892                 kdc_log(context, config, 0, 
893                         "MS-CHAP-V2 hash mismatch for %s",
894                         ireq.u.digestRequest.username);
895                 r.u.response.success = FALSE;
896             }
897             free(mdx);
898
899             if (r.u.response.success) {
900                 unsigned char hashhash[MD4_DIGEST_LENGTH];
901
902                 /* hashhash */
903                 {
904                     MD4_CTX hctx;
905
906                     MD4_Init(&hctx);
907                     MD4_Update(&hctx, key->key.keyvalue.data, 
908                                key->key.keyvalue.length);
909                     MD4_Final(hashhash, &hctx);
910                 }
911
912                 /* GenerateAuthenticatorResponse */
913                 SHA1_Init(&ctx);
914                 SHA1_Update(&ctx, hashhash, sizeof(hashhash));
915                 SHA1_Update(&ctx, answer.data, answer.length);
916                 SHA1_Update(&ctx, ms_chap_v2_magic1,sizeof(ms_chap_v2_magic1));
917                 SHA1_Final(md, &ctx);
918
919                 SHA1_Init(&ctx);
920                 SHA1_Update(&ctx, md, sizeof(md));
921                 SHA1_Update(&ctx, challange, 8);
922                 SHA1_Update(&ctx, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2));
923                 SHA1_Final(md, &ctx);
924
925                 r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp));
926                 if (r.u.response.rsp == NULL) {
927                     free(answer.data);
928                     krb5_clear_error_string(context);
929                     ret = ENOMEM;
930                     goto out;
931                 }
932
933                 hex_encode(md, sizeof(md), r.u.response.rsp);
934                 if (r.u.response.rsp == NULL) {
935                     free(answer.data);
936                     krb5_clear_error_string(context);
937                     ret = ENOMEM;
938                     goto out;
939                 }
940
941                 /* get_master, rfc 3079 3.4 */
942                 SHA1_Init(&ctx);
943                 SHA1_Update(&ctx, hashhash, 16); /* md4(hash) */
944                 SHA1_Update(&ctx, answer.data, answer.length);
945                 SHA1_Update(&ctx, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1));
946                 SHA1_Final(md, &ctx);
947
948                 free(answer.data);
949
950                 r.u.response.session_key = 
951                     calloc(1, sizeof(*r.u.response.session_key));
952                 if (r.u.response.session_key == NULL) {
953                     krb5_clear_error_string(context);
954                     ret = ENOMEM;
955                     goto out;
956                 }
957
958                 ret = krb5_data_copy(r.u.response.session_key, md, 16);
959                 if (ret) {
960                     krb5_clear_error_string(context);
961                     goto out;
962                 }
963             }
964
965         } else {
966             r.element = choice_DigestRepInner_error;
967             asprintf(&r.u.error.reason, "Unsupported digest type %s", 
968                      ireq.u.digestRequest.type);
969             if (r.u.error.reason == NULL) {
970                 krb5_set_error_string(context, "out of memory");
971                 ret = ENOMEM;
972                 goto out;
973             }
974             r.u.error.code = EINVAL;
975         }
976
977         kdc_log(context, config, 0, "Digest %s request successful %s",
978                 ireq.u.digestRequest.type, ireq.u.digestRequest.username);
979
980         break;
981     }
982     case choice_DigestReqInner_ntlmInit:
983
984         if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) {
985             kdc_log(context, config, 0, "NTLM not allowed");
986             goto failed;
987         }
988
989         r.element = choice_DigestRepInner_ntlmInitReply;
990
991         r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE;
992
993         if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) {
994             kdc_log(context, config, 0, "NTLM client have no unicode");
995             goto failed;
996         }
997
998         if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM)
999             r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM;
1000         else {
1001             kdc_log(context, config, 0, "NTLM client doesn't support NTLM");
1002             goto failed;
1003         }
1004
1005         r.u.ntlmInitReply.flags |= 
1006             NTLM_NEG_TARGET_DOMAIN |
1007             NTLM_ENC_128;
1008
1009 #define ALL                                     \
1010         NTLM_NEG_SIGN|                          \
1011             NTLM_NEG_SEAL|                      \
1012             NTLM_NEG_ALWAYS_SIGN|               \
1013             NTLM_NEG_NTLM2_SESSION|             \
1014             NTLM_NEG_KEYEX
1015
1016         r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
1017
1018 #undef ALL
1019
1020         r.u.ntlmInitReply.targetname = 
1021             get_ntlm_targetname(context, client);
1022         if (r.u.ntlmInitReply.targetname == NULL) {
1023             krb5_set_error_string(context, "out of memory");
1024             ret = ENOMEM;
1025             goto out;
1026         }
1027         r.u.ntlmInitReply.challange.data = malloc(8);
1028         if (r.u.ntlmInitReply.challange.data == NULL) {
1029             krb5_set_error_string(context, "out of memory");
1030             ret = ENOMEM;
1031             goto out;
1032         }
1033         r.u.ntlmInitReply.challange.length = 8;
1034         if (RAND_bytes(r.u.ntlmInitReply.challange.data,
1035                        r.u.ntlmInitReply.challange.length) != 1) 
1036         {
1037             krb5_set_error_string(context, "out of random error");
1038             ret = ENOMEM;
1039             goto out;
1040         }
1041         /* XXX fix targetinfo */
1042         ALLOC(r.u.ntlmInitReply.targetinfo);
1043         if (r.u.ntlmInitReply.targetinfo == NULL) {
1044             krb5_set_error_string(context, "out of memory");
1045             ret = ENOMEM;
1046             goto out;
1047         }
1048
1049         ret = fill_targetinfo(context,
1050                               r.u.ntlmInitReply.targetname,
1051                               client,
1052                               r.u.ntlmInitReply.targetinfo);
1053         if (ret) {
1054             krb5_set_error_string(context, "out of memory");
1055             ret = ENOMEM;
1056             goto out;
1057         }
1058
1059         /* 
1060          * Save data encryted in opaque for the second part of the
1061          * ntlm authentication
1062          */
1063         sp = krb5_storage_emem();
1064         if (sp == NULL) {
1065             ret = ENOMEM;
1066             krb5_set_error_string(context, "out of memory");
1067             goto out;
1068         }
1069         
1070         ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8);
1071         if (ret != 8) {
1072             ret = ENOMEM;
1073             krb5_set_error_string(context, "storage write challange");
1074             goto out;
1075         }
1076         ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
1077         if (ret) {
1078             krb5_clear_error_string(context);
1079             goto out;
1080         }
1081
1082         ret = krb5_storage_to_data(sp, &buf);
1083         if (ret) {
1084             krb5_clear_error_string(context);
1085             goto out;
1086         }
1087
1088         ret = get_digest_key(context, config, server, &crypto);
1089         if (ret)
1090             goto out;
1091
1092         ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1093                            buf.data, buf.length, &r.u.ntlmInitReply.opaque);
1094         krb5_data_free(&buf);
1095         krb5_crypto_destroy(context, crypto);
1096         crypto = NULL;
1097         if (ret)
1098             goto out;
1099
1100         kdc_log(context, config, 0, "NTLM init from %s", from);
1101
1102         break;
1103
1104     case choice_DigestReqInner_ntlmRequest: {
1105         krb5_principal clientprincipal;
1106         unsigned char sessionkey[16];
1107         unsigned char challange[8];
1108         uint32_t flags;
1109         Key *key = NULL;
1110         int version;
1111             
1112         r.element = choice_DigestRepInner_ntlmResponse;
1113         r.u.ntlmResponse.success = 0;
1114         r.u.ntlmResponse.flags = 0;
1115         r.u.ntlmResponse.sessionkey = NULL;
1116         r.u.ntlmResponse.tickets = NULL;
1117
1118         /* get username */
1119         ret = krb5_parse_name(context,
1120                               ireq.u.ntlmRequest.username,
1121                               &clientprincipal);
1122         if (ret)
1123             goto failed;
1124
1125         ret = _kdc_db_fetch(context, config, clientprincipal,
1126                             HDB_F_GET_CLIENT, NULL, &user);
1127         krb5_free_principal(context, clientprincipal);
1128         if (ret) {
1129             krb5_set_error_string(context, "NTLM user %s not in database",
1130                                   ireq.u.ntlmRequest.username);
1131             goto failed;
1132         }
1133
1134         ret = get_digest_key(context, config, server, &crypto);
1135         if (ret)
1136             goto failed;
1137
1138         ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1139                            ireq.u.ntlmRequest.opaque.data,
1140                            ireq.u.ntlmRequest.opaque.length, &buf);
1141         krb5_crypto_destroy(context, crypto);
1142         crypto = NULL;
1143         if (ret) {
1144             kdc_log(context, config, 0, 
1145                     "Failed to decrypt nonce from %s", from);
1146             goto failed;
1147         }
1148
1149         sp = krb5_storage_from_data(&buf);
1150         if (sp == NULL) {
1151             ret = ENOMEM;
1152             krb5_set_error_string(context, "out of memory");
1153             goto out;
1154         }
1155         
1156         ret = krb5_storage_read(sp, challange, sizeof(challange));
1157         if (ret != sizeof(challange)) {
1158             krb5_set_error_string(context, "NTLM storage read challange");
1159             ret = ENOMEM;
1160             goto out;
1161         }
1162         ret = krb5_ret_uint32(sp, &flags);
1163         if (ret) {
1164             krb5_set_error_string(context, "NTLM storage read flags");
1165             goto out;
1166         }
1167         krb5_data_free(&buf);
1168
1169         if ((flags & NTLM_NEG_NTLM) == 0) {
1170             ret = EINVAL;
1171             krb5_set_error_string(context, "NTLM not negotiated");
1172             goto out;
1173         }
1174
1175         ret = hdb_enctype2key(context, &user->entry, 
1176                               ETYPE_ARCFOUR_HMAC_MD5, &key);
1177         if (ret) {
1178             krb5_set_error_string(context, "NTLM missing arcfour key");
1179             goto out;
1180         }
1181
1182         /* check if this is NTLMv2 */
1183         if (ireq.u.ntlmRequest.ntlm.length != 24) {
1184             struct ntlm_buf infotarget, answer;
1185             char *targetname;
1186
1187             if ((config->digests_allowed & NTLM_V2) == 0) {
1188                 kdc_log(context, config, 0, "NTLM v2 not allowed");
1189                 goto out;
1190             }
1191
1192             version = 2;
1193
1194             targetname = get_ntlm_targetname(context, client);
1195             if (targetname == NULL) {
1196                 krb5_set_error_string(context, "out of memory");
1197                 ret = ENOMEM;
1198                 goto out;
1199             }
1200
1201             answer.length = ireq.u.ntlmRequest.ntlm.length;
1202             answer.data = ireq.u.ntlmRequest.ntlm.data;
1203
1204             ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
1205                                          key->key.keyvalue.length,
1206                                          ireq.u.ntlmRequest.username,
1207                                          targetname,
1208                                          0,
1209                                          challange,
1210                                          &answer,
1211                                          &infotarget,
1212                                          sessionkey);
1213             free(targetname);
1214             if (ret) {
1215                 krb5_set_error_string(context, "NTLM v2 verify failed");
1216                 goto failed;
1217             }
1218
1219             /* XXX verify infotarget matches client (checksum ?) */
1220
1221             free(infotarget.data);
1222             /* */
1223
1224         } else {
1225             struct ntlm_buf answer;
1226
1227             version = 1;
1228
1229             if (flags & NTLM_NEG_NTLM2_SESSION) {
1230                 unsigned char sessionhash[MD5_DIGEST_LENGTH];
1231                 MD5_CTX md5ctx;
1232                 
1233                 if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
1234                     kdc_log(context, config, 0, "NTLM v1-session not allowed");
1235                     ret = EINVAL;
1236                     goto failed;
1237                 }
1238
1239                 if (ireq.u.ntlmRequest.lm.length != 24) {
1240                     krb5_set_error_string(context, "LM hash have wrong length "
1241                                           "for NTLM session key");
1242                     ret = EINVAL;
1243                     goto failed;
1244                 }
1245                 
1246                 MD5_Init(&md5ctx);
1247                 MD5_Update(&md5ctx, challange, sizeof(challange));
1248                 MD5_Update(&md5ctx, ireq.u.ntlmRequest.lm.data, 8);
1249                 MD5_Final(sessionhash, &md5ctx);
1250                 memcpy(challange, sessionhash, sizeof(challange));
1251             } else {
1252                 if ((config->digests_allowed & NTLM_V1) == 0) {
1253                     kdc_log(context, config, 0, "NTLM v1 not allowed");
1254                     goto failed;
1255                 }
1256             }
1257             
1258             ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
1259                                             key->key.keyvalue.length,
1260                                             challange, &answer);
1261             if (ret) {
1262                 krb5_set_error_string(context, "NTLM missing arcfour key");
1263                 goto failed;
1264             }
1265             
1266             if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
1267                 memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
1268             {
1269                 free(answer.data);
1270                 ret = EINVAL;
1271                 krb5_set_error_string(context, "NTLM hash mismatch");
1272                 goto failed;
1273             }
1274             free(answer.data);
1275
1276             {
1277                 MD4_CTX ctx;
1278
1279                 MD4_Init(&ctx);
1280                 MD4_Update(&ctx, 
1281                            key->key.keyvalue.data, key->key.keyvalue.length);
1282                 MD4_Final(sessionkey, &ctx);
1283             }
1284         }
1285
1286         if (ireq.u.ntlmRequest.sessionkey) {
1287             unsigned char masterkey[MD4_DIGEST_LENGTH];
1288             RC4_KEY rc4;
1289             size_t len;
1290             
1291             if ((flags & NTLM_NEG_KEYEX) == 0) {
1292                 krb5_set_error_string(context,
1293                                       "NTLM client failed to neg key "
1294                                       "exchange but still sent key");
1295                 ret = EINVAL;
1296                 goto failed;
1297             }
1298             
1299             len = ireq.u.ntlmRequest.sessionkey->length;
1300             if (len != sizeof(masterkey)){
1301                 krb5_set_error_string(context,
1302                                       "NTLM master key wrong length: %lu",
1303                                       (unsigned long)len);
1304                 goto failed;
1305             }
1306             
1307             RC4_set_key(&rc4, sizeof(sessionkey), sessionkey);
1308             
1309             RC4(&rc4, sizeof(masterkey),
1310                 ireq.u.ntlmRequest.sessionkey->data, 
1311                 masterkey);
1312             memset(&rc4, 0, sizeof(rc4));
1313             
1314             r.u.ntlmResponse.sessionkey = 
1315                 malloc(sizeof(*r.u.ntlmResponse.sessionkey));
1316             if (r.u.ntlmResponse.sessionkey == NULL) {
1317                 krb5_set_error_string(context, "out of memory");
1318                 goto out;
1319             }
1320             
1321             ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
1322                                  masterkey, sizeof(masterkey));
1323             if (ret) {
1324                 krb5_set_error_string(context, "out of memory");
1325                 goto out;
1326             }
1327         }
1328
1329         r.u.ntlmResponse.success = 1;
1330         kdc_log(context, config, 0, "NTLM version %d successful for %s",
1331                 version, ireq.u.ntlmRequest.username);
1332         break;
1333     }
1334     default: {
1335         char *s;
1336         krb5_set_error_string(context, "unknown operation to digest");
1337         ret = EINVAL;
1338
1339     failed:
1340
1341         s = krb5_get_error_message(context, ret);
1342         if (s == NULL) {
1343             krb5_clear_error_string(context);
1344             goto out;
1345         }
1346         
1347         kdc_log(context, config, 0, "Digest failed with: %s", s);
1348
1349         r.element = choice_DigestRepInner_error;
1350         r.u.error.reason = strdup("unknown error");
1351         krb5_free_error_string(context, s);
1352         if (r.u.error.reason == NULL) {
1353             krb5_set_error_string(context, "out of memory");
1354             ret = ENOMEM;
1355             goto out;
1356         }
1357         r.u.error.code = EINVAL;
1358         break;
1359     }
1360     }
1361
1362     ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
1363     if (ret) {
1364         krb5_set_error_string(context, "Failed to encode inner digest reply");
1365         goto out;
1366     }
1367     if (size != buf.length)
1368         krb5_abortx(context, "ASN1 internal error");
1369
1370     krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
1371
1372     ret = krb5_mk_rep (context, ac, &rep.apRep);
1373     if (ret)
1374         goto out;
1375
1376     {
1377         krb5_keyblock *key;
1378
1379         ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
1380         if (ret)
1381             goto out;
1382
1383         ret = krb5_crypto_init(context, key, 0, &crypto);
1384         krb5_free_keyblock (context, key);
1385         if (ret)
1386             goto out;
1387     }
1388
1389     ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT, 
1390                                      buf.data, buf.length, 0,
1391                                      &rep.innerRep);
1392     
1393     ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
1394     if (ret) {
1395         krb5_set_error_string(context, "Failed to encode digest reply");
1396         goto out;
1397     }
1398     if (size != reply->length)
1399         krb5_abortx(context, "ASN1 internal error");
1400
1401     
1402 out:
1403     if (ac)
1404         krb5_auth_con_free(context, ac);
1405     if (ret)
1406         krb5_warn(context, ret, "Digest request from %s failed", from);
1407     if (ticket)
1408         krb5_free_ticket(context, ticket);
1409     if (id)
1410         krb5_kt_close(context, id);
1411     if (crypto)
1412         krb5_crypto_destroy(context, crypto);
1413     if (sp)
1414         krb5_storage_free(sp);
1415     if (user)
1416         _kdc_free_ent (context, user);
1417     if (server)
1418         _kdc_free_ent (context, server);
1419     if (client)
1420         _kdc_free_ent (context, client);
1421     if (password) {
1422         memset(password, 0, strlen(password));
1423         free (password);
1424     }
1425     if (client_name)
1426         free (client_name);
1427     krb5_data_free(&buf);
1428     krb5_data_free(&serverNonce);
1429     free_DigestREP(&rep);
1430     free_DigestRepInner(&r);
1431     free_DigestReqInner(&ireq);
1432
1433     return ret;
1434 }