From 704ff3380764cc524d50bcf728822b530656354d Mon Sep 17 00:00:00 2001 From: Paul Park Date: Tue, 9 May 1995 19:53:01 +0000 Subject: [PATCH] Add new functions to support new administrative operations git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@5781 dc483132-0cff-0310-8789-dd5450dbe970 --- src/kadmin/v5server/.Sanitize | 1 + src/kadmin/v5server/ChangeLog | 30 + src/kadmin/v5server/Makefile.in | 4 +- src/kadmin/v5server/admin.c | 1386 +++++++++++++++++++++++++++++++ 4 files changed, 1419 insertions(+), 2 deletions(-) create mode 100644 src/kadmin/v5server/admin.c 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); +} + -- 2.26.2