r8302: import mini HEIMDAL into the tree
[tprouty/samba.git] / source / heimdal / kdc / kerberos4.c
1 /*
2  * Copyright (c) 1997 - 2005 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
36 #include <krb5-v4compat.h>
37
38 RCSID("$Id: kerberos4.c,v 1.54 2005/06/30 01:51:43 lha Exp $");
39
40 #ifndef swap32
41 static u_int32_t
42 swap32(u_int32_t x)
43 {
44     return ((x << 24) & 0xff000000) |
45         ((x << 8) & 0xff0000) |
46         ((x >> 8) & 0xff00) |
47         ((x >> 24) & 0xff);
48 }
49 #endif /* swap32 */
50
51 int
52 _kdc_maybe_version4(unsigned char *buf, int len)
53 {
54     return len > 0 && *buf == 4;
55 }
56
57 static void
58 make_err_reply(krb5_context context, krb5_data *reply,
59                int code, const char *msg)
60 {
61     _krb5_krb_cr_err_reply(context, "", "", "", 
62                            kdc_time, code, msg, reply);
63 }
64
65 static krb5_boolean
66 valid_princ(krb5_context context,
67             void *funcctx,
68             krb5_principal princ)
69 {
70     krb5_kdc_configuration *config = funcctx;
71     krb5_error_code ret;
72     char *s;
73     hdb_entry *ent;
74
75     ret = krb5_unparse_name(context, princ, &s);
76     if (ret)
77         return FALSE;
78     ret = _kdc_db_fetch(context, config, princ, HDB_ENT_TYPE_ANY, &ent);
79     if (ret) {
80         kdc_log(context, config, 7, "Lookup %s failed: %s", s,
81                 krb5_get_err_text (context, ret));
82         free(s);
83         return FALSE;
84     }
85     kdc_log(context, config, 7, "Lookup %s succeeded", s);
86     free(s);
87     _kdc_free_ent(context, ent);
88     return TRUE;
89 }
90
91 krb5_error_code
92 _kdc_db_fetch4(krb5_context context,
93           krb5_kdc_configuration *config,
94           const char *name, const char *instance, const char *realm,
95           enum hdb_ent_type ent_type, 
96           hdb_entry **ent)
97 {
98     krb5_principal p;
99     krb5_error_code ret;
100     
101     ret = krb5_425_conv_principal_ext2(context, name, instance, realm, 
102                                        valid_princ, config, 0, &p);
103     if(ret)
104         return ret;
105     ret = _kdc_db_fetch(context, config, p, ent_type, ent);
106     krb5_free_principal(context, p);
107     return ret;
108 }
109
110 #define RCHECK(X, L) if(X){make_err_reply(context, reply, KFAILURE, "Packet too short"); goto L;}
111
112 /*
113  * Process the v4 request in `buf, len' (received from `addr'
114  * (with string `from').
115  * Return an error code and a reply in `reply'.
116  */
117
118 krb5_error_code
119 _kdc_do_version4(krb5_context context, 
120                  krb5_kdc_configuration *config,
121                  unsigned char *buf,
122                  size_t len,
123                  krb5_data *reply,
124                  const char *from,
125                  struct sockaddr_in *addr)
126 {
127     krb5_storage *sp;
128     krb5_error_code ret;
129     hdb_entry *client = NULL, *server = NULL;
130     Key *ckey, *skey;
131     int8_t pvno;
132     int8_t msg_type;
133     int lsb;
134     char *name = NULL, *inst = NULL, *realm = NULL;
135     char *sname = NULL, *sinst = NULL;
136     int32_t req_time;
137     time_t max_life;
138     u_int8_t life;
139     char client_name[256];
140     char server_name[256];
141
142     if(!config->enable_v4) {
143         kdc_log(context, config, 0,
144                 "Rejected version 4 request from %s", from);
145         make_err_reply(context, reply, KDC_GEN_ERR, "function not enabled");
146         return 0;
147     }
148
149     sp = krb5_storage_from_mem(buf, len);
150     RCHECK(krb5_ret_int8(sp, &pvno), out);
151     if(pvno != 4){
152         kdc_log(context, config, 0,
153                 "Protocol version mismatch (krb4) (%d)", pvno);
154         make_err_reply(context, reply, KDC_PKT_VER, "protocol mismatch");
155         goto out;
156     }
157     RCHECK(krb5_ret_int8(sp, &msg_type), out);
158     lsb = msg_type & 1;
159     msg_type &= ~1;
160     switch(msg_type){
161     case AUTH_MSG_KDC_REQUEST: {
162         krb5_data ticket, cipher;
163         krb5_keyblock session;
164         
165         krb5_data_zero(&ticket);
166         krb5_data_zero(&cipher);
167
168         RCHECK(krb5_ret_stringz(sp, &name), out1);
169         RCHECK(krb5_ret_stringz(sp, &inst), out1);
170         RCHECK(krb5_ret_stringz(sp, &realm), out1);
171         RCHECK(krb5_ret_int32(sp, &req_time), out1);
172         if(lsb)
173             req_time = swap32(req_time);
174         RCHECK(krb5_ret_int8(sp, &life), out1);
175         RCHECK(krb5_ret_stringz(sp, &sname), out1);
176         RCHECK(krb5_ret_stringz(sp, &sinst), out1);
177         snprintf (client_name, sizeof(client_name),
178                   "%s.%s@%s", name, inst, realm);
179         snprintf (server_name, sizeof(server_name),
180                   "%s.%s@%s", sname, sinst, config->v4_realm);
181         
182         kdc_log(context, config, 0, "AS-REQ (krb4) %s from %s for %s",
183                 client_name, from, server_name);
184
185         ret = _kdc_db_fetch4(context, config, name, inst, realm, HDB_ENT_TYPE_CLIENT, &client);
186         if(ret) {
187             kdc_log(context, config, 0, "Client not found in database: %s: %s",
188                     client_name, krb5_get_err_text(context, ret));
189             make_err_reply(context, reply, KERB_ERR_PRINCIPAL_UNKNOWN,
190                            "principal unknown");
191             goto out1;
192         }
193         ret = _kdc_db_fetch4(context, config, sname, sinst, 
194                         config->v4_realm, HDB_ENT_TYPE_SERVER, &server);
195         if(ret){
196             kdc_log(context, config, 0, "Server not found in database: %s: %s",
197                     server_name, krb5_get_err_text(context, ret));
198             make_err_reply(context, reply, KERB_ERR_PRINCIPAL_UNKNOWN,
199                            "principal unknown");
200             goto out1;
201         }
202
203         ret = _kdc_check_flags (context, config, 
204                                 client, client_name,
205                                 server, server_name,
206                                 TRUE);
207         if (ret) {
208             /* good error code? */
209             make_err_reply(context, reply, KERB_ERR_NAME_EXP,
210                            "operation not allowed");
211             goto out1;
212         }
213
214         /*
215          * There's no way to do pre-authentication in v4 and thus no
216          * good error code to return if preauthentication is required.
217          */
218
219         if (config->require_preauth
220             || client->flags.require_preauth
221             || server->flags.require_preauth) {
222             kdc_log(context, config, 0,
223                     "Pre-authentication required for v4-request: "
224                     "%s for %s",
225                     client_name, server_name);
226             make_err_reply(context, reply, KERB_ERR_NULL_KEY,
227                            "preauth required");
228             goto out1;
229         }
230
231         ret = _kdc_get_des_key(context, client, FALSE, FALSE, &ckey);
232         if(ret){
233             kdc_log(context, config, 0, "no suitable DES key for client");
234             make_err_reply(context, reply, KDC_NULL_KEY, 
235                            "no suitable DES key for client");
236             goto out1;
237         }
238
239 #if 0
240         /* this is not necessary with the new code in libkrb */
241         /* find a properly salted key */
242         while(ckey->salt == NULL || ckey->salt->salt.length != 0)
243             ret = hdb_next_keytype2key(context, client, KEYTYPE_DES, &ckey);
244         if(ret){
245             kdc_log(context, config, 0, "No version-4 salted key in database -- %s.%s@%s", 
246                     name, inst, realm);
247             make_err_reply(context, reply, KDC_NULL_KEY, 
248                            "No version-4 salted key in database");
249             goto out1;
250         }
251 #endif
252         
253         ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey);
254         if(ret){
255             kdc_log(context, config, 0, "no suitable DES key for server");
256             /* XXX */
257             make_err_reply(context, reply, KDC_NULL_KEY, 
258                            "no suitable DES key for server");
259             goto out1;
260         }
261
262         max_life = _krb5_krb_life_to_time(0, life);
263         if(client->max_life)
264             max_life = min(max_life, *client->max_life);
265         if(server->max_life)
266             max_life = min(max_life, *server->max_life);
267
268         life = krb_time_to_life(kdc_time, kdc_time + max_life);
269     
270         ret = krb5_generate_random_keyblock(context,
271                                             ETYPE_DES_PCBC_NONE,
272                                             &session);
273         if (ret) {
274             make_err_reply(context, reply, KFAILURE,
275                            "Not enough random i KDC");
276             goto out1;
277         }
278         
279         ret = _krb5_krb_create_ticket(context,
280                                       0,
281                                       name,
282                                       inst,
283                                       config->v4_realm,
284                                       addr->sin_addr.s_addr,
285                                       &session,
286                                       life,
287                                       kdc_time,
288                                       sname,
289                                       sinst,
290                                       &skey->key,
291                                       &ticket);
292         if (ret) {
293             krb5_free_keyblock_contents(context, &session);
294             make_err_reply(context, reply, KFAILURE,
295                            "failed to create v4 ticket");
296             goto out1;
297         }
298
299         ret = _krb5_krb_create_ciph(context,
300                                     &session,
301                                     sname,
302                                     sinst,
303                                     config->v4_realm,
304                                     life,
305                                     server->kvno % 255,
306                                     &ticket,
307                                     kdc_time,
308                                     &ckey->key,
309                                     &cipher);
310         krb5_free_keyblock_contents(context, &session);
311         krb5_data_free(&ticket);
312         if (ret) {
313             make_err_reply(context, reply, KFAILURE, 
314                            "Failed to create v4 cipher");
315             goto out1;
316         }
317         
318         ret = _krb5_krb_create_auth_reply(context,
319                                           name,
320                                           inst,
321                                           realm,
322                                           req_time,
323                                           0,
324                                           client->pw_end ? *client->pw_end : 0,
325                                           client->kvno % 256,
326                                           &cipher,
327                                           reply);
328         krb5_data_free(&cipher);
329
330     out1:
331         break;
332     }
333     case AUTH_MSG_APPL_REQUEST: {
334         struct _krb5_krb_auth_data ad;
335         int8_t kvno;
336         int8_t ticket_len;
337         int8_t req_len;
338         krb5_data auth;
339         int32_t address;
340         size_t pos;
341         krb5_principal tgt_princ = NULL;
342         hdb_entry *tgt = NULL;
343         Key *tkey;
344         time_t max_end, actual_end, issue_time;
345         
346         memset(&ad, 0, sizeof(ad));
347         krb5_data_zero(&auth);
348
349         RCHECK(krb5_ret_int8(sp, &kvno), out2);
350         RCHECK(krb5_ret_stringz(sp, &realm), out2);
351         
352         ret = krb5_425_conv_principal(context, "krbtgt", realm,
353                                       config->v4_realm,
354                                       &tgt_princ);
355         if(ret){
356             kdc_log(context, config, 0,
357                     "Converting krbtgt principal (krb4): %s", 
358                     krb5_get_err_text(context, ret));
359             make_err_reply(context, reply, KFAILURE, 
360                            "Failed to convert v4 principal (krbtgt)");
361             goto out2;
362         }
363
364         ret = _kdc_db_fetch(context, config, tgt_princ, HDB_ENT_TYPE_SERVER, &tgt);
365         if(ret){
366             char *s;
367             s = kdc_log_msg(context, config, 0, "Ticket-granting ticket not "
368                             "found in database (krb4): krbtgt.%s@%s: %s", 
369                             realm, config->v4_realm,
370                             krb5_get_err_text(context, ret));
371             make_err_reply(context, reply, KFAILURE, s);
372             free(s);
373             goto out2;
374         }
375         
376         if(tgt->kvno % 256 != kvno){
377             kdc_log(context, config, 0,
378                     "tgs-req (krb4) with old kvno %d (current %d) for "
379                     "krbtgt.%s@%s", kvno, tgt->kvno % 256, 
380                     realm, config->v4_realm);
381             make_err_reply(context, reply, KDC_AUTH_EXP,
382                            "old krbtgt kvno used");
383             goto out2;
384         }
385
386         ret = _kdc_get_des_key(context, tgt, TRUE, FALSE, &tkey);
387         if(ret){
388             kdc_log(context, config, 0, 
389                     "no suitable DES key for krbtgt (krb4)");
390             /* XXX */
391             make_err_reply(context, reply, KDC_NULL_KEY, 
392                            "no suitable DES key for krbtgt");
393             goto out2;
394         }
395
396         RCHECK(krb5_ret_int8(sp, &ticket_len), out2);
397         RCHECK(krb5_ret_int8(sp, &req_len), out2);
398         
399         pos = krb5_storage_seek(sp, ticket_len + req_len, SEEK_CUR);
400         
401         auth.data = buf;
402         auth.length = pos;
403
404         if (config->check_ticket_addresses)
405             address = addr->sin_addr.s_addr;
406         else
407             address = 0;
408
409         ret = _krb5_krb_rd_req(context, &auth, "krbtgt", realm, 
410                                config->v4_realm,
411                                address, &tkey->key, &ad);
412         if(ret){
413             kdc_log(context, config, 0, "krb_rd_req: %d", ret);
414             make_err_reply(context, reply, ret, "failed to parse request");
415             goto out2;
416         }
417         
418         RCHECK(krb5_ret_int32(sp, &req_time), out2);
419         if(lsb)
420             req_time = swap32(req_time);
421         RCHECK(krb5_ret_int8(sp, &life), out2);
422         RCHECK(krb5_ret_stringz(sp, &sname), out2);
423         RCHECK(krb5_ret_stringz(sp, &sinst), out2);
424         snprintf (server_name, sizeof(server_name),
425                   "%s.%s@%s",
426                   sname, sinst, config->v4_realm);
427         snprintf (client_name, sizeof(client_name),
428                   "%s.%s@%s",
429                   ad.pname, ad.pinst, ad.prealm);
430
431         kdc_log(context, config, 0, "TGS-REQ (krb4) %s from %s for %s",
432                 client_name, from, server_name);
433         
434         if(strcmp(ad.prealm, realm)){
435             kdc_log(context, config, 0, 
436                     "Can't hop realms (krb4) %s -> %s", realm, ad.prealm);
437             make_err_reply(context, reply, KERB_ERR_PRINCIPAL_UNKNOWN, 
438                            "Can't hop realms");
439             goto out2;
440         }
441
442         if (!config->enable_v4_cross_realm && strcmp(realm, config->v4_realm) != 0) {
443             kdc_log(context, config, 0, 
444                     "krb4 Cross-realm %s -> %s disabled",
445                     realm, config->v4_realm);
446             make_err_reply(context, reply, KERB_ERR_PRINCIPAL_UNKNOWN, 
447                            "Can't hop realms");
448             goto out2;
449         }
450
451         if(strcmp(sname, "changepw") == 0){
452             kdc_log(context, config, 0, 
453                     "Bad request for changepw ticket (krb4)");
454             make_err_reply(context, reply, KERB_ERR_PRINCIPAL_UNKNOWN, 
455                            "Can't authorize password change based on TGT");
456             goto out2;
457         }
458         
459         ret = _kdc_db_fetch4(context, config, ad.pname, ad.pinst, ad.prealm, HDB_ENT_TYPE_CLIENT, &client);
460         if(ret && ret != HDB_ERR_NOENTRY) {
461             char *s;
462             s = kdc_log_msg(context, config, 0,
463                             "Client not found in database: (krb4) %s: %s",
464                             client_name, krb5_get_err_text(context, ret));
465             make_err_reply(context, reply, KERB_ERR_PRINCIPAL_UNKNOWN, s);
466             free(s);
467             goto out2;
468         }
469         if (client == NULL && strcmp(ad.prealm, config->v4_realm) == 0) {
470             char *s;
471             s = kdc_log_msg(context, config, 0,
472                             "Local client not found in database: (krb4) "
473                             "%s", client_name);
474             make_err_reply(context, reply, KERB_ERR_PRINCIPAL_UNKNOWN, s);
475             free(s);
476             goto out2;
477         }
478
479         ret = _kdc_db_fetch4(context, config, sname, sinst, config->v4_realm, 
480                              HDB_ENT_TYPE_SERVER, &server);
481         if(ret){
482             char *s;
483             s = kdc_log_msg(context, config, 0,
484                             "Server not found in database (krb4): %s: %s",
485                             server_name, krb5_get_err_text(context, ret));
486             make_err_reply(context, reply, KERB_ERR_PRINCIPAL_UNKNOWN, s);
487             free(s);
488             goto out2;
489         }
490
491         ret = _kdc_check_flags (context, config, 
492                                 client, client_name,
493                                 server, server_name,
494                                 FALSE);
495         if (ret) {
496             /* good error code? */
497             make_err_reply(context, reply, KERB_ERR_NAME_EXP,
498                            "operation not allowed");
499             goto out2;
500         }
501
502         ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey);
503         if(ret){
504             kdc_log(context, config, 0, 
505                     "no suitable DES key for server (krb4)");
506             /* XXX */
507             make_err_reply(context, reply, KDC_NULL_KEY, 
508                            "no suitable DES key for server");
509             goto out2;
510         }
511
512         max_end = _krb5_krb_life_to_time(ad.time_sec, ad.life);
513         max_end = min(max_end, _krb5_krb_life_to_time(kdc_time, life));
514         if(server->max_life)
515             max_end = min(max_end, kdc_time + *server->max_life);
516         if(client && client->max_life)
517             max_end = min(max_end, kdc_time + *client->max_life);
518         life = min(life, krb_time_to_life(kdc_time, max_end));
519         
520         issue_time = kdc_time;
521         actual_end = _krb5_krb_life_to_time(issue_time, life);
522         while (actual_end > max_end && life > 1) {
523             /* move them into the next earlier lifetime bracket */
524             life--;
525             actual_end = _krb5_krb_life_to_time(issue_time, life);
526         }
527         if (actual_end > max_end) {
528             /* if life <= 1 and it's still too long, backdate the ticket */
529             issue_time -= actual_end - max_end;
530         }
531
532         {
533             krb5_data ticket, cipher;
534             krb5_keyblock session;
535
536             krb5_data_zero(&ticket);
537             krb5_data_zero(&cipher);
538
539             ret = krb5_generate_random_keyblock(context,
540                                                 ETYPE_DES_PCBC_NONE,
541                                                 &session);
542             if (ret) {
543                 make_err_reply(context, reply, KFAILURE,
544                                "Not enough random i KDC");
545                 goto out2;
546             }
547         
548             ret = _krb5_krb_create_ticket(context,
549                                           0,
550                                           ad.pname,
551                                           ad.pinst,
552                                           ad.prealm,
553                                           addr->sin_addr.s_addr,
554                                           &session,
555                                           life,
556                                           issue_time,
557                                           sname,
558                                           sinst,
559                                           &skey->key,
560                                           &ticket);
561             if (ret) {
562                 krb5_free_keyblock_contents(context, &session);
563                 make_err_reply(context, reply, KFAILURE,
564                                "failed to create v4 ticket");
565                 goto out2;
566             }
567
568             ret = _krb5_krb_create_ciph(context,
569                                         &session,
570                                         sname,
571                                         sinst,
572                                         config->v4_realm,
573                                         life,
574                                         server->kvno % 255,
575                                         &ticket,
576                                         issue_time,
577                                         &ad.session,
578                                         &cipher);
579             krb5_free_keyblock_contents(context, &session);
580             if (ret) {
581                 make_err_reply(context, reply, KFAILURE,
582                                "failed to create v4 cipher");
583                 goto out2;
584             }
585             
586             ret = _krb5_krb_create_auth_reply(context,
587                                               ad.pname,
588                                               ad.pinst,
589                                               ad.prealm,
590                                               req_time,
591                                               0,
592                                               0,
593                                               0,
594                                               &cipher,
595                                               reply);
596             krb5_data_free(&cipher);
597         }
598     out2:
599         _krb5_krb_free_auth_data(context, &ad);
600         if(tgt_princ)
601             krb5_free_principal(context, tgt_princ);
602         if(tgt)
603             _kdc_free_ent(context, tgt);
604         break;
605     }
606     case AUTH_MSG_ERR_REPLY:
607         break;
608     default:
609         kdc_log(context, config, 0, "Unknown message type (krb4): %d from %s", 
610                 msg_type, from);
611         
612         make_err_reply(context, reply, KFAILURE, "Unknown message type");
613     }
614  out:
615     if(name)
616         free(name);
617     if(inst)
618         free(inst);
619     if(realm)
620         free(realm);
621     if(sname)
622         free(sname);
623     if(sinst)
624         free(sinst);
625     if(client)
626         _kdc_free_ent(context, client);
627     if(server)
628         _kdc_free_ent(context, server);
629     krb5_storage_free(sp);
630     return 0;
631 }
632
633 krb5_error_code
634 _kdc_encode_v4_ticket(krb5_context context, 
635                       krb5_kdc_configuration *config,
636                       void *buf, size_t len, const EncTicketPart *et,
637                       const PrincipalName *service, size_t *size)
638 {
639     krb5_storage *sp;
640     krb5_error_code ret;
641     char name[40], inst[40], realm[40];
642     char sname[40], sinst[40];
643
644     {
645         krb5_principal princ;
646         _krb5_principalname2krb5_principal(&princ,
647                                            *service,
648                                            et->crealm);
649         ret = krb5_524_conv_principal(context, 
650                                       princ,
651                                       sname,
652                                       sinst,
653                                       realm);
654         krb5_free_principal(context, princ);
655         if(ret)
656             return ret;
657
658         _krb5_principalname2krb5_principal(&princ,
659                                            et->cname,
660                                            et->crealm);
661                                      
662         ret = krb5_524_conv_principal(context, 
663                                       princ,
664                                       name,
665                                       inst,
666                                       realm);
667         krb5_free_principal(context, princ);
668     }
669     if(ret)
670         return ret;
671
672     sp = krb5_storage_emem();
673     
674     krb5_store_int8(sp, 0); /* flags */
675     krb5_store_stringz(sp, name);
676     krb5_store_stringz(sp, inst);
677     krb5_store_stringz(sp, realm);
678     {
679         unsigned char tmp[4] = { 0, 0, 0, 0 };
680         int i;
681         if(et->caddr){
682             for(i = 0; i < et->caddr->len; i++)
683                 if(et->caddr->val[i].addr_type == AF_INET &&
684                    et->caddr->val[i].address.length == 4){
685                     memcpy(tmp, et->caddr->val[i].address.data, 4);
686                     break;
687                 }
688         }
689         krb5_storage_write(sp, tmp, sizeof(tmp));
690     }
691
692     if((et->key.keytype != ETYPE_DES_CBC_MD5 &&
693         et->key.keytype != ETYPE_DES_CBC_MD4 &&
694         et->key.keytype != ETYPE_DES_CBC_CRC) || 
695        et->key.keyvalue.length != 8)
696         return -1;
697     krb5_storage_write(sp, et->key.keyvalue.data, 8);
698     
699     {
700         time_t start = et->starttime ? *et->starttime : et->authtime;
701         krb5_store_int8(sp, krb_time_to_life(start, et->endtime));
702         krb5_store_int32(sp, start);
703     }
704
705     krb5_store_stringz(sp, sname);
706     krb5_store_stringz(sp, sinst);
707     
708     {
709         krb5_data data;
710         krb5_storage_to_data(sp, &data);
711         krb5_storage_free(sp);
712         *size = (data.length + 7) & ~7; /* pad to 8 bytes */
713         if(*size > len)
714             return -1;
715         memset((unsigned char*)buf - *size + 1, 0, *size);
716         memcpy((unsigned char*)buf - *size + 1, data.data, data.length);
717         krb5_data_free(&data);
718     }
719     return 0;
720 }
721
722 krb5_error_code
723 _kdc_get_des_key(krb5_context context, 
724                  hdb_entry *principal, krb5_boolean is_server, 
725                  krb5_boolean prefer_afs_key, Key **ret_key)
726 {
727     Key *v5_key = NULL, *v4_key = NULL, *afs_key = NULL, *server_key = NULL;
728     int i;
729     krb5_enctype etypes[] = { ETYPE_DES_CBC_MD5, 
730                               ETYPE_DES_CBC_MD4, 
731                               ETYPE_DES_CBC_CRC };
732
733     for(i = 0;
734         i < sizeof(etypes)/sizeof(etypes[0])
735             && (v5_key == NULL || v4_key == NULL || 
736                 afs_key == NULL || server_key == NULL);
737         ++i) {
738         Key *key = NULL;
739         while(hdb_next_enctype2key(context, principal, etypes[i], &key) == 0) {
740             if(key->salt == NULL) {
741                 if(v5_key == NULL)
742                     v5_key = key;
743             } else if(key->salt->type == hdb_pw_salt && 
744                       key->salt->salt.length == 0) {
745                 if(v4_key == NULL)
746                     v4_key = key;
747             } else if(key->salt->type == hdb_afs3_salt) {
748                 if(afs_key == NULL)
749                     afs_key = key;
750             } else if(server_key == NULL)
751                 server_key = key;
752         }
753     }
754
755     if(prefer_afs_key) {
756         if(afs_key)
757             *ret_key = afs_key;
758         else if(v4_key)
759             *ret_key = v4_key;
760         else if(v5_key)
761             *ret_key = v5_key;
762         else if(is_server && server_key)
763             *ret_key = server_key;
764         else
765             return KERB_ERR_NULL_KEY;
766     } else {
767         if(v4_key)
768             *ret_key = v4_key;
769         else if(afs_key)
770             *ret_key = afs_key;
771         else  if(v5_key)
772             *ret_key = v5_key;
773         else if(is_server && server_key)
774             *ret_key = server_key;
775         else
776             return KERB_ERR_NULL_KEY;
777     }
778
779     if((*ret_key)->key.keyvalue.length == 0)
780         return KERB_ERR_NULL_KEY;
781     return 0;
782 }
783