/* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Copyright 1994 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * kadmin.c: base functions for a kadmin command line interface using * the OVSecure library */ #include #include #include #include #include #include #include #include #include /* #include */ #include #include /* * Solaris: the following are needed for paging */ #include #include /* command name when called "locally" (i.e. non-networked client ) */ #define KADMIN_LOCAL_NAME "kadmin.local" /* functions defined in remote/local specific files */ extern void usage(const char *); extern void debugEnable(int); /* local principal helpers */ static char *find_component(const char *, char); static char *trim_principal(char *); static char *build_admin_princ(const char *, const char *); /* * special struct to convert flag names for principals * to actual krb5_flags for a principal */ struct pflag { char *flagname; /* name of flag as typed to CLI */ int flaglen; /* length of string (not counting -,+) */ krb5_flags theflag; /* actual principal flag to set/clear */ int set; /* 0 means clear, 1 means set (on '-') */ }; static struct pflag flags[] = { {"allow_postdated", 15, KRB5_KDB_DISALLOW_POSTDATED, 1}, {"allow_forwardable", 17, KRB5_KDB_DISALLOW_FORWARDABLE, 1}, {"allow_tgs_req", 13, KRB5_KDB_DISALLOW_TGT_BASED, 1}, {"allow_renewable", 15, KRB5_KDB_DISALLOW_RENEWABLE, 1}, {"allow_proxiable", 15, KRB5_KDB_DISALLOW_PROXIABLE, 1}, {"allow_dup_skey", 14, KRB5_KDB_DISALLOW_DUP_SKEY, 1}, {"allow_tix", 9, KRB5_KDB_DISALLOW_ALL_TIX, 1}, {"requires_preauth", 16, KRB5_KDB_REQUIRES_PRE_AUTH, 0}, {"requires_hwauth", 15, KRB5_KDB_REQUIRES_HW_AUTH, 0}, {"needchange", 10, KRB5_KDB_REQUIRES_PWCHANGE, 0}, {"allow_svr", 9, KRB5_KDB_DISALLOW_SVR, 1}, {"password_changing_service", 25, KRB5_KDB_PWCHANGE_SERVICE, 0 }, {"support_desmd5", 14, KRB5_KDB_SUPPORT_DESMD5, 0 } }; static char *prflags[] = { "DISALLOW_POSTDATED", /* 0x00000001 */ "DISALLOW_FORWARDABLE", /* 0x00000002 */ "DISALLOW_TGT_BASED", /* 0x00000004 */ "DISALLOW_RENEWABLE", /* 0x00000008 */ "DISALLOW_PROXIABLE", /* 0x00000010 */ "DISALLOW_DUP_SKEY", /* 0x00000020 */ "DISALLOW_ALL_TIX", /* 0x00000040 */ "REQUIRES_PRE_AUTH", /* 0x00000080 */ "REQUIRES_HW_AUTH", /* 0x00000100 */ "REQUIRES_PWCHANGE", /* 0x00000200 */ "UNKNOWN_0x00000400", /* 0x00000400 */ "UNKNOWN_0x00000800", /* 0x00000800 */ "DISALLOW_SVR", /* 0x00001000 */ "PWCHANGE_SERVICE", /* 0x00002000 */ "SUPPORT_DESMD5", /* 0x00004000 */ "NEW_PRINC", /* 0x00008000 */ }; char *getenv(); int exit_status = 0; char *def_realm = NULL; char *whoami = NULL; time_t get_date(); void *handle = NULL; krb5_context context; char *ccache_name = NULL; char * strdur(duration) time_t duration; { static char out[100]; int days, hours, minutes, seconds; days = duration / (24 * 3600); duration %= 24 * 3600; hours = duration / 3600; duration %= 3600; minutes = duration / 60; duration %= 60; seconds = duration; if (days == 1) { snprintf(out, sizeof (out), gettext("%d day %02d:%02d:%02d"), days, hours, minutes, seconds); } else { snprintf(out, sizeof (out), gettext("%d days %02d:%02d:%02d"), days, hours, minutes, seconds); } return (out); } char * strdate(when) krb5_timestamp when; { struct tm *tm; static char out[30]; time_t lcltim = when; tm = localtime(&lcltim); strftime(out, 30, gettext("%a %b %d %H:%M:%S %Z %Y"), tm); return (out); } /* * this is a wrapper to go around krb5_parse_principal so we can set * the default realm up properly */ krb5_error_code kadmin_parse_name(name, principal) char *name; krb5_principal *principal; { char *cp, *fullname; krb5_error_code retval; if (name == NULL) return (EINVAL); /* assumes def_realm is initialized! */ fullname = (char *)malloc(strlen(name) + 1 + strlen(def_realm) + 1); if (fullname == NULL) return (ENOMEM); strcpy(fullname, name); cp = strchr(fullname, '@'); while (cp) { if (cp - fullname && *(cp - 1) != '\\') break; else cp = strchr((cp + 1), '@'); } if (cp == NULL) { strcat(fullname, "@"); strcat(fullname, def_realm); } retval = krb5_parse_name(context, fullname, principal); free(fullname); return (retval); } char * kadmin_startup(argc, argv) int argc; char *argv[]; { extern krb5_kt_ops krb5_ktf_writable_ops; extern char *optarg; char *princstr = NULL, *keytab_name = NULL, *query = NULL; char *password = NULL; char *kadmin_princ = NULL; char *luser, *canon, *cp; int optchar, use_keytab = 0, debug = 0; struct passwd *pw; kadm5_ret_t retval; krb5_ccache cc; krb5_principal princ; kadm5_config_params params; memset((char *) ¶ms, 0, sizeof(params)); if (retval = krb5_init_context(&context)) { com_err(whoami, retval, gettext("while initializing krb5 library")); exit(1); } while ((optchar = getopt(argc, argv, "Dr:p:kq:w:d:s:mc:t:e:O")) != EOF) { switch (optchar) { case 'O': /* Undocumented option for testing only */ kadmin_princ = KADM5_ADMIN_SERVICE_P; break; case 'D': debug++; break; case 'r': def_realm = optarg; break; case 'p': princstr = strdup(optarg); if (princstr == NULL) { fprintf(stderr, gettext("Out of memory in %s\n"), whoami); exit(1); } break; case 'c': ccache_name = optarg; break; case 'k': use_keytab++; break; case 't': keytab_name = optarg; break; case 'w': password = optarg; break; case 'q': query = optarg; break; case 'd': params.dbname = optarg; params.mask |= KADM5_CONFIG_DBNAME; break; case 's': params.admin_server = optarg; params.mask |= KADM5_CONFIG_ADMIN_SERVER; break; case 'm': params.mkey_from_kbd = 1; params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; break; case 'e': retval = krb5_string_to_keysalts(optarg, ", \t", ":.-", 0, ¶ms.keysalts, ¶ms.num_keysalts); if (retval) { com_err(whoami, retval, gettext("while parsing keysalts %s"), optarg); exit(1); } params.mask |= KADM5_CONFIG_ENCTYPES; break; default: usage(whoami); } } debugEnable(debug); if ((ccache_name && use_keytab) || (keytab_name && !use_keytab)) usage(whoami); if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) { free(princstr); fprintf(stderr, gettext("%s: unable to get default realm\n"), whoami); exit(1); } params.mask |= KADM5_CONFIG_REALM; params.realm = def_realm; if (kadmin_princ == NULL) { if (kadm5_get_adm_host_srv_name(context, def_realm, &kadmin_princ)) { fprintf(stderr, gettext("%s: unable to get host based " "service name for realm %s\n"), whoami, def_realm); free(princstr); exit(1); } } /* * Set cc to an open credentials cache, either specified by the -c * argument or the default. */ if (ccache_name == NULL) { if (retval = krb5_cc_default(context, &cc)) { com_err(whoami, retval, gettext("while opening default " "credentials cache")); exit(1); } } else { if (retval = krb5_cc_resolve(context, ccache_name, &cc)) { com_err(whoami, retval, gettext("while opening credentials cache %s"), ccache_name); exit(1); } } /* * If no principal name is specified: If a ccache was specified and * its primary principal name can be read, it is used, else if a * keytab was specified, the principal name is host/hostname, * otherwise append "/admin" to the primary name of the default * ccache, $USER, or pw_name. * * Gee, 100+ lines to figure out the client principal name. This * should be compressed... */ if (princstr == NULL) { if (ccache_name != NULL && !krb5_cc_get_principal(context, cc, &princ)) { if (retval = krb5_unparse_name(context, princ, &princstr)) { com_err(whoami, retval, gettext("while canonicalizing principal name")); krb5_free_principal(context, princ); exit(1); } krb5_free_principal(context, princ); } else if (use_keytab != 0) { if (retval = krb5_sname_to_principal(context, NULL, "host", KRB5_NT_SRV_HST, &princ)) { com_err(whoami, retval, gettext("creating host service principal")); exit(1); } if (retval = krb5_unparse_name(context, princ, &princstr)) { com_err(whoami, retval, gettext("while canonicalizing " "principal name")); krb5_free_principal(context, princ); exit(1); } krb5_free_principal(context, princ); } else if (!krb5_cc_get_principal(context, cc, &princ)) { char *realm = NULL; if (krb5_unparse_name(context, princ, &canon)) { fprintf(stderr, gettext("%s: unable to canonicalize " "principal\n"), whoami); krb5_free_principal(context, princ); exit(1); } krb5_free_principal(context, princ); (void) trim_principal(canon); princstr = build_admin_princ(canon, def_realm); free(canon); } else if (luser = getenv("USER")) { princstr = build_admin_princ(luser, def_realm); } else if (pw = getpwuid(getuid())) { princstr = build_admin_princ(pw->pw_name, def_realm); } else { fprintf(stderr, gettext("%s: unable to figure out " "a principal name\n"), whoami); exit(1); } } else { /* (princstr != NULL) */ /* See if we need to add the default realm */ if (find_component(princstr, '@') == NULL) { size_t len; /* principal @ realm NULL */ len = strlen(princstr) + 1 + strlen(def_realm) + 1; princstr = realloc(princstr, len); if (princstr == NULL) { fprintf(stderr, gettext("%s: out of memory\n"), whoami); exit(1); } strcat(princstr, "@"); strcat(princstr, def_realm); } } /* * Initialize the kadm5 connection. If we were given a ccache, use * it. Otherwise, use/prompt for the password. */ if (ccache_name) { printf(gettext( "Authenticating as principal %s with existing credentials.\n"), princstr); retval = kadm5_init_with_creds(princstr, cc, kadmin_princ, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, &handle); } else if (use_keytab) { if (keytab_name) printf(gettext("Authenticating as principal %s with keytab %s.\n"), princstr, keytab_name); else printf(gettext( "Authenticating as principal %s with default keytab.\n"), princstr); retval = kadm5_init_with_skey(princstr, keytab_name, kadmin_princ, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, &handle); } else { printf(gettext("Authenticating as principal %s with password.\n"), princstr); retval = kadm5_init_with_password(princstr, password, kadmin_princ, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, &handle); } if (retval) { if (retval == KADM5_RPC_ERROR_CANTENCODEARGS || retval == KADM5_RPC_ERROR_CANTDECODEARGS) { com_err(whoami, KADM5_RPC_ERROR, gettext("while initializing %s interface"), whoami); /* privacy-enabled mech probably not installed/configed */ com_err(whoami, retval, gettext("."), whoami); } else { com_err(whoami, retval, gettext("while initializing %s interface"), whoami); if (retval == KADM5_BAD_CLIENT_PARAMS || retval == KADM5_BAD_SERVER_PARAMS) usage(whoami); } exit(1); } free(princstr); if (retval = krb5_cc_close(context, cc)) { com_err(whoami, retval, gettext("while closing ccache %s"), ccache_name); exit(1); } /* register the WRFILE keytab type and set it as the default */ if (retval = krb5_kt_register(context, &krb5_ktf_writable_ops)) { com_err(whoami, retval, gettext("while registering writable key table functions")); exit(1); } { /* * XXX krb5_defkeyname is an internal library global and * should go away */ extern char *krb5_defkeyname; krb5_defkeyname = DEFAULT_KEYTAB; } if ((retval = kadm5_init_iprop(handle)) != 0) { com_err(whoami, retval, gettext("while mapping update log")); exit(1); } /* Solaris kerberos: fix memory leak */ if (kadmin_princ) free(kadmin_princ); return (query); } static char * find_component(const char *principal, char sep) { char *p = strchr(principal, sep); for(p = strchr(principal, sep); p; p = strchr(p, sep)) if (p != principal && *(p - 1) != '\\') break; return (p); } static char * trim_principal(char *principal) { char *p = find_component(principal, '/'); if (p == NULL) p = find_component(principal, '@'); if (p) *p = '\0'; return (principal); } static char * build_admin_princ(const char *user, const char *realm) { char *princstr; /* Add 7 to the length for "/admin@" */ princstr = (char *) malloc(strlen(user) + 7 + strlen(realm) + 1); if (princstr == NULL) { fprintf(stderr, gettext("%s: out of memory\n"), whoami); exit(1); } sprintf(princstr, "%s/admin@%s", user, realm); return (princstr); } int quit() { krb5_ccache cc; int retval; kadm5_destroy(handle); if (ccache_name != NULL) { fprintf(stderr, gettext("\n\a\a\aAdministration credentials " "NOT DESTROYED.\n")); } /* insert more random cleanup here */ krb5_free_context(context); context = NULL; return (0); } void kadmin_delprinc(argc, argv) int argc; char *argv[]; { kadm5_ret_t retval; krb5_principal princ; char *canon; char reply[32]; if (! (argc == 2 || (argc == 3 && strcmp("-force", argv[1]) == 0))) { fprintf(stderr, "%s: delete_principal [-force] %s\n", gettext("usage"), gettext("principal")); return; } retval = kadmin_parse_name(argv[argc - 1], &princ); if (retval) { com_err("delete_principal", retval, gettext("while parsing principal name")); return; } retval = krb5_unparse_name(context, princ, &canon); if (retval) { com_err("delete_principal", retval, gettext("while canonicalizing principal")); krb5_free_principal(context, princ); return; } if (argc == 2) { printf(gettext("Are you sure you want to delete " "the principal \"%s\"? (yes/no): "), canon); fgets(reply, sizeof (reply), stdin); if (strncmp(gettext("yes\n"), reply, sizeof (reply)) && strncmp(gettext("y\n"), reply, sizeof (reply)) && strncmp(gettext("Y\n"), reply, sizeof (reply))) { fprintf(stderr, gettext("Principal \"%s\" not deleted\n"), canon); free(canon); krb5_free_principal(context, princ); return; } } retval = kadm5_delete_principal(handle, princ); krb5_free_principal(context, princ); if (retval) { com_err("delete_principal", retval, gettext("while deleting principal \"%s\""), canon); free(canon); return; } printf(gettext("Principal \"%s\" deleted.\n"), canon); printf(gettext("Make sure that you have removed this principal " "from all ACLs before reusing.\n")); free(canon); } void kadmin_cpw(argc, argv) int argc; char *argv[]; { kadm5_ret_t retval; static char newpw[1024]; static char prompt1[1024], prompt2[1024]; char *canon; char *pwarg = NULL; int n_ks_tuple = 0, keepold = 0, randkey = 0; krb5_key_salt_tuple *ks_tuple = NULL; krb5_principal princ; if (argc < 2) { goto usage; } for (argv++, argc--; argc > 1; argc--, argv++) { if (!strcmp("-pw", *argv)) { argc--; if (argc < 1) { fprintf(stderr, "change_password: %s", gettext("missing password arg\n")); goto usage; } pwarg = *++argv; continue; } if (!strcmp("-randkey", *argv)) { randkey++; continue; } if (!strcmp("-keepold", *argv)) { keepold++; continue; } if (!strcmp("-e", *argv)) { argc--; if (argc < 1) { fprintf(stderr, "change_password: %s", gettext("missing keysaltlist arg\n")); goto usage; } retval = krb5_string_to_keysalts(*++argv, ", \t", ":.-", 0, &ks_tuple, &n_ks_tuple); if (retval) { com_err("change_password", retval, gettext("while parsing keysalts %s"), *argv); return; } continue; } goto usage; } retval = kadmin_parse_name(*argv, &princ); if (retval) { com_err("change_password", retval, gettext("while parsing principal name")); if (ks_tuple != NULL) free(ks_tuple); goto usage; } retval = krb5_unparse_name(context, princ, &canon); if (retval) { com_err("change_password", retval, gettext("while canonicalizing principal")); krb5_free_principal(context, princ); if (ks_tuple != NULL) free(ks_tuple); return; } if (pwarg != NULL) { if (keepold || ks_tuple != NULL) { retval = kadm5_chpass_principal_3(handle, princ, keepold, n_ks_tuple, ks_tuple, pwarg); if (ks_tuple != NULL) free(ks_tuple); } else { retval = kadm5_chpass_principal(handle, princ, pwarg); } krb5_free_principal(context, princ); if (retval) { com_err("change_password", retval, gettext("while changing password for \"%s\"."), canon); free(canon); return; } printf(gettext("Password for \"%s\" changed.\n"), canon); free(canon); return; } else if (randkey) { if (keepold || ks_tuple != NULL) { retval = kadm5_randkey_principal_3(handle, princ, keepold, n_ks_tuple, ks_tuple, NULL, NULL); if (ks_tuple != NULL) free(ks_tuple); } else { retval = kadm5_randkey_principal(handle, princ, NULL, NULL); } krb5_free_principal(context, princ); if (retval) { com_err("change_password", retval, gettext("while randomizing key for \"%s\"."), canon); free(canon); return; } printf(gettext("Key for \"%s\" randomized.\n"), canon); free(canon); return; } else if (argc == 1) { unsigned int i = sizeof (newpw) - 1; snprintf(prompt1, sizeof (prompt1), gettext("Enter password for principal \"%.900s\""), *argv); snprintf(prompt2, sizeof (prompt2), gettext("Re-enter password for principal \"%.900s\""), *argv); retval = krb5_read_password(context, prompt1, prompt2, newpw, &i); if (retval) { com_err("change_password", retval, gettext("while reading password for \"%s\"."), canon); free(canon); if (ks_tuple != NULL) free(ks_tuple); krb5_free_principal(context, princ); return; } if (keepold || ks_tuple != NULL) { retval = kadm5_chpass_principal_3(handle, princ, keepold, n_ks_tuple, ks_tuple, newpw); if (ks_tuple != NULL) free(ks_tuple); } else { retval = kadm5_chpass_principal(handle, princ, newpw); } krb5_free_principal(context, princ); memset(newpw, 0, sizeof (newpw)); if (retval) { com_err("change_password", retval, gettext("while changing password for \"%s\"."), canon); free(canon); return; } printf(gettext("Password for \"%s\" changed.\n"), canon); free(canon); return; } else { free(canon); krb5_free_principal(context, princ); usage: fprintf(stderr, "%s: change_password [-randkey] [-keepold] " "[-e keysaltlist] [-pw password] %s\n", gettext("usage"), gettext("principal")); return; } } int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, randkey, ks_tuple, n_ks_tuple, caller) int argc; char *argv[]; kadm5_principal_ent_t oprinc; long *mask; char **pass; int *randkey; krb5_key_salt_tuple **ks_tuple; int *n_ks_tuple; char *caller; { int i, j, attrib_set; time_t date; time_t now; krb5_error_code retval; *mask = 0; *pass = NULL; *n_ks_tuple = 0; *ks_tuple = NULL; time(&now); *randkey = 0; for (i = 1; i < argc - 1; i++) { attrib_set = 0; if (strlen(argv[i]) == 7 && strcmp("-expire", argv[i]) == 0) { if (++i > argc - 2) return (-1); else { date = get_date(argv[i], NULL); if (date == (time_t)-1) { fprintf(stderr, gettext("Invalid date " "specification " "\"%s\".\n"), argv[i]); return (-1); } oprinc->princ_expire_time = date; *mask |= KADM5_PRINC_EXPIRE_TIME; continue; } } if (strlen(argv[i]) == 9 && strcmp("-pwexpire", argv[i]) == 0) { if (++i > argc - 2) return (-1); else { date = get_date(argv[i], NULL); if (date == (time_t)-1) { fprintf(stderr, gettext("Invalid date " "specification " "\"%s\".\n"), argv[i]); return (-1); } oprinc->pw_expiration = date; *mask |= KADM5_PW_EXPIRATION; continue; } } if (strlen(argv[i]) == 8 && strcmp("-maxlife", argv[i]) == 0) { if (++i > argc - 2) return (-1); else { date = get_date(argv[i], NULL); if (date == (time_t)-1) { fprintf(stderr, gettext("Invalid date " "specification " "\"%s\".\n"), argv[i]); return (-1); } if (date <= now) { fprintf(stderr, gettext("Date specified is " "in the past " "\"%s\".\n"), argv[i]); return (-1); } oprinc->max_life = date - now; *mask |= KADM5_MAX_LIFE; continue; } } if (strlen(argv[i]) == 13 && strcmp("-maxrenewlife", argv[i]) == 0) { if (++i > argc - 2) return (-1); else { date = get_date(argv[i], NULL); if (date == (time_t)-1) { fprintf(stderr, gettext("Invalid date " "specification " "\"%s\".\n"), argv[i]); return (-1); } if (date <= now) { fprintf(stderr, gettext("Date specified is " "in the past " "\"%s\".\n"), argv[i]); return (-1); } oprinc->max_renewable_life = date - now; *mask |= KADM5_MAX_RLIFE; continue; } } if (strlen(argv[i]) == 5 && strcmp("-kvno", argv[i]) == 0) { if (++i > argc - 2) return (-1); else { oprinc->kvno = atoi(argv[i]); *mask |= KADM5_KVNO; continue; } } if (strlen(argv[i]) == 7 && strcmp("-policy", argv[i]) == 0) { if (++i > argc - 2) return (-1); else { oprinc->policy = argv[i]; *mask |= KADM5_POLICY; continue; } } if (strlen(argv[i]) == 12 && strcmp("-clearpolicy", argv[i]) == 0) { oprinc->policy = NULL; *mask |= KADM5_POLICY_CLR; continue; } if (strlen(argv[i]) == 3 && strcmp("-pw", argv[i]) == 0) { if (++i > argc - 2) return (-1); else { *pass = argv[i]; continue; } } if (strlen(argv[i]) == 8 && strcmp("-randkey", argv[i]) == 0) { ++*randkey; continue; } if (!strcmp("-e", argv[i])) { if (++i > argc - 2) return -1; else { retval = krb5_string_to_keysalts(argv[i], ", \t", ":.-", 0, ks_tuple, n_ks_tuple); if (retval) { com_err(caller, retval, gettext("while parsing keysalts %s"), argv[i]); return -1; } } continue; } for (j = 0; j < sizeof (flags) / sizeof (struct pflag); j++) { if (strlen(argv[i]) == flags[j].flaglen + 1 && strcmp(flags[j].flagname, /* strip off leading + or - */ &argv[i][1]) == 0) { if (flags[j].set && argv[i][0] == '-' || !flags[j].set && argv[i][0] == '+') { oprinc->attributes |= flags[j].theflag; *mask |= KADM5_ATTRIBUTES; attrib_set++; break; } else if (flags[j].set && argv[i][0] == '+' || !flags[j].set && argv[i][0] == '-') { oprinc->attributes &= ~flags[j].theflag; *mask |= KADM5_ATTRIBUTES; attrib_set++; break; } else { return (-1); } } } if (!attrib_set) return (-1); /* nothing was parsed */ } if (i != argc - 1) { return (-1); } retval = kadmin_parse_name(argv[i], &oprinc->principal); if (retval) { com_err(caller, retval, gettext("while parsing principal")); return (-1); } return (0); } void kadmin_addprinc_usage(func) char *func; { fprintf(stderr, "%s: %s %s\n", gettext("usage"), func, gettext("[options] principal")); fprintf(stderr, gettext("\toptions are:\n")); fprintf(stderr, "\t\t[-expire expdate] [-pwexpire pwexpdate] " "[-maxlife maxtixlife]\n\t\t[-kvno kvno] [-policy policy] " "[-randkey] [-pw password]\n\t\t[-maxrenewlife maxrenewlife] " "[-e keysaltlist] [{+|-}attribute]\n"); fprintf(stderr, gettext("\tattributes are:\n")); fprintf(stderr, "%s%s%s", "\t\tallow_postdated allow_forwardable allow_tgs_req " "allow_renewable\n", "\t\tallow_proxiable allow_dup_skey allow_tix " "requires_preauth\n", "\t\trequires_hwauth needchange allow_svr " "password_changing_service\n"); } void kadmin_modprinc_usage(func) char *func; { fprintf(stderr, "%s: %s %s\n", gettext("usage"), func, gettext("[options] principal")); fprintf(stderr, gettext("\toptions are:\n")); fprintf(stderr, "\t\t[-expire expdate] [-pwexpire pwexpdate] " "[-maxlife maxtixlife]\n\t\t[-kvno kvno] [-policy policy] " "[-clearpolicy]\n\t\t[-maxrenewlife maxrenewlife] " "[{+|-}attribute]\n"); fprintf(stderr, gettext("\tattributes are:\n")); fprintf(stderr, "%s%s%s", "\t\tallow_postdated allow_forwardable allow_tgs_req " "allow_renewable\n", "\t\tallow_proxiable allow_dup_skey allow_tix " "requires_preauth\n", "\t\trequires_hwauth needchange allow_svr " "password_changing_service\n"); } void kadmin_addprinc(argc, argv) int argc; char *argv[]; { kadm5_principal_ent_rec princ, dprinc; kadm5_policy_ent_rec defpol; long mask; int randkey = 0, i; int n_ks_tuple; krb5_key_salt_tuple *ks_tuple; char *pass, *canon; krb5_error_code retval; static char newpw[1024], dummybuf[256]; static char prompt1[1024], prompt2[1024]; int local_kadmin = 0; local_kadmin = (strcmp(whoami, KADMIN_LOCAL_NAME) == 0); if (dummybuf[0] == 0) { for (i = 0; i < 256; i++) dummybuf[i] = (i+1) % 256; } /* Zero all fields in request structure */ memset(&princ, 0, sizeof(princ)); memset(&dprinc, 0, sizeof(dprinc)); princ.attributes = dprinc.attributes = 0; if (kadmin_parse_princ_args(argc, argv, &princ, &mask, &pass, &randkey, &ks_tuple, &n_ks_tuple, "add_principal")) { kadmin_addprinc_usage("add_principal"); return; } retval = krb5_unparse_name(context, princ.principal, &canon); if (retval) { com_err("add_principal", retval, gettext("while canonicalizing principal")); krb5_free_principal(context, princ.principal); if (ks_tuple != NULL) free(ks_tuple); return; } /* * If -policy was not specified, and -clearpolicy was not * specified, and the policy "default" exists, assign it. If * -clearpolicy was specified, then KADM5_POLICY_CLR should be * unset, since it is never valid for kadm5_create_principal. */ if ((! (mask & KADM5_POLICY)) && (! (mask & KADM5_POLICY_CLR))) { if (! kadm5_get_policy(handle, "default", &defpol)) { fprintf(stderr, gettext( "NOTICE: no policy specified for %s; assigning \"default\"\n"), canon); princ.policy = "default"; mask |= KADM5_POLICY; (void) kadm5_free_policy_ent(handle, &defpol); } else fprintf(stderr, gettext("WARNING: no policy specified " "for %s; defaulting to no policy\n"), canon); } mask &= ~KADM5_POLICY_CLR; /* * Set 'notix' for randkey principals and also for principals which have * specified flag options on the cmdline. This is because we want to apply * generic flag settings from 'default_principal_flags' first (during * principal creation), followed by a kadm5_modify_principal() which * correctly applies the cli flag options. So, we do *not* want any tix * issued in the interim. */ if (randkey || (mask & KADM5_ATTRIBUTES)) princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; if (randkey) { pass = dummybuf; mask |= KADM5_ATTRIBUTES; } else if (pass == NULL) { unsigned int i = sizeof (newpw) - 1; snprintf(prompt1, sizeof (prompt1), gettext("Enter password for principal \"%.900s\""), canon); snprintf(prompt2, sizeof (prompt1), gettext("Re-enter password for principal \"%.900s\""), canon); retval = krb5_read_password(context, prompt1, prompt2, newpw, &i); if (retval) { com_err("add_principal", retval, gettext("while reading password for \"%s\"."), canon); free(canon); krb5_free_principal(context, princ.principal); return; } pass = newpw; } mask |= KADM5_PRINCIPAL; /* * If the client being used is local, always use the new * API so we get the full set of enctype support. */ if (ks_tuple != NULL || local_kadmin) { retval = kadm5_create_principal_3(handle, &princ, mask, n_ks_tuple, ks_tuple, pass); } else { retval = kadm5_create_principal(handle, &princ, mask, pass); } if (retval) { com_err("add_principal", retval, gettext("while creating \"%s\"."), canon); krb5_free_principal(context, princ.principal); free(canon); if (ks_tuple != NULL) free(ks_tuple); return; } if (randkey) { /* more special stuff for -randkey */ if (ks_tuple != NULL || local_kadmin) { retval = kadm5_randkey_principal_3(handle, princ.principal, FALSE, n_ks_tuple, ks_tuple, NULL, NULL); } else { retval = kadm5_randkey_principal(handle, princ.principal, NULL, NULL); } if (retval) { com_err("add_principal", retval, gettext("while randomizing key for \"%s\"."), canon); krb5_free_principal(context, princ.principal); free(canon); if (ks_tuple != NULL) free(ks_tuple); return; } } /* * We now retrieve the intersection set of the generic flag settings and * the ones specified on the cli & re-parse the princ args, just to make * sure we account for conflicts between 'default_principal_flags' and * the cmdline flag args. While we are here, also clear 'notix'. */ if (randkey || (mask & KADM5_ATTRIBUTES)) { retval = kadm5_get_principal(handle, princ.principal, &dprinc, KADM5_PRINCIPAL_NORMAL_MASK); if (retval == 0) { if (dprinc.attributes != 0) princ.attributes = dprinc.attributes; } else { com_err("add_principal", retval, gettext("while doing a get_principal on \"%s\"."), canon); printf(gettext("\nWarning: Principal \"%s\" could have incomplete " "flag settings, as a result of a failed get_principal.\n" "Check the 'default_principal_flags' setting in kdc.conf(4).\n" "If there is a mismatch, use modprinc in kadmin(1M) to rectify " "the same.\n\n"), canon); } (void) kadmin_parse_princ_args(argc, argv, &princ, &mask, &pass, &randkey, &ks_tuple, &n_ks_tuple, "add_principal"); princ.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX; mask = KADM5_ATTRIBUTES; retval = kadm5_modify_principal(handle, &princ, mask); if (retval) { com_err("add_principal", retval, gettext("while doing a modify_principal to restore flag " "settings for \"%s\"."), canon); krb5_free_principal(context, princ.principal); free(canon); if (ks_tuple != NULL) free(ks_tuple); return; } } krb5_free_principal(context, princ.principal); printf(gettext("Principal \"%s\" created.\n"), canon); if (ks_tuple != NULL) free(ks_tuple); free(canon); } void kadmin_modprinc(argc, argv) int argc; char *argv[]; { kadm5_principal_ent_rec princ, oldprinc; krb5_principal kprinc; long mask; krb5_error_code retval; char *pass, *canon; int randkey = 0; int n_ks_tuple = 0; krb5_key_salt_tuple *ks_tuple; if (argc < 2) { kadmin_modprinc_usage("modify_principal"); return; } memset(&oldprinc, 0, sizeof(oldprinc)); memset(&princ, 0, sizeof(princ)); retval = kadmin_parse_name(argv[argc - 1], &kprinc); if (retval) { com_err("modify_principal", retval, gettext("while parsing principal")); return; } retval = krb5_unparse_name(context, kprinc, &canon); if (retval) { com_err("modify_principal", retval, gettext("while canonicalizing principal")); krb5_free_principal(context, kprinc); return; } retval = kadm5_get_principal(handle, kprinc, &oldprinc, KADM5_PRINCIPAL_NORMAL_MASK); krb5_free_principal(context, kprinc); if (retval) { com_err("modify_principal", retval, gettext("while getting \"%s\"."), canon); free(canon); return; } princ.attributes = oldprinc.attributes; kadm5_free_principal_ent(handle, &oldprinc); retval = kadmin_parse_princ_args(argc, argv, &princ, &mask, &pass, &randkey, &ks_tuple, &n_ks_tuple, "modify_principal"); if (ks_tuple != NULL) { free(ks_tuple); kadmin_modprinc_usage("modify_principal"); free(canon); return; } if (retval) { kadmin_modprinc_usage("modify_principal"); free(canon); return; } if (randkey) { fprintf(stderr, "modify_principal: -randkey %s ", gettext("not allowed\n")); krb5_free_principal(context, princ.principal); free(canon); return; } if (pass) { fprintf(stderr, "modify_principal: -pw %s change_password\n", gettext("not allowed; use")); krb5_free_principal(context, princ.principal); free(canon); return; } retval = kadm5_modify_principal(handle, &princ, mask); krb5_free_principal(context, princ.principal); if (retval) { com_err("modify_principal", retval, gettext("while modifying \"%s\"."), canon); free(canon); return; } printf(gettext("Principal \"%s\" modified.\n"), canon); free(canon); } void kadmin_getprinc(argc, argv) int argc; char *argv[]; { kadm5_principal_ent_rec dprinc; krb5_principal princ; krb5_error_code retval; char *canon, *modcanon; int i; if (! (argc == 2 || (argc == 3 && strcmp("-terse", argv[1]) == 0))) { fprintf(stderr, "%s: get_principal [-terse] %s\n", gettext("usage"), gettext("principal")); return; } memset(&dprinc, 0, sizeof(dprinc)); memset(&princ, 0, sizeof(princ)); retval = kadmin_parse_name(argv[argc - 1], &princ); if (retval) { com_err("get_principal", retval, gettext("while parsing principal")); return; } retval = krb5_unparse_name(context, princ, &canon); if (retval) { com_err("get_principal", retval, gettext("while canonicalizing principal")); krb5_free_principal(context, princ); return; } retval = kadm5_get_principal(handle, princ, &dprinc, KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA); krb5_free_principal(context, princ); if (retval) { com_err("get_principal", retval, gettext("while retrieving \"%s\"."), canon); free(canon); return; } retval = krb5_unparse_name(context, dprinc.mod_name, &modcanon); if (retval) { com_err("get_principal", retval, gettext("while unparsing modname")); kadm5_free_principal_ent(handle, &dprinc); free(canon); return; } if (argc == 2) { printf(gettext("Principal: %s\n"), canon); printf(gettext("Expiration date: %s\n"), dprinc.princ_expire_time ? strdate(dprinc.princ_expire_time) : gettext("[never]")); printf(gettext("Last password change: %s\n"), dprinc.last_pwd_change ? strdate(dprinc.last_pwd_change) : gettext("[never]")); printf(gettext("Password expiration date: %s\n"), dprinc.pw_expiration ? strdate(dprinc.pw_expiration) : gettext("[none]")); printf(gettext("Maximum ticket life: %s\n"), strdur(dprinc.max_life)); printf(gettext("Maximum renewable life: %s\n"), strdur(dprinc.max_renewable_life)); printf(gettext("Last modified: %s (%s)\n"), strdate(dprinc.mod_date), modcanon); printf(gettext("Last successful authentication: %s\n"), dprinc.last_success ? strdate(dprinc.last_success) : gettext("[never]")); printf(gettext("Last failed authentication: %s\n"), dprinc.last_failed ? strdate(dprinc.last_failed) : gettext("[never]")); printf(gettext("Failed password attempts: %d\n"), dprinc.fail_auth_count); printf(gettext("Number of keys: %d\n"), dprinc.n_key_data); for (i = 0; i < dprinc.n_key_data; i++) { krb5_key_data *key_data = &dprinc.key_data[i]; char enctype[BUFSIZ], salttype[BUFSIZ]; if (krb5_enctype_to_string(key_data->key_data_type[0], enctype, sizeof(enctype))) snprintf(enctype, sizeof (enctype), gettext(""), key_data->key_data_type[0]); printf(gettext("Key: vno %d, %s, "), key_data->key_data_kvno, enctype); if (key_data->key_data_ver > 1) { if (krb5_salttype_to_string( key_data->key_data_type[1], salttype, sizeof(salttype))) snprintf(salttype, sizeof (salttype), gettext(""), key_data->key_data_type[1]); printf("%s\n", salttype); } else printf(gettext("no salt\n")); } printf(gettext("Attributes:")); for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) { if (dprinc.attributes & (krb5_flags) 1 << i) printf(" %s", prflags[i]); } printf("\n"); printf(gettext("Policy: %s\n"), dprinc.policy ? dprinc.policy : gettext("[none]")); } else { printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"" "\t%d\t%d\t%d\t%d\t%d", canon, dprinc.princ_expire_time, dprinc.last_pwd_change, dprinc.pw_expiration, dprinc.max_life, modcanon, dprinc.mod_date, dprinc.attributes, dprinc.kvno, dprinc.mkvno, dprinc.policy ? dprinc.policy : gettext("[none]"), dprinc.max_renewable_life, dprinc.last_success, dprinc.last_failed, dprinc.fail_auth_count, dprinc.n_key_data); for (i = 0; i < dprinc.n_key_data; i++) printf("\t%d\t%d\t%d\t%d", dprinc.key_data[i].key_data_ver, dprinc.key_data[i].key_data_kvno, dprinc.key_data[i].key_data_type[0], dprinc.key_data[i].key_data_type[1]); printf("\n"); } free(modcanon); kadm5_free_principal_ent(handle, &dprinc); free(canon); } void kadmin_getprincs(argc, argv) int argc; char *argv[]; { krb5_error_code retval; char *exp, **names; int i, count; FILE *output; int fd; struct sigaction nsig, osig; sigset_t nmask, omask; int waitb; exp = NULL; if (! (argc == 1 || (argc == 2 && (exp = argv[1])))) { fprintf(stderr, "%s: get_principals %s\n", gettext("usage"), gettext("[expression]")); return; } retval = kadm5_get_principals(handle, exp, &names, &count); if (retval) { com_err("get_principals", retval, gettext("while retrieving list.")); return; } /* * Solaris: the following code is used for paging */ sigemptyset(&nmask); sigaddset(&nmask, SIGINT); sigprocmask(SIG_BLOCK, &nmask, &omask); nsig.sa_handler = SIG_IGN; sigemptyset(&nsig.sa_mask); nsig.sa_flags = 0; sigaction(SIGINT, &nsig, &osig); fd = ss_pager_create(); output = fdopen(fd, "w"); sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0); for (i = 0; i < count; i++) fprintf(output, "%s\n", names[i]); fclose(output); wait(&waitb); kadm5_free_name_list(handle, names, count); } int kadmin_parse_policy_args(argc, argv, policy, mask, caller) int argc; char *argv[]; kadm5_policy_ent_t policy; long *mask; char *caller; { int i; time_t now; time_t date; krb5_error_code retval; time(&now); *mask = 0; for (i = 1; i < argc - 1; i++) { if (strlen(argv[i]) == 8 && strcmp(argv[i], "-maxlife") == 0) { if (++i > argc -2) return (-1); else { date = get_date(argv[i], NULL); if (date == (time_t)-1) { fprintf(stderr, gettext("Invalid date " "specification " "\"%s\".\n"), argv[i]); return (-1); } if (date <= now) { fprintf(stderr, gettext("Date specified is " "in the past " "\"%s\".\n"), argv[i]); return (-1); } policy->pw_max_life = date - now; *mask |= KADM5_PW_MAX_LIFE; continue; } } else if (strlen(argv[i]) == 8 && strcmp(argv[i], "-minlife") == 0) { if (++i > argc - 2) return (-1); else { date = get_date(argv[i], NULL); if (date == (time_t)-1) { fprintf(stderr, gettext("Invalid date " "specification " "\"%s\".\n"), argv[i]); return (-1); } if (date <= now) { fprintf(stderr, gettext("Date specified is " "in the past " "\"%s\".\n"), argv[i]); return (-1); } policy->pw_min_life = date - now; *mask |= KADM5_PW_MIN_LIFE; continue; } } else if (strlen(argv[i]) == 10 && strcmp(argv[i], "-minlength") == 0) { if (++i > argc - 2) return (-1); else { policy->pw_min_length = atoi(argv[i]); *mask |= KADM5_PW_MIN_LENGTH; continue; } } else if (strlen(argv[i]) == 11 && strcmp(argv[i], "-minclasses") == 0) { if (++i > argc - 2) return (-1); else { policy->pw_min_classes = atoi(argv[i]); *mask |= KADM5_PW_MIN_CLASSES; continue; } } else if (strlen(argv[i]) == 8 && strcmp(argv[i], "-history") == 0) { if (++i > argc - 2) return (-1); else { policy->pw_history_num = atoi(argv[i]); *mask |= KADM5_PW_HISTORY_NUM; continue; } } else return (-1); } if (i != argc -1) { fprintf(stderr, gettext("%s: parser lost count!\n"), caller); return (-1); } else return (0); } void kadmin_addmodpol_usage(func) char *func; { fprintf(stderr, "%s: %s %s\n", gettext("usage"), func, gettext("[options] policy")); fprintf(stderr, gettext("\toptions are:\n")); fprintf(stderr, "\t\t[-maxlife time] [-minlife time] " "[-minlength length]\n\t\t[-minclasses number] " "[-history number]\n"); } void kadmin_addpol(argc, argv) int argc; char *argv[]; { krb5_error_code retval; long mask; kadm5_policy_ent_rec policy; memset(&policy, 0, sizeof(policy)); if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) { kadmin_addmodpol_usage("add_policy"); return; } else { policy.policy = argv[argc - 1]; mask |= KADM5_POLICY; retval = kadm5_create_policy(handle, &policy, mask); if (retval) { com_err("add_policy", retval, gettext("while creating policy \"%s\"."), policy.policy); return; } } } void kadmin_modpol(argc, argv) int argc; char *argv[]; { krb5_error_code retval; long mask; kadm5_policy_ent_rec policy; memset(&policy, 0, sizeof(policy)); if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "modify_policy")) { kadmin_addmodpol_usage("modify_policy"); return; } else { policy.policy = argv[argc - 1]; retval = kadm5_modify_policy(handle, &policy, mask); if (retval) { com_err("modify_policy", retval, gettext("while modifying policy \"%s\"."), policy.policy); return; } } } void kadmin_delpol(argc, argv) int argc; char *argv[]; { krb5_error_code retval; char reply[32]; if (! (argc == 2 || (argc == 3 && strcmp("-force", argv[1]) == 0))) { fprintf(stderr, "%s: delete_policy [-force] %s\n", gettext("usage"), gettext("policy")); return; } if (argc == 2) { printf(gettext("Are you sure you want to delete the policy " "\"%s\"? (yes/no): "), argv[1]); fgets(reply, sizeof (reply), stdin); if (strncmp(gettext("yes\n"), reply, sizeof (reply)) && strncmp(gettext("y\n"), reply, sizeof (reply)) && strncmp(gettext("Y\n"), reply, sizeof (reply)) ) { fprintf(stderr, gettext("Policy \"%s\" not deleted.\n"), argv[1]); return; } } retval = kadm5_delete_policy(handle, argv[argc - 1]); if (retval) { com_err("delete_policy:", retval, gettext("while deleting policy \"%s\""), argv[argc - 1]); return; } } void kadmin_getpol(argc, argv) int argc; char *argv[]; { krb5_error_code retval; kadm5_policy_ent_rec policy; if (! (argc == 2 || (argc == 3 && strcmp("-terse", argv[1]) == 0))) { fprintf(stderr, "%s: get_policy [-terse] %s\n", gettext("usage"), gettext("policy")); return; } retval = kadm5_get_policy(handle, argv[argc - 1], &policy); if (retval) { com_err("get_policy", retval, gettext("while retrieving policy \"%s\"."), argv[argc - 1]); return; } if (argc == 2) { printf(gettext("Policy: %s\n"), policy.policy); printf(gettext("Maximum password life: %d\n"), policy.pw_max_life); printf(gettext("Minimum password life: %d\n"), policy.pw_min_life); printf(gettext("Minimum password length: %d\n"), policy.pw_min_length); printf(gettext("Minimum number of password " "character classes: %d\n"), policy.pw_min_classes); printf(gettext("Number of old keys kept: %d\n"), policy.pw_history_num); printf(gettext("Reference count: %d\n"), policy.policy_refcnt); } else { printf("\"%s\"\t%d\t%d\t%d\t%d\t%d\t%d\n", policy.policy, policy.pw_max_life, policy.pw_min_life, policy.pw_min_length, policy.pw_min_classes, policy.pw_history_num, policy.policy_refcnt); } kadm5_free_policy_ent(handle, &policy); } void kadmin_getpols(argc, argv) int argc; char *argv[]; { krb5_error_code retval; char *exp, **names; int i, count; exp = NULL; if (! (argc == 1 || (argc == 2 && (exp = argv[1])))) { fprintf(stderr, "%s: get_policies %s\n", gettext("usage"), gettext("[expression]\n")); return; } retval = kadm5_get_policies(handle, exp, &names, &count); if (retval) { com_err("get_policies", retval, gettext("while retrieving list.")); return; } for (i = 0; i < count; i++) printf("%s\n", names[i]); kadm5_free_name_list(handle, names, count); }