hprop.c revision 103423
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
36RCSID("$Id: hprop.c,v 1.70 2002/09/04 18:19:41 joda Exp $");
37
38static int version_flag;
39static int help_flag;
40static const char *ktname = HPROP_KEYTAB;
41static const char *database;
42static char *mkeyfile;
43static int to_stdout;
44static int verbose_flag;
45static int encrypt_flag;
46static int decrypt_flag;
47static hdb_master_key mkey5;
48
49static char *source_type;
50
51static char *afs_cell;
52static char *v4_realm;
53
54static int kaspecials_flag;
55static int ka_use_null_salt;
56
57static char *local_realm=NULL;
58
59static int
60open_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
95krb5_error_code
96v5_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
134static char realm_buf[REALM_SZ];
135
136static int
137kdb_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
169static time_t
170krb_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
203int
204v4_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' */
325static void
326read_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
343static int
344ka_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
435static int
436ka_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
465struct 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
491static int num_args = sizeof(args) / sizeof(args[0]);
492
493static void
494usage(int ret)
495{
496    arg_printusage (args, num_args, NULL, "[host[:port]] ...");
497    exit (ret);
498}
499
500static void
501get_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
543enum 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
553struct {
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
566static int
567parse_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
577static void
578iterate (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
617static int
618dump_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
638static int
639propagate_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
733int
734main(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