155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2005 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
34233294Sstas#define KRB5_DEPRECATED /* uses v4 functions that will die */
35233294Sstas
3655682Smarkm#include "hprop.h"
3755682Smarkm
3855682Smarkmstatic int version_flag;
3955682Smarkmstatic int help_flag;
40102644Snectarstatic const char *ktname = HPROP_KEYTAB;
41102644Snectarstatic const char *database;
4255682Smarkmstatic char *mkeyfile;
4355682Smarkmstatic int to_stdout;
4455682Smarkmstatic int verbose_flag;
4555682Smarkmstatic int encrypt_flag;
4655682Smarkmstatic int decrypt_flag;
4772445Sassarstatic hdb_master_key mkey5;
4855682Smarkm
4972445Sassarstatic char *source_type;
5072445Sassar
5172445Sassarstatic char *local_realm=NULL;
5272445Sassar
5355682Smarkmstatic int
5472445Sassaropen_socket(krb5_context context, const char *hostname, const char *port)
5555682Smarkm{
5655682Smarkm    struct addrinfo *ai, *a;
5755682Smarkm    struct addrinfo hints;
5855682Smarkm    int error;
5955682Smarkm
6055682Smarkm    memset (&hints, 0, sizeof(hints));
6155682Smarkm    hints.ai_socktype = SOCK_STREAM;
6255682Smarkm    hints.ai_protocol = IPPROTO_TCP;
6355682Smarkm
6472445Sassar    error = getaddrinfo (hostname, port, &hints, &ai);
6555682Smarkm    if (error) {
6655682Smarkm	warnx ("%s: %s", hostname, gai_strerror(error));
6755682Smarkm	return -1;
6855682Smarkm    }
69233294Sstas
7055682Smarkm    for (a = ai; a != NULL; a = a->ai_next) {
7155682Smarkm	int s;
7255682Smarkm
7355682Smarkm	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
7455682Smarkm	if (s < 0)
7555682Smarkm	    continue;
7655682Smarkm	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
7755682Smarkm	    warn ("connect(%s)", hostname);
7855682Smarkm	    close (s);
7955682Smarkm	    continue;
8055682Smarkm	}
8155682Smarkm	freeaddrinfo (ai);
8255682Smarkm	return s;
8355682Smarkm    }
8455682Smarkm    warnx ("failed to contact %s", hostname);
8555682Smarkm    freeaddrinfo (ai);
8655682Smarkm    return -1;
8755682Smarkm}
8855682Smarkm
8972445Sassarkrb5_error_code
90178825Sdfrv5_prop(krb5_context context, HDB *db, hdb_entry_ex *entry, void *appdata)
9155682Smarkm{
9255682Smarkm    krb5_error_code ret;
9355682Smarkm    struct prop_data *pd = appdata;
9455682Smarkm    krb5_data data;
9555682Smarkm
9672445Sassar    if(encrypt_flag) {
97178825Sdfr	ret = hdb_seal_keys_mkey(context, &entry->entry, mkey5);
9872445Sassar	if (ret) {
9972445Sassar	    krb5_warn(context, ret, "hdb_seal_keys_mkey");
10072445Sassar	    return ret;
10172445Sassar	}
10272445Sassar    }
10372445Sassar    if(decrypt_flag) {
104178825Sdfr	ret = hdb_unseal_keys_mkey(context, &entry->entry, mkey5);
10572445Sassar	if (ret) {
10672445Sassar	    krb5_warn(context, ret, "hdb_unseal_keys_mkey");
10772445Sassar	    return ret;
10872445Sassar	}
109233294Sstas    }
11055682Smarkm
111178825Sdfr    ret = hdb_entry2value(context, &entry->entry, &data);
11272445Sassar    if(ret) {
11372445Sassar	krb5_warn(context, ret, "hdb_entry2value");
11472445Sassar	return ret;
11572445Sassar    }
11655682Smarkm
11755682Smarkm    if(to_stdout)
11872445Sassar	ret = krb5_write_message(context, &pd->sock, &data);
11955682Smarkm    else
120233294Sstas	ret = krb5_write_priv_message(context, pd->auth_context,
12172445Sassar				      &pd->sock, &data);
12255682Smarkm    krb5_data_free(&data);
12355682Smarkm    return ret;
12455682Smarkm}
12555682Smarkm
12655682Smarkmstruct getargs args[] = {
12755682Smarkm    { "master-key", 'm', arg_string, &mkeyfile, "v5 master key file", "file" },
128233294Sstas    { "database", 'd',	arg_string, rk_UNCONST(&database), "database", "file" },
129233294Sstas    { "source",   0,	arg_string, &source_type, "type of database to read",
13072445Sassar      "heimdal"
13172445Sassar      "|mit-dump"
13272445Sassar    },
133233294Sstas
134233294Sstas    { "keytab",   'k',	arg_string, rk_UNCONST(&ktname),
135233294Sstas      "keytab to use for authentication", "keytab" },
136233294Sstas    { "v5-realm", 'R',  arg_string, &local_realm, "v5 realm to use", NULL },
137233294Sstas    { "decrypt",  'D',  arg_flag,   &decrypt_flag,   "decrypt keys", NULL },
138233294Sstas    { "encrypt",  'E',  arg_flag,   &encrypt_flag,   "encrypt keys", NULL },
139233294Sstas    { "stdout",	  'n',  arg_flag,   &to_stdout, "dump to stdout", NULL },
140233294Sstas    { "verbose",  'v',	arg_flag, &verbose_flag, NULL, NULL },
141233294Sstas    { "version",   0,	arg_flag, &version_flag, NULL, NULL },
142233294Sstas    { "help",     'h',	arg_flag, &help_flag, NULL, NULL }
14355682Smarkm};
14455682Smarkm
14555682Smarkmstatic int num_args = sizeof(args) / sizeof(args[0]);
14655682Smarkm
14755682Smarkmstatic void
14855682Smarkmusage(int ret)
14955682Smarkm{
15090926Snectar    arg_printusage (args, num_args, NULL, "[host[:port]] ...");
15155682Smarkm    exit (ret);
15255682Smarkm}
15355682Smarkm
15455682Smarkmstatic void
15555682Smarkmget_creds(krb5_context context, krb5_ccache *cache)
15655682Smarkm{
15755682Smarkm    krb5_keytab keytab;
15855682Smarkm    krb5_principal client;
15955682Smarkm    krb5_error_code ret;
160178825Sdfr    krb5_get_init_creds_opt *init_opts;
16155682Smarkm    krb5_preauthtype preauth = KRB5_PADATA_ENC_TIMESTAMP;
16255682Smarkm    krb5_creds creds;
163233294Sstas
16472445Sassar    ret = krb5_kt_register(context, &hdb_kt_ops);
16572445Sassar    if(ret) krb5_err(context, 1, ret, "krb5_kt_register");
16672445Sassar
16755682Smarkm    ret = krb5_kt_resolve(context, ktname, &keytab);
16855682Smarkm    if(ret) krb5_err(context, 1, ret, "krb5_kt_resolve");
169233294Sstas
170233294Sstas    ret = krb5_make_principal(context, &client, NULL,
17155682Smarkm			      "kadmin", HPROP_NAME, NULL);
17255682Smarkm    if(ret) krb5_err(context, 1, ret, "krb5_make_principal");
17355682Smarkm
174178825Sdfr    ret = krb5_get_init_creds_opt_alloc(context, &init_opts);
175178825Sdfr    if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
176178825Sdfr    krb5_get_init_creds_opt_set_preauth_list(init_opts, &preauth, 1);
17755682Smarkm
178178825Sdfr    ret = krb5_get_init_creds_keytab(context, &creds, client, keytab, 0, NULL, init_opts);
17955682Smarkm    if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds");
180178825Sdfr
181178825Sdfr    krb5_get_init_creds_opt_free(context, init_opts);
182233294Sstas
18355682Smarkm    ret = krb5_kt_close(context, keytab);
18455682Smarkm    if(ret) krb5_err(context, 1, ret, "krb5_kt_close");
18555682Smarkm
186233294Sstas    ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, cache);
187233294Sstas    if(ret) krb5_err(context, 1, ret, "krb5_cc_new_unique");
188233294Sstas
18955682Smarkm    ret = krb5_cc_initialize(context, *cache, client);
19055682Smarkm    if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize");
19155682Smarkm
19290926Snectar    krb5_free_principal(context, client);
19390926Snectar
19455682Smarkm    ret = krb5_cc_store_cred(context, *cache, &creds);
19555682Smarkm    if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred");
19690926Snectar
197178825Sdfr    krb5_free_cred_contents(context, &creds);
19855682Smarkm}
19955682Smarkm
20072445Sassarenum hprop_source {
20172445Sassar    HPROP_HEIMDAL = 1,
20272445Sassar    HPROP_MIT_DUMP
20372445Sassar};
20472445Sassar
20572445Sassarstruct {
20672445Sassar    int type;
20772445Sassar    const char *name;
20872445Sassar} types[] = {
20972445Sassar    { HPROP_HEIMDAL,	"heimdal" },
21072445Sassar    { HPROP_MIT_DUMP,	"mit-dump" }
21172445Sassar};
21272445Sassar
21372445Sassarstatic int
21472445Sassarparse_source_type(const char *s)
21572445Sassar{
216233294Sstas    size_t i;
21772445Sassar    for(i = 0; i < sizeof(types) / sizeof(types[0]); i++) {
21872445Sassar	if(strstr(types[i].name, s) == types[i].name)
21972445Sassar	    return types[i].type;
22072445Sassar    }
22172445Sassar    return 0;
22272445Sassar}
22372445Sassar
224178825Sdfrstatic int
22555682Smarkmiterate (krb5_context context,
226178825Sdfr	 const char *database_name,
22755682Smarkm	 HDB *db,
22872445Sassar	 int type,
22955682Smarkm	 struct prop_data *pd)
23055682Smarkm{
23172445Sassar    int ret;
23272445Sassar
23372445Sassar    switch(type) {
23472445Sassar    case HPROP_MIT_DUMP:
235178825Sdfr	ret = mit_prop_dump(pd, database_name);
23672445Sassar	if (ret)
237233294Sstas	    krb5_warn(context, ret, "mit_prop_dump");
23872445Sassar	break;
23972445Sassar    case HPROP_HEIMDAL:
24072445Sassar	ret = hdb_foreach(context, db, HDB_F_DECRYPT, v5_prop, pd);
24155682Smarkm	if(ret)
242178825Sdfr	    krb5_warn(context, ret, "hdb_foreach");
24372445Sassar	break;
244178825Sdfr    default:
245178825Sdfr	krb5_errx(context, 1, "unknown prop type: %d", type);
24655682Smarkm    }
247178825Sdfr    return ret;
24855682Smarkm}
24955682Smarkm
25055682Smarkmstatic int
25172445Sassardump_database (krb5_context context, int type,
252178825Sdfr	       const char *database_name, HDB *db)
25355682Smarkm{
25472445Sassar    krb5_error_code ret;
25555682Smarkm    struct prop_data pd;
25672445Sassar    krb5_data data;
25755682Smarkm
25855682Smarkm    pd.context      = context;
25955682Smarkm    pd.auth_context = NULL;
26055682Smarkm    pd.sock         = STDOUT_FILENO;
261233294Sstas
262178825Sdfr    ret = iterate (context, database_name, db, type, &pd);
263178825Sdfr    if (ret)
264178825Sdfr	krb5_errx(context, 1, "iterate failure");
26572445Sassar    krb5_data_zero (&data);
26672445Sassar    ret = krb5_write_message (context, &pd.sock, &data);
26772445Sassar    if (ret)
26872445Sassar	krb5_err(context, 1, ret, "krb5_write_message");
26972445Sassar
27055682Smarkm    return 0;
27155682Smarkm}
27255682Smarkm
27355682Smarkmstatic int
27472445Sassarpropagate_database (krb5_context context, int type,
275233294Sstas		    const char *database_name,
27655682Smarkm		    HDB *db, krb5_ccache ccache,
277178825Sdfr		    int optidx, int argc, char **argv)
27855682Smarkm{
27955682Smarkm    krb5_principal server;
28055682Smarkm    krb5_error_code ret;
281178825Sdfr    int i, failed = 0;
28255682Smarkm
283178825Sdfr    for(i = optidx; i < argc; i++){
28455682Smarkm	krb5_auth_context auth_context;
28555682Smarkm	int fd;
28655682Smarkm	struct prop_data pd;
28755682Smarkm	krb5_data data;
28855682Smarkm
28972445Sassar	char *port, portstr[NI_MAXSERV];
290178825Sdfr	char *host = argv[i];
291178825Sdfr
292178825Sdfr	port = strchr(host, ':');
29372445Sassar	if(port == NULL) {
294233294Sstas	    snprintf(portstr, sizeof(portstr), "%u",
295233294Sstas		     ntohs(krb5_getportbyname (context, "hprop", "tcp",
29672445Sassar					       HPROP_PORT)));
29772445Sassar	    port = portstr;
29872445Sassar	} else
29972445Sassar	    *port++ = '\0';
30072445Sassar
301178825Sdfr	fd = open_socket(context, host, port);
30255682Smarkm	if(fd < 0) {
303178825Sdfr	    failed++;
304178825Sdfr	    krb5_warn (context, errno, "connect %s", host);
30555682Smarkm	    continue;
30655682Smarkm	}
30755682Smarkm
30855682Smarkm	ret = krb5_sname_to_principal(context, argv[i],
30955682Smarkm				      HPROP_NAME, KRB5_NT_SRV_HST, &server);
31055682Smarkm	if(ret) {
311178825Sdfr	    failed++;
312178825Sdfr	    krb5_warn(context, ret, "krb5_sname_to_principal(%s)", host);
31355682Smarkm	    close(fd);
31455682Smarkm	    continue;
31555682Smarkm	}
31672445Sassar
31772445Sassar        if (local_realm) {
31872445Sassar            krb5_realm my_realm;
31972445Sassar            krb5_get_default_realm(context,&my_realm);
320233294Sstas            krb5_principal_set_realm(context,server,my_realm);
321233294Sstas	    krb5_xfree(my_realm);
322233294Sstas        }
32372445Sassar
32455682Smarkm	auth_context = NULL;
32555682Smarkm	ret = krb5_sendauth(context,
32655682Smarkm			    &auth_context,
32755682Smarkm			    &fd,
32855682Smarkm			    HPROP_VERSION,
32955682Smarkm			    NULL,
33055682Smarkm			    server,
331103423Snectar			    AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
33255682Smarkm			    NULL, /* in_data */
33355682Smarkm			    NULL, /* in_creds */
33455682Smarkm			    ccache,
33555682Smarkm			    NULL,
33655682Smarkm			    NULL,
33755682Smarkm			    NULL);
33855682Smarkm
33990926Snectar	krb5_free_principal(context, server);
34090926Snectar
34155682Smarkm	if(ret) {
342178825Sdfr	    failed++;
343178825Sdfr	    krb5_warn(context, ret, "krb5_sendauth (%s)", host);
34455682Smarkm	    close(fd);
345178825Sdfr	    goto next_host;
34655682Smarkm	}
347233294Sstas
34855682Smarkm	pd.context      = context;
34955682Smarkm	pd.auth_context = auth_context;
35055682Smarkm	pd.sock         = fd;
35155682Smarkm
352178825Sdfr	ret = iterate (context, database_name, db, type, &pd);
353178825Sdfr	if (ret) {
354178825Sdfr	    krb5_warnx(context, "iterate to host %s failed", host);
355178825Sdfr	    failed++;
356178825Sdfr	    goto next_host;
357178825Sdfr	}
35855682Smarkm
35972445Sassar	krb5_data_zero (&data);
36072445Sassar	ret = krb5_write_priv_message(context, auth_context, &fd, &data);
361178825Sdfr	if(ret) {
36272445Sassar	    krb5_warn(context, ret, "krb5_write_priv_message");
363178825Sdfr	    failed++;
364178825Sdfr	    goto next_host;
365178825Sdfr	}
36655682Smarkm
36772445Sassar	ret = krb5_read_priv_message(context, auth_context, &fd, &data);
368178825Sdfr	if(ret) {
369178825Sdfr	    krb5_warn(context, ret, "krb5_read_priv_message: %s", host);
370178825Sdfr	    failed++;
371178825Sdfr	    goto next_host;
372178825Sdfr	} else
37355682Smarkm	    krb5_data_free (&data);
374233294Sstas
375178825Sdfr    next_host:
37655682Smarkm	krb5_auth_con_free(context, auth_context);
37755682Smarkm	close(fd);
37855682Smarkm    }
379178825Sdfr    if (failed)
380178825Sdfr	return 1;
38155682Smarkm    return 0;
38255682Smarkm}
38355682Smarkm
38455682Smarkmint
38555682Smarkmmain(int argc, char **argv)
38655682Smarkm{
38755682Smarkm    krb5_error_code ret;
38855682Smarkm    krb5_context context;
38990926Snectar    krb5_ccache ccache = NULL;
39090926Snectar    HDB *db = NULL;
391178825Sdfr    int optidx = 0;
39255682Smarkm
393178825Sdfr    int type, exit_code;
39472445Sassar
39578527Sassar    setprogname(argv[0]);
39655682Smarkm
397178825Sdfr    if(getarg(args, num_args, argc, argv, &optidx))
39855682Smarkm	usage(1);
39955682Smarkm
40055682Smarkm    if(help_flag)
40155682Smarkm	usage(0);
402233294Sstas
40355682Smarkm    if(version_flag){
40455682Smarkm	print_version(NULL);
40555682Smarkm	exit(0);
40655682Smarkm    }
40755682Smarkm
40855682Smarkm    ret = krb5_init_context(&context);
40955682Smarkm    if(ret)
41055682Smarkm	exit(1);
41155682Smarkm
412233294Sstas    /* We may be reading an old database encrypted with a DES master key. */
413233294Sstas    ret = krb5_allow_weak_crypto(context, 1);
414233294Sstas    if(ret)
415233294Sstas        krb5_err(context, 1, ret, "krb5_allow_weak_crypto");
416233294Sstas
41772445Sassar    if(local_realm)
41872445Sassar	krb5_set_default_realm(context, local_realm);
41972445Sassar
42055682Smarkm    if(encrypt_flag && decrypt_flag)
421233294Sstas	krb5_errx(context, 1,
42272445Sassar		  "only one of `--encrypt' and `--decrypt' is meaningful");
42355682Smarkm
42472445Sassar    if(source_type != NULL) {
42572445Sassar	type = parse_source_type(source_type);
42672445Sassar	if(type == 0)
42772445Sassar	    krb5_errx(context, 1, "unknown source type `%s'", source_type);
428178825Sdfr    } else
42972445Sassar	type = HPROP_HEIMDAL;
43072445Sassar
43155682Smarkm    if(!to_stdout)
43255682Smarkm	get_creds(context, &ccache);
433233294Sstas
43472445Sassar    if(decrypt_flag || encrypt_flag) {
43572445Sassar	ret = hdb_read_master_key(context, mkeyfile, &mkey5);
43672445Sassar	if(ret && ret != ENOENT)
43772445Sassar	    krb5_err(context, 1, ret, "hdb_read_master_key");
43872445Sassar	if(ret)
43955682Smarkm	    krb5_errx(context, 1, "No master key file found");
44055682Smarkm    }
44155682Smarkm
44272445Sassar    switch(type) {
44372445Sassar    case HPROP_MIT_DUMP:
44472445Sassar	if (database == NULL)
44572445Sassar	    krb5_errx(context, 1, "no dump file specified");
44672445Sassar	break;
44772445Sassar    case HPROP_HEIMDAL:
44872445Sassar	ret = hdb_create (context, &db, database);
44972445Sassar	if(ret)
45072445Sassar	    krb5_err(context, 1, ret, "hdb_create: %s", database);
451178825Sdfr	ret = db->hdb_open(context, db, O_RDONLY, 0);
45272445Sassar	if(ret)
453178825Sdfr	    krb5_err(context, 1, ret, "db->hdb_open");
45472445Sassar	break;
45572445Sassar    default:
45672445Sassar	krb5_errx(context, 1, "unknown dump type `%d'", type);
45772445Sassar	break;
45872445Sassar    }
45955682Smarkm
46055682Smarkm    if (to_stdout)
461178825Sdfr	exit_code = dump_database (context, type, database, db);
46255682Smarkm    else
463233294Sstas	exit_code = propagate_database (context, type, database,
464178825Sdfr					db, ccache, optidx, argc, argv);
46590926Snectar
46690926Snectar    if(ccache != NULL)
46790926Snectar	krb5_cc_destroy(context, ccache);
468233294Sstas
46990926Snectar    if(db != NULL)
470178825Sdfr	(*db->hdb_destroy)(context, db);
47190926Snectar
47290926Snectar    krb5_free_context(context);
473178825Sdfr    return exit_code;
47455682Smarkm}
475