hprop.c revision 78527
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
36RCSID("$Id: hprop.c,v 1.62 2001/02/20 01:44:50 assar Exp $");
37
38static int version_flag;
39static int help_flag;
40static char *ktname = HPROP_KEYTAB;
41static 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 *realm;
53
54#ifdef KRB4
55static int v4_db;
56
57static des_cblock mkey4;
58static des_key_schedule msched4;
59
60#ifdef KASERVER_DB
61static int kaspecials_flag;
62static int ka_db;
63static int ka_use_null_salt;
64#endif
65#endif
66
67static char *local_realm=NULL;
68
69static int
70open_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
105krb5_error_code
106v5_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
144static char realm_buf[REALM_SZ];
145
146static int
147kdb_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
180static time_t
181krb_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
214int
215v4_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' */
343static void
344read_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
356static int
357ka_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
449static int
450ka_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: %ld/%ld",
461		  (long)ntohl(header.version1), (long)ntohl(header.version2));
462    if(ntohl(header.version1) != 5)
463	krb5_errx(pd->context, 1, "Unknown database version %ld (expected 5)",
464		  (long)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
482struct 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
516static int num_args = sizeof(args) / sizeof(args[0]);
517
518static void
519usage(int ret)
520{
521    arg_printusage (args, num_args, NULL, "host ...");
522    exit (ret);
523}
524
525static void
526get_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
564enum 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
574struct {
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
589static int
590parse_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
600static void
601iterate (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
643static int
644dump_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
665static int
666propagate_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
759static void
760v4_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
779int
780main(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    setprogname(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