a [Dis]allows the addition of principals or policies
c [Dis]allows the changing of passwords for principals
d [Dis]allows the deletion of principals or policies
+ e [Dis]allows the extraction of principal keys
i [Dis]allows inquiries about principals or policies
l [Dis]allows the listing of all principals or policies
m [Dis]allows the modification of principals or policies
p [Dis]allows the propagation of the principal database (used in :ref:`incr_db_prop`)
s [Dis]allows the explicit setting of the key for a principal
- x Short for admcilsp. All privileges
+ x Short for admcilsp. All privileges (except ``e``)
\* Same as x.
== ======================================================
static void
add_usage()
{
-#ifdef KADMIN_LOCAL
fprintf(stderr, _("Usage: ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] "
"[-norandkey] [principal | -glob princ-exp] [...]\n"));
-#else
- fprintf(stderr, _("Usage: ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] "
- "[principal | -glob princ-exp] [...]\n"));
-#endif
}
static void
} else if (strcmp(*argv, "-q") == 0) {
quiet++;
} else if (strcmp(*argv, "-norandkey") == 0) {
-#ifdef KADMIN_LOCAL
norandkey++;
-#else
- fprintf(stderr,
- _("-norandkey option only valid for kadmin.local\n"));
- return;
-#endif
} else if (strcmp(*argv, "-e") == 0) {
argc--;
if (argc < 1) {
free(keytab_str);
}
+/* Generate new random keys for princ, and convert them into a kadm5_key_data
+ * array (with no salt information). */
+static krb5_error_code
+fetch_new_keys(void *lhandle, krb5_principal princ, krb5_boolean keepold,
+ int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+ kadm5_key_data **key_data_out, int *nkeys_out)
+{
+ krb5_error_code code;
+ kadm5_key_data *key_data;
+ kadm5_principal_ent_rec princ_rec;
+ krb5_keyblock *keys = NULL;
+ int i, nkeys = 0;
+
+ *key_data_out = NULL;
+ *nkeys_out = 0;
+ memset(&princ_rec, 0, sizeof(princ_rec));
+
+ /* Generate new random keys. */
+ code = randkey_princ(lhandle, princ, keepold, n_ks_tuple, ks_tuple,
+ &keys, &nkeys);
+ if (code)
+ goto cleanup;
+
+ /* Get the principal entry to find the kvno of the new keys. (This is not
+ * atomic, but randkey doesn't report the new kvno.) */
+ code = kadm5_get_principal(lhandle, princ, &princ_rec,
+ KADM5_PRINCIPAL_NORMAL_MASK);
+ if (code)
+ goto cleanup;
+
+ key_data = k5calloc(nkeys, sizeof(*key_data), &code);
+ if (key_data == NULL)
+ goto cleanup;
+
+ /* Transfer the keyblocks and free the container array. */
+ for (i = 0; i < nkeys; i++) {
+ key_data[i].key = keys[i];
+ key_data[i].kvno = princ_rec.kvno;
+ }
+ *key_data_out = key_data;
+ *nkeys_out = nkeys;
+ free(keys);
+ keys = NULL;
+ nkeys = 0;
+
+cleanup:
+ for (i = 0; i < nkeys; i++)
+ krb5_free_keyblock_contents(context, &keys[i]);
+ free(keys);
+ kadm5_free_principal_ent(lhandle, &princ_rec);
+ return code;
+}
+
static void
add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab,
krb5_boolean keepold, int n_ks_tuple,
krb5_key_salt_tuple *ks_tuple, char *princ_str)
{
- kadm5_principal_ent_rec princ_rec;
krb5_principal princ = NULL;
krb5_keytab_entry new_entry;
- krb5_keyblock *keys;
+ kadm5_key_data *key_data;
int code, nkeys, i;
- memset(&princ_rec, 0, sizeof(princ_rec));
-
princ = NULL;
- keys = NULL;
+ key_data = NULL;
nkeys = 0;
code = krb5_parse_name(context, princ_str, &princ);
goto cleanup;
}
-#ifdef KADMIN_LOCAL
- if (norandkey)
- code = kadm5_get_principal_keys(handle, princ, &keys, &nkeys);
- else
-#endif
- code = randkey_princ(lhandle, princ, keepold, n_ks_tuple, ks_tuple,
- &keys, &nkeys);
+ if (norandkey) {
+ code = kadm5_get_principal_keys(handle, princ, 0, &key_data, &nkeys);
+ } else {
+ code = fetch_new_keys(handle, princ, keepold, n_ks_tuple, ks_tuple,
+ &key_data, &nkeys);
+ }
+
if (code != 0) {
if (code == KADM5_UNK_PRINC) {
fprintf(stderr, _("%s: Principal %s does not exist.\n"),
goto cleanup;
}
- code = kadm5_get_principal(lhandle, princ, &princ_rec,
- KADM5_PRINCIPAL_NORMAL_MASK);
- if (code != 0) {
- com_err(whoami, code, _("while retrieving principal"));
- goto cleanup;
- }
-
for (i = 0; i < nkeys; i++) {
memset(&new_entry, 0, sizeof(new_entry));
new_entry.principal = princ;
- new_entry.key = keys[i];
- new_entry.vno = princ_rec.kvno;
+ new_entry.key = key_data[i].key;
+ new_entry.vno = key_data[i].kvno;
code = krb5_kt_add_entry(context, keytab, &new_entry);
if (code != 0) {
com_err(whoami, code, _("while adding key to keytab"));
- kadm5_free_principal_ent(lhandle, &princ_rec);
goto cleanup;
}
if (!quiet) {
printf(_("Entry for principal %s with kvno %d, "
"encryption type %s added to keytab %s.\n"),
- princ_str, princ_rec.kvno,
- etype_string(keys[i].enctype), keytab_str);
+ princ_str, key_data[i].kvno,
+ etype_string(key_data[i].key.enctype), keytab_str);
}
}
- code = kadm5_free_principal_ent(lhandle, &princ_rec);
- if (code != 0) {
- com_err(whoami, code, _("while freeing principal entry"));
- goto cleanup;
- }
-
cleanup:
- for (i = 0; i < nkeys; i++)
- krb5_free_keyblock_contents(context, &keys[i]);
- free(keys);
+ kadm5_free_kadm5_key_data(context, nkeys, key_data);
krb5_free_principal(context, princ);
}
chrand3_arg chrand_principal3_2_arg;
setkey3_arg setkey_principal3_2_arg;
setkey4_arg setkey_principal4_2_arg;
+ getpkeys_arg get_principal_keys_2_arg;
} argument;
char *result;
bool_t (*xdr_argument)(), (*xdr_result)();
local = (char *(*)()) setkey_principal4_2_svc;
break;
+ case EXTRACT_KEYS:
+ xdr_argument = xdr_getpkeys_arg;
+ xdr_result = xdr_getpkeys_ret;
+ local = (char *(*)()) get_principal_keys_2_svc;
+ break;
+
default:
krb5_klog_syslog(LOG_ERR, "Invalid KADM5 procedure number: %s, %d",
client_addr(rqstp->rq_xprt), rqstp->rq_proc);
else
return rqstp->rq_clntcred;
}
+
+getpkeys_ret *
+get_principal_keys_2_svc(getpkeys_arg *arg, struct svc_req *rqstp)
+{
+ static getpkeys_ret ret;
+ char *prime_arg;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ xdr_free(xdr_getpkeys_ret, &ret);
+
+ if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
+ goto exit_func;
+
+ if ((ret.code = check_handle((void *)handle)))
+ goto exit_func;
+
+ ret.api_version = handle->api_version;
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ goto exit_func;
+ }
+ if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
+ ret.code = KADM5_BAD_PRINCIPAL;
+ goto exit_func;
+ }
+
+ if (!(CHANGEPW_SERVICE(rqstp)) &&
+ kadm5int_acl_check(handle->context, rqst2name(rqstp),
+ ACL_EXTRACT, arg->princ, NULL)) {
+ ret.code = kadm5_get_principal_keys((void *)handle, arg->princ,
+ arg->kvno, &ret.key_data,
+ &ret.n_key_data);
+ } else {
+ log_unauth("kadm5_get_principal_keys", prime_arg,
+ &client_name, &service_name, rqstp);
+ ret.code = KADM5_AUTH_EXTRACT;
+ }
+
+ if (ret.code != KADM5_AUTH_EXTRACT) {
+ if (ret.code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret.code);
+
+ log_done("kadm5_get_principal_keys", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+ free(prime_arg);
+exit_func:
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ free_server_handle(handle);
+ return &ret;
+}
fi
cat > $K5ROOT/ovsec_adm.acl <<EOF
-admin@$REALM admcils
+admin@$REALM admcilse
admin/get@$REALM il
admin/modify@$REALM mc
admin/delete@$REALM d
krb5_error_code kadm5_init_iprop(void *server_handle, char **db_args);
-/*
- * kadm5_get_principal_keys is used only by kadmin.local to extract existing
- * keys from the database without changing them. It should never be exposed
- * to the network protocol.
- */
kadm5_ret_t kadm5_get_principal_keys(void *server_handle,
krb5_principal principal,
- krb5_keyblock **keyblocks,
- int *n_keys);
-
+ krb5_kvno kvno,
+ kadm5_key_data **key_data,
+ int *n_key_data);
kadm5_ret_t kadm5_purgekeys(void *server_handle,
krb5_principal principal,
krb5_string_attr *strings,
int count);
+kadm5_ret_t kadm5_free_kadm5_key_data(krb5_context context, int n_key_data,
+ kadm5_key_data *key_data);
+
KADM5INT_END_DECLS
#endif /* __KADM5_ADMIN_H__ */
bool_t xdr_krb5_string_attr(XDR *xdrs, krb5_string_attr *objp);
bool_t xdr_osa_pw_hist_ent(XDR *xdrs, osa_pw_hist_ent *objp);
bool_t xdr_kadm5_key_data(XDR *xdrs, kadm5_key_data *objp);
+bool_t xdr_getpkeys_arg(XDR *xdrs, getpkeys_arg *objp);
+bool_t xdr_getpkeys_ret(XDR *xdrs, getpkeys_ret *objp);
LOCALINCLUDES = -I$(BUILDTOP)/include/kadm5
LIBBASE=kadm5clnt_mit
-LIBMAJOR=10
+LIBMAJOR=11
LIBMINOR=0
STOBJLISTS=../OBJS.ST OBJS.ST
SHLIB_EXPDEPS=\
eret();
return r->code;
}
+
+kadm5_ret_t
+kadm5_get_principal_keys(void *server_handle, krb5_principal princ,
+ krb5_kvno kvno, kadm5_key_data **key_data,
+ int *n_key_data)
+{
+ getpkeys_arg arg;
+ getpkeys_ret *r;
+ kadm5_server_handle_t handle = server_handle;
+
+ CHECK_HANDLE(server_handle);
+
+ arg.api_version = handle->api_version;
+ arg.princ = princ;
+ arg.kvno = kvno;
+
+ if (princ == NULL || key_data == NULL || n_key_data == 0)
+ return EINVAL;
+ r = get_principal_keys_2(&arg, handle->clnt);
+ if (r == NULL)
+ eret();
+ if (r->code == 0) {
+ *key_data = r->key_data;
+ *n_key_data = r->n_key_data;
+ }
+ return r->code;
+}
}
return (&clnt_res);
}
+
+getpkeys_ret *
+get_principal_keys_2(getpkeys_arg *argp, CLIENT *clnt)
+{
+ static getpkeys_ret clnt_res;
+
+ memset(&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call(clnt, EXTRACT_KEYS,
+ (xdrproc_t)xdr_getpkeys_arg, (caddr_t)argp,
+ (xdrproc_t)xdr_getpkeys_ret, (caddr_t)&clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return NULL;
+ }
+ return &clnt_res;
+}
kadm5_destroy
kadm5_flush
kadm5_free_config_params
+kadm5_free_kadm5_key_data
kadm5_free_key_data
kadm5_free_name_list
kadm5_free_policy_ent
kadm5_get_policies
kadm5_get_policy
kadm5_get_principal
+kadm5_get_principal_keys
kadm5_get_principals
kadm5_get_privs
kadm5_get_strings
xdr_dpol_arg
xdr_dprinc_arg
xdr_generic_ret
+xdr_getpkeys_arg
+xdr_getpkeys_ret
xdr_getprivs_ret
xdr_gpol_arg
xdr_gpol_ret
error_code KADM5_PASS_Q_GENERIC, "Unspecified password quality failure"
error_code KADM5_BAD_KEYSALTS, "Invalid key/salt tuples"
error_code KADM5_SETKEY_BAD_KVNO, "Invalid multiple or duplicate kvnos in setkey operation"
+error_code KADM5_AUTH_EXTRACT, "Operation requires ``extract-keys'' privilege"
end
};
typedef struct sstring_arg sstring_arg;
+struct getpkeys_arg {
+ krb5_ui_4 api_version;
+ krb5_principal princ;
+ krb5_kvno kvno;
+};
+typedef struct getpkeys_arg getpkeys_arg;
+
+struct getpkeys_ret {
+ krb5_ui_4 api_version;
+ kadm5_ret_t code;
+ kadm5_key_data *key_data;
+ int n_key_data;
+};
+typedef struct getpkeys_ret getpkeys_ret;
+
#define KADM 2112
#define KADMVERS 2
#define CREATE_PRINCIPAL 1
#define SETKEY_PRINCIPAL4 25
extern generic_ret * setkey_principal4_2(setkey4_arg *, CLIENT *);
extern generic_ret * setkey_principal4_2_svc(setkey4_arg *, struct svc_req *);
+#define EXTRACT_KEYS 26
+extern getpkeys_ret * get_principal_keys_2(getpkeys_arg *, CLIENT *);
+extern getpkeys_ret * get_principal_keys_2_svc(getpkeys_arg *, struct svc_req *);
extern bool_t xdr_cprinc_arg ();
extern bool_t xdr_cprinc3_arg ();
extern bool_t xdr_sstring_arg ();
extern bool_t xdr_krb5_string_attr ();
extern bool_t xdr_kadm5_key_data ();
-
+extern bool_t xdr_getpkeys_arg ();
+extern bool_t xdr_getpkeys_ret ();
#endif /* __KADM_RPC_H__ */
return FALSE;
return TRUE;
}
+
+bool_t
+xdr_getpkeys_arg(XDR *xdrs, getpkeys_arg *objp)
+{
+ if (!xdr_ui_4(xdrs, &objp->api_version)) {
+ return FALSE;
+ }
+ if (!xdr_krb5_principal(xdrs, &objp->princ)) {
+ return FALSE;
+ }
+ if (!xdr_krb5_kvno(xdrs, &objp->kvno)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool_t
+xdr_getpkeys_ret(XDR *xdrs, getpkeys_ret *objp)
+{
+ if (!xdr_ui_4(xdrs, &objp->api_version)) {
+ return FALSE;
+ }
+ if (!xdr_kadm5_ret_t(xdrs, &objp->code)) {
+ return FALSE;
+ }
+ if (objp->code == KADM5_OK) {
+ if (!xdr_array(xdrs, (caddr_t *) &objp->key_data,
+ (unsigned int *) &objp->n_key_data, ~0,
+ sizeof(kadm5_key_data), xdr_kadm5_key_data)) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
free(strings);
return KADM5_OK;
}
+
+kadm5_ret_t
+kadm5_free_kadm5_key_data(krb5_context context, int n_key_data,
+ kadm5_key_data *key_data)
+{
+ int i;
+
+ if (key_data == NULL)
+ return KADM5_OK;
+
+ for (i = 0; i < n_key_data; i++) {
+ krb5_free_keyblock_contents(context, &key_data[i].key);
+ krb5_free_data_contents(context, &key_data[i].salt.data);
+ }
+ free(key_data);
+
+ return KADM5_OK;
+}
##DOSLIBNAME = libkadm5srv.lib
LIBBASE=kadm5srv_mit
-LIBMAJOR=10
+LIBMAJOR=11
LIBMINOR=0
STOBJLISTS=../OBJS.ST OBJS.ST
kadm5_destroy
kadm5_flush
kadm5_free_config_params
+kadm5_free_kadm5_key_data
kadm5_free_key_data
kadm5_free_name_list
kadm5_free_policy_ent
xdr_dpol_arg
xdr_dprinc_arg
xdr_generic_ret
+xdr_getpkeys_arg
+xdr_getpkeys_ret
xdr_getprivs_ret
xdr_gpol_arg
xdr_gpol_ret
{ 's', ACL_SETKEY },
{ 'x', ACL_ALL_MASK },
{ '*', ACL_ALL_MASK },
+ { 'e', ACL_EXTRACT },
{ '\0', 0 }
};
#define ACL_CHANGEPW 8
/* #define ACL_CHANGE_OWN_PW 16 */
#define ACL_INQUIRE 32
-/* #define ACL_EXTRACT 64 */
+#define ACL_EXTRACT 64
#define ACL_LIST 128
#define ACL_SETKEY 256
#define ACL_IPROP 512
kadm5_ret_t
kadm5_get_principal_keys(void *server_handle /* IN */,
krb5_principal principal /* IN */,
- krb5_keyblock **keyblocks /* OUT */,
- int *n_keys /* OUT */)
+ krb5_kvno kvno /* IN */,
+ kadm5_key_data **key_data_out /* OUT */,
+ int *n_key_data_out /* OUT */)
{
krb5_db_entry *kdb;
osa_princ_ent_rec adb;
kadm5_ret_t ret;
kadm5_server_handle_t handle = server_handle;
+ kadm5_key_data *key_data = NULL;
+ int i, nkeys = 0;
- if (keyblocks)
- *keyblocks = NULL;
+ if (principal == NULL || key_data_out == NULL || n_key_data_out == NULL)
+ return EINVAL;
CHECK_HANDLE(server_handle);
- if (principal == NULL)
- return EINVAL;
-
if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
return(ret);
- if (keyblocks) {
- ret = decrypt_key_data(handle->context,
- kdb->n_key_data, kdb->key_data,
- keyblocks, n_keys);
+ key_data = calloc(kdb->n_key_data, sizeof(kadm5_key_data));
+ if (key_data == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0, nkeys = 0; i < kdb->n_key_data; i++) {
+ if (kvno != 0 && kvno != kdb->key_data[i].key_data_kvno)
+ continue;
+ key_data[nkeys].kvno = kdb->key_data[i].key_data_kvno;
+
+ ret = krb5_dbe_decrypt_key_data(handle->context, NULL,
+ &kdb->key_data[i],
+ &key_data[nkeys].key,
+ &key_data[nkeys].salt);
if (ret)
goto done;
+ nkeys++;
}
+ *n_key_data_out = nkeys;
+ *key_data_out = key_data;
+ key_data = NULL;
+ nkeys = 0;
ret = KADM5_OK;
+
done:
kdb_free_entry(handle, kdb, &adb);
+ kadm5_free_kadm5_key_data(handle->context, nkeys, key_data);
return ret;
}
*/
for (test = 0; tests[test] != NULL; test++) {
krb5_keyblock *testp = tests[test];
+ kadm5_key_data *extracted;
+ int n_extracted, match;
printf("+ Test %d:\n", test);
for (encnum = 0; testp[encnum].magic != -1; encnum++) {
exit(1);
}
+ ret = kadm5_get_principal_keys(handle, princ, 0, &extracted,
+ &n_extracted);
+ if (ret) {
+ com_err(whoami, ret, "while extracting keys");
+ exit(1);
+ }
+
for (encnum = 0; testp[encnum].magic != -1; encnum++) {
printf("+ enctype %d\n", testp[encnum].enctype);
+ for (match = 0; match < n_extracted; match++) {
+ if (extracted[match].key.enctype == testp[encnum].enctype)
+ break;
+ }
+ if (match >= n_extracted) {
+ com_err(whoami, KRB5_WRONG_ETYPE, "while matching enctypes");
+ exit(1);
+ }
+ if (extracted[match].key.length != testp[encnum].length ||
+ memcmp(extracted[match].key.contents, testp[encnum].contents,
+ testp[encnum].length) != 0) {
+ com_err(whoami, KRB5_KDB_NO_MATCHING_KEY, "verifying keys");
+ exit(1);
+ }
+
memset(&ktent, 0, sizeof(ktent));
ktent.principal = princ;
ktent.key = testp[encnum];
exit(1);
}
}
+
+ (void)kadm5_free_kadm5_key_data(context, n_extracted, extracted);
}
ret = krb5_kt_close(context, kt);
fail('Expected output not seen from kinit -i')
realm.klist(realm.user_princ)
+# Test extracting keys with multiple key versions present.
+os.remove(realm.keytab)
+realm.run([kadminl, 'cpw', '-randkey', '-keepold', realm.host_princ])
+out = realm.run([kadminl, 'ktadd', '-norandkey', realm.host_princ])
+if 'with kvno 1,' not in out or 'with kvno 2,' not in out:
+ fail('Expected output not seen from kadmin.local ktadd -norandkey')
+out = realm.run([klist, '-k', '-e'])
+if ' 1 host/' not in out or ' 2 host/' not in out:
+ fail('Expected output not seen from klist -k -e')
+
+# Test again using kadmin over the network.
+realm.prep_kadmin()
+os.remove(realm.keytab)
+out = realm.run_kadmin(['ktadd', '-norandkey', realm.host_princ])
+if 'with kvno 1,' not in out or 'with kvno 2,' not in out:
+ fail('Expected output not seen from kadmin.local ktadd -norandkey')
+out = realm.run([klist, '-k', '-e'])
+if ' 1 host/' not in out or ' 2 host/' not in out:
+ fail('Expected output not seen from klist -k -e')
+
# Test handling of kvno values beyond 255. Use kadmin over the
# network since we used to have an 8-bit limit on kvno marshalling.
if ('Key: vno %d,' % expected_kvno) not in out:
fail('vno %d not seen in getprinc output' % expected_kvno)
-realm.prep_kadmin()
princ = 'foo/bar@%s' % realm.realm
realm.addprinc(princ)
os.remove(realm.keytab)
global hostname
filename = os.path.join(self.testdir, 'acl')
file = open(filename, 'w')
- file.write('%s *\n' % self.admin_princ)
+ file.write('%s *e\n' % self.admin_princ)
file.write('kiprop/%s@%s p\n' % (hostname, self.realm))
file.close()