summaryrefslogtreecommitdiffstats
path: root/src/kadmin
diff options
context:
space:
mode:
Diffstat (limited to 'src/kadmin')
-rw-r--r--src/kadmin/v5server/.Sanitize1
-rw-r--r--src/kadmin/v5server/ChangeLog30
-rw-r--r--src/kadmin/v5server/Makefile.in4
-rw-r--r--src/kadmin/v5server/admin.c1386
4 files changed, 1419 insertions, 2 deletions
diff --git a/src/kadmin/v5server/.Sanitize b/src/kadmin/v5server/.Sanitize
index 6f928923b..17fd59392 100644
--- a/src/kadmin/v5server/.Sanitize
+++ b/src/kadmin/v5server/.Sanitize
@@ -26,6 +26,7 @@ Things-to-keep:
.cvsignore
ChangeLog
Makefile.in
+admin.c
configure
configure.in
kadm5_defs.h
diff --git a/src/kadmin/v5server/ChangeLog b/src/kadmin/v5server/ChangeLog
index 0cacb68a9..b9e2ba171 100644
--- a/src/kadmin/v5server/ChangeLog
+++ b/src/kadmin/v5server/ChangeLog
@@ -1,4 +1,34 @@
+Tue May 9 15:29:10 EDT 1995 Paul Park (pjpark@mit.edu)
+ Add new administrative protocol support functions and fix bugs.
+ * kadm5_defs.h - Add extract operation. Move error subcodes
+ to adm.h. Update prototypes.
+ * kadmind5.M - Add descriptions of -n, -p and -T options. Change
+ name of -i to -m option. Add description of
+ 'e' acl entry.
+ * passwd.c - Make passwd_check_npass_ok a global routine.
+ * proto_serv.c - Use keytab supplied with -T option. Support
+ new administrative protocol functions. Destroy
+ replay cache when done with request.
+ * srv_acl.c - Add catchall entry which allows principals to
+ change their own passwords. This is the last entry
+ inserted and can be overridden with an entry in
+ the ACL file. By default, we now deny operations
+ unless there is an entry.
+ * srv_key.c - Use keytab supplied with -T option. Actually set
+ the default realm to be the specified realm. Return
+ salt values from key_string_to_keys(). Add random
+ key generator.
+ * srv_main.c - Add support for -T option. Change -i to -m. Add
+ daemon() call if -n option is not specified. Add
+ support for -p option.
+ * srv_net.c - Close connected socket in parent. This was causing
+ the connection to remain open even when the child
+ was finished with it. Add support for -p option.
+ Determine our port otherwise from the profile, and
+ then the /etc/services entry.
+ * srv_output.c - Add support for new error codes.
+
Fri May 5 11:11:39 EDT 1995 Paul Park (pjpark@mit.edu)
#ifdef out language flags. Remove description from manpage.
diff --git a/src/kadmin/v5server/Makefile.in b/src/kadmin/v5server/Makefile.in
index d73336a54..d0e67ae08 100644
--- a/src/kadmin/v5server/Makefile.in
+++ b/src/kadmin/v5server/Makefile.in
@@ -15,11 +15,11 @@ DEPKLIB = $(KADMLIB) $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(SSLIB) $(COME
SRCS = \
$(srcdir)/srv_main.c $(srcdir)/srv_key.c \
$(srcdir)/srv_acl.c $(srcdir)/srv_output.c $(srcdir)/srv_net.c \
- $(srcdir)/proto_serv.c $(srcdir)/passwd.c
+ $(srcdir)/proto_serv.c $(srcdir)/passwd.c $(srcdir)/admin.c
OBJS = \
srv_main.o srv_key.o srv_acl.o srv_output.o srv_net.o \
- proto_serv.o passwd.o
+ proto_serv.o passwd.o admin.o
all:: kadmind5
diff --git a/src/kadmin/v5server/admin.c b/src/kadmin/v5server/admin.c
new file mode 100644
index 000000000..137eb0ded
--- /dev/null
+++ b/src/kadmin/v5server/admin.c
@@ -0,0 +1,1386 @@
+/*
+ * kadmin/v5server/admin.c
+ *
+ * Copyright 1995 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. 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.
+ *
+ */
+
+/*
+ * admin.c - Handle administrative requests.
+ */
+
+#include "k5-int.h"
+#include "kadm5_defs.h"
+#include "adm_proto.h"
+#include "adm.h"
+
+/*
+ * Data structure used to pass information in and out of krb5_db_iterate.
+ */
+struct inq_context {
+ krb5_context ic_context; /* in */
+ int ic_level; /* in */
+ krb5_principal ic_who; /* in */
+ krb5_boolean ic_entry_found; /* out */
+ krb5_int32 *ic_ncomps; /* out */
+ krb5_data **ic_clist; /* out */
+ char *ic_next; /* out */
+};
+
+static krb5_db_entry admin_def_dbent;
+static krb5_boolean admin_def_dbent_inited = 0;
+
+static const char *admin_perm_denied_fmt = "ACL entry prevents %s operation by %s";
+static const char *admin_db_write_err_fmt = "database write failed during %s operation by %s";
+static const char *admin_db_success_fmt = "%s operation for %s successfully issued by %s";
+static const char *admin_db_read_err_fmt = "database read failed during %s operation by %s";
+static const char *admin_no_cl_ident_fmt = "cannot get client identity from ticket for %s operation";
+static const char *admin_db_rename_fmt = "%s operation from %s to %s successfully issued by %s";
+static const char *admin_db_del_err_fmt = "database delete entry(%s) failed during %s operation by %s";
+static const char *admin_key_dec_err_fmt = "key decode failed for %s's key during %s operation by %s";
+
+static const char *admin_add_principal_text = "Add Principal";
+static const char *admin_modify_principal_text = "Modify Principal";
+static const char *admin_delete_principal_text = "Delete Principal";
+static const char *admin_rename_principal_text = "Rename Principal";
+static const char *admin_change_pwd_text = "Change Password";
+static const char *admin_change_rpwd_text = "Change Random Password";
+static const char *admin_inquire_text = "Inquire";
+static const char *admin_extract_key_text = "Extract Key";
+
+extern char *programname;
+
+/*
+ * admin_init_def_dbent() - Initialize the default database entry.
+ */
+static void
+admin_init_def_dbent()
+{
+ /* Zero it all out, and fill in non-zero defaults */
+ memset((char *) &admin_def_dbent, 0, sizeof(admin_def_dbent));
+ admin_def_dbent.kvno = 1;
+ admin_def_dbent.max_life = KRB5_KDB_MAX_LIFE;
+ admin_def_dbent.max_renewable_life = KRB5_KDB_MAX_RLIFE;
+ admin_def_dbent.expiration = KRB5_KDB_EXPIRATION;
+ admin_def_dbent.attributes = KRB5_KDB_DEF_FLAGS;
+ admin_def_dbent_inited = 1;
+}
+
+/*
+ * admin_client_identity() - Determine client identity from ticket
+ */
+static krb5_error_code
+admin_client_identity(kcontext, debug_level, ticket, clientp, clnamep)
+ krb5_context kcontext;
+ int debug_level;
+ krb5_ticket *ticket;
+ krb5_principal *clientp;
+ char **clnamep;
+{
+ krb5_error_code kret = 0;
+
+ DPRINT(DEBUG_CALLS, debug_level, ("* admin_client_identity()\n"));
+ *clientp = (krb5_principal) NULL;
+ *clnamep = (char *) NULL;
+
+ /* Copy principal out of ticket */
+ if (!(kret = krb5_copy_principal(kcontext,
+ ticket->enc_part2->client,
+ clientp))) {
+ /* Flatten name */
+ if (kret = krb5_unparse_name(kcontext, *clientp, clnamep)) {
+ krb5_free_principal(kcontext, *clientp);
+ *clientp = (krb5_principal) NULL;
+ }
+ }
+
+ DPRINT(DEBUG_CALLS, debug_level, ("X admin_client_identity()=%d\n", kret));
+ return(kret);
+}
+
+/*
+ * admin_merge_dbentries() - Merge two database entries and a password.
+ */
+static krb5_error_code
+admin_merge_dbentries(kcontext, debug_level, who, defaultp,
+ valid, dbentp, password, is_pwchange)
+ krb5_context kcontext;
+ int debug_level;
+ krb5_principal who;
+ krb5_db_entry *defaultp;
+ krb5_ui_4 valid;
+ krb5_db_entry *dbentp;
+ char *password;
+ krb5_boolean is_pwchange;
+{
+ krb5_error_code kret = 0;
+ krb5_timestamp now;
+ DPRINT(DEBUG_CALLS, debug_level, ("* admin_merge_dbentries()\n"));
+
+ /*
+ * Ensure that there's a principal,
+ * we have the current t-o-d,
+ * and that we don't have a password and the random-password option.
+ */
+ kret = EINVAL;
+ if (dbentp->principal &&
+ !(kret = krb5_timeofday(kcontext, &now)) &&
+ (!password || ((valid & KRB5_ADM_M_RANDOMKEY) == 0))) {
+
+ /*
+ * Now fill in unsupplied values from defaults.
+ */
+ if ((valid & KRB5_ADM_M_KVNO) == 0)
+ dbentp->kvno = defaultp->kvno;
+ if ((valid & KRB5_ADM_M_MAXLIFE) == 0)
+ dbentp->max_life = defaultp->max_life;
+ if ((valid & KRB5_ADM_M_MAXRENEWLIFE) == 0)
+ dbentp->max_renewable_life = defaultp->max_renewable_life;
+ if ((valid & KRB5_ADM_M_EXPIRATION) == 0)
+ dbentp->expiration = defaultp->expiration;
+ if ((valid & KRB5_ADM_M_PWEXPIRATION) == 0)
+ dbentp->pw_expiration = defaultp->pw_expiration;
+ if ((valid & KRB5_ADM_M_FLAGS) == 0)
+ dbentp->attributes = defaultp->attributes;
+ if ((valid & KRB5_ADM_M_SALTTYPE) == 0) {
+ dbentp->salt_type = defaultp->salt_type;
+ dbentp->alt_salt_type = defaultp->alt_salt_type;
+ }
+
+ /*
+ * Now fill in un-suppliable values from our data.
+ */
+ if (password || is_pwchange)
+ dbentp->last_pwd_change = now; /* Changing password now */
+ else
+ dbentp->last_pwd_change = defaultp->last_pwd_change;
+ dbentp->last_success = defaultp->last_success;
+ dbentp->last_failed = defaultp->last_failed;
+ dbentp->fail_auth_count = defaultp->fail_auth_count;
+ dbentp->mod_name = who; /* Modified by caller */
+ dbentp->mod_date = now; /* Modified now */
+ dbentp->mkvno = key_master_entry()->kvno;
+
+ /* See if this is a random key or not */
+ if (password) {
+ krb5_data pwdata;
+ krb5_keyblock pkey, akey;
+ krb5_data psalt, asalt;
+
+ DPRINT(DEBUG_OPERATION, debug_level, ("> string-to-key\n"));
+ /*
+ * Now handle string-to-key and salt.
+ */
+ pwdata.data = password;
+ pwdata.length = strlen(password);
+ memset((char *) &pkey, 0, sizeof(pkey));
+ memset((char *) &akey, 0, sizeof(akey));
+ memset((char *) &psalt, 0, sizeof(psalt));
+ memset((char *) &asalt, 0, sizeof(asalt));
+
+ /* Convert password string to key */
+ if (!(kret = key_string_to_keys(kcontext,
+ dbentp->principal,
+ &pwdata,
+ dbentp->salt_type,
+ dbentp->alt_salt_type,
+ &pkey,
+ &akey,
+ &psalt,
+ &asalt))) {
+ /* Encrypt the keys */
+ DPRINT(DEBUG_OPERATION, debug_level, ("> encode\n"));
+ if (!(kret = key_encrypt_keys(kcontext,
+ dbentp->principal,
+ &pkey,
+ &akey,
+ &dbentp->key,
+ &dbentp->alt_key))) {
+ /* Squirrel away the salt. */
+ dbentp->salt_length = psalt.length;
+ dbentp->salt = (krb5_octet *) psalt.data;
+ dbentp->alt_salt_length = asalt.length;
+ dbentp->alt_salt = (krb5_octet *) asalt.data;
+ kret = 0;
+ }
+
+ /* Clean up the cleartext keys */
+ if (pkey.contents) {
+ memset((char *) pkey.contents, 0, (size_t) pkey.length);
+ krb5_xfree(pkey.contents);
+ }
+ if (akey.contents) {
+ memset((char *) akey.contents, 0, (size_t) akey.length);
+ krb5_xfree(akey.contents);
+ }
+ if (kret) {
+ /* If failed, cleanup the salt data too. */
+ if (psalt.data) {
+ memset((char *) psalt.data, 0, (size_t) psalt.length);
+ krb5_xfree(psalt.data);
+ }
+ if (asalt.data) {
+ memset((char *) asalt.data, 0, (size_t) asalt.length);
+ krb5_xfree(asalt.data);
+ }
+ }
+ }
+ }
+ else {
+ krb5_keyblock rkey, akey;
+
+ /* Random key */
+ DPRINT(DEBUG_OPERATION, debug_level, ("> random key\n"));
+ memset((char *) &rkey, 0, sizeof(rkey));
+ memset((char *) &akey, 0, sizeof(akey));
+ if ((dbentp->salt_type == KRB5_KDB_SALTTYPE_NORMAL) &&
+ (!(kret = key_random_key(kcontext, &rkey)))) {
+
+ /* Encrypt the keys */
+ DPRINT(DEBUG_OPERATION, debug_level, ("> encode\n"));
+ if (!(kret = key_encrypt_keys(kcontext,
+ dbentp->principal,
+ &rkey,
+ &akey,
+ &dbentp->key,
+ &dbentp->alt_key))) {
+ /* Squirrel away the salt. */
+ dbentp->salt_length = 0;
+ dbentp->salt = (krb5_octet *) NULL;
+ dbentp->alt_salt_length = 0;
+ dbentp->alt_salt = (krb5_octet *) NULL;
+ kret = 0;
+ }
+ /* Clean up the cleartext key */
+ if (rkey.contents) {
+ memset((char *) rkey.contents, 0, (size_t) rkey.length);
+ krb5_xfree(rkey.contents);
+ }
+ }
+ }
+ }
+ DPRINT(DEBUG_CALLS, debug_level, ("X admin_merge_dbentries()=%d\n", kret));
+ return(kret);
+}
+
+/*
+ * admin_add_modify() - Adds or modifies a database entry.
+ *
+ * Does all the work for add_principal, modify_principal, change_opwd,
+ * and change_orandpwd.
+ *
+ * Supplied argument list must contain at least one entry, the name of the
+ * target principal.
+ */
+static krb5_int32
+admin_add_modify(kcontext, debug_level, ticket, nargs, arglist,
+ should_exist, pwd_supplied, supp_pwd)
+ krb5_context kcontext; /* Kerberos context */ /* In */
+ int debug_level; /* Debug level */ /* In */
+ krb5_ticket *ticket; /* Kerberos ticket */ /* In */
+ krb5_int32 nargs; /* # rem. arguments */ /* In */
+ krb5_data *arglist; /* Remaining arguments */ /* In */
+ krb5_boolean should_exist; /* Should it exist? */ /* In */
+ krb5_boolean pwd_supplied; /* Password supplied? */ /* In */
+ char *supp_pwd; /* Supplied password */ /* In */
+{
+ krb5_int32 retval = KRB5_ADM_SUCCESS;
+ krb5_error_code kret = 0;
+ krb5_principal client;
+ char *client_name;
+ krb5_ui_4 valid_mask;
+ krb5_db_entry new_dbentry;
+ krb5_db_entry cur_dbentry;
+ krb5_principal principal;
+ char *new_password;
+ krb5_int32 operation;
+ const char *op_msg;
+#ifdef DEBUG
+ char *dbg_op_msg;
+#endif /* DEBUG */
+
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("* admin_add_modify(%s)\n", arglist[0].data));
+ /* Initialize */
+ client = (krb5_principal) NULL;
+ client_name = (char *) NULL;
+ valid_mask = 0;
+ memset((char *) &new_dbentry, 0, sizeof(new_dbentry));
+ memset((char *) &cur_dbentry, 0, sizeof(cur_dbentry));
+ principal = (krb5_principal) NULL;
+ new_password = (char *) NULL;
+
+ /* Determine what our operation is based on should_exist/pwd_supplied */
+ if (should_exist) {
+ if (pwd_supplied) {
+ operation = ACL_CHANGEPW;
+ op_msg = (supp_pwd) ? admin_change_pwd_text :
+ admin_change_rpwd_text;
+#ifdef DEBUG
+ dbg_op_msg = (supp_pwd) ? "CHANGE PASSWORD" :
+ "CHANGE RANDOM PASSWORD";
+#endif /* DEBUG */
+ }
+ else {
+ operation = ACL_MODIFY_PRINCIPAL;
+ op_msg = admin_modify_principal_text;
+#ifdef DEBUG
+ dbg_op_msg = "MODIFY PRINCIPAL";
+#endif /* DEBUG */
+ }
+ }
+ else {
+ if (pwd_supplied) {
+ return(KRB5_ADM_SYSTEM_ERROR);
+ }
+ else {
+ operation = ACL_ADD_PRINCIPAL;
+ op_msg = admin_add_principal_text;
+#ifdef DEBUG
+ dbg_op_msg = "ADD PRINCIPAL";
+#endif /* DEBUG */
+ }
+ }
+
+ /* Get the identity of our client */
+ if (!(kret = admin_client_identity(kcontext,
+ debug_level,
+ ticket,
+ &client,
+ &client_name))) {
+
+ /* See if this client can perform this operation. */
+ if (acl_op_permitted(kcontext, client, operation)) {
+
+ /* Parse the specified principal name */
+ if (!(kret = krb5_parse_name(kcontext,
+ arglist[0].data,
+ &principal))) {
+ int howmany;
+ krb5_boolean more;
+
+ howmany = 1;
+
+ /* Try to get the entry */
+ kret = krb5_db_get_principal(kcontext,
+ principal,
+ &cur_dbentry,
+ &howmany,
+ &more);
+
+ /*
+ * If we're modifying, there'd better be an entry.
+ * If we're adding, there'd better not be an entry.
+ */
+ if (!kret &&
+ ((should_exist && howmany) ||
+ (!should_exist && !howmany))) {
+
+ /* We need to have a principal */
+ new_dbentry.principal = principal;
+
+ /*
+ * Parse the argument list and make sure that only valid
+ * options are set.
+ */
+ if (!(kret = krb5_adm_proto_to_dbent(kcontext,
+ nargs-1,
+ &arglist[1],
+ &valid_mask,
+ &new_dbentry,
+ &new_password)) &&
+ ((valid_mask & ~KRB5_ADM_M_SET_VALID) == 0)) {
+ krb5_data pword_data;
+ krb5_int32 temp;
+
+ pword_data.data = (pwd_supplied) ? supp_pwd :
+ new_password;
+ pword_data.length = (pword_data.data) ?
+ strlen(pword_data.data) : 0;
+
+ /*
+ * Check viability of options specified. One
+ * of the following must be true:
+ * 1) randomkey was specified and no password.
+ * 2) randomkey is not specified and there
+ * is a password to change/set and it is
+ * is suitable.
+ * 3) randomkey is not specified and there is
+ * no password to change and this is
+ * is a modify entry request.
+ *
+ * Check the suitability of the new password, if
+ * one was supplied.
+ */
+ if (((valid_mask & KRB5_ADM_M_RANDOMKEY) &&
+ !pword_data.data)
+ ||
+ (!(valid_mask & KRB5_ADM_M_RANDOMKEY) &&
+ ((!pword_data.data && should_exist)||
+ passwd_check_npass_ok(kcontext,
+ debug_level,
+ new_dbentry.principal,
+ &new_dbentry,
+ &pword_data,
+ &temp)))) {
+ krb5_db_entry *merge;
+
+ /* Check if the default is initialized */
+ if (!admin_def_dbent_inited)
+ admin_init_def_dbent();
+
+ merge = (should_exist) ?
+ &cur_dbentry : &admin_def_dbent;
+
+ /* Merge the specified entries with the defaults */
+ if (!(kret = admin_merge_dbentries(kcontext,
+ debug_level,
+ client,
+ merge,
+ valid_mask,
+ &new_dbentry,
+ pword_data.data,
+ pwd_supplied)
+ )) {
+ int nument = 1;
+
+ /*
+ * Update the key version number if we're
+ * changing it.
+ */
+ if (should_exist && pwd_supplied)
+ new_dbentry.kvno++;
+
+ /* Write the entry. */
+ kret = krb5_db_put_principal(kcontext,
+ &new_dbentry,
+ &nument);
+ if (kret || (nument != 1)) {
+ /* Ultimate failure */
+ com_err(programname, kret,
+ admin_db_write_err_fmt,
+ op_msg,
+ client_name);
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> db write failed for %s\n",
+ dbg_op_msg));
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ else {
+ /* Ultimate success */
+ com_err(programname, 0,
+ admin_db_success_fmt,
+ op_msg,
+ arglist[0].data,
+ client_name);
+ }
+
+ /*
+ * Clean up droppings from
+ * admin_merge_dbentries
+ */
+ if (new_dbentry.alt_salt) {
+ memset((char *) new_dbentry.alt_salt,
+ 0,
+ (size_t) new_dbentry.alt_salt_length
+ );
+ krb5_xfree(new_dbentry.alt_salt);
+ }
+ if (new_dbentry.salt) {
+ memset((char *) new_dbentry.salt,
+ 0,
+ (size_t) new_dbentry.salt_length);
+ krb5_xfree(new_dbentry.salt);
+ }
+ if (new_dbentry.key.contents)
+ krb5_xfree(new_dbentry.key.contents);
+ if (new_dbentry.alt_key.contents)
+ krb5_xfree(new_dbentry.alt_key.contents);
+ }
+ else {
+ /* Option merge failed */
+ DPRINT(DEBUG_PROTO, debug_level,
+ ("= option merge failed for %s\n",
+ dbg_op_msg));
+ retval = KRB5_ADM_BAD_OPTION;
+ }
+ }
+ else {
+ /* Password was not suitable or conflicts */
+ if (valid_mask & KRB5_ADM_M_RANDOMKEY) {
+ DPRINT(DEBUG_PROTO, debug_level,
+ ("= conflicting options for %s\n",
+ dbg_op_msg));
+ retval = KRB5_ADM_BAD_OPTION;
+ }
+ else {
+ DPRINT(DEBUG_PROTO, debug_level,
+ ("= bad password for %s\n",
+ dbg_op_msg));
+ retval = KRB5_ADM_PW_UNACCEPT;
+ }
+ }
+
+ /* Clean up droppings from krb5_adm_proto_to_dbent */
+ if (new_password)
+ krb5_xfree(new_password);
+ if (new_dbentry.mod_name)
+ krb5_free_principal(kcontext,
+ new_dbentry.mod_name);
+ }
+ else {
+ /* Argument list parse failed or bad options */
+ DPRINT(DEBUG_PROTO, debug_level,
+ ("= argument list bad for %s\n", dbg_op_msg));
+ retval = KRB5_ADM_BAD_OPTION;
+ }
+ krb5_db_free_principal(kcontext,
+ ((should_exist) ?
+ &new_dbentry : &cur_dbentry),
+ 1);
+ }
+ else {
+ /* Database entry failed or yielded unexpected results */
+ if (kret) {
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> database read error\n"));
+ com_err(programname, kret,
+ admin_db_read_err_fmt,
+ op_msg,
+ client_name);
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ else {
+ if (should_exist) {
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> principal %s not in database\n",
+ arglist[0].data));
+ retval = KRB5_ADM_P_DOES_NOT_EXIST;
+ }
+ else {
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> principal %s already in database\n",
+ arglist[0].data));
+ retval = KRB5_ADM_P_ALREADY_EXISTS;
+ }
+ }
+ }
+
+ /* Clean up from krb5_parse_name */
+ krb5_free_principal(kcontext, principal);
+ }
+ else {
+ /* Principal name parse failed */
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> bad principal string \"%s\"\n", arglist[0].data));
+ retval = (should_exist) ? KRB5_ADM_P_DOES_NOT_EXIST :
+ KRB5_ADM_BAD_OPTION;
+ }
+ }
+ else {
+ /* ACL check failed */
+ com_err(programname, 0, admin_perm_denied_fmt,
+ op_msg, client_name);
+ retval = KRB5_ADM_NOT_AUTHORIZED;
+ }
+
+ /* Clean up admin_client_identity droppings */
+ krb5_xfree(client_name);
+ krb5_free_principal(kcontext, client);
+ }
+ else {
+ /* We really choked here. */
+ com_err(programname, kret, admin_no_cl_ident_fmt, op_msg);
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("X admin_add_modify() = %d\n", retval));
+ return(retval);
+}
+
+/*
+ * admin_delete_rename() - Delete or rename a named principal.
+ */
+static krb5_int32
+admin_delete_rename(kcontext, debug_level, ticket, original, new)
+ krb5_context kcontext;
+ int debug_level;
+ krb5_ticket *ticket;
+ char *original;
+ char *new;
+{
+ krb5_int32 retval = 0;
+ krb5_error_code kret;
+ krb5_principal client;
+ char *client_name;
+ krb5_db_entry orig_entry;
+ krb5_principal orig_principal;
+ krb5_int32 operation;
+ const char * op_msg;
+
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("* admin_delete_rename(%s,%s)\n",
+ original,((new) ? new : "(null)")));
+
+ /* Initialize */
+ client = (krb5_principal) NULL;
+ client_name = (char *) NULL;
+ memset((char *) &orig_entry, 0, sizeof(orig_entry));
+ orig_principal = (krb5_principal) NULL;
+ operation = (new) ? ACL_RENAME_PRINCIPAL : ACL_DELETE_PRINCIPAL;
+ op_msg = (new) ? admin_rename_principal_text : admin_delete_principal_text;
+
+ /* Get the identity of our client */
+ if (!(kret = admin_client_identity(kcontext,
+ debug_level,
+ ticket,
+ &client,
+ &client_name))) {
+
+ /* See if this client can perform this operation. */
+ if (acl_op_permitted(kcontext, client, operation)) {
+
+ /* Parse the specified principal name */
+ if (!(kret = krb5_parse_name(kcontext,
+ original,
+ &orig_principal))) {
+ int howmany;
+ krb5_boolean more;
+
+ howmany = 1;
+
+ /* Try to get the entry */
+ if (!(kret = krb5_db_get_principal(kcontext,
+ orig_principal,
+ &orig_entry,
+ &howmany,
+ &more))
+ && howmany) {
+
+ /*
+ * We've got the entry. If we're renaming, we have
+ * to make sure that the new entry's not there. Then
+ * we can put the new entry. If we're deleting or
+ * renaming, we delete the entry last.
+ */
+ if (new) {
+ krb5_principal new_principal;
+
+ if (!(kret = krb5_parse_name(kcontext,
+ new,
+ &new_principal))) {
+ int n_howmany;
+ krb5_boolean n_more;
+ krb5_db_entry xxx_dbentry;
+ krb5_timestamp now;
+
+ n_howmany = 1;
+
+ /* Try to get the entry */
+ if (!(kret = krb5_db_get_principal(kcontext,
+ new_principal,
+ &xxx_dbentry,
+ &n_howmany,
+ &n_more))
+ && !(kret = krb5_timeofday(kcontext, &now))
+ && !n_howmany) {
+ /* Change our name */
+ krb5_free_principal(kcontext,
+ orig_entry.principal);
+ orig_entry.principal = new_principal;
+
+ /* Update our stats */
+ orig_entry.mod_name = client;
+ orig_entry.mod_date = now;
+
+ n_howmany = 1;
+ if ((kret = krb5_db_put_principal(kcontext,
+ &orig_entry,
+ &n_howmany))
+ || (n_howmany != 1)) {
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ else {
+ com_err(programname, 0,
+ admin_db_rename_fmt,
+ op_msg, original, new,
+ client_name);
+ }
+ orig_entry.principal = (krb5_principal) NULL;
+ }
+ else {
+ if (kret) {
+ com_err(programname, kret,
+ admin_db_read_err_fmt,
+ op_msg,
+ client_name);
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ else {
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> principal \"%s\" already exists\n",
+ new));
+ retval = KRB5_ADM_P_ALREADY_EXISTS;
+ }
+ if (!kret)
+ krb5_db_free_principal(kcontext,
+ &xxx_dbentry,
+ n_howmany);
+ }
+ /* Clean up from krb5_parse_name */
+ krb5_free_principal(kcontext, new_principal);
+ }
+ else {
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> bad principal string \"%s\"\n",
+ new));
+ retval = KRB5_ADM_BAD_OPTION;
+ }
+ }
+ /*
+ * If we've fallen through, or if the new addition was
+ * successful, delete the original entry.
+ */
+ if (!kret && !retval) {
+ int num2do = 1;
+ /* Delete operation */
+ kret = krb5_db_delete_principal(kcontext,
+ orig_principal,
+ &num2do);
+ if ((kret != 0) || (num2do != 1)) {
+ if (kret) {
+ com_err(programname, kret,
+ admin_db_del_err_fmt,
+ original, op_msg, client_name);
+ }
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ else {
+ if (!new)
+ com_err(programname, 0,
+ admin_db_success_fmt,
+ op_msg, original, client_name);
+ }
+ }
+ krb5_db_free_principal(kcontext, &orig_entry, 1);
+ }
+ else {
+ /* Database lookup failed or returned unexpected result */
+ if (kret) {
+ com_err(programname, kret,
+ admin_db_read_err_fmt, op_msg, client_name);
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ else {
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> principal %s not in database\n",
+ original));
+ retval = KRB5_ADM_P_DOES_NOT_EXIST;
+ }
+ }
+
+ /* Clean up from krb5_parse_name */
+ krb5_free_principal(kcontext, orig_principal);
+ }
+ else {
+ /* Principal name parse failed */
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> bad principal string \"%s\"\n", original));
+ retval = KRB5_ADM_P_DOES_NOT_EXIST;
+ }
+ }
+ else {
+ /* ACL check failed */
+ com_err(programname, 0, admin_perm_denied_fmt,
+ op_msg, client_name);
+ retval = KRB5_ADM_NOT_AUTHORIZED;
+ }
+
+ /* Clean up admin_client_identity droppings */
+ krb5_xfree(client_name);
+ krb5_free_principal(kcontext, client);
+ }
+ else {
+ /* We really choked here. */
+ com_err(programname, kret, admin_no_cl_ident_fmt, op_msg);
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("X admin_delete_rename() = %d\n", retval));
+ return(retval);
+}
+
+/*
+ * admin_inq_iterator() - Routine called by krb5_db_iterate to scan through the
+ * database for a particular entry and its next entry.
+ */
+static krb5_error_code
+admin_inq_iterator(pointer, dbentp)
+ krb5_pointer pointer;
+ krb5_db_entry *dbentp;
+{
+ krb5_error_code kret;
+ struct inq_context *iargp;
+
+ kret = 0;
+ iargp = (struct inq_context *) pointer;
+ /*
+ * See if we have found a target of our search.
+ */
+ if (!iargp->ic_entry_found) {
+ /*
+ * No, now we are looking for a particular entry.
+ */
+ if (!iargp->ic_who ||
+ krb5_principal_compare(iargp->ic_context,
+ iargp->ic_who,
+ dbentp->principal)) {
+ if (iargp->ic_who) {
+ DPRINT(DEBUG_OPERATION, iargp->ic_level,
+ ("> found entry\n"));
+ kret = krb5_adm_dbent_to_proto(iargp->ic_context,
+ KRB5_ADM_M_GET_VALID,
+ dbentp,
+ (char *) NULL,
+ iargp->ic_ncomps,
+ iargp->ic_clist);
+ if (!kret) {
+ iargp->ic_entry_found = 1;
+ DPRINT(DEBUG_OPERATION, iargp->ic_level,
+ ("> converted entry to protocol\n"));
+ }
+ else {
+ DPRINT(DEBUG_OPERATION, iargp->ic_level,
+ ("> convert entry to protocol failed\n"));
+ }
+ }
+ else {
+ iargp->ic_entry_found = 1;
+ *(iargp->ic_ncomps) = 0;
+ *(iargp->ic_clist) = (krb5_data *) NULL;
+ if (!iargp->ic_next) {
+ kret = krb5_unparse_name(iargp->ic_context,
+ dbentp->principal,
+ &iargp->ic_next);
+ if (kret) {
+ DPRINT(DEBUG_OPERATION, iargp->ic_level,
+ ("> unparse next entry failed\n"));
+ }
+ else {
+ DPRINT(DEBUG_OPERATION, iargp->ic_level,
+ ("> next entry is %s\n", iargp->ic_next));
+ }
+ }
+ }
+ }
+ }
+ else {
+ if (!iargp->ic_next) {
+ kret = krb5_unparse_name(iargp->ic_context,
+ dbentp->principal,
+ &iargp->ic_next);
+ if (kret) {
+ DPRINT(DEBUG_OPERATION, iargp->ic_level,
+ ("> unparse next entry failed\n"));
+ }
+ else {
+ DPRINT(DEBUG_OPERATION, iargp->ic_level,
+ ("> next entry is %s\n", iargp->ic_next));
+ }
+ }
+ }
+ return(kret);
+}
+
+/*
+ * admin_add_principal() - Add a principal with the specified attributes
+ */
+krb5_int32
+admin_add_principal(kcontext, debug_level, ticket, nargs, arglist)
+ krb5_context kcontext; /* Kerberos context */ /* In */
+ int debug_level; /* Debug level */ /* In */
+ krb5_ticket *ticket; /* Kerberos ticket */ /* In */
+ krb5_int32 nargs; /* # rem. arguments */ /* In */
+ krb5_data *arglist; /* Remaining arguments */ /* In */
+{
+ krb5_int32 retval;
+
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("* admin_add_principal(%s)\n", arglist[0].data));
+ retval = admin_add_modify(kcontext,
+ debug_level,
+ ticket,
+ nargs,
+ arglist,
+ 0,
+ 0,
+ (char *) NULL);
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("X admin_add_principal() = %d\n", retval));
+ return(retval);
+}
+
+/*
+ * admin_delete_principal() - Delete the specified principal.
+ */
+krb5_int32
+admin_delete_principal(kcontext, debug_level, ticket, principal)
+ krb5_context kcontext; /* Kerberos context */ /* In */
+ int debug_level; /* Debug level */ /* In */
+ krb5_ticket *ticket; /* Kerberos ticket */ /* In */
+ krb5_data *principal; /* Principal to delete */ /* In */
+{
+ krb5_int32 retval;
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("* admin_delete_principal(%s)\n", principal->data));
+ retval = admin_delete_rename(kcontext,
+ debug_level,
+ ticket,
+ principal->data,
+ (char *) NULL);
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("X admin_delete_principal() = %d\n", retval));
+ return(retval);
+}
+
+/*
+ * admin_rename_principal() - Rename the original principal to the
+ * specified principal.
+ */
+krb5_int32
+admin_rename_principal(kcontext, debug_level, ticket, original, new)
+ krb5_context kcontext; /* Kerberos context */ /* In */
+ int debug_level; /* Debug level */ /* In */
+ krb5_ticket *ticket; /* Kerberos ticket */ /* In */
+ krb5_data *original; /* Original principal */ /* In */
+ krb5_data *new; /* New Principal */ /* In */
+{
+ krb5_int32 retval;
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("* admin_rename_principal(%s)\n", original->data));
+ retval = admin_delete_rename(kcontext,
+ debug_level,
+ ticket,
+ original->data,
+ new->data);
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("X admin_rename_principal() = %d\n", retval));
+ return(retval);
+}
+
+/*
+ * admin_modify_principal() - Modify the specified principal with the
+ * specifed attributes using the existing
+ * entry as a template.
+ */
+krb5_int32
+admin_modify_principal(kcontext, debug_level, ticket, nargs, arglist)
+ krb5_context kcontext; /* Kerberos context */ /* In */
+ int debug_level; /* Debug level */ /* In */
+ krb5_ticket *ticket; /* Kerberos ticket */ /* In */
+ krb5_int32 nargs; /* # rem. arguments */ /* In */
+ krb5_data *arglist; /* Remaining arguments */ /* In */
+{
+ krb5_int32 retval;
+
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("* admin_modify_principal(%s)\n", arglist[0].data));
+ retval = admin_add_modify(kcontext,
+ debug_level,
+ ticket,
+ nargs,
+ arglist,
+ 1,
+ 0,
+ (char *) NULL);
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("X admin_modify_principal() = %d\n", retval));
+ return(retval);
+}
+
+/*
+ * admin_change_opwd() - Change the password of a principal.
+ */
+krb5_int32
+admin_change_opwd(kcontext, debug_level, ticket, principal, password)
+ krb5_context kcontext; /* Kerberos context */ /* In */
+ int debug_level; /* Debug level */ /* In */
+ krb5_ticket *ticket; /* Kerberos ticket */ /* In */
+ krb5_data *principal; /* Principal */ /* In */
+ krb5_data *password; /* New Password */ /* In */
+{
+ krb5_int32 retval;
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("* admin_change_opw(%s)\n", principal->data));
+ retval = admin_add_modify(kcontext,
+ debug_level,
+ ticket,
+ 1,
+ principal,
+ 1,
+ 1,
+ password->data);
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("X admin_change_opw() = %d\n", retval));
+ return(retval);
+}
+
+/*
+ * admin_change_orandpwd() - Change the random key of a principal.
+ */
+krb5_int32
+admin_change_orandpwd(kcontext, debug_level, ticket, principal)
+ krb5_context kcontext; /* Kerberos context */ /* In */
+ int debug_level; /* Debug level */ /* In */
+ krb5_ticket *ticket; /* Kerberos ticket */ /* In */
+ krb5_data *principal; /* Principal */ /* In */
+{
+ krb5_int32 retval = 0;
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("* admin_change_orandpw(%s)\n", principal->data));
+ retval = admin_add_modify(kcontext,
+ debug_level,
+ ticket,
+ 1,
+ principal,
+ 1,
+ 1,
+ (char *) NULL);
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("X admin_change_orandpw() = %d\n", retval));
+ return(retval);
+}
+
+/*
+ * admin_inquire() - Retrieve the attributes of a principal.
+ */
+krb5_int32
+admin_inquire(kcontext, debug_level, ticket, principal, ncompp, complistp)
+ krb5_context kcontext; /* Kerberos context */ /* In */
+ int debug_level; /* Debug level */ /* In */
+ krb5_ticket *ticket; /* Kerberos ticket */ /* In */
+ krb5_data *principal; /* Principal */ /* In */
+ krb5_int32 *ncompp; /* # reply components */ /* Out */
+ krb5_data **complistp; /* Reply component list */ /* Out */
+{
+ krb5_int32 retval = KRB5_ADM_SUCCESS;
+ krb5_error_code kret = 0;
+ krb5_principal client;
+ krb5_principal target;
+ char *client_name;
+
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("* admin_inquire(%s)\n", principal->data));
+ /* Initialize */
+ client = target = (krb5_principal) NULL;
+ client_name = (char *) NULL;
+
+ /* Get the identity of our client */
+ if (!(kret = admin_client_identity(kcontext,
+ debug_level,
+ ticket,
+ &client,
+ &client_name))) {
+
+ /* See if this client can perform this operation. */
+ if (acl_op_permitted(kcontext, client, ACL_INQUIRE)) {
+
+ /* Parse the specified principal name */
+ if (!principal->length ||
+ !(kret = krb5_parse_name(kcontext,
+ principal->data,
+ &target))) {
+ struct inq_context iargs;
+ krb5_data *tmplistp;
+ krb5_int32 tmpncomp;
+
+ /*
+ * Now - if we had a "find/findnext" set of database operators
+ * then we could do this easily, but here we're going to have
+ * to iterate through the whole database and find our entry.
+ * If we find it, continue to the next entry so that we can
+ * return it.
+ */
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> Find entry %s\n",
+ ((principal->length) ? principal->data : "(null)")));
+ iargs.ic_context = kcontext;
+ iargs.ic_level = debug_level;
+ iargs.ic_who = (principal->length) ? target :
+ (krb5_principal) NULL;
+ iargs.ic_entry_found = 0;
+ iargs.ic_ncomps = &tmpncomp;
+ iargs.ic_clist = &tmplistp;
+ iargs.ic_next = (char *) NULL;
+ if (!(kret = krb5_db_iterate(kcontext,
+ admin_inq_iterator,
+ &iargs))) {
+
+ /* After scanning, did we find it? */
+ if (iargs.ic_entry_found) {
+
+ /*
+ * How inconvenient. We have the list already
+ * packaged up, but we have to insert our next
+ * element first.
+ */
+ if (*complistp = (krb5_data *)
+ malloc((size_t) (tmpncomp+1) * sizeof(krb5_data))
+ ) {
+ memset((char *) &(*complistp)[0], 0,
+ sizeof(krb5_data));
+ if (iargs.ic_next) {
+ (*complistp)[0].data = iargs.ic_next;
+ (*complistp)[0].length =
+ strlen((*complistp)[0].data);
+ }
+ memcpy(&(*complistp)[1], &tmplistp[0],
+ (size_t) tmpncomp * sizeof(krb5_data));
+ *ncompp = tmpncomp+1;
+ krb5_xfree(tmplistp);
+ }
+ else {
+ /* Could not get memory for new component list */
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> could not get memory\n"));
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ }
+ else {
+ /* Could not find principal */
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> cannot find principal \"%s\"\n",
+ principal->data));
+ retval = KRB5_ADM_P_DOES_NOT_EXIST;
+ }
+ }
+ else {
+ /* Could not iterate */
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> could not iterate database\n"));
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ /* Cleanup */
+ krb5_free_principal(kcontext, target);
+ }
+ else {
+ /* Could not parse principal name */
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> bad principal string \"%s\"\n", principal->data));
+ retval = KRB5_ADM_P_DOES_NOT_EXIST;
+ }
+ }
+ else {
+ /* Not authorized to perform this function */
+ com_err(programname, 0, admin_perm_denied_fmt,
+ admin_inquire_text, client_name);
+ retval = KRB5_ADM_NOT_AUTHORIZED;
+ }
+
+ /* Clean up */
+ krb5_xfree(client_name);
+ krb5_free_principal(kcontext, client);
+ }
+ else {
+ /* Something is really wrong here. */
+ com_err(programname, kret, admin_no_cl_ident_fmt, admin_inquire_text);
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("X admin_inquire() = %d\n", retval));
+ return(retval);
+}
+
+/*
+ * admin_extract_key() - Extract the service key entry for this name/instance
+ */
+krb5_int32
+admin_extract_key(kcontext, debug_level, ticket,
+ instance, name, ncompp, complistp)
+ krb5_context kcontext; /* Kerberos context */ /* In */
+ int debug_level; /* Debug level */ /* In */
+ krb5_ticket *ticket; /* Kerberos ticket */ /* In */
+ krb5_data *instance; /* Instance of principal*/ /* In */
+ krb5_data *name; /* Name of principal */ /* In */
+ krb5_int32 *ncompp; /* # reply components */ /* Out */
+ krb5_data **complistp; /* Reply component list */ /* Out */
+{
+ krb5_int32 retval = KRB5_ADM_SUCCESS;
+ krb5_error_code kret = 0;
+ krb5_principal client;
+ char *client_name;
+ char *realm;
+ char *princname;
+ krb5_principal principal;
+ krb5_db_entry dbentry;
+ int nentries;
+ krb5_boolean more;
+ krb5_keytab_entry ktabentry;
+ krb5_keyblock xxxkey;
+
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("* admin_extract_key(%s/%s)\n", name->data, instance->data));
+
+ /* Get the identity of our client */
+ if (!(kret = admin_client_identity(kcontext,
+ debug_level,
+ ticket,
+ &client,
+ &client_name))) {
+
+ /* See if this client can perform this operation. */
+ if (acl_op_permitted(kcontext, client, ACL_EXTRACT)) {
+
+ realm = key_master_realm();
+ if (princname = (char *) malloc((size_t) name->length + 1 +
+ instance->length + 1 +
+ strlen(realm) + 1)) {
+
+ /* Formulate the name of our target */
+ sprintf(princname, "%s/%s@%s", name->data,
+ instance->data, realm);
+
+ /* See if it's a valid name */
+ if (!(kret = krb5_parse_name(kcontext,
+ princname,
+ &principal))) {
+
+ /* Get the database entry */
+ nentries = 1;
+ if (!(kret = krb5_db_get_principal(kcontext,
+ principal,
+ &dbentry,
+ &nentries,
+ &more)) &&
+ (nentries == 1) && (!more)) {
+
+ /* Decrypt the key entries. */
+ memset((char *) &ktabentry, 0, sizeof(ktabentry));
+ memset((char *) &xxxkey, 0, sizeof(xxxkey));
+ if (!(kret = key_decrypt_keys(kcontext,
+ principal,
+ &dbentry.key,
+ &dbentry.alt_key,
+ &ktabentry.key,
+ &xxxkey))) {
+ ktabentry.principal = principal;
+ ktabentry.vno = dbentry.kvno;
+
+ /* Pack the response */
+ if (kret = krb5_adm_ktent_to_proto(kcontext,
+ &ktabentry,
+ ncompp,
+ complistp)) {
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> cannot package keytab protocol\n"));
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+
+ /* Cleanup from key_decrypt_keys */
+ if (xxxkey.contents) {
+ memset((char *) xxxkey.contents, 0,
+ (size_t) xxxkey.length);
+ krb5_xfree(xxxkey.contents);
+ }
+ if (ktabentry.key.contents) {
+ memset((char *) ktabentry.key.contents, 0,
+ (size_t) ktabentry.key.length);
+ krb5_xfree(ktabentry.key.contents);
+ }
+ }
+ else {
+ /* key_decrypt_keys failed */
+ com_err(programname, kret,
+ admin_key_dec_err_fmt,
+ princname, admin_extract_key_text,
+ client_name);
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ krb5_db_free_principal(kcontext, &dbentry, nentries);
+ }
+ else {
+ /*
+ * Database lookup failed or produced unexpected
+ * results.
+ */
+ if (kret) {
+ com_err(programname, kret,
+ admin_db_read_err_fmt,
+ admin_extract_key_text,
+ client_name);
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ else {
+ if (nentries == 0) {
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> principal \"%s\" does not exist\n",
+ princname));
+ retval = KRB5_ADM_P_DOES_NOT_EXIST;
+ }
+ else {
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> principal \"%s\" not unique\n",
+ princname));
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ krb5_db_free_principal(kcontext,
+ &dbentry,
+ nentries);
+ }
+ }
+ }
+ krb5_free_principal(kcontext, principal);
+ }
+ else {
+ /* Name parse failed */
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> bad principal string \"%s\"\n", princname));
+ retval = KRB5_ADM_P_DOES_NOT_EXIST;
+ }
+ free(princname);
+ }
+ else {
+ /* No memory. */
+ DPRINT(DEBUG_OPERATION, debug_level,
+ ("> no memory for principal name\n"));
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ }
+ else {
+ /* Not authorized to perform this operation */
+ com_err(programname, 0, admin_perm_denied_fmt,
+ admin_extract_key_text, client_name);
+ retval = KRB5_ADM_NOT_AUTHORIZED;
+ }
+
+ /* Clean up */
+ krb5_xfree(client_name);
+ krb5_free_principal(kcontext, client);
+ }
+ else {
+ /* Cannot get our identity */
+ com_err(programname, kret, admin_no_cl_ident_fmt,
+ admin_extract_key_text);
+ retval = KRB5_ADM_SYSTEM_ERROR;
+ }
+ DPRINT(DEBUG_CALLS, debug_level,
+ ("X admin_extract_key() = %d\n", retval));
+ return(retval);
+}
+