1 /*
2  * Copyright 2006 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 	exit_status++;
490 	return(0);
491     }
492     if ((retval = krb5_db_verify_master_key(util_context, master_princ,
493 		&master_key))) {
494 	com_err(progname, retval,
495 		gettext("while verifying master key"));
496 	exit_status++;
497 	krb5_free_keyblock_contents(util_context, &master_key);
498 	return(1);
499     }
500 
501     seed.length = master_key.length;
502     seed.data = (char *)master_key.contents;
503 
504     if ((retval = krb5_c_random_seed(util_context, &seed))) {
505 	com_err(progname, retval,
506 		gettext("while initializing random key generator"));
507 	exit_status++;
508 	krb5_free_keyblock_contents(util_context, &master_key);
509 	return(1);
510     }
511 
512     valid_master_key = 1;
513     dbactive = TRUE;
514     return 0;
515 }
516 
517 #ifdef HAVE_GETCWD
518 #undef getwd
519 #endif
520 
521 int
522 quit()
523 {
524     krb5_error_code retval;
525     static krb5_boolean finished = 0;
526 
527     if (finished)
528 	return 0;
529     retval = krb5_db_fini(util_context);
530     krb5_free_keyblock_contents(util_context, &master_key);
531     finished = TRUE;
532     krb5_free_context(util_context);
533     if (retval && retval != KRB5_KDB_DBNOTINITED) {
534 		com_err(progname, retval, gettext("while closing database"));
535 	exit_status++;
536 	return 1;
537     }
538     return 0;
539 }
540 
541 static void
542 add_random_key(argc, argv)
543     int argc;
544     char **argv;
545 {
546     krb5_error_code ret;
547     krb5_principal princ;
548     krb5_db_entry dbent;
549     int n;
550     krb5_boolean more;
551     krb5_timestamp now;
552 
553     krb5_key_salt_tuple *keysalts = NULL;
554     krb5_int32 num_keysalts = 0;
555 
556     int free_keysalts;
557     char *me = argv[0];
558     char *ks_str = NULL;
559     char *pr_str;
560 
561     if (argc < 2)
562 	usage();
563     for (argv++, argc--; *argv; argv++, argc--) {
564 	if (!strcmp(*argv, "-e")) {
565 	    argv++; argc--;
566 	    ks_str = *argv;
567 	    continue;
568 	} else
569 	    break;
570     }
571     if (argc < 1)
572 	usage();
573     pr_str = *argv;
574     ret = krb5_parse_name(util_context, pr_str, &princ);
575     if (ret) {
576 	com_err(me, ret, gettext("while parsing principal name %s"), pr_str);
577 	exit_status++;
578 	return;
579     }
580     n = 1;
581     ret = krb5_db_get_principal(util_context, princ, &dbent,
582 				&n, &more);
583     if (ret) {
584 	com_err(me, ret, gettext("while fetching principal %s"), pr_str);
585 	exit_status++;
586 	return;
587     }
588     if (n != 1) {
589 	fprintf(stderr, gettext("principal %s not found\n"), pr_str);
590 	exit_status++;
591 	return;
592     }
593     if (more) {
594 	fprintf(stderr, gettext("principal %s not unique\n"), pr_str);
595 	krb5_dbe_free_contents(util_context, &dbent);
596 	exit_status++;
597 	return;
598     }
599     ret = krb5_string_to_keysalts(ks_str,
600 				  ", \t", ":.-", 0,
601 				  &keysalts,
602 				  &num_keysalts);
603     if (ret) {
604 	com_err(me, ret, gettext("while parsing keysalts %s"), ks_str);
605 	exit_status++;
606 	return;
607     }
608     if (!num_keysalts || keysalts == NULL) {
609 	num_keysalts = global_params.num_keysalts;
610 	keysalts = global_params.keysalts;
611 	free_keysalts = 0;
612     } else
613 	free_keysalts = 1;
614     ret = krb5_dbe_ark(util_context, &master_key,
615 		       keysalts, num_keysalts,
616 		       &dbent);
617     if (free_keysalts)
618 	free(keysalts);
619     if (ret) {
620 	com_err(me, ret, gettext("while randomizing principal %s"), pr_str);
621 	krb5_dbe_free_contents(util_context, &dbent);
622 	exit_status++;
623 	return;
624     }
625     dbent.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
626     ret = krb5_timeofday(util_context, &now);
627     if (ret) {
628 	com_err(me, ret, gettext("while getting time"));
629 	krb5_dbe_free_contents(util_context, &dbent);
630 	exit_status++;
631 	return;
632     }
633     ret = krb5_dbe_update_last_pwd_change(util_context, &dbent, now);
634     if (ret) {
635 	com_err(me, ret, gettext("while setting changetime"));
636 	krb5_dbe_free_contents(util_context, &dbent);
637 	exit_status++;
638 	return;
639     }
640     ret = krb5_db_put_principal(util_context, &dbent, &n);
641     krb5_dbe_free_contents(util_context, &dbent);
642     if (ret) {
643 	com_err(me, ret, gettext("while saving principal %s"), pr_str);
644 	exit_status++;
645 	return;
646     }
647     printf("%s changed\n", pr_str);
648 }
649