xref: /titanic_44/usr/src/cmd/krb5/kadmin/dbutil/kdb5_util.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
10  *
11  *	Openvision retains the copyright to derivative works of
12  *	this source code.  Do *NOT* create a derivative of this
13  *	source code before consulting with your legal department.
14  *	Do *NOT* integrate *ANY* of this source code into another
15  *	product before consulting with your legal department.
16  *
17  *	For further information, read the top-level Openvision
18  *	copyright which is contained in the top-level MIT Kerberos
19  *	copyright.
20  *
21  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
22  *
23  */
24 
25 
26 /*
27  * admin/edit/kdb5_edit.c
28  *
29  * (C) Copyright 1990,1991, 1996 by the Massachusetts Institute of Technology.
30  * All Rights Reserved.
31  *
32  * Export of this software from the United States of America may
33  *   require a specific license from the United States Government.
34  *   It is the responsibility of any person or organization contemplating
35  *   export to obtain such a license before exporting.
36  *
37  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
38  * distribute this software and its documentation for any purpose and
39  * without fee is hereby granted, provided that the above copyright
40  * notice appear in all copies and that both that copyright notice and
41  * this permission notice appear in supporting documentation, and that
42  * the name of M.I.T. not be used in advertising or publicity pertaining
43  * to distribution of the software without specific, written prior
44  * permission.  Furthermore if you modify this software you must label
45  * your software as modified software and not distribute it in such a
46  * fashion that it might be confused with the original M.I.T. software.
47  * M.I.T. makes no representations about the suitability of
48  * this software for any purpose.  It is provided "as is" without express
49  * or implied warranty.
50  *
51  *
52  * Edit a KDC database.
53  */
54 
55 /*
56  *  Yes, I know this is a hack, but we need admin.h without including the
57  *  rpc.h header. Additionally, our rpc.h header brings in
58  *  a des.h header which causes other problems.
59  */
60 #define	_RPC_RPC_H
61 
62 #include <stdio.h>
63 #define KDB5_DISPATCH
64 #define KRB5_KDB5_DBM__
65 #include <k5-int.h>
66 /* #define these to avoid an indirection function; for future implementations,
67    these may be redirected from a dispatch table/routine */
68 #define krb5_dbm_db_set_name krb5_db_set_name
69 #define krb5_dbm_db_set_nonblocking krb5_db_set_nonblocking
70 #define krb5_dbm_db_init krb5_db_init
71 #define krb5_dbm_db_get_age krb5_db_get_age
72 #define krb5_dbm_db_create krb5_db_create
73 #define krb5_dbm_db_rename krb5_db_rename
74 #define krb5_dbm_db_get_principal krb5_db_get_principal
75 #define krb5_dbm_db_free_principal krb5_db_free_principal
76 #define krb5_dbm_db_put_principal krb5_db_put_principal
77 #define krb5_dbm_db_delete_principal krb5_db_delete_principal
78 #define krb5_dbm_db_lock krb5_db_lock
79 #define krb5_dbm_db_unlock krb5_db_unlock
80 #define krb5_dbm_db_set_lockmode krb5_db_set_lockmode
81 #define krb5_dbm_db_close_database krb5_db_close_database
82 #define krb5_dbm_db_open_database krb5_db_open_database
83 
84 #include <kadm5/admin.h>
85 #include <rpc/types.h>
86 #include <rpc/xdr.h>
87 #include <kadm5/adb.h>
88 #include <time.h>
89 #include <libintl.h>
90 #include <locale.h>
91 #include "kdb5_util.h"
92 
93 char	*Err_no_master_msg = "Master key not entered!\n";
94 char	*Err_no_database = "Database not currently opened!\n";
95 
96 /*
97  * XXX Ick, ick, ick.  These global variables shouldn't be global....
98  */
99 char *mkey_password = 0;
100 
101 /*
102  * I can't figure out any way for this not to be global, given how ss
103  * works.
104  */
105 
106 int exit_status = 0;
107 krb5_context util_context;
108 osa_adb_policy_t policy_db;
109 kadm5_config_params global_params;
110 
111 usage()
112 {
113 	fprintf(stderr, "%s: "
114 	   "kdb5_util cmd [-r realm] [-d dbname] [-k mkeytype] [-M mkeyname]\n"
115 	    "\t         [-f] [stashfile] [-P password] [-m ] [cmd options]\n"
116 	    "\tcreate	[-s]\n"
117 	    "\tdestroy	\n"
118 	    "\tstash	\n"
119 	    "\tdump	[-old] [-ov] [-b6] [-verbose] [filename	[princs...]]\n"
120 	    "\tload	[-old] [-ov] [-b6] [-verbose] [-update] filename\n"
121 #ifdef SUNWOFF
122 	    "\tload_v4	[-t] [-n] [-v] [-K] [-s stashfile] inputfile\n"
123 #endif
124 	    "\tark	[-e etype_list] principal\n",
125 	    gettext("Usage"));
126      exit(1);
127 }
128 
129 krb5_keyblock master_key;
130 extern krb5_principal master_princ;
131 krb5_db_entry master_entry;
132 int	valid_master_key = 0;
133 int     close_policy_db = 0;
134 
135 char *progname;
136 krb5_boolean manual_mkey = FALSE;
137 krb5_boolean dbactive = FALSE;
138 
139 int kdb5_create(int, char **);
140 int kdb5_destroy(int, char **);
141 int kdb5_stash(int, char **);
142 int dump_db(int, char **);
143 int load_db(int, char **);
144 int open_db_and_mkey();
145 int add_random_key(int, char **);
146 
147 typedef int (*cmd_func)(int, char **);
148 
149 struct _cmd_table {
150      char *name;
151      cmd_func func;
152      int opendb;
153 } cmd_table[] = {
154      "create", kdb5_create, 0,
155      "destroy", kdb5_destroy, 1,
156      "stash", kdb5_stash, 1,
157      "dump", dump_db, 1,
158      "load", load_db, 0,
159      "ark", add_random_key, 1,
160      NULL, NULL, 0,
161 };
162 
163 struct _cmd_table *
164 cmd_lookup(name)
165    char *name;
166 {
167      struct _cmd_table *cmd = cmd_table;
168 
169      while (cmd->name) {
170 	  if (strcmp(cmd->name, name) == 0)
171 			return (cmd);
172 	  else
173 	       cmd++;
174      }
175 
176 	return (NULL);
177 }
178 
179 #define ARG_VAL (--argc > 0 ? (optarg = *(++argv)) : (char *)(usage(), NULL))
180 
181 int
182 main(argc, argv)
183     int argc;
184     char *argv[];
185 {
186     struct _cmd_table *cmd = NULL;
187     char *optarg, **cmd_argv;
188     int cmd_argc;
189     krb5_error_code retval;
190 
191 	(void) setlocale(LC_ALL, "");
192 
193 #if !defined(TEXT_DOMAIN)  /* Should be defined by cc -D */
194 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
195 #endif
196 
197 	(void) textdomain(TEXT_DOMAIN);
198 
199 	Err_no_master_msg = gettext("Master key not entered!\n");
200 	Err_no_database = gettext("Database not currently opened!\n");
201 
202     retval = krb5_init_context(&util_context);
203     if (retval) {
204 	    com_err (progname, retval,
205 		gettext("while initializing Kerberos code"));
206 	    exit(1);
207     }
208 	progname = (strrchr(argv[0], '/') ?
209 		    strrchr(argv[0], '/') + 1 : argv[0]);
210 
211     cmd_argv = (char **) malloc(sizeof(char *)*argc);
212     if (cmd_argv == NULL) {
213 		com_err(progname, ENOMEM,
214 		    gettext("while creating sub-command arguments"));
215 	 exit(1);
216     }
217     memset(cmd_argv, 0, sizeof(char *)*argc);
218     cmd_argc = 1;
219 
220 	argv++;
221 	argc--;
222     while (*argv) {
223        if (strcmp(*argv, "-P") == 0 && ARG_VAL) {
224 	    mkey_password = optarg;
225 	    manual_mkey = TRUE;
226        } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) {
227 	    global_params.dbname = optarg;
228 	    global_params.mask |= KADM5_CONFIG_DBNAME;
229        } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) {
230 	    global_params.realm = optarg;
231 	    global_params.mask |= KADM5_CONFIG_REALM;
232 	    /* not sure this is really necessary */
233 	    if ((retval = krb5_set_default_realm(util_context,
234 						 global_params.realm))) {
235 				com_err(progname, retval,
236 					gettext("while setting default "
237 						"realm name"));
238 		 exit(1);
239 	    }
240        } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) {
241 			if (krb5_string_to_enctype(optarg,
242 						    &global_params.enctype))
243 				com_err(argv[0], 0,
244 					gettext("%s is an invalid enctype"),
245 					optarg);
246 	    else
247 		 global_params.mask |= KADM5_CONFIG_ENCTYPE;
248        } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) {
249 	    global_params.mkey_name = optarg;
250 	    global_params.mask |= KADM5_CONFIG_MKEY_NAME;
251        } else if (((strcmp(*argv, "-sf") == 0)
252 		/* SUNWresync121 - carry the old -f forward too */
253 		|| (strcmp(*argv, "-f") == 0)) && ARG_VAL) {
254 	    global_params.stash_file = optarg;
255 	    global_params.mask |= KADM5_CONFIG_STASH_FILE;
256        } else if (strcmp(*argv, "-m") == 0) {
257 	    manual_mkey = TRUE;
258 	    global_params.mkey_from_kbd = 1;
259 	    global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
260        } else if (cmd_lookup(*argv) != NULL) {
261 	    if (cmd_argv[0] == NULL)
262 		 cmd_argv[0] = *argv;
263 	    else
264 		 usage();
265        } else {
266 	    cmd_argv[cmd_argc++] = *argv;
267        }
268 		argv++;
269 		argc--;
270     }
271 
272     if (cmd_argv[0] == NULL)
273 	 usage();
274 
275     if (retval = kadm5_get_config_params(util_context, NULL, NULL,
276 					 &global_params, &global_params)) {
277 		com_err(argv[0], retval,
278 		    gettext("while retreiving configuration parameters"));
279 	 exit(1);
280     }
281     /*
282      * Dump creates files which should not be world-readable.  It is
283      * easiest to do a single umask call here.
284      */
285     (void) umask(077);
286 
287     (void) memset(&master_key, 0, sizeof (krb5_keyblock));
288 
289     if ((global_params.enctype != ENCTYPE_UNKNOWN) &&
290 	(!valid_enctype(global_params.enctype))) {
291 	com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP,
292 	    gettext("while setting up enctype %d"), global_params.enctype);
293     }
294 
295     cmd = cmd_lookup(cmd_argv[0]);
296     if (cmd->opendb && open_db_and_mkey())
297 		return (exit_status);
298 
299 	if (global_params.iprop_enabled == TRUE)
300 		ulog_set_role(util_context, IPROP_MASTER);
301 	else
302 		ulog_set_role(util_context, IPROP_NULL);
303 
304     (*cmd->func)(cmd_argc, cmd_argv);
305 
306     if(close_policy_db) {
307          (void) osa_adb_close_policy(policy_db);
308     }
309     kadm5_free_config_params(util_context, &global_params);
310     krb5_free_context(util_context);
311     return (exit_status);
312 }
313 
314 #if 0
315 /*
316  * This function is no longer used in kdb5_util (and it would no
317  * longer work, anyway).
318  */
319 void
320 set_dbname(argc, argv)
321     int argc;
322     char *argv[];
323 {
324     krb5_error_code retval;
325 
326     if (argc < 3) {
327 		com_err(argv[0], 0, gettext("Too few arguments"));
328 		com_err(argv[0], 0, gettext("Usage: %s dbpathname realmname"),
329 			argv[0]);
330 	exit_status++;
331 	return;
332     }
333     if (dbactive) {
334 		if ((retval = krb5_db_fini(util_context)) &&
335 				retval != KRB5_KDB_DBNOTINITED) {
336 			com_err(argv[0], retval,
337 				gettext("while closing previous database"));
338 	    exit_status++;
339 	    return;
340 	}
341 	if (valid_master_key) {
342 	    krb5_free_keyblock_contents(util_context, &master_key);
343 	    valid_master_key = 0;
344 	}
345 	krb5_free_principal(util_context, master_princ);
346 	dbactive = FALSE;
347     }
348 
349     (void) set_dbname_help(argv[0], argv[1]);
350 }
351 
352 #endif
353 
354 /*
355  * open_db_and_mkey: Opens the KDC and policy database, and sets the
356  * global master_* variables.  Sets dbactive to TRUE if the databases
357  * are opened, and valid_master_key to 1 if the global master
358  * variables are set properly.  Returns 0 on success, and 1 on
359  * failure, but it is not considered a failure if the master key
360  * cannot be fetched (the master key stash file may not exist when the
361  * program is run).
362  */
363 int
364 open_db_and_mkey()
365 {
366     krb5_error_code retval;
367     int nentries;
368     krb5_boolean more;
369     krb5_data scratch, pwd, seed;
370 
371     dbactive = FALSE;
372     valid_master_key = 0;
373 
374     if ((retval = krb5_db_set_name(util_context, global_params.dbname))) {
375 		com_err(progname, retval,
376 		    gettext("while setting active database to '%s'"),
377 		global_params.dbname);
378 	exit_status++;
379 	return(1);
380     }
381     if ((retval = krb5_db_init(util_context))) {
382 		com_err(progname, retval,
383 		    gettext("while initializing database"));
384 	exit_status++;
385 	return(1);
386     }
387     if (retval = osa_adb_open_policy(&policy_db, &global_params)) {
388 		com_err(progname, retval,
389 		    gettext("opening policy database"));
390 	 exit_status++;
391 	 return (1);
392     }
393    /* assemble & parse the master key name */
394 
395     if ((retval = krb5_db_setup_mkey_name(util_context,
396 					  global_params.mkey_name,
397 					  global_params.realm,
398 					  0, &master_princ))) {
399 		com_err(progname, retval,
400 		    gettext("while setting up master key name"));
401 	exit_status++;
402 	return(1);
403     }
404     nentries = 1;
405     if ((retval = krb5_db_get_principal(util_context, master_princ,
406 					&master_entry, &nentries, &more))) {
407 		com_err(progname, retval,
408 		    gettext("while retrieving master entry"));
409 	exit_status++;
410 	(void) krb5_db_fini(util_context);
411 	return(1);
412     } else if (more) {
413 	com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
414 		    gettext("while retrieving master entry"));
415 	exit_status++;
416 	(void) krb5_db_fini(util_context);
417 	return(1);
418     } else if (!nentries) {
419 		com_err(progname, KRB5_KDB_NOENTRY,
420 		    gettext("while retrieving master entry"));
421 	exit_status++;
422 	(void) krb5_db_fini(util_context);
423 	return(1);
424     }
425     krb5_db_free_principal(util_context, &master_entry, nentries);
426 
427     /* the databases are now open, and the master principal exists */
428     dbactive = TRUE;
429 
430     if (mkey_password) {
431 	pwd.data = mkey_password;
432 	pwd.length = strlen(mkey_password);
433 	retval = krb5_principal2salt(util_context,
434 			    master_princ, &scratch);
435 	if (retval) {
436 		com_err(progname, retval,
437 		    gettext("while calculated master key salt"));
438 		return(1);
439 	}
440 	/* If no encryption type is set, use the default */
441 	if (global_params.enctype == ENCTYPE_UNKNOWN) {
442 	    global_params.enctype = DEFAULT_KDC_ENCTYPE;
443 	    if (!valid_enctype(global_params.enctype))
444 		com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
445 			gettext("while setting up enctype %d"),
446 			global_params.enctype);
447 	}
448 
449 	retval = krb5_c_string_to_key(util_context,
450 				global_params.enctype,
451 				&pwd, &scratch, &master_key);
452 	if (retval) {
453 	    com_err(progname, retval,
454 		gettext("while transforming master key from password"));
455 	    return(1);
456 	}
457 	free(scratch.data);
458 	mkey_password = 0;
459     } else if ((retval = krb5_db_fetch_mkey(util_context, master_princ,
460 					    global_params.enctype,
461 					    manual_mkey, FALSE,
462 					    global_params.stash_file,
463 					    0, &master_key))) {
464 	com_err(progname, retval,
465 	    gettext("while reading master key"));
466 	com_err(progname, 0,
467 	    gettext("Warning: proceeding without master key"));
468 	exit_status++;
469 	return(0);
470     }
471     if ((retval = krb5_db_verify_master_key(util_context, master_princ,
472 		&master_key))) {
473 	com_err(progname, retval,
474 		gettext("while verifying master key"));
475 	exit_status++;
476 	krb5_free_keyblock_contents(util_context, &master_key);
477 	return(1);
478     }
479 
480     seed.length = master_key.length;
481     seed.data = (char *)master_key.contents;
482 
483     if ((retval = krb5_c_random_seed(util_context, &seed))) {
484 	com_err(progname, retval,
485 		gettext("while initializing random key generator"));
486 	exit_status++;
487 	krb5_free_keyblock_contents(util_context, &master_key);
488 	return(1);
489     }
490 
491     valid_master_key = 1;
492     dbactive = TRUE;
493 	return (0);
494 }
495 
496 #ifdef HAVE_GETCWD
497 #undef getwd
498 #endif
499 
500 int
501 quit()
502 {
503     krb5_error_code retval;
504     static krb5_boolean finished = 0;
505 
506     if (finished)
507 		return (0);
508     retval = krb5_db_fini(util_context);
509     krb5_free_keyblock_contents(util_context, &master_key);
510     finished = TRUE;
511     krb5_free_context(util_context);
512     if (retval && retval != KRB5_KDB_DBNOTINITED) {
513 		com_err(progname, retval, gettext("while closing database"));
514 	exit_status++;
515 	return (1);
516     }
517     return (0);
518 }
519 
520 int
521 add_random_key(argc, argv)
522     int argc;
523     char **argv;
524 {
525     krb5_error_code ret;
526     krb5_principal princ;
527     krb5_db_entry dbent;
528     int n, i;
529     krb5_boolean more;
530     krb5_timestamp now;
531 
532     krb5_key_salt_tuple *keysalts = NULL;
533     krb5_int32 num_keysalts = 0;
534 
535     int free_keysalts;
536     char *me = argv[0];
537     char *ks_str = NULL;
538     char *pr_str;
539 
540     if (argc < 2)
541 	usage();
542     for (argv++, argc--; *argv; argv++, argc--) {
543 	if (!strcmp(*argv, "-e")) {
544 	    argv++; argc--;
545 	    ks_str = *argv;
546 	    continue;
547 	} else
548 	    break;
549     }
550     if (argc < 1)
551 	usage();
552     pr_str = *argv;
553     ret = krb5_parse_name(util_context, pr_str, &princ);
554     if (ret) {
555 	com_err(me, ret, gettext("while parsing principal name %s"), pr_str);
556 	return 1;
557     }
558     n = 1;
559     ret = krb5_db_get_principal(util_context, princ, &dbent,
560 				&n, &more);
561     if (ret) {
562 	com_err(me, ret, gettext("while fetching principal %s"), pr_str);
563 	return 1;
564     }
565     if (n != 1) {
566 	fprintf(stderr, gettext("principal %s not found\n"), pr_str);
567 	return 1;
568     }
569     if (more) {
570 	fprintf(stderr, gettext("principal %s not unique\n"), pr_str);
571 	krb5_dbe_free_contents(util_context, &dbent);
572 	return 1;
573     }
574     ret = krb5_string_to_keysalts(ks_str,
575 				  ", \t", ":.-", 0,
576 				  &keysalts,
577 				  &num_keysalts);
578     if (ret) {
579 	com_err(me, ret, gettext("while parsing keysalts %s"), ks_str);
580 	return 1;
581     }
582     if (!num_keysalts || keysalts == NULL) {
583 	num_keysalts = global_params.num_keysalts;
584 	keysalts = global_params.keysalts;
585 	free_keysalts = 0;
586     } else
587 	free_keysalts = 1;
588     ret = krb5_dbe_ark(util_context, &master_key,
589 		       keysalts, num_keysalts,
590 		       &dbent);
591     if (free_keysalts)
592 	free(keysalts);
593     if (ret) {
594 	com_err(me, ret, gettext("while randomizing principal %s"), pr_str);
595 	krb5_dbe_free_contents(util_context, &dbent);
596 	return 1;
597     }
598     dbent.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
599     ret = krb5_timeofday(util_context, &now);
600     if (ret) {
601 	com_err(me, ret, gettext("while getting time"));
602 	krb5_dbe_free_contents(util_context, &dbent);
603 	return 1;
604     }
605     ret = krb5_dbe_update_last_pwd_change(util_context, &dbent, now);
606     if (ret) {
607 	com_err(me, ret, gettext("while setting changetime"));
608 	krb5_dbe_free_contents(util_context, &dbent);
609 	return 1;
610     }
611     ret = krb5_db_put_principal(util_context, &dbent, &n);
612     krb5_dbe_free_contents(util_context, &dbent);
613     if (ret) {
614 	com_err(me, ret, gettext("while saving principal %s"), pr_str);
615 	return 1;
616     }
617     printf("%s changed\n", pr_str);
618     return 0;
619 }
620