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