xref: /freebsd/crypto/heimdal/kdc/hprop.c (revision 1669d8afc64812c8d2d1d147ae1fd42ff441e1b1)
1 /*
2  * Copyright (c) 1997 - 2002 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 "hprop.h"
35 
36 RCSID("$Id: hprop.c,v 1.70 2002/09/04 18:19:41 joda Exp $");
37 
38 static int version_flag;
39 static int help_flag;
40 static const char *ktname = HPROP_KEYTAB;
41 static const char *database;
42 static char *mkeyfile;
43 static int to_stdout;
44 static int verbose_flag;
45 static int encrypt_flag;
46 static int decrypt_flag;
47 static hdb_master_key mkey5;
48 
49 static char *source_type;
50 
51 static char *afs_cell;
52 static char *v4_realm;
53 
54 static int kaspecials_flag;
55 static int ka_use_null_salt;
56 
57 static char *local_realm=NULL;
58 
59 static int
60 open_socket(krb5_context context, const char *hostname, const char *port)
61 {
62     struct addrinfo *ai, *a;
63     struct addrinfo hints;
64     int error;
65 
66     memset (&hints, 0, sizeof(hints));
67     hints.ai_socktype = SOCK_STREAM;
68     hints.ai_protocol = IPPROTO_TCP;
69 
70     error = getaddrinfo (hostname, port, &hints, &ai);
71     if (error) {
72 	warnx ("%s: %s", hostname, gai_strerror(error));
73 	return -1;
74     }
75 
76     for (a = ai; a != NULL; a = a->ai_next) {
77 	int s;
78 
79 	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
80 	if (s < 0)
81 	    continue;
82 	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
83 	    warn ("connect(%s)", hostname);
84 	    close (s);
85 	    continue;
86 	}
87 	freeaddrinfo (ai);
88 	return s;
89     }
90     warnx ("failed to contact %s", hostname);
91     freeaddrinfo (ai);
92     return -1;
93 }
94 
95 krb5_error_code
96 v5_prop(krb5_context context, HDB *db, hdb_entry *entry, void *appdata)
97 {
98     krb5_error_code ret;
99     struct prop_data *pd = appdata;
100     krb5_data data;
101 
102     if(encrypt_flag) {
103 	ret = hdb_seal_keys_mkey(context, entry, mkey5);
104 	if (ret) {
105 	    krb5_warn(context, ret, "hdb_seal_keys_mkey");
106 	    return ret;
107 	}
108     }
109     if(decrypt_flag) {
110 	ret = hdb_unseal_keys_mkey(context, entry, mkey5);
111 	if (ret) {
112 	    krb5_warn(context, ret, "hdb_unseal_keys_mkey");
113 	    return ret;
114 	}
115     }
116 
117     ret = hdb_entry2value(context, entry, &data);
118     if(ret) {
119 	krb5_warn(context, ret, "hdb_entry2value");
120 	return ret;
121     }
122 
123     if(to_stdout)
124 	ret = krb5_write_message(context, &pd->sock, &data);
125     else
126 	ret = krb5_write_priv_message(context, pd->auth_context,
127 				      &pd->sock, &data);
128     krb5_data_free(&data);
129     return ret;
130 }
131 
132 #ifdef KRB4
133 
134 static char realm_buf[REALM_SZ];
135 
136 static int
137 kdb_prop(void *arg, Principal *p)
138 {
139     int ret;
140     struct v4_principal pr;
141 
142     memset(&pr, 0, sizeof(pr));
143 
144     if(p->attributes != 0) {
145 	warnx("%s.%s has non-zero attributes - skipping",
146 	      p->name, p->instance);
147 	    return 0;
148     }
149     strlcpy(pr.name, p->name, sizeof(pr.name));
150     strlcpy(pr.instance, p->instance, sizeof(pr.instance));
151 
152     copy_to_key(&p->key_low, &p->key_high, pr.key);
153     pr.exp_date = p->exp_date;
154     pr.mod_date = p->mod_date;
155     strlcpy(pr.mod_name, p->mod_name, sizeof(pr.mod_name));
156     strlcpy(pr.mod_instance, p->mod_instance, sizeof(pr.mod_instance));
157     pr.max_life = p->max_life;
158     pr.mkvno = p->kdc_key_ver;
159     pr.kvno = p->key_version;
160 
161     ret = v4_prop(arg, &pr);
162     memset(&pr, 0, sizeof(pr));
163     return ret;
164 }
165 
166 #endif /* KRB4 */
167 
168 #ifndef KRB4
169 static time_t
170 krb_life_to_time(time_t start, int life)
171 {
172     static int lifetimes[] = {
173 	  38400,   41055,   43894,   46929,   50174,   53643,   57352,   61318,
174 	  65558,   70091,   74937,   80119,   85658,   91581,   97914,  104684,
175 	 111922,  119661,  127935,  136781,  146239,  156350,  167161,  178720,
176 	 191077,  204289,  218415,  233517,  249664,  266926,  285383,  305116,
177 	 326213,  348769,  372885,  398668,  426234,  455705,  487215,  520904,
178 	 556921,  595430,  636601,  680618,  727680,  777995,  831789,  889303,
179 	 950794, 1016537, 1086825, 1161973, 1242318, 1328218, 1420057, 1518247,
180 	1623226, 1735464, 1855462, 1983758, 2120925, 2267576, 2424367, 2592000
181     };
182 
183 #if 0
184     int i;
185     double q = exp((log(2592000.0) - log(38400.0)) / 63);
186     double x = 38400;
187     for(i = 0; i < 64; i++) {
188 	lifetimes[i] = (int)x;
189 	x *= q;
190     }
191 #endif
192 
193     if(life == 0xff)
194 	return NEVERDATE;
195     if(life < 0x80)
196 	return start + life * 5 * 60;
197     if(life > 0xbf)
198 	life = 0xbf;
199     return start + lifetimes[life - 0x80];
200 }
201 #endif /* !KRB4 */
202 
203 int
204 v4_prop(void *arg, struct v4_principal *p)
205 {
206     struct prop_data *pd = arg;
207     hdb_entry ent;
208     krb5_error_code ret;
209 
210     memset(&ent, 0, sizeof(ent));
211 
212     ret = krb5_425_conv_principal(pd->context, p->name, p->instance, v4_realm,
213 				  &ent.principal);
214     if(ret) {
215 	krb5_warn(pd->context, ret,
216 		  "krb5_425_conv_principal %s.%s@%s",
217 		  p->name, p->instance, v4_realm);
218 	return 0;
219     }
220 
221     if(verbose_flag) {
222 	char *s;
223 	krb5_unparse_name_short(pd->context, ent.principal, &s);
224 	krb5_warnx(pd->context, "%s.%s -> %s", p->name, p->instance, s);
225 	free(s);
226     }
227 
228     ent.kvno = p->kvno;
229     ent.keys.len = 3;
230     ent.keys.val = malloc(ent.keys.len * sizeof(*ent.keys.val));
231     if(p->mkvno != -1) {
232 	ent.keys.val[0].mkvno = malloc (sizeof(*ent.keys.val[0].mkvno));
233 	*(ent.keys.val[0].mkvno) = p->mkvno;
234     } else
235 	ent.keys.val[0].mkvno = NULL;
236     ent.keys.val[0].salt = calloc(1, sizeof(*ent.keys.val[0].salt));
237     ent.keys.val[0].salt->type = KRB5_PADATA_PW_SALT;
238     ent.keys.val[0].key.keytype = ETYPE_DES_CBC_MD5;
239     krb5_data_alloc(&ent.keys.val[0].key.keyvalue, sizeof(des_cblock));
240     memcpy(ent.keys.val[0].key.keyvalue.data, p->key, 8);
241 
242     copy_Key(&ent.keys.val[0], &ent.keys.val[1]);
243     ent.keys.val[1].key.keytype = ETYPE_DES_CBC_MD4;
244     copy_Key(&ent.keys.val[0], &ent.keys.val[2]);
245     ent.keys.val[2].key.keytype = ETYPE_DES_CBC_CRC;
246 
247     {
248 	int life = krb_life_to_time(0, p->max_life);
249 	if(life == NEVERDATE){
250 	    ent.max_life = NULL;
251 	} else {
252 	    /* clean up lifetime a bit */
253 	    if(life > 86400)
254 		life = (life + 86399) / 86400 * 86400;
255 	    else if(life > 3600)
256 		life = (life + 3599) / 3600 * 3600;
257 	    ALLOC(ent.max_life);
258 	    *ent.max_life = life;
259 	}
260     }
261 
262     ALLOC(ent.valid_end);
263     *ent.valid_end = p->exp_date;
264 
265     ret = krb5_make_principal(pd->context, &ent.created_by.principal,
266 			      v4_realm,
267 			      "kadmin",
268 			      "hprop",
269 			      NULL);
270     if(ret){
271 	krb5_warn(pd->context, ret, "krb5_make_principal");
272 	ret = 0;
273 	goto out;
274     }
275     ent.created_by.time = time(NULL);
276     ALLOC(ent.modified_by);
277     ret = krb5_425_conv_principal(pd->context, p->mod_name, p->mod_instance,
278 				  v4_realm, &ent.modified_by->principal);
279     if(ret){
280 	krb5_warn(pd->context, ret, "%s.%s@%s", p->name, p->instance, v4_realm);
281 	ent.modified_by->principal = NULL;
282 	ret = 0;
283 	goto out;
284     }
285     ent.modified_by->time = p->mod_date;
286 
287     ent.flags.forwardable = 1;
288     ent.flags.renewable = 1;
289     ent.flags.proxiable = 1;
290     ent.flags.postdate = 1;
291     ent.flags.client = 1;
292     ent.flags.server = 1;
293 
294     /* special case password changing service */
295     if(strcmp(p->name, "changepw") == 0 &&
296        strcmp(p->instance, "kerberos") == 0) {
297 	ent.flags.forwardable = 0;
298 	ent.flags.renewable = 0;
299 	ent.flags.proxiable = 0;
300 	ent.flags.postdate = 0;
301 	ent.flags.initial = 1;
302 	ent.flags.change_pw = 1;
303     }
304 
305     ret = v5_prop(pd->context, NULL, &ent, pd);
306 
307     if (strcmp (p->name, "krbtgt") == 0
308 	&& strcmp (v4_realm, p->instance) != 0) {
309 	krb5_free_principal (pd->context, ent.principal);
310 	ret = krb5_425_conv_principal (pd->context, p->name,
311 				       v4_realm, p->instance,
312 				       &ent.principal);
313 	if (ret == 0)
314 	    ret = v5_prop (pd->context, NULL, &ent, pd);
315     }
316 
317   out:
318     hdb_free_entry(pd->context, &ent);
319     return ret;
320 }
321 
322 #include "kadb.h"
323 
324 /* read a `ka_entry' from `fd' at offset `pos' */
325 static void
326 read_block(krb5_context context, int fd, int32_t pos, void *buf, size_t len)
327 {
328     krb5_error_code ret;
329 #ifdef HAVE_PREAD
330     if((ret = pread(fd, buf, len, 64 + pos)) < 0)
331 	krb5_err(context, 1, errno, "pread(%u)", 64 + pos);
332 #else
333     if(lseek(fd, 64 + pos, SEEK_SET) == (off_t)-1)
334 	krb5_err(context, 1, errno, "lseek(%u)", 64 + pos);
335     ret = read(fd, buf, len);
336     if(ret < 0)
337 	krb5_err(context, 1, errno, "read(%lu)", (unsigned long)len);
338 #endif
339     if(ret != len)
340 	krb5_errx(context, 1, "read(%lu) = %u", (unsigned long)len, ret);
341 }
342 
343 static int
344 ka_convert(struct prop_data *pd, int fd, struct ka_entry *ent)
345 {
346     int32_t flags = ntohl(ent->flags);
347     krb5_error_code ret;
348     hdb_entry hdb;
349 
350     if(!kaspecials_flag
351        && (flags & KAFNORMAL) == 0) /* remove special entries */
352 	return 0;
353     memset(&hdb, 0, sizeof(hdb));
354     ret = krb5_425_conv_principal(pd->context, ent->name, ent->instance,
355 				  v4_realm, &hdb.principal);
356     if(ret) {
357 	krb5_warn(pd->context, ret,
358 		  "krb5_425_conv_principal (%s.%s@%s)",
359 		  ent->name, ent->instance, v4_realm);
360 	return 0;
361     }
362     hdb.kvno = ntohl(ent->kvno);
363     hdb.keys.len = 3;
364     hdb.keys.val = malloc(hdb.keys.len * sizeof(*hdb.keys.val));
365     hdb.keys.val[0].mkvno = NULL;
366     hdb.keys.val[0].salt = calloc(1, sizeof(*hdb.keys.val[0].salt));
367     if (ka_use_null_salt) {
368 	hdb.keys.val[0].salt->type = hdb_pw_salt;
369 	hdb.keys.val[0].salt->salt.data = NULL;
370 	hdb.keys.val[0].salt->salt.length = 0;
371     } else {
372 	hdb.keys.val[0].salt->type = hdb_afs3_salt;
373 	hdb.keys.val[0].salt->salt.data = strdup(afs_cell);
374 	hdb.keys.val[0].salt->salt.length = strlen(afs_cell);
375     }
376 
377     hdb.keys.val[0].key.keytype = ETYPE_DES_CBC_MD5;
378     krb5_data_copy(&hdb.keys.val[0].key.keyvalue, ent->key, sizeof(ent->key));
379     copy_Key(&hdb.keys.val[0], &hdb.keys.val[1]);
380     hdb.keys.val[1].key.keytype = ETYPE_DES_CBC_MD4;
381     copy_Key(&hdb.keys.val[0], &hdb.keys.val[2]);
382     hdb.keys.val[2].key.keytype = ETYPE_DES_CBC_CRC;
383 
384     ALLOC(hdb.max_life);
385     *hdb.max_life = ntohl(ent->max_life);
386 
387     if(ntohl(ent->valid_end) != NEVERDATE && ntohl(ent->valid_end) != -1){
388 	ALLOC(hdb.valid_end);
389 	*hdb.valid_end = ntohl(ent->valid_end);
390     }
391 
392     if (ntohl(ent->pw_change) != NEVERDATE &&
393 	ent->pw_expire != 255 &&
394 	ent->pw_expire != 0) {
395 	ALLOC(hdb.pw_end);
396 	*hdb.pw_end = ntohl(ent->pw_change)
397 	    + 24 * 60 * 60 * ent->pw_expire;
398     }
399 
400     ret = krb5_make_principal(pd->context, &hdb.created_by.principal,
401 			      v4_realm,
402 			      "kadmin",
403 			      "hprop",
404 			      NULL);
405     hdb.created_by.time = time(NULL);
406 
407     if(ent->mod_ptr){
408 	struct ka_entry mod;
409 	ALLOC(hdb.modified_by);
410 	read_block(pd->context, fd, ntohl(ent->mod_ptr), &mod, sizeof(mod));
411 
412 	krb5_425_conv_principal(pd->context, mod.name, mod.instance, v4_realm,
413 				&hdb.modified_by->principal);
414 	hdb.modified_by->time = ntohl(ent->mod_time);
415 	memset(&mod, 0, sizeof(mod));
416     }
417 
418     hdb.flags.forwardable = 1;
419     hdb.flags.renewable = 1;
420     hdb.flags.proxiable = 1;
421     hdb.flags.postdate = 1;
422     /* XXX - AFS 3.4a creates krbtgt.REALMOFCELL as NOTGS+NOSEAL */
423     if (strcmp(ent->name, "krbtgt") == 0 &&
424 	(flags & (KAFNOTGS|KAFNOSEAL)) == (KAFNOTGS|KAFNOSEAL))
425 	flags &= ~(KAFNOTGS|KAFNOSEAL);
426 
427     hdb.flags.client = (flags & KAFNOTGS) == 0;
428     hdb.flags.server = (flags & KAFNOSEAL) == 0;
429 
430     ret = v5_prop(pd->context, NULL, &hdb, pd);
431     hdb_free_entry(pd->context, &hdb);
432     return ret;
433 }
434 
435 static int
436 ka_dump(struct prop_data *pd, const char *file)
437 {
438     struct ka_header header;
439     int i;
440     int fd = open(file, O_RDONLY);
441 
442     if(fd < 0)
443 	krb5_err(pd->context, 1, errno, "open(%s)", file);
444     read_block(pd->context, fd, 0, &header, sizeof(header));
445     if(header.version1 != header.version2)
446 	krb5_errx(pd->context, 1, "Version mismatch in header: %ld/%ld",
447 		  (long)ntohl(header.version1), (long)ntohl(header.version2));
448     if(ntohl(header.version1) != 5)
449 	krb5_errx(pd->context, 1, "Unknown database version %ld (expected 5)",
450 		  (long)ntohl(header.version1));
451     for(i = 0; i < ntohl(header.hashsize); i++){
452 	int32_t pos = ntohl(header.hash[i]);
453 	while(pos){
454 	    struct ka_entry ent;
455 	    read_block(pd->context, fd, pos, &ent, sizeof(ent));
456 	    ka_convert(pd, fd, &ent);
457 	    pos = ntohl(ent.next);
458 	}
459     }
460     return 0;
461 }
462 
463 
464 
465 struct getargs args[] = {
466     { "master-key", 'm', arg_string, &mkeyfile, "v5 master key file", "file" },
467     { "database", 'd',	arg_string, &database, "database", "file" },
468     { "source",   0,	arg_string, &source_type, "type of database to read",
469       "heimdal"
470       "|mit-dump"
471       "|krb4-dump"
472 #ifdef KRB4
473       "|krb4-db"
474 #endif
475       "|kaserver"
476     },
477 
478     { "v4-realm", 'r',  arg_string, &v4_realm, "v4 realm to use" },
479     { "cell",	  'c',  arg_string, &afs_cell, "name of AFS cell" },
480     { "kaspecials", 'S', arg_flag,   &kaspecials_flag, "dump KASPECIAL keys"},
481     { "keytab",   'k',	arg_string, &ktname, "keytab to use for authentication", "keytab" },
482     { "v5-realm", 'R',  arg_string, &local_realm, "v5 realm to use" },
483     { "decrypt",  'D',  arg_flag,   &decrypt_flag,   "decrypt keys" },
484     { "encrypt",  'E',  arg_flag,   &encrypt_flag,   "encrypt keys" },
485     { "stdout",	  'n',  arg_flag,   &to_stdout, "dump to stdout" },
486     { "verbose",  'v',	arg_flag, &verbose_flag },
487     { "version",   0,	arg_flag, &version_flag },
488     { "help",     'h',	arg_flag, &help_flag }
489 };
490 
491 static int num_args = sizeof(args) / sizeof(args[0]);
492 
493 static void
494 usage(int ret)
495 {
496     arg_printusage (args, num_args, NULL, "[host[:port]] ...");
497     exit (ret);
498 }
499 
500 static void
501 get_creds(krb5_context context, krb5_ccache *cache)
502 {
503     krb5_keytab keytab;
504     krb5_principal client;
505     krb5_error_code ret;
506     krb5_get_init_creds_opt init_opts;
507     krb5_preauthtype preauth = KRB5_PADATA_ENC_TIMESTAMP;
508     krb5_creds creds;
509 
510     ret = krb5_kt_register(context, &hdb_kt_ops);
511     if(ret) krb5_err(context, 1, ret, "krb5_kt_register");
512 
513     ret = krb5_kt_resolve(context, ktname, &keytab);
514     if(ret) krb5_err(context, 1, ret, "krb5_kt_resolve");
515 
516     ret = krb5_make_principal(context, &client, NULL,
517 			      "kadmin", HPROP_NAME, NULL);
518     if(ret) krb5_err(context, 1, ret, "krb5_make_principal");
519 
520     krb5_get_init_creds_opt_init(&init_opts);
521     krb5_get_init_creds_opt_set_preauth_list(&init_opts, &preauth, 1);
522 
523     ret = krb5_get_init_creds_keytab(context, &creds, client, keytab, 0, NULL, &init_opts);
524     if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds");
525 
526     ret = krb5_kt_close(context, keytab);
527     if(ret) krb5_err(context, 1, ret, "krb5_kt_close");
528 
529     ret = krb5_cc_gen_new(context, &krb5_mcc_ops, cache);
530     if(ret) krb5_err(context, 1, ret, "krb5_cc_gen_new");
531 
532     ret = krb5_cc_initialize(context, *cache, client);
533     if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize");
534 
535     krb5_free_principal(context, client);
536 
537     ret = krb5_cc_store_cred(context, *cache, &creds);
538     if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred");
539 
540     krb5_free_creds_contents(context, &creds);
541 }
542 
543 enum hprop_source {
544     HPROP_HEIMDAL = 1,
545     HPROP_KRB4_DB,
546     HPROP_KRB4_DUMP,
547     HPROP_KASERVER,
548     HPROP_MIT_DUMP
549 };
550 
551 #define IS_TYPE_V4(X) ((X) == HPROP_KRB4_DB || (X) == HPROP_KRB4_DUMP || (X) == HPROP_KASERVER)
552 
553 struct {
554     int type;
555     const char *name;
556 } types[] = {
557     { HPROP_HEIMDAL,	"heimdal" },
558     { HPROP_KRB4_DUMP,	"krb4-dump" },
559 #ifdef KRB4
560     { HPROP_KRB4_DB,	"krb4-db" },
561 #endif
562     { HPROP_KASERVER, 	"kaserver" },
563     { HPROP_MIT_DUMP,	"mit-dump" }
564 };
565 
566 static int
567 parse_source_type(const char *s)
568 {
569     int i;
570     for(i = 0; i < sizeof(types) / sizeof(types[0]); i++) {
571 	if(strstr(types[i].name, s) == types[i].name)
572 	    return types[i].type;
573     }
574     return 0;
575 }
576 
577 static void
578 iterate (krb5_context context,
579 	 const char *database,
580 	 HDB *db,
581 	 int type,
582 	 struct prop_data *pd)
583 {
584     int ret;
585 
586     switch(type) {
587     case HPROP_KRB4_DUMP:
588 	ret = v4_prop_dump(pd, database);
589 	break;
590 #ifdef KRB4
591     case HPROP_KRB4_DB:
592 	ret = kerb_db_iterate ((k_iter_proc_t)kdb_prop, pd);
593 	if(ret)
594 	    krb5_errx(context, 1, "kerb_db_iterate: %s",
595 		      krb_get_err_text(ret));
596 	break;
597 #endif /* KRB4 */
598     case HPROP_KASERVER:
599 	ret = ka_dump(pd, database);
600 	if(ret)
601 	    krb5_err(context, 1, ret, "ka_dump");
602 	break;
603     case HPROP_MIT_DUMP:
604 	ret = mit_prop_dump(pd, database);
605 	if (ret)
606 	    krb5_errx(context, 1, "mit_prop_dump: %s",
607 		      krb5_get_err_text(context, ret));
608 	break;
609     case HPROP_HEIMDAL:
610 	ret = hdb_foreach(context, db, HDB_F_DECRYPT, v5_prop, pd);
611 	if(ret)
612 	    krb5_err(context, 1, ret, "hdb_foreach");
613 	break;
614     }
615 }
616 
617 static int
618 dump_database (krb5_context context, int type,
619 	       const char *database, HDB *db)
620 {
621     krb5_error_code ret;
622     struct prop_data pd;
623     krb5_data data;
624 
625     pd.context      = context;
626     pd.auth_context = NULL;
627     pd.sock         = STDOUT_FILENO;
628 
629     iterate (context, database, db, type, &pd);
630     krb5_data_zero (&data);
631     ret = krb5_write_message (context, &pd.sock, &data);
632     if (ret)
633 	krb5_err(context, 1, ret, "krb5_write_message");
634 
635     return 0;
636 }
637 
638 static int
639 propagate_database (krb5_context context, int type,
640 		    const char *database,
641 		    HDB *db, krb5_ccache ccache,
642 		    int optind, int argc, char **argv)
643 {
644     krb5_principal server;
645     krb5_error_code ret;
646     int i;
647 
648     for(i = optind; i < argc; i++){
649 	krb5_auth_context auth_context;
650 	int fd;
651 	struct prop_data pd;
652 	krb5_data data;
653 
654 	char *port, portstr[NI_MAXSERV];
655 
656 	port = strchr(argv[i], ':');
657 	if(port == NULL) {
658 	    snprintf(portstr, sizeof(portstr), "%u",
659 		     ntohs(krb5_getportbyname (context, "hprop", "tcp",
660 					       HPROP_PORT)));
661 	    port = portstr;
662 	} else
663 	    *port++ = '\0';
664 
665 	fd = open_socket(context, argv[i], port);
666 	if(fd < 0) {
667 	    krb5_warn (context, errno, "connect %s", argv[i]);
668 	    continue;
669 	}
670 
671 	ret = krb5_sname_to_principal(context, argv[i],
672 				      HPROP_NAME, KRB5_NT_SRV_HST, &server);
673 	if(ret) {
674 	    krb5_warn(context, ret, "krb5_sname_to_principal(%s)", argv[i]);
675 	    close(fd);
676 	    continue;
677 	}
678 
679         if (local_realm) {
680             krb5_realm my_realm;
681             krb5_get_default_realm(context,&my_realm);
682 
683 	    free (*krb5_princ_realm(context, server));
684             krb5_princ_set_realm(context,server,&my_realm);
685         }
686 
687 	auth_context = NULL;
688 	ret = krb5_sendauth(context,
689 			    &auth_context,
690 			    &fd,
691 			    HPROP_VERSION,
692 			    NULL,
693 			    server,
694 			    AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
695 			    NULL, /* in_data */
696 			    NULL, /* in_creds */
697 			    ccache,
698 			    NULL,
699 			    NULL,
700 			    NULL);
701 
702 	krb5_free_principal(context, server);
703 
704 	if(ret) {
705 	    krb5_warn(context, ret, "krb5_sendauth");
706 	    close(fd);
707 	    continue;
708 	}
709 
710 	pd.context      = context;
711 	pd.auth_context = auth_context;
712 	pd.sock         = fd;
713 
714 	iterate (context, database, db, type, &pd);
715 
716 	krb5_data_zero (&data);
717 	ret = krb5_write_priv_message(context, auth_context, &fd, &data);
718 	if(ret)
719 	    krb5_warn(context, ret, "krb5_write_priv_message");
720 
721 	ret = krb5_read_priv_message(context, auth_context, &fd, &data);
722 	if(ret)
723 	    krb5_warn(context, ret, "krb5_read_priv_message");
724 	else
725 	    krb5_data_free (&data);
726 
727 	krb5_auth_con_free(context, auth_context);
728 	close(fd);
729     }
730     return 0;
731 }
732 
733 int
734 main(int argc, char **argv)
735 {
736     krb5_error_code ret;
737     krb5_context context;
738     krb5_ccache ccache = NULL;
739     HDB *db = NULL;
740     int optind = 0;
741 
742     int type = 0;
743 
744     setprogname(argv[0]);
745 
746     if(getarg(args, num_args, argc, argv, &optind))
747 	usage(1);
748 
749     if(help_flag)
750 	usage(0);
751 
752     if(version_flag){
753 	print_version(NULL);
754 	exit(0);
755     }
756 
757     ret = krb5_init_context(&context);
758     if(ret)
759 	exit(1);
760 
761     if(local_realm)
762 	krb5_set_default_realm(context, local_realm);
763 
764     if(v4_realm == NULL) {
765 	ret = krb5_get_default_realm(context, &v4_realm);
766 	if(ret)
767 	    krb5_err(context, 1, ret, "krb5_get_default_realm");
768     }
769 
770     if(afs_cell == NULL) {
771 	afs_cell = strdup(v4_realm);
772 	if(afs_cell == NULL)
773 	    krb5_errx(context, 1, "out of memory");
774 	strlwr(afs_cell);
775     }
776 
777 
778     if(encrypt_flag && decrypt_flag)
779 	krb5_errx(context, 1,
780 		  "only one of `--encrypt' and `--decrypt' is meaningful");
781 
782     if(source_type != NULL) {
783 	if(type != 0)
784 	    krb5_errx(context, 1, "more than one database type specified");
785 	type = parse_source_type(source_type);
786 	if(type == 0)
787 	    krb5_errx(context, 1, "unknown source type `%s'", source_type);
788     } else if(type == 0)
789 	type = HPROP_HEIMDAL;
790 
791     if(!to_stdout)
792 	get_creds(context, &ccache);
793 
794     if(decrypt_flag || encrypt_flag) {
795 	ret = hdb_read_master_key(context, mkeyfile, &mkey5);
796 	if(ret && ret != ENOENT)
797 	    krb5_err(context, 1, ret, "hdb_read_master_key");
798 	if(ret)
799 	    krb5_errx(context, 1, "No master key file found");
800     }
801 
802 #ifdef KRB4
803     if (IS_TYPE_V4(type)) {
804 	int e;
805 
806 	if (v4_realm == NULL) {
807 	    e = krb_get_lrealm(realm_buf, 1);
808 	    if(e)
809 		krb5_errx(context, 1, "krb_get_lrealm: %s",
810 			  krb_get_err_text(e));
811 	    v4_realm = realm_buf;
812 	}
813     }
814 #endif
815 
816     switch(type) {
817 #ifdef KRB4
818     case HPROP_KRB4_DB:
819 	if (database == NULL)
820 	    krb5_errx(context, 1, "no database specified");
821 	break;
822 #endif
823     case HPROP_KASERVER:
824 	if (database == NULL)
825 	    database = DEFAULT_DATABASE;
826 	ka_use_null_salt = krb5_config_get_bool_default(context, NULL, FALSE,
827 							"hprop",
828 							"afs_uses_null_salt",
829 							NULL);
830 
831 	break;
832     case HPROP_KRB4_DUMP:
833 	if (database == NULL)
834 	    krb5_errx(context, 1, "no dump file specified");
835 
836 	break;
837     case HPROP_MIT_DUMP:
838 	if (database == NULL)
839 	    krb5_errx(context, 1, "no dump file specified");
840 	break;
841     case HPROP_HEIMDAL:
842 	ret = hdb_create (context, &db, database);
843 	if(ret)
844 	    krb5_err(context, 1, ret, "hdb_create: %s", database);
845 	ret = db->open(context, db, O_RDONLY, 0);
846 	if(ret)
847 	    krb5_err(context, 1, ret, "db->open");
848 	break;
849     default:
850 	krb5_errx(context, 1, "unknown dump type `%d'", type);
851 	break;
852     }
853 
854     if (to_stdout)
855 	dump_database (context, type, database, db);
856     else
857 	propagate_database (context, type, database,
858 			    db, ccache, optind, argc, argv);
859 
860     if(ccache != NULL)
861 	krb5_cc_destroy(context, ccache);
862 
863     if(db != NULL)
864 	(*db->destroy)(context, db);
865 
866     krb5_free_context(context);
867     return 0;
868 }
869