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