From c1406c5a1fd18a055e4e98bb4c52716dbd4b8be5 Mon Sep 17 00:00:00 2001 From: Paul Park Date: Fri, 4 Aug 1995 20:35:01 +0000 Subject: [PATCH] Add support for add-key and delete-key git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@6416 dc483132-0cff-0310-8789-dd5450dbe970 --- src/kadmin/v5server/admin.c | 723 +++++++++++++++++++++++++++++-- src/kadmin/v5server/proto_serv.c | 48 +- 2 files changed, 744 insertions(+), 27 deletions(-) diff --git a/src/kadmin/v5server/admin.c b/src/kadmin/v5server/admin.c index 0a061ba07..cbdca94ed 100644 --- a/src/kadmin/v5server/admin.c +++ b/src/kadmin/v5server/admin.c @@ -52,6 +52,7 @@ static const char *admin_db_success_fmt = "\007%s operation for %s successfully static const char *admin_db_read_err_fmt = "\004database read failed during %s operation by %s"; static const char *admin_no_cl_ident_fmt = "\004cannot get client identity from ticket for %s operation"; static const char *admin_db_rename_fmt = "\007%s operation from %s to %s successfully issued by %s"; +static const char *admin_db_key_op_fmt = "\007%s operation for %s successfully issued by %s"; static const char *admin_db_del_err_fmt = "\004database delete entry(%s) failed during %s operation by %s"; static const char *admin_key_dec_err_fmt = "\004key decode failed for %s's key during %s operation by %s"; @@ -63,6 +64,8 @@ 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"; +static const char *admin_add_key_text = "Add Keytype"; +static const char *admin_delete_key_text = "Delete Keytype"; extern char *programname; @@ -120,6 +123,167 @@ admin_client_identity(kcontext, debug_level, ticket, clientp, clnamep) return(kret); } +/* + * admin_merge_keys() - Merge two keylists. + * + * Fold "in1" into "in2" and put the results in "outp" + */ +static krb5_error_code +admin_merge_keys(kcontext, dbentp, unique, + nkeys1, in1, nkeys2, in2, nkeysout, outp) + krb5_context kcontext; + krb5_db_entry *dbentp; + krb5_boolean unique; + krb5_int32 nkeys1; + krb5_key_data *in1; + krb5_int32 nkeys2; + krb5_key_data *in2; + krb5_int32 *nkeysout; + krb5_key_data **outp; +{ + krb5_error_code kret; + int i; + krb5_int32 numout; + krb5_key_salt_tuple *kslist; + krb5_int32 nksents; + krb5_key_data *keylist; + krb5_db_entry xxx1,xxx2; + krb5_key_data *kp1, *kp2; + + keylist = (krb5_key_data *) NULL; + if ((keylist = (krb5_key_data *) malloc(sizeof(krb5_key_data) * + (nkeys1+nkeys2)))) { + memset(keylist, 0, sizeof(krb5_key_data) * (nkeys1+nkeys2)); + numout = 0; + if (!unique) { + /* The easy case */ + /* + * Start with "in1" - it's newer. + */ + for (i=0; ikey_data_kvno > kp1->key_data_kvno) + kp1 = kp2; + } + else { + if (!kp1) + kp1 = kp2; + if (!kp1) { + kret = KRB5KRB_ERR_GENERIC; + break; + } + } + if ((keylist[numout].key_data_contents[0] = + (krb5_octet *) malloc(kp1->key_data_length[0])) && + (!kp1->key_data_length[1] || + (keylist[numout].key_data_contents[1] = + (krb5_octet *) malloc(kp1->key_data_length[1])))) { + keylist[numout].key_data_ver = kp1->key_data_ver; + keylist[numout].key_data_kvno = kp1->key_data_kvno; + keylist[numout].key_data_type[0] = + kp1->key_data_type[0]; + keylist[numout].key_data_type[1] = + kp1->key_data_type[1]; + if (keylist[numout].key_data_length[0] = + kp1->key_data_length[0]) + memcpy(keylist[numout].key_data_contents[0], + kp1->key_data_contents[0], + kp1->key_data_length[0]); + if (keylist[numout].key_data_length[1] = + kp1->key_data_length[1]) + memcpy(keylist[numout].key_data_contents[1], + kp1->key_data_contents[1], + kp1->key_data_length[1]); + numout++; + } + } + krb5_xfree(kslist); + } + } + } + else + kret = ENOMEM; + + if (!kret) { + if (*outp && *nkeysout) + key_free_key_data(*outp, *nkeysout); + *outp = keylist; + *nkeysout = numout; + } + else { + free(keylist); + } + return(kret); +} + /* * admin_merge_dbentries() - Merge two database entries and a password. */ @@ -140,8 +304,9 @@ admin_merge_dbentries(kcontext, debug_level, who, defaultp, krb5_tl_data *pwchg, *def_pwchg; krb5_tl_data *new, *def; krb5_tl_mod_princ modent; - krb5_int32 num_keys; + krb5_int32 num_keys, num_ekeys, num_rkeys; krb5_key_data *key_list; + krb5_key_data *ekey_list; DPRINT(DEBUG_CALLS, debug_level, ("* admin_merge_dbentries()\n")); /* @@ -150,8 +315,9 @@ admin_merge_dbentries(kcontext, debug_level, who, defaultp, * and that we don't have a password and the random-password option. */ kret = EINVAL; - num_keys = 0; + num_keys = num_ekeys = num_rkeys = 0; key_list = (krb5_key_data *) NULL; + ekey_list = (krb5_key_data *) NULL; if (dbentp->princ && !(kret = krb5_timeofday(kcontext, &now)) && (!password || ((valid & KRB5_ADM_M_RANDOMKEY) == 0))) { @@ -219,13 +385,7 @@ admin_merge_dbentries(kcontext, debug_level, who, defaultp, * If we're changing the password, the time is now. */ if (password || is_pwchange || !def_pwchg) { - pwchg->tl_data_contents[0] = - (unsigned char) ((now >> 24) & 0xff); - pwchg->tl_data_contents[1] = - (unsigned char) ((now >> 16) & 0xff); - pwchg->tl_data_contents[2] = - (unsigned char) ((now >> 8) & 0xff); - pwchg->tl_data_contents[3] = (unsigned char) (now & 0xff); + krb5_kdb_encode_int32(now, pwchg->tl_data_contents); } else { /* @@ -265,17 +425,32 @@ admin_merge_dbentries(kcontext, debug_level, who, defaultp, if (!(kret = key_string_to_keys(kcontext, dbentp, &pwdata, + 0, + (krb5_key_salt_tuple *) NULL, &num_keys, &key_list))) { /* Encrypt the keys */ DPRINT(DEBUG_OPERATION, debug_level, ("> encode\n")); + num_ekeys = num_keys; kret = key_encrypt_keys(kcontext, dbentp, - &num_keys, + &num_ekeys, key_list, - &dbentp->key_data); - if (!kret) - dbentp->n_key_data = (krb5_int16) num_keys; + &ekey_list); + if (!kret) { + num_rkeys = (krb5_int32) dbentp->n_key_data; + kret = admin_merge_keys(kcontext, + dbentp, + 1, + num_ekeys, + ekey_list, + (krb5_int32) + dbentp->n_key_data, + dbentp->key_data, + &num_rkeys, + &dbentp->key_data); + dbentp->n_key_data = (krb5_int16) num_rkeys; + } } } else { @@ -288,13 +463,26 @@ admin_merge_dbentries(kcontext, debug_level, who, defaultp, /* Encrypt the keys */ DPRINT(DEBUG_OPERATION, debug_level, ("> encode\n")); + num_ekeys = num_keys; kret = key_encrypt_keys(kcontext, dbentp, - &num_keys, + &num_ekeys, key_list, - &dbentp->key_data); - if (!kret) - dbentp->n_key_data = (krb5_int16) num_keys; + &ekey_list); + if (!kret) { + num_rkeys = (krb5_int32) dbentp->n_key_data; + kret = admin_merge_keys(kcontext, + dbentp, + 0, + num_ekeys, + ekey_list, + (krb5_int32) + dbentp->n_key_data, + dbentp->key_data, + &num_rkeys, + &dbentp->key_data); + dbentp->n_key_data = (krb5_int16) num_rkeys; + } } } } @@ -302,6 +490,8 @@ admin_merge_dbentries(kcontext, debug_level, who, defaultp, if (key_list) key_free_key_data(key_list, num_keys); + if (ekey_list) + key_free_key_data(ekey_list, num_ekeys); DPRINT(DEBUG_CALLS, debug_level, ("X admin_merge_dbentries()=%d\n", kret)); return(kret); } @@ -483,15 +673,6 @@ admin_add_modify(kcontext, debug_level, ticket, nargs, arglist, )) { int nument = 1; -#ifdef xxx - /* - * Update the key version number if we're - * changing it. - */ - if (should_exist && pwd_supplied) - new_dbentry.kvno++; -#endif /* xxx */ - /* Write the entry. */ kret = krb5_db_put_principal(kcontext, &new_dbentry, @@ -846,6 +1027,446 @@ admin_delete_rename(kcontext, debug_level, ticket, original, new) return(retval); } +/* + * admin_keysalt_parse() - Parse a list of :[:]. + */ +static krb5_int32 +admin_keysalt_parse(kcontext, debug_level, nents, entries, dups, + nksp, kslistp, kvnolistp) + krb5_context kcontext; + int debug_level; + krb5_int32 nents; + krb5_data *entries; + krb5_boolean dups; + krb5_int32 *nksp; + krb5_key_salt_tuple **kslistp; + krb5_int32 **kvnolistp; +{ + krb5_int32 retval; + krb5_key_salt_tuple *keysalts; + krb5_int32 *kvnolist; + krb5_int32 ndone; + int i,j; + char *kvnop; + int ncolon; + krb5_int32 nparsed; + + DPRINT(DEBUG_CALLS, debug_level, ("* admin_keysalt_parse()\n")); + retval = 0; + ndone = 0; + keysalts = (krb5_key_salt_tuple *) NULL; + if (kvnolist = (krb5_int32 *) malloc(nents * sizeof(krb5_int32))) { + for (i=0; in_key_data; + for (i=0; ikey_data_contents[0]) + krb5_xfree(kdata->key_data_contents[0]); + if (kdata->key_data_contents[1]) + krb5_xfree(kdata->key_data_contents[1]); + memset(kdata, 0, sizeof(krb5_key_data)); + count--; + } + else { + retval = KRB5_ADM_SYSTEM_ERROR; + break; + } + } + kdata = dbentp->key_data; + if (dbentp->key_data = (krb5_key_data *) malloc(count * + sizeof(krb5_key_data)) + ) { + j = 0; + for (i = 0; in_key_data; i++) { + if (kdata[i].key_data_ver || + kdata[i].key_data_kvno || + kdata[i].key_data_type[0] || + kdata[i].key_data_type[1] || + kdata[i].key_data_length[0] || + kdata[i].key_data_length[1] || + kdata[i].key_data_contents[0] || + kdata[i].key_data_contents[1]) { + memcpy(&dbentp->key_data[j], + &kdata[i], + sizeof(krb5_key_data)); + j++; + } + } + krb5_xfree(kdata); + dbentp->n_key_data = count; + } + else { + dbentp->key_data = kdata; + retval = KRB5_ADM_SYSTEM_ERROR; + } + } + else { + /* Convert the string to key for the new key types */ + kdata = ekdata = (krb5_key_data *) NULL; + if (!key_string_to_keys(kcontext, + dbentp, + password, + nksents, + kslist, + &num_keys, + &kdata) && + !key_encrypt_keys(kcontext, + dbentp, + &num_keys, + kdata, + &ekdata)) { + num_ekeys = (krb5_int32) dbentp->n_key_data; + if (admin_merge_keys(kcontext, + dbentp, + 0, + num_keys, + ekdata, + (krb5_int32) dbentp->n_key_data, + dbentp->key_data, + &num_ekeys, + &dbentp->key_data)) { + retval = KRB5_ADM_SYSTEM_ERROR; + } + else + dbentp->n_key_data = (krb5_int16) num_ekeys; + } + if (kdata && num_keys) + key_free_key_data(kdata, num_keys); + if (ekdata && num_keys) + key_free_key_data(ekdata, num_keys); + } + DPRINT(DEBUG_CALLS, debug_level, ("X admin_keysalt_operate() = %d\n", + retval)); + return(retval); +} + +/* + * admin_key_op() - Handle an add_key or delete_key request. + */ +static krb5_int32 +admin_key_op(kcontext, debug_level, ticket, nargs, arglist, is_delete) + krb5_context kcontext; + int debug_level; + krb5_ticket *ticket; + krb5_int32 nargs; + krb5_data *arglist; + krb5_boolean is_delete; +{ + krb5_int32 retval = 0; + krb5_error_code kret; + krb5_principal client; + char *client_name; + krb5_db_entry entry; + krb5_principal principal; + krb5_int32 operation; + const char * op_msg; + krb5_tl_mod_princ *mprinc; + krb5_int32 nkeysalts; + krb5_key_salt_tuple *keysalt_list; + krb5_int32 *kvno_list; + krb5_timestamp now; + int n_howmany; + + DPRINT(DEBUG_CALLS, debug_level, + ("* admin_key_op(%s,%d)\n", arglist[0].data, is_delete)); + + /* Initialize */ + client = (krb5_principal) NULL; + client_name = (char *) NULL; + memset((char *) &entry, 0, sizeof(entry)); + principal = (krb5_principal) NULL; + operation = ACL_MODIFY_PRINCIPAL; + op_msg = (is_delete) ? admin_delete_key_text : admin_add_key_text; + keysalt_list = (krb5_key_salt_tuple *) NULL; + kvno_list = (krb5_int32 *) 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, operation)) { + + /* Parse the specified principal name */ + if (!(kret = krb5_parse_name(kcontext, + arglist[0].data, + &principal))) { + int how_many; + krb5_boolean more; + + how_many = 1; + + /* Try to get the entry */ + if (!(kret = krb5_db_get_principal(kcontext, + principal, + &entry, + &how_many, + &more)) + && how_many) { + + /* See if the supplied old password is good */ + if (passwd_check_opass_ok(kcontext, + debug_level, + principal, + &entry, + &arglist[1])) { + + if ( + /* Parse the keysalt arguments */ + !(retval = admin_keysalt_parse(kcontext, + debug_level, + nargs-2, + &arglist[2], + is_delete, + &nkeysalts, + &keysalt_list, + &kvno_list)) && + /* Verify the disposition */ + !(retval = admin_keysalt_verify(kcontext, + debug_level, + &entry, + is_delete, + nkeysalts, + keysalt_list, + kvno_list)) && + /* Perform the surgery */ + !(retval = admin_keysalt_operate(kcontext, + debug_level, + &entry, + arglist[1], + is_delete, + nkeysalts, + keysalt_list, + kvno_list)) && + /* Get the time of day */ + !(kret = krb5_timeofday(kcontext, &now))) { + /* Update our stats */ + if (!krb5_dbe_decode_mod_princ_data(kcontext, + &entry, + &mprinc)) { + krb5_free_principal(kcontext, + mprinc->mod_princ); + krb5_copy_principal(kcontext, + client, + &mprinc->mod_princ); + mprinc->mod_date = now; + krb5_dbe_encode_mod_princ_data(kcontext, + mprinc, + &entry); + krb5_free_principal(kcontext, + mprinc->mod_princ); + krb5_xfree(mprinc); + } + + n_howmany = 1; + if ((kret = krb5_db_put_principal(kcontext, + &entry, + &n_howmany)) + || (n_howmany != 1)) { + retval = KRB5_ADM_SYSTEM_ERROR; + } + else { + com_err(programname, 0, + admin_db_key_op_fmt, + op_msg, arglist[0].data, + client_name); + } + } + else { + if (kret) + retval = KRB5_ADM_SYSTEM_ERROR; + } + if (keysalt_list) + krb5_xfree(keysalt_list); + if (kvno_list) + free(kvno_list); + } + else + retval = KRB5_ADM_BAD_PW; + krb5_db_free_principal(kcontext, &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, 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_key_op() = %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. @@ -1401,7 +2022,57 @@ admin_extract_key(kcontext, debug_level, ticket, ("X admin_extract_key() = %d\n", retval)); return(retval); } + +/* + * admin_add_key() - Add new keytypes for the given principal. + */ +krb5_int32 +admin_add_key(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_key(%s)\n", arglist[0].data)); + retval = admin_key_op(kcontext, + debug_level, + ticket, + nargs, + arglist, + 0); + DPRINT(DEBUG_CALLS, debug_level, ("X admin_add_key() = %d\n", retval)); + return(retval); +} + +/* + * admin_delete_key() - Delete keytypes for the given principal. + */ +krb5_int32 +admin_delete_key(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_delete_key(%s)\n", arglist[0].data)); + retval = admin_key_op(kcontext, + debug_level, + ticket, + nargs, + arglist, + 1); + DPRINT(DEBUG_CALLS, debug_level, ("X admin_delete_key() = %d\n", retval)); + return(retval); +} + void admin_init(max_life, max_renew_life, e_valid, e, f_valid, f) krb5_deltat max_life; diff --git a/src/kadmin/v5server/proto_serv.c b/src/kadmin/v5server/proto_serv.c index 95ef77820..0876dfe3c 100644 --- a/src/kadmin/v5server/proto_serv.c +++ b/src/kadmin/v5server/proto_serv.c @@ -650,6 +650,52 @@ proto_serv(kcontext, my_id, cl_sock, sv_p, cl_p) err_aux = KADM_BAD_ARGS; } } + else if (!strcasecmp(arglist[0].data, + KRB5_ADM_ADD_KEY_CMD)) { + /* + * ADD KEY command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:ADD KEY command\n", my_id)); + /* Must have at least three arguments */ + if (num_args > 3) { + cmd_error = admin_add_key(kcontext, + proto_debug_level, + ticket, + num_args-1, + &arglist[1]); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:ADD KEY command syntax BAD\n", + my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } + else if (!strcasecmp(arglist[0].data, + KRB5_ADM_DEL_KEY_CMD)) { + /* + * DELETE KEY command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:DELETE KEY command\n", my_id)); + /* At least three arguments */ + if (num_args > 3) { + cmd_error = admin_delete_key(kcontext, + proto_debug_level, + ticket, + num_args-1, + &arglist[1]); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:DELETE KEY command syntax BAD\n", + my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } else { DPRINT(DEBUG_REQUESTS, proto_debug_level, ("> %d:UNKNOWN command %s\n", my_id, @@ -737,7 +783,7 @@ proto_serv(kcontext, my_id, cl_sock, sv_p, cl_p) errbuf.server = net_server_princ(); errbuf.error = kret - ERROR_TABLE_BASE_krb5; if (errbuf.error > 127) - errbuf.error = KRB_ERR_GENERIC; + errbuf.error = KRB5KRB_ERR_GENERIC; /* Format the error message in our language */ errmsg = output_krb5_errmsg(curr_lang, mime_setting, kret); errbuf.text.length = strlen(errmsg); -- 2.26.2