xref: /minix/crypto/external/bsd/heimdal/dist/kdc/hprop.c (revision 0a6a1f1d)
1 /*	$NetBSD: hprop.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #define KRB5_DEPRECATED /* uses v4 functions that will die */
37 
38 #include "hprop.h"
39 
40 static int version_flag;
41 static int help_flag;
42 static const char *ktname = HPROP_KEYTAB;
43 static const char *database;
44 static char *mkeyfile;
45 static int to_stdout;
46 static int verbose_flag;
47 static int encrypt_flag;
48 static int decrypt_flag;
49 static hdb_master_key mkey5;
50 
51 static char *source_type;
52 
53 static char *local_realm=NULL;
54 
55 static int
open_socket(krb5_context context,const char * hostname,const char * port)56 open_socket(krb5_context context, const char *hostname, const char *port)
57 {
58     struct addrinfo *ai, *a;
59     struct addrinfo hints;
60     int error;
61 
62     memset (&hints, 0, sizeof(hints));
63     hints.ai_socktype = SOCK_STREAM;
64     hints.ai_protocol = IPPROTO_TCP;
65 
66     error = getaddrinfo (hostname, port, &hints, &ai);
67     if (error) {
68 	warnx ("%s: %s", hostname, gai_strerror(error));
69 	return -1;
70     }
71 
72     for (a = ai; a != NULL; a = a->ai_next) {
73 	int s;
74 
75 	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
76 	if (s < 0)
77 	    continue;
78 	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
79 	    warn ("connect(%s)", hostname);
80 	    close (s);
81 	    continue;
82 	}
83 	freeaddrinfo (ai);
84 	return s;
85     }
86     warnx ("failed to contact %s", hostname);
87     freeaddrinfo (ai);
88     return -1;
89 }
90 
91 krb5_error_code
v5_prop(krb5_context context,HDB * db,hdb_entry_ex * entry,void * appdata)92 v5_prop(krb5_context context, HDB *db, hdb_entry_ex *entry, void *appdata)
93 {
94     krb5_error_code ret;
95     struct prop_data *pd = appdata;
96     krb5_data data;
97 
98     if(encrypt_flag) {
99 	ret = hdb_seal_keys_mkey(context, &entry->entry, mkey5);
100 	if (ret) {
101 	    krb5_warn(context, ret, "hdb_seal_keys_mkey");
102 	    return ret;
103 	}
104     }
105     if(decrypt_flag) {
106 	ret = hdb_unseal_keys_mkey(context, &entry->entry, mkey5);
107 	if (ret) {
108 	    krb5_warn(context, ret, "hdb_unseal_keys_mkey");
109 	    return ret;
110 	}
111     }
112 
113     ret = hdb_entry2value(context, &entry->entry, &data);
114     if(ret) {
115 	krb5_warn(context, ret, "hdb_entry2value");
116 	return ret;
117     }
118 
119     if(to_stdout)
120 	ret = krb5_write_message(context, &pd->sock, &data);
121     else
122 	ret = krb5_write_priv_message(context, pd->auth_context,
123 				      &pd->sock, &data);
124     krb5_data_free(&data);
125     return ret;
126 }
127 
128 struct getargs args[] = {
129     { "master-key", 'm', arg_string, &mkeyfile, "v5 master key file", "file" },
130     { "database", 'd',	arg_string, rk_UNCONST(&database), "database", "file" },
131     { "source",   0,	arg_string, &source_type, "type of database to read",
132       "heimdal"
133       "|mit-dump"
134     },
135 
136     { "keytab",   'k',	arg_string, rk_UNCONST(&ktname),
137       "keytab to use for authentication", "keytab" },
138     { "v5-realm", 'R',  arg_string, &local_realm, "v5 realm to use", NULL },
139     { "decrypt",  'D',  arg_flag,   &decrypt_flag,   "decrypt keys", NULL },
140     { "encrypt",  'E',  arg_flag,   &encrypt_flag,   "encrypt keys", NULL },
141     { "stdout",	  'n',  arg_flag,   &to_stdout, "dump to stdout", NULL },
142     { "verbose",  'v',	arg_flag, &verbose_flag, NULL, NULL },
143     { "version",   0,	arg_flag, &version_flag, NULL, NULL },
144     { "help",     'h',	arg_flag, &help_flag, NULL, NULL }
145 };
146 
147 static int num_args = sizeof(args) / sizeof(args[0]);
148 
149 static void
usage(int ret)150 usage(int ret)
151 {
152     arg_printusage (args, num_args, NULL, "[host[:port]] ...");
153     exit (ret);
154 }
155 
156 static void
get_creds(krb5_context context,krb5_ccache * cache)157 get_creds(krb5_context context, krb5_ccache *cache)
158 {
159     krb5_keytab keytab;
160     krb5_principal client;
161     krb5_error_code ret;
162     krb5_get_init_creds_opt *init_opts;
163     krb5_preauthtype preauth = KRB5_PADATA_ENC_TIMESTAMP;
164     krb5_creds creds;
165 
166     ret = krb5_kt_register(context, &hdb_kt_ops);
167     if(ret) krb5_err(context, 1, ret, "krb5_kt_register");
168 
169     ret = krb5_kt_resolve(context, ktname, &keytab);
170     if(ret) krb5_err(context, 1, ret, "krb5_kt_resolve");
171 
172     ret = krb5_make_principal(context, &client, NULL,
173 			      "kadmin", HPROP_NAME, NULL);
174     if(ret) krb5_err(context, 1, ret, "krb5_make_principal");
175 
176     ret = krb5_get_init_creds_opt_alloc(context, &init_opts);
177     if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
178     krb5_get_init_creds_opt_set_preauth_list(init_opts, &preauth, 1);
179 
180     ret = krb5_get_init_creds_keytab(context, &creds, client, keytab, 0, NULL, init_opts);
181     if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds");
182 
183     krb5_get_init_creds_opt_free(context, init_opts);
184 
185     ret = krb5_kt_close(context, keytab);
186     if(ret) krb5_err(context, 1, ret, "krb5_kt_close");
187 
188     ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, cache);
189     if(ret) krb5_err(context, 1, ret, "krb5_cc_new_unique");
190 
191     ret = krb5_cc_initialize(context, *cache, client);
192     if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize");
193 
194     krb5_free_principal(context, client);
195 
196     ret = krb5_cc_store_cred(context, *cache, &creds);
197     if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred");
198 
199     krb5_free_cred_contents(context, &creds);
200 }
201 
202 enum hprop_source {
203     HPROP_HEIMDAL = 1,
204     HPROP_MIT_DUMP
205 };
206 
207 struct {
208     int type;
209     const char *name;
210 } types[] = {
211     { HPROP_HEIMDAL,	"heimdal" },
212     { HPROP_MIT_DUMP,	"mit-dump" }
213 };
214 
215 static int
parse_source_type(const char * s)216 parse_source_type(const char *s)
217 {
218     size_t i;
219     for(i = 0; i < sizeof(types) / sizeof(types[0]); i++) {
220 	if(strstr(types[i].name, s) == types[i].name)
221 	    return types[i].type;
222     }
223     return 0;
224 }
225 
226 static int
iterate(krb5_context context,const char * database_name,HDB * db,int type,struct prop_data * pd)227 iterate (krb5_context context,
228 	 const char *database_name,
229 	 HDB *db,
230 	 int type,
231 	 struct prop_data *pd)
232 {
233     int ret;
234 
235     switch(type) {
236     case HPROP_MIT_DUMP:
237 	ret = mit_prop_dump(pd, database_name);
238 	if (ret)
239 	    krb5_warn(context, ret, "mit_prop_dump");
240 	break;
241     case HPROP_HEIMDAL:
242 	ret = hdb_foreach(context, db, HDB_F_DECRYPT, v5_prop, pd);
243 	if(ret)
244 	    krb5_warn(context, ret, "hdb_foreach");
245 	break;
246     default:
247 	krb5_errx(context, 1, "unknown prop type: %d", type);
248     }
249     return ret;
250 }
251 
252 static int
dump_database(krb5_context context,int type,const char * database_name,HDB * db)253 dump_database (krb5_context context, int type,
254 	       const char *database_name, HDB *db)
255 {
256     krb5_error_code ret;
257     struct prop_data pd;
258     krb5_data data;
259 
260     pd.context      = context;
261     pd.auth_context = NULL;
262     pd.sock         = STDOUT_FILENO;
263 
264     ret = iterate (context, database_name, db, type, &pd);
265     if (ret)
266 	krb5_errx(context, 1, "iterate failure");
267     krb5_data_zero (&data);
268     ret = krb5_write_message (context, &pd.sock, &data);
269     if (ret)
270 	krb5_err(context, 1, ret, "krb5_write_message");
271 
272     return 0;
273 }
274 
275 static int
propagate_database(krb5_context context,int type,const char * database_name,HDB * db,krb5_ccache ccache,int optidx,int argc,char ** argv)276 propagate_database (krb5_context context, int type,
277 		    const char *database_name,
278 		    HDB *db, krb5_ccache ccache,
279 		    int optidx, int argc, char **argv)
280 {
281     krb5_principal server;
282     krb5_error_code ret;
283     int i, failed = 0;
284 
285     for(i = optidx; i < argc; i++){
286 	krb5_auth_context auth_context;
287 	int fd;
288 	struct prop_data pd;
289 	krb5_data data;
290 
291 	char *port, portstr[NI_MAXSERV];
292 	char *host = argv[i];
293 
294 	port = strchr(host, ':');
295 	if(port == NULL) {
296 	    snprintf(portstr, sizeof(portstr), "%u",
297 		     ntohs(krb5_getportbyname (context, "hprop", "tcp",
298 					       HPROP_PORT)));
299 	    port = portstr;
300 	} else
301 	    *port++ = '\0';
302 
303 	fd = open_socket(context, host, port);
304 	if(fd < 0) {
305 	    failed++;
306 	    krb5_warn (context, errno, "connect %s", host);
307 	    continue;
308 	}
309 
310 	ret = krb5_sname_to_principal(context, argv[i],
311 				      HPROP_NAME, KRB5_NT_SRV_HST, &server);
312 	if(ret) {
313 	    failed++;
314 	    krb5_warn(context, ret, "krb5_sname_to_principal(%s)", host);
315 	    close(fd);
316 	    continue;
317 	}
318 
319         if (local_realm) {
320             krb5_realm my_realm;
321             krb5_get_default_realm(context,&my_realm);
322             krb5_principal_set_realm(context,server,my_realm);
323 	    krb5_xfree(my_realm);
324         }
325 
326 	auth_context = NULL;
327 	ret = krb5_sendauth(context,
328 			    &auth_context,
329 			    &fd,
330 			    HPROP_VERSION,
331 			    NULL,
332 			    server,
333 			    AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
334 			    NULL, /* in_data */
335 			    NULL, /* in_creds */
336 			    ccache,
337 			    NULL,
338 			    NULL,
339 			    NULL);
340 
341 	krb5_free_principal(context, server);
342 
343 	if(ret) {
344 	    failed++;
345 	    krb5_warn(context, ret, "krb5_sendauth (%s)", host);
346 	    close(fd);
347 	    goto next_host;
348 	}
349 
350 	pd.context      = context;
351 	pd.auth_context = auth_context;
352 	pd.sock         = fd;
353 
354 	ret = iterate (context, database_name, db, type, &pd);
355 	if (ret) {
356 	    krb5_warnx(context, "iterate to host %s failed", host);
357 	    failed++;
358 	    goto next_host;
359 	}
360 
361 	krb5_data_zero (&data);
362 	ret = krb5_write_priv_message(context, auth_context, &fd, &data);
363 	if(ret) {
364 	    krb5_warn(context, ret, "krb5_write_priv_message");
365 	    failed++;
366 	    goto next_host;
367 	}
368 
369 	ret = krb5_read_priv_message(context, auth_context, &fd, &data);
370 	if(ret) {
371 	    krb5_warn(context, ret, "krb5_read_priv_message: %s", host);
372 	    failed++;
373 	    goto next_host;
374 	} else
375 	    krb5_data_free (&data);
376 
377     next_host:
378 	krb5_auth_con_free(context, auth_context);
379 	close(fd);
380     }
381     if (failed)
382 	return 1;
383     return 0;
384 }
385 
386 int
main(int argc,char ** argv)387 main(int argc, char **argv)
388 {
389     krb5_error_code ret;
390     krb5_context context;
391     krb5_ccache ccache = NULL;
392     HDB *db = NULL;
393     int optidx = 0;
394 
395     int type, exit_code;
396 
397     setprogname(argv[0]);
398 
399     if(getarg(args, num_args, argc, argv, &optidx))
400 	usage(1);
401 
402     if(help_flag)
403 	usage(0);
404 
405     if(version_flag){
406 	print_version(NULL);
407 	exit(0);
408     }
409 
410     ret = krb5_init_context(&context);
411     if(ret)
412 	exit(1);
413 
414     /* We may be reading an old database encrypted with a DES master key. */
415     ret = krb5_allow_weak_crypto(context, 1);
416     if(ret)
417         krb5_err(context, 1, ret, "krb5_allow_weak_crypto");
418 
419     if(local_realm)
420 	krb5_set_default_realm(context, local_realm);
421 
422     if(encrypt_flag && decrypt_flag)
423 	krb5_errx(context, 1,
424 		  "only one of `--encrypt' and `--decrypt' is meaningful");
425 
426     if(source_type != NULL) {
427 	type = parse_source_type(source_type);
428 	if(type == 0)
429 	    krb5_errx(context, 1, "unknown source type `%s'", source_type);
430     } else
431 	type = HPROP_HEIMDAL;
432 
433     if(!to_stdout)
434 	get_creds(context, &ccache);
435 
436     if(decrypt_flag || encrypt_flag) {
437 	ret = hdb_read_master_key(context, mkeyfile, &mkey5);
438 	if(ret && ret != ENOENT)
439 	    krb5_err(context, 1, ret, "hdb_read_master_key");
440 	if(ret)
441 	    krb5_errx(context, 1, "No master key file found");
442     }
443 
444     switch(type) {
445     case HPROP_MIT_DUMP:
446 	if (database == NULL)
447 	    krb5_errx(context, 1, "no dump file specified");
448 	break;
449     case HPROP_HEIMDAL:
450 	ret = hdb_create (context, &db, database);
451 	if(ret)
452 	    krb5_err(context, 1, ret, "hdb_create: %s", database);
453 	ret = db->hdb_open(context, db, O_RDONLY, 0);
454 	if(ret)
455 	    krb5_err(context, 1, ret, "db->hdb_open");
456 	break;
457     default:
458 	krb5_errx(context, 1, "unknown dump type `%d'", type);
459 	break;
460     }
461 
462     if (to_stdout)
463 	exit_code = dump_database (context, type, database, db);
464     else
465 	exit_code = propagate_database (context, type, database,
466 					db, ccache, optidx, argc, argv);
467 
468     if(ccache != NULL)
469 	krb5_cc_destroy(context, ccache);
470 
471     if(db != NULL)
472 	(*db->hdb_destroy)(context, db);
473 
474     krb5_free_context(context);
475     return exit_code;
476 }
477