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