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