a5517fb89635294f22353472315e0fdb670f7358
[samba.git] / source4 / heimdal / kdc / digest.c
1 /*
2  * Copyright (c) 2006 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 <digest_asn1.h>
36 #include <hex.h>
37
38 RCSID("$Id: digest.c,v 1.7 2006/10/22 20:11:44 lha Exp $");
39
40 krb5_error_code
41 _kdc_do_digest(krb5_context context, 
42                krb5_kdc_configuration *config,
43                const DigestREQ *req, krb5_data *reply,
44                const char *from, struct sockaddr *addr)
45 {
46     krb5_error_code ret = 0;
47     krb5_ticket *ticket = NULL;
48     krb5_auth_context ac = NULL;
49     krb5_keytab id = NULL;
50     krb5_crypto crypto = NULL;
51     DigestReqInner ireq;
52     DigestRepInner r;
53     DigestREP rep;
54     krb5_flags ap_req_options;
55     krb5_data buf;
56     size_t size;
57     krb5_storage *sp = NULL;
58     Checksum res;
59     hdb_entry_ex *server = NULL, *user = NULL;
60     char *password = NULL;
61     krb5_data serverNonce;
62
63     if(!config->enable_digest) {
64         kdc_log(context, config, 0, "Rejected digest request from %s", from);
65         return KRB5KDC_ERR_POLICY;
66     }
67
68     krb5_data_zero(&buf);
69     krb5_data_zero(reply);
70     krb5_data_zero(&serverNonce);
71     memset(&ireq, 0, sizeof(ireq));
72     memset(&r, 0, sizeof(r));
73     memset(&rep, 0, sizeof(rep));
74
75     kdc_log(context, config, 0, "Digest request from %s", from);
76
77     ret = krb5_kt_resolve(context, "HDB:", &id);
78     if (ret) {
79         kdc_log(context, config, 0, "Can't open database for digest");
80         goto out;
81     }
82
83     ret = krb5_rd_req(context, 
84                       &ac,
85                       &req->apReq,
86                       NULL,
87                       id,
88                       &ap_req_options,
89                       &ticket);
90     if (ret)
91         goto out;
92
93     /* check the server principal in the ticket matches digest/R@R */
94     {
95         krb5_principal principal = NULL;
96         const char *p, *r;
97
98         ret = krb5_ticket_get_server(context, ticket, &principal);
99         if (ret)
100             goto out;
101
102         ret = EINVAL;
103         krb5_set_error_string(context, "Wrong digest server principal used");
104         p = krb5_principal_get_comp_string(context, principal, 0);
105         if (p == NULL) {
106             krb5_free_principal(context, principal);
107             goto out;
108         }
109         if (strcmp(p, KRB5_DIGEST_NAME) != 0) {
110             krb5_free_principal(context, principal);
111             goto out;
112         }
113
114         p = krb5_principal_get_comp_string(context, principal, 1);
115         if (p == NULL) {
116             krb5_free_principal(context, principal);
117             goto out;
118         }
119         r = krb5_principal_get_realm(context, principal);
120         if (r == NULL) {
121             krb5_free_principal(context, principal);
122             goto out;
123         }
124         if (strcmp(p, r) != 0) {
125             krb5_free_principal(context, principal);
126             goto out;
127         }
128
129         ret = _kdc_db_fetch(context, config, principal,
130                             HDB_F_GET_SERVER, NULL, &server);
131         if (ret)
132             goto out;
133
134         krb5_free_principal(context, principal);
135     }
136
137     /* check the client is allowed to do digest auth */
138     {
139         krb5_principal principal = NULL;
140         hdb_entry_ex *client;
141
142         ret = krb5_ticket_get_client(context, ticket, &principal);
143         if (ret)
144             goto out;
145
146         ret = _kdc_db_fetch(context, config, principal,
147                             HDB_F_GET_CLIENT, NULL, &client);
148         krb5_free_principal(context, principal);
149         if (ret)
150             goto out;
151
152         if (client->entry.flags.allow_digest == 0) {
153             krb5_set_error_string(context, 
154                                   "Client is not permitted to use digest");
155             ret = KRB5KDC_ERR_POLICY;
156             _kdc_free_ent (context, client);
157             goto out;
158         }
159         _kdc_free_ent (context, client);
160     }
161
162     /* unpack request */
163     {
164         krb5_keyblock *key;
165
166         ret = krb5_auth_con_getremotesubkey(context, ac, &key);
167         if (ret)
168             goto out;
169         if (key == NULL) {
170             krb5_set_error_string(context, "digest: remote subkey not found");
171             ret = EINVAL;
172             goto out;
173         }
174
175         ret = krb5_crypto_init(context, key, 0, &crypto);
176         krb5_free_keyblock (context, key);
177         if (ret)
178             goto out;
179     }
180
181     ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
182                                      &req->innerReq, &buf);
183     krb5_crypto_destroy(context, crypto);
184     crypto = NULL;
185     if (ret)
186         goto out;
187            
188     ret = decode_DigestReqInner(buf.data, buf.length, &ireq, NULL);
189     krb5_data_free(&buf);
190     if (ret) {
191         krb5_set_error_string(context, "Failed to decode digest inner request");
192         goto out;
193     }
194
195     /*
196      * Process the inner request
197      */
198
199     switch (ireq.element) {
200     case choice_DigestReqInner_init: {
201         unsigned char server_nonce[16], identifier;
202
203         RAND_pseudo_bytes(&identifier, sizeof(identifier));
204         RAND_pseudo_bytes(server_nonce, sizeof(server_nonce));
205
206         server_nonce[0] = kdc_time & 0xff;
207         server_nonce[1] = (kdc_time >> 8) & 0xff;
208         server_nonce[2] = (kdc_time >> 16) & 0xff;
209         server_nonce[3] = (kdc_time >> 24) & 0xff;
210
211         r.element = choice_DigestRepInner_initReply;
212
213         hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce);
214         if (r.u.initReply.nonce == NULL) {
215             krb5_set_error_string(context, "Failed to decode server nonce");
216             ret = ENOMEM;
217             goto out;
218         }
219
220         sp = krb5_storage_emem();
221         if (sp == NULL) {
222             ret = ENOMEM;
223             krb5_set_error_string(context, "out of memory");
224             goto out;
225         }
226         ret = krb5_store_stringz(sp, ireq.u.init.type);
227         if (ret) {
228             krb5_clear_error_string(context);
229             goto out;
230         }
231
232         if (ireq.u.init.channel) {
233             char *s;
234
235             asprintf(&s, "%s-%s:%s", r.u.initReply.nonce,
236                      ireq.u.init.channel->cb_type,
237                      ireq.u.init.channel->cb_binding);
238             if (s == NULL) {
239                 krb5_set_error_string(context, "Failed to allocate "
240                                       "channel binding");
241                 ret = ENOMEM;
242                 goto out;
243             }
244             free(r.u.initReply.nonce);
245             r.u.initReply.nonce = s;
246         }
247         
248         ret = krb5_store_stringz(sp, r.u.initReply.nonce);
249         if (ret) {
250             krb5_clear_error_string(context);
251             goto out;
252         }
253
254         if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
255             r.u.initReply.identifier = 
256                 malloc(sizeof(*r.u.initReply.identifier));
257             if (r.u.initReply.identifier == NULL) {
258                 krb5_set_error_string(context, "out of memory");
259                 ret = ENOMEM;
260                 goto out;
261             }
262
263             asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff);
264             if (*r.u.initReply.identifier == NULL) {
265                 krb5_set_error_string(context, "out of memory");
266                 ret = ENOMEM;
267                 goto out;
268             }
269
270             ret = krb5_store_stringz(sp, *r.u.initReply.identifier);
271             if (ret) {
272                 krb5_clear_error_string(context);
273                 goto out;
274             }
275         } else
276             r.u.initReply.identifier = NULL;
277
278         if (ireq.u.init.hostname) {
279             ret = krb5_store_stringz(sp, *ireq.u.init.hostname);
280             if (ret) {
281                 krb5_clear_error_string(context);
282                 goto out;
283             }
284         }
285
286         ret = krb5_storage_to_data(sp, &buf);
287         if (ret) {
288             krb5_clear_error_string(context);
289             goto out;
290         }
291
292         {
293             Key *key;
294             krb5_enctype enctype;
295
296             ret = _kdc_get_preferred_key(context,
297                                          config,
298                                          server,
299                                          "digest-service",
300                                          &enctype,
301                                          &key);
302             if (ret)
303                 goto out;
304             ret = krb5_crypto_init(context, &key->key, 0, &crypto);
305             if (ret)
306                 goto out;
307         }
308
309         ret = krb5_create_checksum(context,
310                                    crypto,
311                                    KRB5_KU_DIGEST_OPAQUE,
312                                    0,
313                                    buf.data,
314                                    buf.length,
315                                    &res);
316         krb5_crypto_destroy(context, crypto);
317         crypto = NULL;
318         krb5_data_free(&buf);
319         if (ret)
320             goto out;
321         
322         ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret);
323         free_Checksum(&res);
324         if (ret) {
325             krb5_set_error_string(context, "Failed to encode "
326                                   "checksum in digest request");
327             goto out;
328         }
329         if (size != buf.length)
330             krb5_abortx(context, "ASN1 internal error");
331
332         hex_encode(buf.data, buf.length, &r.u.initReply.opaque);
333         free(buf.data);
334         if (r.u.initReply.opaque == NULL) {
335             krb5_clear_error_string(context);
336             ret = ENOMEM;
337             goto out;
338         }
339
340         break;
341     }
342     case choice_DigestReqInner_digestRequest: {
343         krb5_principal clientprincipal;
344         HDB *db;
345
346         sp = krb5_storage_emem();
347         if (sp == NULL) {
348             ret = ENOMEM;
349             krb5_set_error_string(context, "out of memory");
350             goto out;
351         }
352         krb5_store_stringz(sp, ireq.u.digestRequest.type);
353
354         krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
355         if (ireq.u.digestRequest.identifier) {
356             ret = krb5_store_stringz(sp, *ireq.u.digestRequest.identifier);
357             if (ret) {
358                 krb5_clear_error_string(context);
359                 goto out;
360             }
361         }
362         if (ireq.u.digestRequest.hostname) {
363             ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname);
364             if (ret) {
365                 krb5_clear_error_string(context);
366                 goto out;
367             }
368         }
369
370         buf.length = strlen(ireq.u.digestRequest.opaque);
371         buf.data = malloc(buf.length);
372         if (buf.data == NULL) {
373             krb5_set_error_string(context, "out of memory");
374             ret = ENOMEM;
375             goto out;
376         }
377
378         ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
379         if (ret <= 0) {
380             krb5_set_error_string(context, "Failed to decode opaque");
381             ret = ENOMEM;
382             goto out;
383         }
384         buf.length = ret;
385
386         ret = decode_Checksum(buf.data, buf.length, &res, NULL);
387         free(buf.data);
388         if (ret) {
389             krb5_set_error_string(context, "Failed to decode digest Checksum");
390             goto out;
391         }
392         
393         ret = krb5_storage_to_data(sp, &buf);
394         if (ret) {
395             krb5_clear_error_string(context);
396             goto out;
397         }
398
399         serverNonce.length = strlen(ireq.u.digestRequest.serverNonce);
400         serverNonce.data = malloc(serverNonce.length);
401         if (serverNonce.data == NULL) {
402             krb5_set_error_string(context, "out of memory");
403             ret = ENOMEM;
404             goto out;
405         }
406             
407         /*
408          * CHAP does the checksum of the raw nonce, but do it for all
409          * types, since we need to check the timestamp.
410          */
411         {
412             ssize_t ssize;
413             
414             ssize = hex_decode(ireq.u.digestRequest.serverNonce, 
415                                serverNonce.data, serverNonce.length);
416             if (ssize <= 0) {
417                 krb5_set_error_string(context, "Failed to decode serverNonce");
418                 ret = ENOMEM;
419                 goto out;
420             }
421             serverNonce.length = ssize;
422         }
423
424         {
425             Key *key;
426             krb5_enctype enctype;
427
428             ret = _kdc_get_preferred_key(context,
429                                          config,
430                                          server,
431                                          "digest-service",
432                                          &enctype,
433                                          &key);
434             if (ret)
435                 goto out;
436             ret = krb5_crypto_init(context, &key->key, 0, &crypto);
437             if (ret)
438                 goto out;
439         }
440
441         ret = krb5_verify_checksum(context, crypto, 
442                                    KRB5_KU_DIGEST_OPAQUE,
443                                    buf.data, buf.length, &res);
444         krb5_crypto_destroy(context, crypto);
445         crypto = NULL;
446         if (ret)
447             goto out;
448
449         /* verify time */
450         {
451             unsigned char *p = serverNonce.data;
452             uint32_t t;
453             
454             if (serverNonce.length < 4) {
455                 krb5_set_error_string(context, "server nonce too short");
456                 ret = EINVAL;
457                 goto out;
458             }
459             t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
460
461             if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) {
462                 krb5_set_error_string(context, "time screw in server nonce ");
463                 ret = EINVAL;
464                 goto out;
465             }
466         }
467
468         /* get username */
469         ret = krb5_parse_name(context,
470                               ireq.u.digestRequest.username,
471                               &clientprincipal);
472         if (ret)
473             goto out;
474
475         ret = _kdc_db_fetch(context, config, clientprincipal,
476                             HDB_F_GET_CLIENT, &db, &user);
477
478         krb5_free_principal(context, clientprincipal);
479         if (ret)
480             goto out;
481
482         ret = hdb_entry_get_password(context, db, &user->entry, &password);
483         if (ret || password == NULL) {
484             if (ret == 0) {
485                 ret = EINVAL;
486                 krb5_set_error_string(context, "password missing");
487             }
488             goto out;
489         }
490
491         if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) {
492             MD5_CTX ctx;
493             unsigned char md[MD5_DIGEST_LENGTH];
494             char id;
495
496             if (ireq.u.digestRequest.identifier == NULL) {
497                 krb5_set_error_string(context, "Identifier missing "
498                                       "from CHAP request");
499                 ret = EINVAL;
500                 goto out;
501             }
502             
503             if (hex_decode(*ireq.u.digestRequest.identifier, &id, 1) != 1) {
504                 krb5_set_error_string(context, "failed to decode identifier");
505                 ret = EINVAL;
506                 goto out;
507             }
508             
509             MD5_Init(&ctx);
510             MD5_Update(&ctx, &id, 1);
511             MD5_Update(&ctx, password, strlen(password));
512             MD5_Update(&ctx, serverNonce.data, serverNonce.length);
513             MD5_Final(md, &ctx);
514
515             r.element = choice_DigestRepInner_response;
516             hex_encode(md, sizeof(md), &r.u.response.responseData);
517             if (r.u.response.responseData == NULL) {
518                 krb5_clear_error_string(context);
519                 ret = ENOMEM;
520                 goto out;
521             }
522         } else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
523             MD5_CTX ctx;
524             unsigned char md[MD5_DIGEST_LENGTH];
525             char *A1, *A2;
526
527             if (ireq.u.digestRequest.nonceCount == NULL) 
528                 goto out;
529             if (ireq.u.digestRequest.clientNonce == NULL) 
530                 goto out;
531             if (ireq.u.digestRequest.qop == NULL) 
532                 goto out;
533             if (ireq.u.digestRequest.realm == NULL) 
534                 goto out;
535             
536             MD5_Init(&ctx);
537             MD5_Update(&ctx, ireq.u.digestRequest.username,
538                        strlen(ireq.u.digestRequest.username));
539             MD5_Update(&ctx, ":", 1);
540             MD5_Update(&ctx, *ireq.u.digestRequest.realm,
541                        strlen(*ireq.u.digestRequest.realm));
542             MD5_Update(&ctx, ":", 1);
543             MD5_Update(&ctx, password, strlen(password));
544             MD5_Final(md, &ctx);
545             
546             MD5_Init(&ctx);
547             MD5_Update(&ctx, md, sizeof(md));
548             MD5_Update(&ctx, ":", 1);
549             MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
550                        strlen(ireq.u.digestRequest.serverNonce));
551             MD5_Update(&ctx, ":", 1);
552             MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
553                        strlen(*ireq.u.digestRequest.nonceCount));
554             if (ireq.u.digestRequest.authid) {
555                 MD5_Update(&ctx, ":", 1);
556                 MD5_Update(&ctx, *ireq.u.digestRequest.authid,
557                            strlen(*ireq.u.digestRequest.authid));
558             }
559             MD5_Final(md, &ctx);
560             hex_encode(md, sizeof(md), &A1);
561             if (A1 == NULL) {
562                 krb5_set_error_string(context, "out of memory");
563                 ret = ENOMEM;
564                 goto out;
565             }
566             
567             MD5_Init(&ctx);
568             MD5_Update(&ctx, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
569             MD5_Update(&ctx, *ireq.u.digestRequest.uri,
570                        strlen(*ireq.u.digestRequest.uri));
571         
572             /* conf|int */
573             if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
574                 static char conf_zeros[] = ":00000000000000000000000000000000";
575                 MD5_Update(&ctx, conf_zeros, sizeof(conf_zeros) - 1);
576             }
577             
578             MD5_Final(md, &ctx);
579             hex_encode(md, sizeof(md), &A2);
580             if (A2 == NULL) {
581                 krb5_set_error_string(context, "out of memory");
582                 ret = ENOMEM;
583                 free(A1);
584                 goto out;
585             }
586
587             MD5_Init(&ctx);
588             MD5_Update(&ctx, A1, strlen(A2));
589             MD5_Update(&ctx, ":", 1);
590             MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
591                        strlen(ireq.u.digestRequest.serverNonce));
592             MD5_Update(&ctx, ":", 1);
593             MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
594                        strlen(*ireq.u.digestRequest.nonceCount));
595             MD5_Update(&ctx, ":", 1);
596             MD5_Update(&ctx, *ireq.u.digestRequest.clientNonce,
597                        strlen(*ireq.u.digestRequest.clientNonce));
598             MD5_Update(&ctx, ":", 1);
599             MD5_Update(&ctx, *ireq.u.digestRequest.qop,
600                        strlen(*ireq.u.digestRequest.qop));
601             MD5_Update(&ctx, ":", 1);
602             MD5_Update(&ctx, A2, strlen(A2));
603
604             MD5_Final(md, &ctx);
605
606             r.element = choice_DigestRepInner_response;
607             hex_encode(md, sizeof(md), &r.u.response.responseData);
608
609             free(A1);
610             free(A2);
611
612             if (r.u.response.responseData == NULL) {
613                 krb5_set_error_string(context, "out of memory");
614                 ret = ENOMEM;
615                 goto out;
616             }
617
618         } else {
619             r.element = choice_DigestRepInner_error;
620             asprintf(&r.u.error.reason, "unsupported digest type %s", 
621                      ireq.u.digestRequest.type);
622             if (r.u.error.reason == NULL) {
623                 krb5_set_error_string(context, "out of memory");
624                 ret = ENOMEM;
625                 goto out;
626             }
627             r.u.error.code = EINVAL;
628         }
629
630         break;
631     }
632     default:
633         r.element = choice_DigestRepInner_error;
634         r.u.error.reason = strdup("unknown operation");
635         if (r.u.error.reason == NULL) {
636             krb5_set_error_string(context, "out of memory");
637             ret = ENOMEM;
638             goto out;
639         }
640         r.u.error.code = EINVAL;
641         break;
642     }
643
644     ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
645     if (ret) {
646         krb5_set_error_string(context, "Failed to encode inner digest reply");
647         goto out;
648     }
649     if (size != buf.length)
650         krb5_abortx(context, "ASN1 internal error");
651
652     krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
653
654     ret = krb5_mk_rep (context, ac, &rep.apRep);
655     if (ret)
656         goto out;
657
658     {
659         krb5_keyblock *key;
660
661         ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
662         if (ret)
663             goto out;
664
665         ret = krb5_crypto_init(context, key, 0, &crypto);
666         krb5_free_keyblock (context, key);
667         if (ret)
668             goto out;
669     }
670
671     ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT, 
672                                      buf.data, buf.length, 0,
673                                      &rep.innerRep);
674     
675     ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
676     if (ret) {
677         krb5_set_error_string(context, "Failed to encode digest reply");
678         goto out;
679     }
680     if (size != reply->length)
681         krb5_abortx(context, "ASN1 internal error");
682
683     
684 out:
685     if (ac)
686         krb5_auth_con_free(context, ac);
687     if (ret)
688         krb5_warn(context, ret, "Digest request from %s failed", from);
689     if (ticket)
690         krb5_free_ticket(context, ticket);
691     if (id)
692         krb5_kt_close(context, id);
693     if (crypto)
694         krb5_crypto_destroy(context, crypto);
695     if (sp)
696         krb5_storage_free(sp);
697     if (user)
698         _kdc_free_ent (context, user);
699     if (server)
700         _kdc_free_ent (context, server);
701     if (password) {
702         memset(password, 0, strlen(password));
703         free (password);
704     }
705     krb5_data_free(&buf);
706     krb5_data_free(&serverNonce);
707     free_DigestREP(&rep);
708     free_DigestRepInner(&r);
709     free_DigestReqInner(&ireq);
710
711     return ret;
712 }