Merge from vendor branch GPERF:
[dragonfly.git] / crypto / heimdal-0.6.3 / kdc / hprop.c
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 }