Add a setexpiry operation in samdb.py
[tprouty/samba.git] / source / 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 23316 2008-06-23 04:32:32Z 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_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 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_message(context, ret, "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             ret = KRB5KDC_ERR_POLICY;
327             krb5_set_error_message(context, ret,
328                                    "Client is not permitted to use digest");
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             ret = EINVAL;
342             krb5_set_error_message(context, ret, "digest: remote subkey not found");
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_message(context, ret, "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             ret = ENOMEM;
390             krb5_set_error_message(context, ret, "Failed to decode server nonce");
391             goto out;
392         }
393
394         sp = krb5_storage_emem();
395         if (sp == NULL) {
396             ret = ENOMEM;
397             krb5_set_error_message(context, ret, "malloc: 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                 ret = ENOMEM;
414                 krb5_set_error_message(context, ret,
415                                        "Failed to allocate channel binding");
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                 ret = ENOMEM;
433                 krb5_set_error_message(context, ret, "malloc: out of memory");
434                 goto out;
435             }
436
437             asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff);
438             if (*r.u.initReply.identifier == NULL) {
439                 ret = ENOMEM;
440                 krb5_set_error_message(context, ret, "malloc: out of memory");
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_message(context, ret, "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_message(context, ret, "malloc: 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             ret = ENOMEM;
528             krb5_set_error_message(context, ret, "malloc: out of memory");
529             goto out;
530         }
531
532         ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
533         if (ret <= 0) {
534             ret = ENOMEM;
535             krb5_set_error_message(context, ret, "Failed to decode opaque");
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_message(context, ret, "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             ret = ENOMEM;
557             krb5_set_error_message(context, ret, "malloc: out of memory");
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                 ret = ENOMEM;
572                 krb5_set_error_message(context, ret, "Failed to decode serverNonce");
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                 ret = EINVAL;
597                 krb5_set_error_message(context, ret, "server nonce too short");
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                 ret = EINVAL;
604                 krb5_set_error_message(context, ret, "time screw in server nonce ");
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                 ret = EINVAL;
622                 krb5_set_error_message(context, ret, "Identifier missing "
623                                        "from CHAP request");
624                 goto out;
625             }
626             
627             if (hex_decode(*ireq.u.digestRequest.identifier, &id, 1) != 1) {
628                 ret = EINVAL;
629                 krb5_set_error_message(context, ret, "failed to decode identifier");
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                 ret = ENOMEM;
718                 krb5_set_error_message(context, ret, "malloc: out of memory");
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                 ret = ENOMEM;
737                 krb5_set_error_message(context, ret, "malloc: out of memory");
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                 ret = EINVAL;
799                 krb5_set_error_message(context, ret, 
800                                        "MS-CHAP-V2 clientNonce missing");
801                 goto failed;
802             }       
803             if (serverNonce.length != 16) {
804                 ret = EINVAL;
805                 krb5_set_error_message(context, ret, 
806                                        "MS-CHAP-V2 serverNonce wrong length");
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_message(context, ret, "malloc: 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                     ret = ENOMEM;
835                     krb5_set_error_message(context, ret, 
836                                            "Failed to decode clientNonce");
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_message(context, ret, 
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_message(context, ret, 
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_message(context, ret, "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                 ret = ENOMEM;
971                 krb5_set_error_message(context, ret, "malloc: out of memory");
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 |
1007             NTLM_TARGET_DOMAIN |
1008             NTLM_ENC_128;
1009
1010 #define ALL                                     \
1011         NTLM_NEG_SIGN|                          \
1012             NTLM_NEG_SEAL|                      \
1013             NTLM_NEG_ALWAYS_SIGN|               \
1014             NTLM_NEG_NTLM2_SESSION|             \
1015             NTLM_NEG_KEYEX
1016
1017         r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
1018
1019 #undef ALL
1020
1021         r.u.ntlmInitReply.targetname = 
1022             get_ntlm_targetname(context, client);
1023         if (r.u.ntlmInitReply.targetname == NULL) {
1024             ret = ENOMEM;
1025             krb5_set_error_message(context, ret, "malloc: out of memory");
1026             goto out;
1027         }
1028         r.u.ntlmInitReply.challange.data = malloc(8);
1029         if (r.u.ntlmInitReply.challange.data == NULL) {
1030             ret = ENOMEM;
1031             krb5_set_error_message(context, ret, "malloc: out of memory");
1032             goto out;
1033         }
1034         r.u.ntlmInitReply.challange.length = 8;
1035         if (RAND_bytes(r.u.ntlmInitReply.challange.data,
1036                        r.u.ntlmInitReply.challange.length) != 1) 
1037             {
1038                 ret = ENOMEM;
1039                 krb5_set_error_message(context, ret, "out of random error");
1040                 goto out;
1041             }
1042         /* XXX fix targetinfo */
1043         ALLOC(r.u.ntlmInitReply.targetinfo);
1044         if (r.u.ntlmInitReply.targetinfo == NULL) {
1045             ret = ENOMEM;
1046             krb5_set_error_message(context, ret, "malloc: out of memory");
1047             goto out;
1048         }
1049
1050         ret = fill_targetinfo(context,
1051                               r.u.ntlmInitReply.targetname,
1052                               client,
1053                               r.u.ntlmInitReply.targetinfo);
1054         if (ret) {
1055             ret = ENOMEM;
1056             krb5_set_error_message(context, ret, "malloc: out of memory");
1057             goto out;
1058         }
1059
1060         /* 
1061          * Save data encryted in opaque for the second part of the
1062          * ntlm authentication
1063          */
1064         sp = krb5_storage_emem();
1065         if (sp == NULL) {
1066             ret = ENOMEM;
1067             krb5_set_error_message(context, ret, "malloc: out of memory");
1068             goto out;
1069         }
1070         
1071         ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8);
1072         if (ret != 8) {
1073             ret = ENOMEM;
1074             krb5_set_error_message(context, ret, "storage write challange");
1075             goto out;
1076         }
1077         ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
1078         if (ret) {
1079             krb5_clear_error_string(context);
1080             goto out;
1081         }
1082
1083         ret = krb5_storage_to_data(sp, &buf);
1084         if (ret) {
1085             krb5_clear_error_string(context);
1086             goto out;
1087         }
1088
1089         ret = get_digest_key(context, config, server, &crypto);
1090         if (ret)
1091             goto out;
1092
1093         ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1094                            buf.data, buf.length, &r.u.ntlmInitReply.opaque);
1095         krb5_data_free(&buf);
1096         krb5_crypto_destroy(context, crypto);
1097         crypto = NULL;
1098         if (ret)
1099             goto out;
1100
1101         kdc_log(context, config, 0, "NTLM init from %s", from);
1102
1103         break;
1104
1105     case choice_DigestReqInner_ntlmRequest: {
1106         krb5_principal clientprincipal;
1107         unsigned char sessionkey[16];
1108         unsigned char challange[8];
1109         uint32_t flags;
1110         Key *key = NULL;
1111         int version;
1112             
1113         r.element = choice_DigestRepInner_ntlmResponse;
1114         r.u.ntlmResponse.success = 0;
1115         r.u.ntlmResponse.flags = 0;
1116         r.u.ntlmResponse.sessionkey = NULL;
1117         r.u.ntlmResponse.tickets = NULL;
1118
1119         /* get username */
1120         ret = krb5_parse_name(context,
1121                               ireq.u.ntlmRequest.username,
1122                               &clientprincipal);
1123         if (ret)
1124             goto failed;
1125
1126         ret = _kdc_db_fetch(context, config, clientprincipal,
1127                             HDB_F_GET_CLIENT, NULL, &user);
1128         krb5_free_principal(context, clientprincipal);
1129         if (ret) {
1130             krb5_set_error_message(context, ret, "NTLM user %s not in database",
1131                                    ireq.u.ntlmRequest.username);
1132             goto failed;
1133         }
1134
1135         ret = get_digest_key(context, config, server, &crypto);
1136         if (ret)
1137             goto failed;
1138
1139         ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1140                            ireq.u.ntlmRequest.opaque.data,
1141                            ireq.u.ntlmRequest.opaque.length, &buf);
1142         krb5_crypto_destroy(context, crypto);
1143         crypto = NULL;
1144         if (ret) {
1145             kdc_log(context, config, 0, 
1146                     "Failed to decrypt nonce from %s", from);
1147             goto failed;
1148         }
1149
1150         sp = krb5_storage_from_data(&buf);
1151         if (sp == NULL) {
1152             ret = ENOMEM;
1153             krb5_set_error_message(context, ret, "malloc: out of memory");
1154             goto out;
1155         }
1156         
1157         ret = krb5_storage_read(sp, challange, sizeof(challange));
1158         if (ret != sizeof(challange)) {
1159             ret = ENOMEM;
1160             krb5_set_error_message(context, ret, "NTLM storage read challange");
1161             goto out;
1162         }
1163         ret = krb5_ret_uint32(sp, &flags);
1164         if (ret) {
1165             krb5_set_error_message(context, ret, "NTLM storage read flags");
1166             goto out;
1167         }
1168         krb5_data_free(&buf);
1169
1170         if ((flags & NTLM_NEG_NTLM) == 0) {
1171             ret = EINVAL;
1172             krb5_set_error_message(context, ret, "NTLM not negotiated");
1173             goto out;
1174         }
1175
1176         ret = hdb_enctype2key(context, &user->entry, 
1177                               ETYPE_ARCFOUR_HMAC_MD5, &key);
1178         if (ret) {
1179             krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1180             goto out;
1181         }
1182
1183         /* check if this is NTLMv2 */
1184         if (ireq.u.ntlmRequest.ntlm.length != 24) {
1185             struct ntlm_buf infotarget, answer;
1186             char *targetname;
1187
1188             if ((config->digests_allowed & NTLM_V2) == 0) {
1189                 kdc_log(context, config, 0, "NTLM v2 not allowed");
1190                 goto out;
1191             }
1192
1193             version = 2;
1194
1195             targetname = get_ntlm_targetname(context, client);
1196             if (targetname == NULL) {
1197                 ret = ENOMEM;
1198                 krb5_set_error_message(context, ret, "malloc: out of memory");
1199                 goto out;
1200             }
1201
1202             answer.length = ireq.u.ntlmRequest.ntlm.length;
1203             answer.data = ireq.u.ntlmRequest.ntlm.data;
1204
1205             ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
1206                                          key->key.keyvalue.length,
1207                                          ireq.u.ntlmRequest.username,
1208                                          targetname,
1209                                          0,
1210                                          challange,
1211                                          &answer,
1212                                          &infotarget,
1213                                          sessionkey);
1214             free(targetname);
1215             if (ret) {
1216                 krb5_set_error_message(context, ret, "NTLM v2 verify failed");
1217                 goto failed;
1218             }
1219
1220             /* XXX verify infotarget matches client (checksum ?) */
1221
1222             free(infotarget.data);
1223             /* */
1224
1225         } else {
1226             struct ntlm_buf answer;
1227
1228             version = 1;
1229
1230             if (flags & NTLM_NEG_NTLM2_SESSION) {
1231                 unsigned char sessionhash[MD5_DIGEST_LENGTH];
1232                 MD5_CTX md5ctx;
1233                 
1234                 if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
1235                     kdc_log(context, config, 0, "NTLM v1-session not allowed");
1236                     ret = EINVAL;
1237                     goto failed;
1238                 }
1239
1240                 if (ireq.u.ntlmRequest.lm.length != 24) {
1241                     ret = EINVAL;
1242                     krb5_set_error_message(context, ret, "LM hash have wrong length "
1243                                            "for NTLM session key");
1244                     goto failed;
1245                 }
1246                 
1247                 MD5_Init(&md5ctx);
1248                 MD5_Update(&md5ctx, challange, sizeof(challange));
1249                 MD5_Update(&md5ctx, ireq.u.ntlmRequest.lm.data, 8);
1250                 MD5_Final(sessionhash, &md5ctx);
1251                 memcpy(challange, sessionhash, sizeof(challange));
1252             } else {
1253                 if ((config->digests_allowed & NTLM_V1) == 0) {
1254                     kdc_log(context, config, 0, "NTLM v1 not allowed");
1255                     goto failed;
1256                 }
1257             }
1258             
1259             ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
1260                                             key->key.keyvalue.length,
1261                                             challange, &answer);
1262             if (ret) {
1263                 krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1264                 goto failed;
1265             }
1266             
1267             if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
1268                 memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
1269                 {
1270                     free(answer.data);
1271                     ret = EINVAL;
1272                     krb5_set_error_message(context, ret, "NTLM hash mismatch");
1273                     goto failed;
1274                 }
1275             free(answer.data);
1276
1277             {
1278                 MD4_CTX ctx;
1279
1280                 MD4_Init(&ctx);
1281                 MD4_Update(&ctx, 
1282                            key->key.keyvalue.data, key->key.keyvalue.length);
1283                 MD4_Final(sessionkey, &ctx);
1284             }
1285         }
1286
1287         if (ireq.u.ntlmRequest.sessionkey) {
1288             unsigned char masterkey[MD4_DIGEST_LENGTH];
1289             RC4_KEY rc4;
1290             size_t len;
1291             
1292             if ((flags & NTLM_NEG_KEYEX) == 0) {
1293                 ret = EINVAL;
1294                 krb5_set_error_message(context, ret,
1295                                        "NTLM client failed to neg key "
1296                                        "exchange but still sent key");
1297                 goto failed;
1298             }
1299             
1300             len = ireq.u.ntlmRequest.sessionkey->length;
1301             if (len != sizeof(masterkey)){
1302                 ret = EINVAL;
1303                 krb5_set_error_message(context, ret,
1304                                        "NTLM master key wrong length: %lu",
1305                                        (unsigned long)len);
1306                 goto failed;
1307             }
1308             
1309             RC4_set_key(&rc4, sizeof(sessionkey), sessionkey);
1310             
1311             RC4(&rc4, sizeof(masterkey),
1312                 ireq.u.ntlmRequest.sessionkey->data, 
1313                 masterkey);
1314             memset(&rc4, 0, sizeof(rc4));
1315             
1316             r.u.ntlmResponse.sessionkey = 
1317                 malloc(sizeof(*r.u.ntlmResponse.sessionkey));
1318             if (r.u.ntlmResponse.sessionkey == NULL) {
1319                 ret = EINVAL;
1320                 krb5_set_error_message(context, ret, "malloc: out of memory");
1321                 goto out;
1322             }
1323             
1324             ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
1325                                  masterkey, sizeof(masterkey));
1326             if (ret) {
1327                 krb5_set_error_message(context, ret, "malloc: out of memory");
1328                 goto out;
1329             }
1330         }
1331
1332         r.u.ntlmResponse.success = 1;
1333         kdc_log(context, config, 0, "NTLM version %d successful for %s",
1334                 version, ireq.u.ntlmRequest.username);
1335         break;
1336     }
1337     case choice_DigestReqInner_supportedMechs:
1338
1339         kdc_log(context, config, 0, "digest supportedMechs from %s", from);
1340
1341         r.element = choice_DigestRepInner_supportedMechs;
1342         memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs));
1343
1344         if (config->digests_allowed & NTLM_V1)
1345             r.u.supportedMechs.ntlm_v1 = 1;
1346         if (config->digests_allowed & NTLM_V1_SESSION)
1347             r.u.supportedMechs.ntlm_v1_session = 1;
1348         if (config->digests_allowed & NTLM_V2)
1349             r.u.supportedMechs.ntlm_v2 = 1;
1350         if (config->digests_allowed & DIGEST_MD5)
1351             r.u.supportedMechs.digest_md5 = 1;
1352         if (config->digests_allowed & CHAP_MD5)
1353             r.u.supportedMechs.chap_md5 = 1;
1354         if (config->digests_allowed & MS_CHAP_V2)
1355             r.u.supportedMechs.ms_chap_v2 = 1;
1356         break;
1357
1358     default: {
1359         const char *s;
1360         ret = EINVAL;
1361         krb5_set_error_message(context, ret, "unknown operation to digest");
1362
1363         failed:
1364
1365         s = krb5_get_error_message(context, ret);
1366         if (s == NULL) {
1367             krb5_clear_error_string(context);
1368             goto out;
1369         }
1370         
1371         kdc_log(context, config, 0, "Digest failed with: %s", s);
1372
1373         r.element = choice_DigestRepInner_error;
1374         r.u.error.reason = strdup("unknown error");
1375         krb5_free_error_message(context, s);
1376         if (r.u.error.reason == NULL) {
1377             ret = ENOMEM;
1378             krb5_set_error_message(context, ret, "malloc: out of memory");
1379             goto out;
1380         }
1381         r.u.error.code = EINVAL;
1382         break;
1383     }
1384     }
1385
1386     ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
1387     if (ret) {
1388         krb5_set_error_message(context, ret, "Failed to encode inner digest reply");
1389         goto out;
1390     }
1391     if (size != buf.length)
1392         krb5_abortx(context, "ASN1 internal error");
1393
1394     krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
1395
1396     ret = krb5_mk_rep (context, ac, &rep.apRep);
1397     if (ret)
1398         goto out;
1399
1400     {
1401         krb5_keyblock *key;
1402
1403         ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
1404         if (ret)
1405             goto out;
1406
1407         ret = krb5_crypto_init(context, key, 0, &crypto);
1408         krb5_free_keyblock (context, key);
1409         if (ret)
1410             goto out;
1411     }
1412
1413     ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT, 
1414                                      buf.data, buf.length, 0,
1415                                      &rep.innerRep);
1416     
1417     ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
1418     if (ret) {
1419         krb5_set_error_message(context, ret, "Failed to encode digest reply");
1420         goto out;
1421     }
1422     if (size != reply->length)
1423         krb5_abortx(context, "ASN1 internal error");
1424
1425     
1426  out:
1427     if (ac)
1428         krb5_auth_con_free(context, ac);
1429     if (ret)
1430         krb5_warn(context, ret, "Digest request from %s failed", from);
1431     if (ticket)
1432         krb5_free_ticket(context, ticket);
1433     if (id)
1434         krb5_kt_close(context, id);
1435     if (crypto)
1436         krb5_crypto_destroy(context, crypto);
1437     if (sp)
1438         krb5_storage_free(sp);
1439     if (user)
1440         _kdc_free_ent (context, user);
1441     if (server)
1442         _kdc_free_ent (context, server);
1443     if (client)
1444         _kdc_free_ent (context, client);
1445     if (password) {
1446         memset(password, 0, strlen(password));
1447         free (password);
1448     }
1449     if (client_name)
1450         free (client_name);
1451     krb5_data_free(&buf);
1452     krb5_data_free(&serverNonce);
1453     free_DigestREP(&rep);
1454     free_DigestRepInner(&r);
1455     free_DigestReqInner(&ireq);
1456
1457     return ret;
1458 }