Path: blob/main/crypto/krb5/src/kadmin/cli/kadmin.c
34907 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* Copyright 1994, 2008 by the Massachusetts Institute of Technology.3* All Rights Reserved.4*5* Export of this software from the United States of America may6* require a specific license from the United States Government.7* It is the responsibility of any person or organization contemplating8* export to obtain such a license before exporting.9*10* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and11* distribute this software and its documentation for any purpose and12* without fee is hereby granted, provided that the above copyright13* notice appear in all copies and that both that copyright notice and14* this permission notice appear in supporting documentation, and that15* the name of M.I.T. not be used in advertising or publicity pertaining16* to distribution of the software without specific, written prior17* permission. Furthermore if you modify this software you must label18* your software as modified software and not distribute it in such a19* fashion that it might be confused with the original M.I.T. software.20* M.I.T. makes no representations about the suitability of21* this software for any purpose. It is provided "as is" without express22* or implied warranty.23*/24/*25* Copyright 2004 Sun Microsystems, Inc. All rights reserved.26* Use is subject to license terms.27*/2829/* Base functions for a kadmin command line interface using the OVSecure30* library */3132/* for "_" macro */33#include "k5-int.h"34#include <kadm5/admin.h>35#include <adm_proto.h>36#include <errno.h>37#include <stdio.h>38#include <string.h>39#include <ctype.h>40#include <sys/types.h>41#include <math.h>42#include <unistd.h>43#include <pwd.h>44/* #include <sys/timeb.h> */45#include <time.h>46#include "kadmin.h"4748static krb5_boolean script_mode = FALSE;49int exit_status = 0;50char *def_realm = NULL;51char *whoami = NULL;5253void *handle = NULL;54krb5_context context;55char *ccache_name = NULL;5657int locked = 0;5859static void60info(const char *fmt, ...)61#if !defined(__cplusplus) && (__GNUC__ > 2)62__attribute__((__format__(__printf__, 1, 2)))63#endif64;6566static void67error(const char *fmt, ...)68#if !defined(__cplusplus) && (__GNUC__ > 2)69__attribute__((__format__(__printf__, 1, 2)))70#endif71;7273/* Like printf, but suppressed if script_mode is set. */74static void75info(const char *fmt, ...)76{77va_list ap;7879if (script_mode)80return;81va_start(ap, fmt);82vprintf(fmt, ap);83va_end(ap);84}8586/* Like fprintf to stderr; also set exit_status if script_mode is set. */87static void88error(const char *fmt, ...)89{90va_list ap;9192if (script_mode)93exit_status = 1;94va_start(ap, fmt);95vfprintf(stderr, fmt, ap);96va_end(ap);97}9899static void100usage(void)101{102error(_("Usage: %s [-r realm] [-p principal] [-q query] "103"[clnt|local args]\n"104" [command args...]\n"105"\tclnt args: [-s admin_server[:port]] "106"[[-c ccache]|[-k [-t keytab]]]|[-n] [-O | -N]\n"107"\tlocal args: [-x db_args]* [-d dbname] "108"[-e \"enc:salt ...\"] [-m] [-w password] "109"where,\n\t[-x db_args]* - any number of database specific "110"arguments.\n"111"\t\t\tLook at each database documentation for supported "112"arguments\n"), whoami);113exit(1);114}115116static char *117strdur(time_t duration)118{119static char out[50];120int neg, days, hours, minutes, seconds;121122if (duration < 0) {123duration *= -1;124neg = 1;125} else126neg = 0;127days = duration / (24 * 3600);128duration %= 24 * 3600;129hours = duration / 3600;130duration %= 3600;131minutes = duration / 60;132duration %= 60;133seconds = duration;134snprintf(out, sizeof(out), "%s%d %s %02d:%02d:%02d", neg ? "-" : "",135days, days == 1 ? "day" : "days",136hours, minutes, seconds);137return out;138}139140static const char *141strdate(krb5_timestamp when)142{143struct tm *tm;144static char out[40];145time_t lcltim = ts2tt(when);146147tm = localtime(&lcltim);148if (tm == NULL ||149strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm) == 0)150strlcpy(out, "(error)", sizeof(out));151return out;152}153154/* Parse a date string using getdate.y. On failure, output an error message155* and return (time_t)-1. */156static time_t157parse_date(char *str, time_t now)158{159time_t date;160161date = get_date_rel(str, now);162if (date == (time_t)-1)163error(_("Invalid date specification \"%s\".\n"), str);164return date;165}166167/*168* Parse a time interval. Use krb5_string_to_deltat() if it works; otherwise169* use getdate.y and subtract now, with sanity checks. On failure, output an170* error message and return (time_t)-1.171*/172static time_t173parse_interval(char *str, time_t now)174{175time_t date;176krb5_deltat delta;177178if (krb5_string_to_deltat(str, &delta) == 0)179return delta;180181date = parse_date(str, now);182if (date == (time_t)-1)183return date;184185/* Interpret an absolute time of 0 (e.g. "never") as an interval of 0. */186if (date == 0)187return 0;188189/* Don't return a negative interval if the date is in the past. */190if (date < now) {191error(_("Interval specification \"%s\" is in the past.\n"), str);192return (time_t)-1;193}194195return date - now;196}197198/* this is a wrapper to go around krb5_parse_principal so we can set199the default realm up properly */200static krb5_error_code201kadmin_parse_name(char *name, krb5_principal *principal)202{203char *cp, *fullname;204krb5_error_code retval;205int result;206207/* assumes def_realm is initialized! */208cp = strchr(name, '@');209while (cp) {210if (cp - name && *(cp - 1) != '\\')211break;212else213cp = strchr(cp + 1, '@');214}215if (cp == NULL)216result = asprintf(&fullname, "%s@%s", name, def_realm);217else218result = asprintf(&fullname, "%s", name);219if (result < 0)220return ENOMEM;221retval = krb5_parse_name(context, fullname, principal);222free(fullname);223return retval;224}225226static void227extended_com_err_fn(const char *myprog, errcode_t code,228const char *fmt, va_list args)229{230const char *emsg;231232if (code) {233emsg = krb5_get_error_message(context, code);234error("%s: %s ", myprog, emsg);235krb5_free_error_message(context, emsg);236} else {237error("%s: ", myprog);238}239vfprintf(stderr, fmt, args);240error("\n");241}242243/* Create a principal using the oldest appropriate kadm5 API. */244static krb5_error_code245create_princ(kadm5_principal_ent_rec *princ, long mask, int n_ks,246krb5_key_salt_tuple *ks, char *pass)247{248if (ks)249return kadm5_create_principal_3(handle, princ, mask, n_ks, ks, pass);250else251return kadm5_create_principal(handle, princ, mask, pass);252}253254/* Randomize a principal's password using the appropriate kadm5 API. */255krb5_error_code256randkey_princ(void *lhandle, krb5_principal princ, krb5_boolean keepold,257int n_ks, krb5_key_salt_tuple *ks, krb5_keyblock **key,258int *n_keys)259{260krb5_error_code ret;261262/* Try the newer API first, because the Solaris kadmind only creates DES263* keys when the old API is used. */264ret = kadm5_randkey_principal_3(lhandle, princ, keepold, n_ks, ks, key,265n_keys);266267/* Fall back to the old version if we get an error and aren't using any new268* parameters. */269if (ret == KADM5_RPC_ERROR && !keepold && ks == NULL)270ret = kadm5_randkey_principal(lhandle, princ, key, n_keys);271272return ret;273}274275static krb5_boolean276policy_exists(const char *name)277{278kadm5_policy_ent_rec pol;279280if (kadm5_get_policy(handle, (char *)name, &pol) != 0)281return FALSE;282kadm5_free_policy_ent(handle, &pol);283return TRUE;284}285286void287kadmin_startup(int argc, char *argv[], char **request_out, char ***args_out)288{289extern char *optarg;290char *princstr = NULL, *keytab_name = NULL, *query = NULL;291char *password = NULL;292char *luser, *canon, *cp;293int optchar, freeprinc = 0, use_keytab = 0, use_anonymous = 0;294struct passwd *pw;295kadm5_ret_t retval;296krb5_ccache cc;297krb5_principal princ;298kadm5_config_params params;299char **db_args = NULL;300size_t db_args_size = 0;301char *db_name = NULL;302char *svcname, *realm;303304memset(¶ms, 0, sizeof(params));305306set_com_err_hook(extended_com_err_fn);307308retval = kadm5_init_krb5_context(&context);309if (retval) {310com_err(whoami, retval, _("while initializing krb5 library"));311exit(1);312}313314while ((optchar = getopt(argc, argv,315"+x:r:p:knq:w:d:s:mc:t:e:ON")) != EOF) {316switch (optchar) {317case 'x':318db_args_size++;319db_args = realloc(db_args, sizeof(char*) * (db_args_size + 1));320if (db_args == NULL) {321error(_("%s: Cannot initialize. Not enough memory\n"), whoami);322exit(1);323}324db_args[db_args_size - 1] = optarg;325db_args[db_args_size] = NULL;326break;327328case 'r':329def_realm = optarg;330break;331case 'p':332princstr = optarg;333break;334case 'c':335ccache_name = optarg;336break;337case 'k':338use_keytab++;339break;340case 'n':341use_anonymous++;342break;343case 't':344keytab_name = optarg;345break;346case 'w':347password = optarg;348break;349case 'q':350query = optarg;351break;352case 'd':353/* db_name has to be passed as part of the db_args. */354free(db_name);355asprintf(&db_name, "dbname=%s", optarg);356357db_args_size++;358db_args = realloc(db_args, sizeof(char*) * (db_args_size + 1));359if (db_args == NULL) {360error(_("%s: Cannot initialize. Not enough memory\n"), whoami);361exit(1);362}363db_args[db_args_size - 1] = db_name;364db_args[db_args_size] = NULL;365break;366case 's':367params.admin_server = optarg;368params.mask |= KADM5_CONFIG_ADMIN_SERVER;369break;370case 'm':371params.mkey_from_kbd = 1;372params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;373break;374case 'e':375retval = krb5_string_to_keysalts(optarg, NULL, NULL, 0,376¶ms.keysalts,377¶ms.num_keysalts);378if (retval) {379com_err(whoami, retval, _("while parsing keysalts %s"),380optarg);381exit(1);382}383params.mask |= KADM5_CONFIG_ENCTYPES;384break;385case 'O':386params.mask |= KADM5_CONFIG_OLD_AUTH_GSSAPI;387break;388case 'N':389params.mask |= KADM5_CONFIG_AUTH_NOFALLBACK;390break;391default:392usage();393}394}395if ((ccache_name && use_keytab) ||396(keytab_name && !use_keytab) ||397(ccache_name && use_anonymous) ||398(use_anonymous && use_keytab))399usage();400401if (query != NULL && argv[optind] != NULL) {402error(_("%s: -q is exclusive with command-line query"), whoami);403usage();404}405406if (argv[optind] != NULL)407script_mode = TRUE;408409if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) {410error(_("%s: unable to get default realm\n"), whoami);411exit(1);412}413414params.mask |= KADM5_CONFIG_REALM;415params.realm = def_realm;416417if (params.mask & KADM5_CONFIG_OLD_AUTH_GSSAPI)418svcname = KADM5_ADMIN_SERVICE;419else420svcname = NULL;421422/*423* Set cc to an open credentials cache, either specified by the -c424* argument or the default.425*/426if (ccache_name == NULL) {427retval = krb5_cc_default(context, &cc);428if (retval) {429com_err(whoami, retval,430_("while opening default credentials cache"));431exit(1);432}433} else {434retval = krb5_cc_resolve(context, ccache_name, &cc);435if (retval) {436com_err(whoami, retval, _("while opening credentials cache %s"),437ccache_name);438exit(1);439}440}441442/*443* If no principal name is specified: If authenticating anonymously, use444* the anonymous principal for the local realm, else if a ccache was445* specified and its primary principal name can be read, it is used, else446* if a keytab was specified, the principal name is host/hostname,447* otherwise append "/admin" to the primary name of the default ccache,448* $USER, or pw_name.449*450* Gee, 100+ lines to figure out the client principal name. This451* should be compressed...452*/453454if (princstr == NULL) {455if (use_anonymous) {456if (asprintf(&princstr, "%s/%s@%s", KRB5_WELLKNOWN_NAMESTR,457KRB5_ANONYMOUS_PRINCSTR, def_realm) < 0) {458error(_("%s: out of memory\n"), whoami);459exit(1);460}461freeprinc++;462} else if (ccache_name != NULL &&463!krb5_cc_get_principal(context, cc, &princ)) {464retval = krb5_unparse_name(context, princ, &princstr);465if (retval) {466com_err(whoami, retval,467_("while canonicalizing principal name"));468exit(1);469}470krb5_free_principal(context, princ);471freeprinc++;472} else if (use_keytab != 0) {473retval = krb5_sname_to_principal(context, NULL, "host",474KRB5_NT_SRV_HST, &princ);475if (retval) {476com_err(whoami, retval, _("creating host service principal"));477exit(1);478}479retval = krb5_unparse_name(context, princ, &princstr);480if (retval) {481com_err(whoami, retval,482_("while canonicalizing principal name"));483exit(1);484}485krb5_free_principal(context, princ);486freeprinc++;487} else if (!krb5_cc_get_principal(context, cc, &princ)) {488if (krb5_unparse_name(context, princ, &canon)) {489error(_("%s: unable to canonicalize principal\n"), whoami);490exit(1);491}492/* Strip out realm of principal if it's there. */493realm = strchr(canon, '@');494while (realm) {495if (realm > canon && *(realm - 1) != '\\')496break;497realm = strchr(realm + 1, '@');498}499if (realm)500*realm++ = '\0';501cp = strchr(canon, '/');502while (cp) {503if (cp > canon && *(cp - 1) != '\\')504break;505cp = strchr(cp + 1, '/');506}507if (cp != NULL)508*cp = '\0';509if (asprintf(&princstr, "%s/admin%s%s", canon,510(realm) ? "@" : "",511(realm) ? realm : "") < 0) {512error(_("%s: out of memory\n"), whoami);513exit(1);514}515free(canon);516krb5_free_principal(context, princ);517freeprinc++;518} else if ((luser = getenv("USER"))) {519if (asprintf(&princstr, "%s/admin@%s", luser, def_realm) < 0) {520error(_("%s: out of memory\n"), whoami);521exit(1);522}523freeprinc++;524} else if ((pw = getpwuid(getuid()))) {525if (asprintf(&princstr, "%s/admin@%s", pw->pw_name,526def_realm) < 0) {527error(_("%s: out of memory\n"), whoami);528exit(1);529}530freeprinc++;531} else {532error(_("%s: unable to figure out a principal name\n"), whoami);533exit(1);534}535}536537retval = krb5_klog_init(context, "admin_server", whoami, 0);538if (retval) {539com_err(whoami, retval, _("while setting up logging"));540exit(1);541}542543/*544* Initialize the kadm5 connection. If we were given a ccache,545* use it. Otherwise, use/prompt for the password.546*/547if (ccache_name) {548info(_("Authenticating as principal %s with existing "549"credentials.\n"), princstr);550retval = kadm5_init_with_creds(context, princstr, cc, svcname, ¶ms,551KADM5_STRUCT_VERSION,552KADM5_API_VERSION_4, db_args, &handle);553} else if (use_anonymous) {554info(_("Authenticating as principal %s with password; "555"anonymous requested.\n"), princstr);556retval = kadm5_init_anonymous(context, princstr, svcname, ¶ms,557KADM5_STRUCT_VERSION,558KADM5_API_VERSION_4, db_args, &handle);559} else if (use_keytab) {560if (keytab_name != NULL) {561info(_("Authenticating as principal %s with keytab %s.\n"),562princstr, keytab_name);563} else {564info(_("Authenticating as principal %s with default keytab.\n"),565princstr);566}567retval = kadm5_init_with_skey(context, princstr, keytab_name, svcname,568¶ms, KADM5_STRUCT_VERSION,569KADM5_API_VERSION_4, db_args, &handle);570} else {571info(_("Authenticating as principal %s with password.\n"),572princstr);573retval = kadm5_init_with_password(context, princstr, password, svcname,574¶ms, KADM5_STRUCT_VERSION,575KADM5_API_VERSION_4, db_args,576&handle);577}578if (retval) {579com_err(whoami, retval, _("while initializing %s interface"), whoami);580if (retval == KADM5_BAD_CLIENT_PARAMS ||581retval == KADM5_BAD_SERVER_PARAMS)582usage();583exit(1);584}585if (freeprinc)586free(princstr);587588free(params.keysalts);589free(db_name);590free(db_args);591592retval = krb5_cc_close(context, cc);593if (retval) {594com_err(whoami, retval, _("while closing ccache %s"), ccache_name);595exit(1);596}597598retval = kadm5_init_iprop(handle, 0);599if (retval) {600com_err(whoami, retval, _("while mapping update log"));601exit(1);602}603604*request_out = query;605*args_out = argv + optind;606}607608int609quit(void)610{611kadm5_ret_t retval;612613if (locked) {614retval = kadm5_unlock(handle);615if (retval) {616com_err("quit", retval, _("while unlocking locked database"));617return 1;618}619locked = 0;620}621622kadm5_destroy(handle);623if (ccache_name != NULL && !script_mode) {624fprintf(stderr, "\n\a\a\a%s",625_("Administration credentials NOT DESTROYED.\n"));626}627628/* insert more random cleanup here */629krb5_klog_close(context);630krb5_free_context(context);631return 0;632}633634void635kadmin_lock(int argc, char *argv[], int sci_idx, void *info_ptr)636{637kadm5_ret_t retval;638639if (locked)640return;641retval = kadm5_lock(handle);642if (retval) {643com_err("lock", retval, "");644return;645}646locked = 1;647}648649void650kadmin_unlock(int argc, char *argv[], int sci_idx, void *info_ptr)651{652kadm5_ret_t retval;653654if (!locked)655return;656retval = kadm5_unlock(handle);657if (retval) {658com_err("unlock", retval, "");659return;660}661locked = 0;662}663664void665kadmin_delprinc(int argc, char *argv[], int sci_idx, void *info_ptr)666{667kadm5_ret_t retval;668krb5_principal princ = NULL;669char *canon = NULL;670char reply[5];671672if (! (argc == 2 ||673(argc == 3 && !strcmp("-force", argv[1])))) {674error(_("usage: delete_principal [-force] principal\n"));675return;676}677retval = kadmin_parse_name(argv[argc - 1], &princ);678if (retval) {679com_err("delete_principal", retval, _("while parsing principal name"));680return;681}682retval = krb5_unparse_name(context, princ, &canon);683if (retval) {684com_err("delete_principal", retval,685_("while canonicalizing principal"));686goto cleanup;687}688if (argc == 2 && !script_mode) {689printf(_("Are you sure you want to delete the principal \"%s\"? "690"(yes/no): "), canon);691fgets(reply, sizeof (reply), stdin);692if (strcmp("yes\n", reply)) {693fprintf(stderr, _("Principal \"%s\" not deleted\n"), canon);694goto cleanup;695}696}697retval = kadm5_delete_principal(handle, princ);698if (retval) {699com_err("delete_principal", retval,700_("while deleting principal \"%s\""), canon);701goto cleanup;702}703info(_("Principal \"%s\" deleted.\n"), canon);704info(_("Make sure that you have removed this principal from all ACLs "705"before reusing.\n"));706707cleanup:708krb5_free_principal(context, princ);709free(canon);710}711712void713kadmin_renameprinc(int argc, char *argv[], int sci_idx, void *info_ptr)714{715kadm5_ret_t retval;716krb5_principal oprinc = NULL, nprinc = NULL;717char *ocanon = NULL, *ncanon = NULL;718char reply[5];719720if (!(argc == 3 || (argc == 4 && !strcmp("-force", argv[1])))) {721error(_("usage: rename_principal [-force] old_principal "722"new_principal\n"));723return;724}725retval = kadmin_parse_name(argv[argc - 2], &oprinc);726if (retval) {727com_err("rename_principal", retval,728_("while parsing old principal name"));729goto cleanup;730}731retval = kadmin_parse_name(argv[argc - 1], &nprinc);732if (retval) {733com_err("rename_principal", retval,734_("while parsing new principal name"));735goto cleanup;736}737retval = krb5_unparse_name(context, oprinc, &ocanon);738if (retval) {739com_err("rename_principal", retval,740_("while canonicalizing old principal"));741goto cleanup;742}743retval = krb5_unparse_name(context, nprinc, &ncanon);744if (retval) {745com_err("rename_principal", retval,746_("while canonicalizing new principal"));747goto cleanup;748}749if (argc == 3 && !script_mode) {750printf(_("Are you sure you want to rename the principal \"%s\" "751"to \"%s\"? (yes/no): "), ocanon, ncanon);752fgets(reply, sizeof(reply), stdin);753if (strcmp("yes\n", reply)) {754fprintf(stderr, _("Principal \"%s\" not renamed\n"), ocanon);755goto cleanup;756}757}758retval = kadm5_rename_principal(handle, oprinc, nprinc);759if (retval) {760com_err("rename_principal", retval,761_("while renaming principal \"%s\" to \"%s\""),762ocanon, ncanon);763goto cleanup;764}765info(_("Principal \"%s\" renamed to \"%s\".\n"), ocanon, ncanon);766info(_("Make sure that you have removed the old principal from all ACLs "767"before reusing.\n"));768769cleanup:770krb5_free_principal(context, nprinc);771krb5_free_principal(context, oprinc);772free(ncanon);773free(ocanon);774}775776void777kadmin_addalias(int argc, char *argv[], int sci_idx, void *info_ptr)778{779kadm5_ret_t retval;780krb5_principal alias = NULL, target = NULL;781char *acanon = NULL, *tcanon = NULL;782783if (argc != 3) {784error(_("usage: add_alias alias_principal target_principal\n"));785return;786}787retval = kadmin_parse_name(argv[1], &alias);788if (retval) {789com_err("add_alias", retval, _("while parsing alias principal name"));790goto cleanup;791}792retval = kadmin_parse_name(argv[2], &target);793if (retval) {794com_err("add_alias", retval, _("while parsing target principal name"));795goto cleanup;796}797retval = krb5_unparse_name(context, alias, &acanon);798if (retval) {799com_err("add_alias", retval,800_("while canonicalizing alias principal"));801goto cleanup;802}803retval = krb5_unparse_name(context, target, &tcanon);804if (retval) {805com_err("add_alias", retval,806_("while canonicalizing target principal"));807goto cleanup;808}809retval = kadm5_create_alias(handle, alias, target);810if (retval) {811com_err("add_alias", retval,812_("while aliasing principal \"%s\" to \"%s\""),813acanon, tcanon);814goto cleanup;815}816info(_("Principal \"%s\" aliased to \"%s\".\n"), acanon, tcanon);817818cleanup:819krb5_free_principal(context, alias);820krb5_free_principal(context, target);821free(acanon);822free(tcanon);823}824825static void826cpw_usage(const char *str)827{828if (str)829error("%s\n", str);830error(_("usage: change_password [-randkey] [-keepold] "831"[-e keysaltlist] [-pw password] principal\n"));832}833834void835kadmin_cpw(int argc, char *argv[], int sci_idx, void *info_ptr)836{837kadm5_ret_t retval;838static char newpw[1024];839static char prompt1[1024], prompt2[1024];840char *canon = NULL, *pwarg = NULL;841int n_ks_tuple = 0, randkey = 0;842krb5_boolean keepold = FALSE;843krb5_key_salt_tuple *ks_tuple = NULL;844krb5_principal princ = NULL;845char **db_args = NULL;846size_t db_args_size = 0;847848if (argc < 1) {849cpw_usage(NULL);850return;851}852for (argv++, argc--; argc > 0 && **argv == '-'; argc--, argv++) {853if (!strcmp("-x", *argv)) {854argc--;855if (argc < 1) {856cpw_usage(_("change_password: missing db argument"));857goto cleanup;858}859db_args_size++;860db_args = realloc(db_args, sizeof(char*) * (db_args_size + 1));861if (db_args == NULL) {862error(_("change_password: Not enough memory\n"));863exit(1);864}865db_args[db_args_size - 1] = *++argv;866db_args[db_args_size] = NULL;867} else if (!strcmp("-pw", *argv)) {868argc--;869if (argc < 1) {870cpw_usage(_("change_password: missing password arg"));871goto cleanup;872}873pwarg = *++argv;874} else if (!strcmp("-randkey", *argv)) {875randkey++;876} else if (!strcmp("-keepold", *argv)) {877keepold = TRUE;878} else if (!strcmp("-e", *argv)) {879argc--;880if (argc < 1) {881cpw_usage(_("change_password: missing keysaltlist arg"));882goto cleanup;883}884retval = krb5_string_to_keysalts(*++argv, NULL, NULL, 0,885&ks_tuple, &n_ks_tuple);886if (retval) {887com_err("change_password", retval,888_("while parsing keysalts %s"), *argv);889goto cleanup;890}891} else {892com_err("change_password", 0, _("unrecognized option %s"), *argv);893cpw_usage(NULL);894goto cleanup;895}896}897if (argc != 1) {898if (argc < 1)899com_err("change_password", 0, _("missing principal name"));900else901com_err("change_password", 0, _("too many arguments"));902cpw_usage(NULL);903goto cleanup;904}905retval = kadmin_parse_name(*argv, &princ);906if (retval) {907com_err("change_password", retval, _("while parsing principal name"));908goto cleanup;909}910retval = krb5_unparse_name(context, princ, &canon);911if (retval) {912com_err("change_password", retval,913_("while canonicalizing principal"));914goto cleanup;915}916if (pwarg != NULL) {917if (keepold || ks_tuple != NULL) {918retval = kadm5_chpass_principal_3(handle, princ, keepold,919n_ks_tuple, ks_tuple, pwarg);920} else {921retval = kadm5_chpass_principal(handle, princ, pwarg);922}923if (retval) {924com_err("change_password", retval,925_("while changing password for \"%s\"."), canon);926goto cleanup;927}928info(_("Password for \"%s\" changed.\n"), canon);929} else if (randkey) {930retval = randkey_princ(handle, princ, keepold, n_ks_tuple, ks_tuple,931NULL, NULL);932if (retval) {933com_err("change_password", retval,934_("while randomizing key for \"%s\"."), canon);935goto cleanup;936}937info(_("Key for \"%s\" randomized.\n"), canon);938} else {939unsigned int i = sizeof (newpw) - 1;940941snprintf(prompt1, sizeof(prompt1),942_("Enter password for principal \"%s\""), canon);943snprintf(prompt2, sizeof(prompt2),944_("Re-enter password for principal \"%s\""), canon);945retval = krb5_read_password(context, prompt1, prompt2,946newpw, &i);947if (retval) {948com_err("change_password", retval,949_("while reading password for \"%s\"."), canon);950goto cleanup;951}952if (keepold || ks_tuple != NULL) {953retval = kadm5_chpass_principal_3(handle, princ, keepold,954n_ks_tuple, ks_tuple,955newpw);956} else {957retval = kadm5_chpass_principal(handle, princ, newpw);958}959memset(newpw, 0, sizeof (newpw));960if (retval) {961com_err("change_password", retval,962_("while changing password for \"%s\"."), canon);963goto cleanup;964}965info(_("Password for \"%s\" changed.\n"), canon);966}967cleanup:968free(canon);969free(db_args);970krb5_free_principal(context, princ);971free(ks_tuple);972}973974static void975kadmin_free_tl_data(krb5_int16 *n_tl_datap, krb5_tl_data **tl_datap)976{977krb5_tl_data *tl_data = *tl_datap, *next;978int n_tl_data = *n_tl_datap;979int i;980981*n_tl_datap = 0;982*tl_datap = NULL;983984for (i = 0; tl_data && (i < n_tl_data); i++) {985next = tl_data->tl_data_next;986free(tl_data->tl_data_contents);987free(tl_data);988tl_data = next;989}990}991992/* Construct a tl_data element and add it to the tail of *tl_datap. */993static void994add_tl_data(krb5_int16 *n_tl_datap, krb5_tl_data **tl_datap,995krb5_int16 tl_type, krb5_ui_2 len, krb5_octet *contents)996{997krb5_tl_data *tl_data;998krb5_octet *copy;9991000copy = malloc(len);1001tl_data = calloc(1, sizeof(*tl_data));1002if (copy == NULL || tl_data == NULL) {1003error(_("Not enough memory\n"));1004exit(1);1005}1006memcpy(copy, contents, len);10071008tl_data->tl_data_type = tl_type;1009tl_data->tl_data_length = len;1010tl_data->tl_data_contents = copy;1011tl_data->tl_data_next = NULL;10121013for (; *tl_datap != NULL; tl_datap = &(*tl_datap)->tl_data_next);1014*tl_datap = tl_data;1015(*n_tl_datap)++;1016}10171018static void1019unlock_princ(kadm5_principal_ent_t princ, long *mask, const char *caller)1020{1021krb5_error_code retval;1022krb5_timestamp now;1023krb5_octet timebuf[4];10241025/* Zero out the failed auth count. */1026princ->fail_auth_count = 0;1027*mask |= KADM5_FAIL_AUTH_COUNT;10281029/* Record the timestamp of this unlock operation so that replica KDCs will1030* see it, since fail_auth_count is unreplicated. */1031retval = krb5_timeofday(context, &now);1032if (retval) {1033com_err(caller, retval, _("while getting time"));1034exit(1);1035}1036store_32_le((krb5_int32)now, timebuf);1037add_tl_data(&princ->n_tl_data, &princ->tl_data,1038KRB5_TL_LAST_ADMIN_UNLOCK, 4, timebuf);1039*mask |= KADM5_TL_DATA;1040}10411042/*1043* Parse addprinc or modprinc arguments. Some output fields may be1044* filled in on error.1045*/1046static int1047kadmin_parse_princ_args(int argc, char *argv[], kadm5_principal_ent_t oprinc,1048long *mask, char **pass, krb5_boolean *randkey,1049krb5_boolean *nokey, krb5_key_salt_tuple **ks_tuple,1050int *n_ks_tuple, char *caller)1051{1052int i;1053time_t now, date, interval;1054krb5_error_code retval;10551056*mask = 0;1057*pass = NULL;1058*n_ks_tuple = 0;1059*ks_tuple = NULL;1060time(&now);1061*randkey = FALSE;1062*nokey = FALSE;1063for (i = 1; i < argc - 1; i++) {1064if (!strcmp("-x",argv[i])) {1065if (++i > argc - 2)1066return -1;10671068add_tl_data(&oprinc->n_tl_data, &oprinc->tl_data,1069KRB5_TL_DB_ARGS, strlen(argv[i]) + 1,1070(krb5_octet *)argv[i]);1071*mask |= KADM5_TL_DATA;1072continue;1073}1074if (!strcmp("-expire", argv[i])) {1075if (++i > argc - 2)1076return -1;1077date = parse_date(argv[i], now);1078if (date == (time_t)-1)1079return -1;1080oprinc->princ_expire_time = date;1081*mask |= KADM5_PRINC_EXPIRE_TIME;1082continue;1083}1084if (!strcmp("-pwexpire", argv[i])) {1085if (++i > argc - 2)1086return -1;1087date = parse_date(argv[i], now);1088if (date == (time_t)-1)1089return -1;1090oprinc->pw_expiration = date;1091*mask |= KADM5_PW_EXPIRATION;1092continue;1093}1094if (!strcmp("-maxlife", argv[i])) {1095if (++i > argc - 2)1096return -1;1097interval = parse_interval(argv[i], now);1098if (interval == (time_t)-1)1099return -1;1100oprinc->max_life = interval;1101*mask |= KADM5_MAX_LIFE;1102continue;1103}1104if (!strcmp("-maxrenewlife", argv[i])) {1105if (++i > argc - 2)1106return -1;1107interval = parse_interval(argv[i], now);1108if (interval == (time_t)-1)1109return -1;1110oprinc->max_renewable_life = interval;1111*mask |= KADM5_MAX_RLIFE;1112continue;1113}1114if (!strcmp("-kvno", argv[i])) {1115if (++i > argc - 2)1116return -1;1117oprinc->kvno = atoi(argv[i]);1118*mask |= KADM5_KVNO;1119continue;1120}1121if (!strcmp("-policy", argv[i])) {1122if (++i > argc - 2)1123return -1;1124oprinc->policy = argv[i];1125*mask |= KADM5_POLICY;1126continue;1127}1128if (!strcmp("-clearpolicy", argv[i])) {1129oprinc->policy = NULL;1130*mask |= KADM5_POLICY_CLR;1131continue;1132}1133if (!strcmp("-pw", argv[i])) {1134if (++i > argc - 2)1135return -1;1136*pass = argv[i];1137continue;1138}1139if (!strcmp("-randkey", argv[i])) {1140*randkey = TRUE;1141continue;1142}1143if (!strcmp("-nokey", argv[i])) {1144*nokey = TRUE;1145continue;1146}1147if (!strcmp("-unlock", argv[i])) {1148unlock_princ(oprinc, mask, caller);1149continue;1150}1151if (!strcmp("-e", argv[i])) {1152if (++i > argc - 2)1153return -1;1154retval = krb5_string_to_keysalts(argv[i], NULL, NULL, 0,1155ks_tuple, n_ks_tuple);1156if (retval) {1157com_err(caller, retval, _("while parsing keysalts %s"),1158argv[i]);1159return -1;1160}1161continue;1162}1163retval = krb5_flagspec_to_mask(argv[i], &oprinc->attributes,1164&oprinc->attributes);1165if (retval)1166return -1;1167else1168*mask |= KADM5_ATTRIBUTES;1169}1170if (i != argc - 1)1171return -1;1172retval = kadmin_parse_name(argv[i], &oprinc->principal);1173if (retval) {1174com_err(caller, retval, _("while parsing principal"));1175return -1;1176}1177return 0;1178}11791180static void1181kadmin_addprinc_usage(void)1182{1183error(_("usage: add_principal [options] principal\n"));1184error(_("\toptions are:\n"));1185error(_("\t\t[-randkey|-nokey] [-x db_princ_args]* [-expire expdate] "1186"[-pwexpire pwexpdate] [-maxlife maxtixlife]\n"1187"\t\t[-kvno kvno] [-policy policy] [-clearpolicy]\n"1188"\t\t[-pw password] [-maxrenewlife maxrenewlife]\n"1189"\t\t[-e keysaltlist]\n\t\t[{+|-}attribute]\n"));1190error(_("\tattributes are:\n"));1191error(_("\t\tallow_postdated allow_forwardable allow_tgs_req "1192"allow_renewable\n"1193"\t\tallow_proxiable allow_dup_skey allow_tix requires_preauth\n"1194"\t\trequires_hwauth needchange allow_svr "1195"password_changing_service\n"1196"\t\tok_as_delegate ok_to_auth_as_delegate no_auth_data_required\n"1197"\t\tlockdown_keys\n"1198"\nwhere,\n\t[-x db_princ_args]* - any number of database "1199"specific arguments.\n"1200"\t\t\tLook at each database documentation for supported "1201"arguments\n"));1202}12031204static void1205kadmin_modprinc_usage(void)1206{1207error(_("usage: modify_principal [options] principal\n"));1208error(_("\toptions are:\n"));1209error(_("\t\t[-x db_princ_args]* [-expire expdate] "1210"[-pwexpire pwexpdate] [-maxlife maxtixlife]\n"1211"\t\t[-kvno kvno] [-policy policy] [-clearpolicy]\n"1212"\t\t[-maxrenewlife maxrenewlife] [-unlock] [{+|-}attribute]\n"));1213error(_("\tattributes are:\n"));1214error(_("\t\tallow_postdated allow_forwardable allow_tgs_req "1215"allow_renewable\n"1216"\t\tallow_proxiable allow_dup_skey allow_tix requires_preauth\n"1217"\t\trequires_hwauth needchange allow_svr "1218"password_changing_service\n"1219"\t\tok_as_delegate ok_to_auth_as_delegate no_auth_data_required\n"1220"\t\tlockdown_keys\n"1221"\nwhere,\n\t[-x db_princ_args]* - any number of database "1222"specific arguments.\n"1223"\t\t\tLook at each database documentation for supported "1224"arguments\n"));1225}12261227/* Create a dummy password for old-style (pre-1.8) randkey creation. */1228static void1229prepare_dummy_password(char *buf, size_t sz)1230{1231size_t i;12321233/* Must try to pass any password policy in place, and be valid UTF-8. */1234strlcpy(buf, "6F a[", sz);1235for (i = strlen(buf); i < sz - 1; i++)1236buf[i] = 'a' + (i % 26);1237buf[sz - 1] = '\0';1238}12391240void1241kadmin_addprinc(int argc, char *argv[], int sci_idx, void *info_ptr)1242{1243kadm5_principal_ent_rec princ;1244long mask;1245krb5_boolean randkey = FALSE, nokey = FALSE, old_style_randkey = FALSE;1246int n_ks_tuple;1247krb5_key_salt_tuple *ks_tuple = NULL;1248char *pass, *canon = NULL;1249krb5_error_code retval;1250char newpw[1024], dummybuf[256];1251static char prompt1[1024], prompt2[1024];12521253/* Zero all fields in request structure */1254memset(&princ, 0, sizeof(princ));12551256princ.attributes = 0;1257if (kadmin_parse_princ_args(argc, argv, &princ, &mask, &pass, &randkey,1258&nokey, &ks_tuple, &n_ks_tuple,1259"add_principal")) {1260kadmin_addprinc_usage();1261goto cleanup;1262}12631264retval = krb5_unparse_name(context, princ.principal, &canon);1265if (retval) {1266com_err("add_principal", retval, _("while canonicalizing principal"));1267goto cleanup;1268}12691270if (mask & KADM5_POLICY) {1271/* Warn if the specified policy does not exist. */1272if (!script_mode && !policy_exists(princ.policy)) {1273fprintf(stderr, _("WARNING: policy \"%s\" does not exist\n"),1274princ.policy);1275}1276} else if (!(mask & KADM5_POLICY_CLR)) {1277/* If the policy "default" exists, assign it. */1278if (policy_exists("default")) {1279if (!script_mode) {1280fprintf(stderr, _("No policy specified for %s; "1281"assigning \"default\"\n"), canon);1282}1283princ.policy = "default";1284mask |= KADM5_POLICY;1285} else if (!script_mode) {1286fprintf(stderr, _("No policy specified for %s; "1287"defaulting to no policy\n"), canon);1288}1289}1290/* Don't send KADM5_POLICY_CLR to the server. */1291mask &= ~KADM5_POLICY_CLR;12921293if (nokey) {1294pass = NULL;1295mask |= KADM5_KEY_DATA;1296} else if (randkey) {1297pass = NULL;1298} else if (pass == NULL) {1299unsigned int sz = sizeof(newpw) - 1;13001301snprintf(prompt1, sizeof(prompt1),1302_("Enter password for principal \"%s\""), canon);1303snprintf(prompt2, sizeof(prompt2),1304_("Re-enter password for principal \"%s\""), canon);1305retval = krb5_read_password(context, prompt1, prompt2, newpw, &sz);1306if (retval) {1307com_err("add_principal", retval,1308_("while reading password for \"%s\"."), canon);1309goto cleanup;1310}1311pass = newpw;1312}1313mask |= KADM5_PRINCIPAL;1314retval = create_princ(&princ, mask, n_ks_tuple, ks_tuple, pass);1315if (retval == EINVAL && randkey) {1316/*1317* The server doesn't support randkey creation. Create the principal1318* with a dummy password and disallow tickets.1319*/1320prepare_dummy_password(dummybuf, sizeof(dummybuf));1321princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;1322mask |= KADM5_ATTRIBUTES;1323pass = dummybuf;1324retval = create_princ(&princ, mask, n_ks_tuple, ks_tuple, pass);1325old_style_randkey = 1;1326}1327if (retval == KADM5_BAD_MASK && nokey) {1328error(_("Admin server does not support -nokey while creating "1329"\"%s\"\n"), canon);1330goto cleanup;1331}1332if (retval) {1333com_err("add_principal", retval, "while creating \"%s\".", canon);1334goto cleanup;1335}1336if (old_style_randkey) {1337/* Randomize the password and re-enable tickets. */1338retval = randkey_princ(handle, princ.principal, FALSE, n_ks_tuple,1339ks_tuple, NULL, NULL);1340if (retval) {1341com_err("add_principal", retval,1342_("while randomizing key for \"%s\"."), canon);1343goto cleanup;1344}1345princ.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX; /* clear notix */1346mask = KADM5_ATTRIBUTES;1347retval = kadm5_modify_principal(handle, &princ, mask);1348if (retval) {1349com_err("add_principal", retval,1350_("while clearing DISALLOW_ALL_TIX for \"%s\"."), canon);1351goto cleanup;1352}1353}1354info("Principal \"%s\" created.\n", canon);13551356cleanup:1357krb5_free_principal(context, princ.principal);1358free(ks_tuple);1359free(canon);1360kadmin_free_tl_data(&princ.n_tl_data, &princ.tl_data);1361}13621363void1364kadmin_modprinc(int argc, char *argv[], int sci_idx, void *info_ptr)1365{1366kadm5_principal_ent_rec princ, oldprinc;1367krb5_principal kprinc = NULL;1368long mask;1369krb5_error_code retval;1370char *pass, *canon = NULL;1371krb5_boolean randkey = FALSE, nokey = FALSE;1372int n_ks_tuple = 0;1373krb5_key_salt_tuple *ks_tuple = NULL;13741375if (argc < 2) {1376kadmin_modprinc_usage();1377return;1378}13791380memset(&oldprinc, 0, sizeof(oldprinc));1381memset(&princ, 0, sizeof(princ));13821383retval = kadmin_parse_name(argv[argc - 1], &kprinc);1384if (retval) {1385com_err("modify_principal", retval, _("while parsing principal"));1386return;1387}1388retval = krb5_unparse_name(context, kprinc, &canon);1389if (retval) {1390com_err("modify_principal", retval,1391_("while canonicalizing principal"));1392goto cleanup;1393}1394retval = kadm5_get_principal(handle, kprinc, &oldprinc,1395KADM5_PRINCIPAL_NORMAL_MASK);1396if (retval) {1397com_err("modify_principal", retval, _("while getting \"%s\"."), canon);1398goto cleanup;1399}1400princ.attributes = oldprinc.attributes;1401kadm5_free_principal_ent(handle, &oldprinc);1402retval = kadmin_parse_princ_args(argc, argv,1403&princ, &mask,1404&pass, &randkey, &nokey,1405&ks_tuple, &n_ks_tuple,1406"modify_principal");1407if (retval || ks_tuple != NULL || randkey || nokey || pass) {1408kadmin_modprinc_usage();1409goto cleanup;1410}1411if (mask & KADM5_POLICY) {1412/* Warn if the specified policy does not exist. */1413if (!script_mode && !policy_exists(princ.policy)) {1414fprintf(stderr, _("WARNING: policy \"%s\" does not exist\n"),1415princ.policy);1416}1417}1418if (mask) {1419/* Skip this if all we're doing is setting certhash. */1420retval = kadm5_modify_principal(handle, &princ, mask);1421}1422if (retval) {1423com_err("modify_principal", retval, _("while modifying \"%s\"."),1424canon);1425goto cleanup;1426}1427info(_("Principal \"%s\" modified.\n"), canon);1428cleanup:1429krb5_free_principal(context, kprinc);1430krb5_free_principal(context, princ.principal);1431kadmin_free_tl_data(&princ.n_tl_data, &princ.tl_data);1432free(canon);1433free(ks_tuple);1434}14351436void1437kadmin_getprinc(int argc, char *argv[], int sci_idx, void *info_ptr)1438{1439kadm5_principal_ent_rec dprinc;1440krb5_principal princ = NULL;1441krb5_error_code retval;1442const char *polname, *noexist;1443char *canon = NULL, *princstr = NULL, *modprincstr = NULL;1444char **sp = NULL, **attrstrs = NULL;1445int i;14461447if (!(argc == 2 || (argc == 3 && !strcmp("-terse", argv[1])))) {1448error(_("usage: get_principal [-terse] principal\n"));1449return;1450}14511452memset(&dprinc, 0, sizeof(dprinc));14531454retval = kadmin_parse_name(argv[argc - 1], &princ);1455if (retval) {1456com_err("get_principal", retval, _("while parsing principal"));1457return;1458}1459retval = krb5_unparse_name(context, princ, &canon);1460if (retval) {1461com_err("get_principal", retval, _("while canonicalizing principal"));1462goto cleanup;1463}1464retval = kadm5_get_principal(handle, princ, &dprinc,1465KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA);1466if (retval) {1467com_err("get_principal", retval, _("while retrieving \"%s\"."), canon);1468goto cleanup;1469}1470retval = krb5_unparse_name(context, dprinc.principal, &princstr);1471if (retval) {1472com_err("get_principal", retval, _("while unparsing principal"));1473goto cleanup;1474}1475retval = krb5_unparse_name(context, dprinc.mod_name, &modprincstr);1476if (retval) {1477com_err("get_principal", retval, _("while unparsing principal"));1478goto cleanup;1479}1480if (argc == 2) {1481printf(_("Principal: %s\n"), princstr);1482printf(_("Expiration date: %s\n"), dprinc.princ_expire_time ?1483strdate(dprinc.princ_expire_time) : _("[never]"));1484printf(_("Last password change: %s\n"), dprinc.last_pwd_change ?1485strdate(dprinc.last_pwd_change) : _("[never]"));1486printf(_("Password expiration date: %s\n"),1487dprinc.pw_expiration ?1488strdate(dprinc.pw_expiration) : _("[never]"));1489printf(_("Maximum ticket life: %s\n"), strdur(dprinc.max_life));1490printf(_("Maximum renewable life: %s\n"),1491strdur(dprinc.max_renewable_life));1492printf(_("Last modified: %s (%s)\n"), strdate(dprinc.mod_date),1493modprincstr);1494printf(_("Last successful authentication: %s\n"),1495dprinc.last_success ? strdate(dprinc.last_success) :1496_("[never]"));1497printf("Last failed authentication: %s\n",1498dprinc.last_failed ? strdate(dprinc.last_failed) :1499"[never]");1500printf(_("Failed password attempts: %d\n"),1501dprinc.fail_auth_count);1502printf(_("Number of keys: %d\n"), dprinc.n_key_data);1503for (i = 0; i < dprinc.n_key_data; i++) {1504krb5_key_data *key_data = &dprinc.key_data[i];1505char enctype[BUFSIZ], salttype[BUFSIZ];1506char *deprecated = "";15071508if (krb5_enctype_to_name(key_data->key_data_type[0], FALSE,1509enctype, sizeof(enctype)))1510snprintf(enctype, sizeof(enctype), _("<Encryption type 0x%x>"),1511key_data->key_data_type[0]);1512if (!krb5_c_valid_enctype(key_data->key_data_type[0]))1513deprecated = "UNSUPPORTED:";1514else if (krb5int_c_deprecated_enctype(key_data->key_data_type[0]))1515deprecated = "DEPRECATED:";1516printf("Key: vno %d, %s%s", key_data->key_data_kvno, deprecated,1517enctype);1518if (key_data->key_data_ver > 1 &&1519key_data->key_data_type[1] != KRB5_KDB_SALTTYPE_NORMAL) {1520if (krb5_salttype_to_string(key_data->key_data_type[1],1521salttype, sizeof(salttype)))1522snprintf(salttype, sizeof(salttype), _("<Salt type 0x%x>"),1523key_data->key_data_type[1]);1524printf(":%s", salttype);1525}1526printf("\n");1527}1528printf(_("MKey: vno %d\n"), dprinc.mkvno);15291530printf(_("Attributes:"));1531retval = krb5_flags_to_strings(dprinc.attributes, &attrstrs);1532if (retval) {1533com_err("get_principal", retval, _("while printing flags"));1534return;1535}1536for (sp = attrstrs; sp != NULL && *sp != NULL; sp++) {1537printf(" %s", *sp);1538free(*sp);1539}1540free(attrstrs);1541printf("\n");1542polname = (dprinc.policy != NULL) ? dprinc.policy : _("[none]");1543noexist = (dprinc.policy != NULL && !policy_exists(dprinc.policy)) ?1544_(" [does not exist]") : "";1545printf(_("Policy: %s%s\n"), polname, noexist);1546} else {1547printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\""1548"\t%d\t%d\t%d\t%d\t%d",1549princstr, dprinc.princ_expire_time, dprinc.last_pwd_change,1550dprinc.pw_expiration, dprinc.max_life, modprincstr,1551dprinc.mod_date, dprinc.attributes, dprinc.kvno,1552dprinc.mkvno, dprinc.policy ? dprinc.policy : "[none]",1553dprinc.max_renewable_life, dprinc.last_success,1554dprinc.last_failed, dprinc.fail_auth_count,1555dprinc.n_key_data);1556for (i = 0; i < dprinc.n_key_data; i++)1557printf("\t%d\t%d\t%d\t%d",1558dprinc.key_data[i].key_data_ver,1559dprinc.key_data[i].key_data_kvno,1560dprinc.key_data[i].key_data_type[0],1561dprinc.key_data[i].key_data_type[1]);1562printf("\n");1563}1564cleanup:1565krb5_free_principal(context, princ);1566kadm5_free_principal_ent(handle, &dprinc);1567free(canon);1568free(princstr);1569free(modprincstr);1570}15711572void1573kadmin_getprincs(int argc, char *argv[], int sci_idx, void *info_ptr)1574{1575krb5_error_code retval;1576char *expr, **names;1577int i, count;15781579expr = NULL;1580if (!(argc == 1 || (argc == 2 && (expr = argv[1])))) {1581error(_("usage: get_principals [expression]\n"));1582return;1583}1584retval = kadm5_get_principals(handle, expr, &names, &count);1585if (retval) {1586com_err("get_principals", retval, _("while retrieving list."));1587return;1588}1589for (i = 0; i < count; i++)1590printf("%s\n", names[i]);1591kadm5_free_name_list(handle, names, count);1592}15931594static int1595kadmin_parse_policy_args(int argc, char *argv[], kadm5_policy_ent_t policy,1596long *mask, char *caller)1597{1598krb5_error_code retval;1599int i;1600time_t now, interval;16011602time(&now);1603*mask = 0;1604for (i = 1; i < argc - 1; i++) {1605if (!strcmp(argv[i], "-maxlife")) {1606if (++i > argc -2)1607return -1;1608interval = parse_interval(argv[i], now);1609if (interval == (time_t)-1)1610return -1;1611policy->pw_max_life = interval;1612*mask |= KADM5_PW_MAX_LIFE;1613continue;1614} else if (!strcmp(argv[i], "-minlife")) {1615if (++i > argc - 2)1616return -1;1617interval = parse_interval(argv[i], now);1618if (interval == (time_t)-1)1619return -1;1620policy->pw_min_life = interval;1621*mask |= KADM5_PW_MIN_LIFE;1622continue;1623} else if (!strcmp(argv[i], "-minlength")) {1624if (++i > argc - 2)1625return -1;1626policy->pw_min_length = atoi(argv[i]);1627*mask |= KADM5_PW_MIN_LENGTH;1628continue;1629} else if (!strcmp(argv[i], "-minclasses")) {1630if (++i > argc - 2)1631return -1;1632policy->pw_min_classes = atoi(argv[i]);1633*mask |= KADM5_PW_MIN_CLASSES;1634continue;1635} else if (!strcmp(argv[i], "-history")) {1636if (++i > argc - 2)1637return -1;1638policy->pw_history_num = atoi(argv[i]);1639*mask |= KADM5_PW_HISTORY_NUM;1640continue;1641} else if (strlen(argv[i]) == 11 &&1642!strcmp(argv[i], "-maxfailure")) {1643if (++i > argc - 2)1644return -1;1645policy->pw_max_fail = atoi(argv[i]);1646*mask |= KADM5_PW_MAX_FAILURE;1647continue;1648} else if (strlen(argv[i]) == 21 &&1649!strcmp(argv[i], "-failurecountinterval")) {1650if (++i > argc - 2)1651return -1;1652interval = parse_interval(argv[i], now);1653if (interval == (time_t)-1)1654return -1;1655policy->pw_failcnt_interval = interval;1656*mask |= KADM5_PW_FAILURE_COUNT_INTERVAL;1657continue;1658} else if (strlen(argv[i]) == 16 &&1659!strcmp(argv[i], "-lockoutduration")) {1660if (++i > argc - 2)1661return -1;1662interval = parse_interval(argv[i], now);1663if (interval == (time_t)-1)1664return -1;1665policy->pw_lockout_duration = interval;1666*mask |= KADM5_PW_LOCKOUT_DURATION;1667continue;1668} else if (!strcmp(argv[i], "-allowedkeysalts")) {1669krb5_key_salt_tuple *ks_tuple = NULL;1670int n_ks_tuple = 0;16711672if (++i > argc - 2)1673return -1;1674if (strcmp(argv[i], "-")) {1675retval = krb5_string_to_keysalts(argv[i], ",", NULL, 0,1676&ks_tuple, &n_ks_tuple);1677if (retval) {1678com_err(caller, retval, _("while parsing keysalts %s"),1679argv[i]);1680return -1;1681}1682free(ks_tuple);1683policy->allowed_keysalts = argv[i];1684}1685*mask |= KADM5_POLICY_ALLOWED_KEYSALTS;1686continue;1687} else1688return -1;1689}1690if (i != argc -1) {1691error(_("%s: parser lost count!\n"), caller);1692return -1;1693} else1694return 0;1695}16961697static void1698kadmin_addmodpol_usage(char *func)1699{1700error(_("usage; %s [options] policy\n"), func);1701error(_("\toptions are:\n"));1702error(_("\t\t[-maxlife time] [-minlife time] [-minlength length]\n"1703"\t\t[-minclasses number] [-history number]\n"1704"\t\t[-maxfailure number] [-failurecountinterval time]\n"1705"\t\t[-allowedkeysalts keysalts]\n"));1706error(_("\t\t[-lockoutduration time]\n"));1707}17081709void1710kadmin_addpol(int argc, char *argv[], int sci_idx, void *info_ptr)1711{1712krb5_error_code retval;1713long mask;1714kadm5_policy_ent_rec policy;17151716memset(&policy, 0, sizeof(policy));1717if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) {1718kadmin_addmodpol_usage("add_policy");1719return;1720}1721policy.policy = argv[argc - 1];1722mask |= KADM5_POLICY;1723retval = kadm5_create_policy(handle, &policy, mask);1724if (retval) {1725com_err("add_policy", retval, _("while creating policy \"%s\"."),1726policy.policy);1727}1728}17291730void1731kadmin_modpol(int argc, char *argv[], int sci_idx, void *info_ptr)1732{1733krb5_error_code retval;1734long mask;1735kadm5_policy_ent_rec policy;17361737memset(&policy, 0, sizeof(policy));1738if (kadmin_parse_policy_args(argc, argv, &policy, &mask,1739"modify_policy")) {1740kadmin_addmodpol_usage("modify_policy");1741return;1742}1743policy.policy = argv[argc - 1];1744retval = kadm5_modify_policy(handle, &policy, mask);1745if (retval) {1746com_err("modify_policy", retval, _("while modifying policy \"%s\"."),1747policy.policy);1748}1749}17501751void1752kadmin_delpol(int argc, char *argv[], int sci_idx, void *info_ptr)1753{1754krb5_error_code retval;1755char reply[5];17561757if (!(argc == 2 || (argc == 3 && !strcmp("-force", argv[1])))) {1758error(_("usage: delete_policy [-force] policy\n"));1759return;1760}1761if (argc == 2 && !script_mode) {1762printf(_("Are you sure you want to delete the policy \"%s\"? "1763"(yes/no): "), argv[1]);1764fgets(reply, sizeof(reply), stdin);1765if (strcmp("yes\n", reply)) {1766fprintf(stderr, _("Policy \"%s\" not deleted.\n"), argv[1]);1767return;1768}1769}1770retval = kadm5_delete_policy(handle, argv[argc - 1]);1771if (retval) {1772com_err("delete_policy:", retval, _("while deleting policy \"%s\""),1773argv[argc - 1]);1774}1775}17761777void1778kadmin_getpol(int argc, char *argv[], int sci_idx, void *info_ptr)1779{1780krb5_error_code retval;1781kadm5_policy_ent_rec policy;17821783if (!(argc == 2 || (argc == 3 && !strcmp("-terse", argv[1])))) {1784error(_("usage: get_policy [-terse] policy\n"));1785return;1786}1787retval = kadm5_get_policy(handle, argv[argc - 1], &policy);1788if (retval) {1789com_err("get_policy", retval, _("while retrieving policy \"%s\"."),1790argv[argc - 1]);1791return;1792}1793if (argc == 2) {1794printf(_("Policy: %s\n"), policy.policy);1795printf(_("Maximum password life: %s\n"), strdur(policy.pw_max_life));1796printf(_("Minimum password life: %s\n"), strdur(policy.pw_min_life));1797printf(_("Minimum password length: %ld\n"), policy.pw_min_length);1798printf(_("Minimum number of password character classes: %ld\n"),1799policy.pw_min_classes);1800printf(_("Number of old keys kept: %ld\n"), policy.pw_history_num);1801printf(_("Maximum password failures before lockout: %lu\n"),1802(unsigned long)policy.pw_max_fail);1803printf(_("Password failure count reset interval: %s\n"),1804strdur(policy.pw_failcnt_interval));1805printf(_("Password lockout duration: %s\n"),1806strdur(policy.pw_lockout_duration));1807if (policy.allowed_keysalts != NULL)1808printf(_("Allowed key/salt types: %s\n"), policy.allowed_keysalts);1809} else {1810/* Output 0 where we used to output policy_refcnt. */1811printf("\"%s\"\t%ld\t%ld\t%ld\t%ld\t%ld\t0\t%lu\t%ld\t%ld\t%s\n",1812policy.policy, policy.pw_max_life, policy.pw_min_life,1813policy.pw_min_length, policy.pw_min_classes,1814policy.pw_history_num, (unsigned long)policy.pw_max_fail,1815(long)policy.pw_failcnt_interval,1816(long)policy.pw_lockout_duration,1817(policy.allowed_keysalts == NULL) ? "-" :1818policy.allowed_keysalts);1819}1820kadm5_free_policy_ent(handle, &policy);1821}18221823void1824kadmin_getpols(int argc, char *argv[], int sci_idx, void *info_ptr)1825{1826krb5_error_code retval;1827char *expr, **names;1828int i, count;18291830expr = NULL;1831if (!(argc == 1 || (argc == 2 && (expr = argv[1])))) {1832error(_("usage: get_policies [expression]\n"));1833return;1834}1835retval = kadm5_get_policies(handle, expr, &names, &count);1836if (retval) {1837com_err("get_policies", retval, _("while retrieving list."));1838return;1839}1840for (i = 0; i < count; i++)1841printf("%s\n", names[i]);1842kadm5_free_name_list(handle, names, count);1843}18441845void1846kadmin_getprivs(int argc, char *argv[], int sci_idx, void *info_ptr)1847{1848static char *privs[] = {"INQUIRE", "ADD", "MODIFY", "DELETE"};1849krb5_error_code retval;1850size_t i;1851long plist;18521853if (argc != 1) {1854error(_("usage: get_privs\n"));1855return;1856}1857retval = kadm5_get_privs(handle, &plist);1858if (retval) {1859com_err("get_privs", retval, _("while retrieving privileges"));1860return;1861}1862printf(_("current privileges:"));1863for (i = 0; i < sizeof (privs) / sizeof (char *); i++) {1864if (plist & 1 << i)1865printf(" %s", privs[i]);1866}1867printf("\n");1868}18691870void1871kadmin_purgekeys(int argc, char *argv[], int sci_idx, void *info_ptr)1872{1873kadm5_ret_t retval;1874int keepkvno = -1;1875char *pname = NULL, *canon = NULL;1876krb5_principal princ;18771878if (argc == 4 && strcmp(argv[1], "-keepkvno") == 0) {1879keepkvno = atoi(argv[2]);1880pname = argv[3];1881} else if (argc == 3 && strcmp(argv[1], "-all") == 0) {1882keepkvno = KRB5_INT32_MAX;1883pname = argv[2];1884} else if (argc == 2) {1885pname = argv[1];1886}1887if (pname == NULL) {1888error(_("usage: purgekeys [-all|-keepkvno oldest_kvno_to_keep] "1889"principal\n"));1890return;1891}18921893retval = kadmin_parse_name(pname, &princ);1894if (retval) {1895com_err("purgekeys", retval, _("while parsing principal"));1896return;1897}18981899retval = krb5_unparse_name(context, princ, &canon);1900if (retval) {1901com_err("purgekeys", retval, _("while canonicalizing principal"));1902goto cleanup;1903}19041905retval = kadm5_purgekeys(handle, princ, keepkvno);1906if (retval) {1907com_err("purgekeys", retval,1908_("while purging keys for principal \"%s\""), canon);1909goto cleanup;1910}19111912if (keepkvno == KRB5_INT32_MAX)1913info(_("All keys for principal \"%s\" removed.\n"), canon);1914else1915info(_("Old keys for principal \"%s\" purged.\n"), canon);1916cleanup:1917krb5_free_principal(context, princ);1918free(canon);1919return;1920}19211922void1923kadmin_getstrings(int argc, char *argv[], int sci_idx, void *info_ptr)1924{1925kadm5_ret_t retval;1926char *pname, *canon = NULL;1927krb5_principal princ = NULL;1928krb5_string_attr *strings = NULL;1929int count, i;19301931if (argc != 2) {1932error(_("usage: get_strings principal\n"));1933return;1934}1935pname = argv[1];19361937retval = kadmin_parse_name(pname, &princ);1938if (retval) {1939com_err("get_strings", retval, _("while parsing principal"));1940return;1941}19421943retval = krb5_unparse_name(context, princ, &canon);1944if (retval) {1945com_err("get_strings", retval, _("while canonicalizing principal"));1946goto cleanup;1947}19481949retval = kadm5_get_strings(handle, princ, &strings, &count);1950if (retval) {1951com_err("get_strings", retval,1952_("while getting attributes for principal \"%s\""), canon);1953goto cleanup;1954}19551956if (count == 0)1957printf(_("(No string attributes.)\n"));1958for (i = 0; i < count; i++)1959printf("%s: %s\n", strings[i].key, strings[i].value);1960kadm5_free_strings(handle, strings, count);19611962cleanup:1963krb5_free_principal(context, princ);1964free(canon);1965return;1966}19671968void1969kadmin_setstring(int argc, char *argv[], int sci_idx, void *info_ptr)1970{1971kadm5_ret_t retval;1972char *pname, *canon = NULL, *key, *value;1973krb5_principal princ = NULL;19741975if (argc != 4) {1976error(_("usage: set_string principal key value\n"));1977return;1978}1979pname = argv[1];1980key = argv[2];1981value = argv[3];19821983retval = kadmin_parse_name(pname, &princ);1984if (retval) {1985com_err("set_string", retval, _("while parsing principal"));1986return;1987}19881989retval = krb5_unparse_name(context, princ, &canon);1990if (retval) {1991com_err("set_string", retval, _("while canonicalizing principal"));1992goto cleanup;1993}19941995retval = kadm5_set_string(handle, princ, key, value);1996if (retval) {1997com_err("set_string", retval,1998_("while setting attribute on principal \"%s\""), canon);1999goto cleanup;2000}20012002info(_("Attribute set for principal \"%s\".\n"), canon);2003cleanup:2004krb5_free_principal(context, princ);2005free(canon);2006return;2007}20082009void2010kadmin_delstring(int argc, char *argv[], int sci_idx, void *info_ptr)2011{2012kadm5_ret_t retval;2013char *pname, *canon = NULL, *key;2014krb5_principal princ = NULL;20152016if (argc != 3) {2017error(_("usage: del_string principal key\n"));2018return;2019}2020pname = argv[1];2021key = argv[2];20222023retval = kadmin_parse_name(pname, &princ);2024if (retval) {2025com_err("delstring", retval, _("while parsing principal"));2026return;2027}20282029retval = krb5_unparse_name(context, princ, &canon);2030if (retval) {2031com_err("del_string", retval, _("while canonicalizing principal"));2032goto cleanup;2033}20342035retval = kadm5_set_string(handle, princ, key, NULL);2036if (retval) {2037com_err("del_string", retval,2038_("while deleting attribute from principal \"%s\""), canon);2039goto cleanup;2040}20412042info(_("Attribute removed from principal \"%s\".\n"), canon);2043cleanup:2044krb5_free_principal(context, princ);2045free(canon);2046return;2047}204820492050