Merge pull request #12 from nicowilliams/krb5_admin_patches_2nd

Krb5 admin patches 2nd

This has all the patches needed for krb5_admind to build and pass most tests, that includes:
- more kadm5 API compatibility (including very basic profile functionality)
- multi-kvno support (useful for key rollovers) (a test for this is included in tests/db/check-kdc)

Unfinished:
- password history (currently uses key history, needs to be separated and use digests)
- policies (only default policy allowed)
- mit kdb changes not tested yet


Signed-off-by: Love Hörnquist Åstrand <lha@h5l.org>
This commit is contained in:
Love Hörnquist Åstrand
2011-07-24 15:41:36 -07:00
58 changed files with 2136 additions and 404 deletions

View File

@@ -78,8 +78,9 @@ add_enctype(struct add_enctype_options*opt, int argc, char **argv)
goto out2;
}
/* The principal might have zero keys, but it will still have a kvno! */
ret = kadm5_get_principal(kadm_handle, princ_ent, &princ,
KADM5_PRINCIPAL | KADM5_KEY_DATA);
KADM5_KVNO | KADM5_PRINCIPAL | KADM5_KEY_DATA);
if (ret) {
krb5_free_principal (context, princ_ent);
krb5_warnx (context, "no such principal: %s", princ_name);
@@ -98,6 +99,7 @@ add_enctype(struct add_enctype_options*opt, int argc, char **argv)
for (j = 0; j < n_etypes; ++j) {
if (etypes[j] == key->key_data_type[0]) {
/* XXX Should this be an error? The admin can del_enctype... */
krb5_warnx(context, "enctype %d already exists",
(int)etypes[j]);
free(new_key_data);
@@ -113,7 +115,7 @@ add_enctype(struct add_enctype_options*opt, int argc, char **argv)
memset(&new_key_data[n], 0, sizeof(new_key_data[n]));
new_key_data[n].key_data_ver = 2;
new_key_data[n].key_data_kvno = 0;
new_key_data[n].key_data_kvno = princ.kvno;
ret = krb5_generate_random_keyblock (context, etypes[i], &keyblock);
if (ret) {

View File

@@ -68,6 +68,7 @@ add_one_principal (const char *name,
int rand_password,
int use_defaults,
char *password,
char *policy,
krb5_key_data *key_data,
const char *max_ticket_life,
const char *max_renewable_life,
@@ -94,7 +95,7 @@ add_one_principal (const char *name,
ret = set_entry(context, &princ, &mask,
max_ticket_life, max_renewable_life,
expiration, pw_expiration, attributes);
expiration, pw_expiration, attributes, policy);
if (ret)
goto out;
@@ -159,10 +160,15 @@ add_one_principal (const char *name,
kadm5_get_principal(kadm_handle, princ_ent, &princ,
KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES);
princ.attributes &= (~KRB5_KDB_DISALLOW_ALL_TIX);
/*
* Updating kvno w/o key data and vice-versa gives _kadm5_setup_entry()
* and _kadm5_set_keys2() headaches. But we used to, so we handle
* this in in those two functions. Might as well leave this code as
* it was then.
*/
princ.kvno = 1;
kadm5_modify_principal(kadm_handle, &princ,
KADM5_ATTRIBUTES | KADM5_KVNO);
kadm5_free_principal_ent(kadm_handle, &princ);
} else if (key_data) {
ret = kadm5_chpass_principal_with_key (kadm_handle, princ_ent,
3, key_data);
@@ -173,7 +179,6 @@ add_one_principal (const char *name,
KADM5_PRINCIPAL | KADM5_ATTRIBUTES);
princ.attributes &= (~KRB5_KDB_DISALLOW_ALL_TIX);
kadm5_modify_principal(kadm_handle, &princ, KADM5_ATTRIBUTES);
kadm5_free_principal_ent(kadm_handle, &princ);
} else if (rand_password) {
char *princ_name;
@@ -182,8 +187,7 @@ add_one_principal (const char *name,
free (princ_name);
}
out:
if (princ_ent)
krb5_free_principal (context, princ_ent);
kadm5_free_principal_ent(kadm_handle, &princ); /* frees princ_ent */
if(default_ent)
kadm5_free_principal_ent (kadm_handle, default_ent);
if (password != NULL)
@@ -245,6 +249,7 @@ add_new_key(struct add_options *opt, int argc, char **argv)
opt->random_password_flag,
opt->use_defaults_flag,
opt->password_string,
opt->policy_string,
kdp,
opt->max_ticket_life_string,
opt->max_renewable_life_string,

View File

@@ -35,6 +35,7 @@
#include "kadmin-commands.h"
struct cpw_entry_data {
int keepold;
int random_key;
int random_password;
char *password;
@@ -42,14 +43,15 @@ struct cpw_entry_data {
};
static int
set_random_key (krb5_principal principal)
set_random_key (krb5_principal principal, int keepold)
{
krb5_error_code ret;
int i;
krb5_keyblock *keys;
int num_keys;
ret = kadm5_randkey_principal(kadm_handle, principal, &keys, &num_keys);
ret = kadm5_randkey_principal_3(kadm_handle, principal, keepold, 0, NULL,
&keys, &num_keys);
if(ret)
return ret;
for(i = 0; i < num_keys; i++)
@@ -59,13 +61,13 @@ set_random_key (krb5_principal principal)
}
static int
set_random_password (krb5_principal principal)
set_random_password (krb5_principal principal, int keepold)
{
krb5_error_code ret;
char pw[128];
random_password (pw, sizeof(pw));
ret = kadm5_chpass_principal(kadm_handle, principal, pw);
ret = kadm5_chpass_principal_3(kadm_handle, principal, keepold, 0, NULL, pw);
if (ret == 0) {
char *princ_name;
@@ -79,7 +81,7 @@ set_random_password (krb5_principal principal)
}
static int
set_password (krb5_principal principal, char *password)
set_password (krb5_principal principal, char *password, int keepold)
{
krb5_error_code ret = 0;
char pwbuf[128];
@@ -99,18 +101,19 @@ set_password (krb5_principal principal, char *password)
password = pwbuf;
}
if(ret == 0)
ret = kadm5_chpass_principal(kadm_handle, principal, password);
ret = kadm5_chpass_principal_3(kadm_handle, principal, keepold, 0, NULL,
password);
memset(pwbuf, 0, sizeof(pwbuf));
return ret;
}
static int
set_key_data (krb5_principal principal, krb5_key_data *key_data)
set_key_data (krb5_principal principal, krb5_key_data *key_data, int keepold)
{
krb5_error_code ret;
ret = kadm5_chpass_principal_with_key (kadm_handle, principal,
3, key_data);
ret = kadm5_chpass_principal_with_key_3(kadm_handle, principal, keepold,
3, key_data);
return ret;
}
@@ -120,13 +123,13 @@ do_cpw_entry(krb5_principal principal, void *data)
struct cpw_entry_data *e = data;
if (e->random_key)
return set_random_key (principal);
return set_random_key (principal, e->keepold);
else if (e->random_password)
return set_random_password (principal);
return set_random_password (principal, e->keepold);
else if (e->key_data)
return set_key_data (principal, e->key_data);
return set_key_data (principal, e->key_data, e->keepold);
else
return set_password (principal, e->password);
return set_password (principal, e->password, e->keepold);
}
int
@@ -138,6 +141,7 @@ cpw_entry(struct passwd_options *opt, int argc, char **argv)
int num;
krb5_key_data key_data[3];
data.keepold = opt->keepold_flag;
data.random_key = opt->random_key_flag;
data.random_password = opt->random_password_flag;
data.password = opt->password_string;

View File

@@ -106,6 +106,10 @@ del_enctype(void *opt, int argc, char **argv)
}
free (princ.key_data);
if (j == 0) {
free(new_key_data);
new_key_data = NULL;
}
princ.n_key_data = j;
princ.key_data = new_key_data;

View File

@@ -60,10 +60,12 @@ static struct field_name {
{ "last_failed", KADM5_LAST_FAILED, 0, 0, "Last fail", "Last failed login", 0 },
{ "fail_auth_count", KADM5_FAIL_AUTH_COUNT, 0, 0, "Fail count", "Failed login count", RTBL_ALIGN_RIGHT },
{ "policy", KADM5_POLICY, 0, 0, "Policy", "Policy", 0 },
{ "keytypes", KADM5_KEY_DATA, 0, KADM5_PRINCIPAL, "Keytypes", "Keytypes", 0 },
{ "keytypes", KADM5_KEY_DATA, 0, KADM5_PRINCIPAL | KADM5_KVNO, "Keytypes", "Keytypes", 0 },
{ "password", KADM5_TL_DATA, KRB5_TL_PASSWORD, KADM5_KEY_DATA, "Password", "Password", 0 },
{ "pkinit-acl", KADM5_TL_DATA, KRB5_TL_PKINIT_ACL, 0, "PK-INIT ACL", "PK-INIT ACL", 0 },
{ "aliases", KADM5_TL_DATA, KRB5_TL_ALIASES, 0, "Aliases", "Aliases", 0 },
{ "hist-kvno-diff-clnt", KADM5_TL_DATA, KRB5_TL_HIST_KVNO_DIFF_CLNT, 0, "Clnt hist keys", "Historic keys allowed for client", 0 },
{ "hist-kvno-diff-svc", KADM5_TL_DATA, KRB5_TL_HIST_KVNO_DIFF_SVC, 0, "Svc hist keys", "Historic keys allowed for service", 0 },
{ NULL }
};

View File

@@ -174,11 +174,31 @@ command = {
argument = "time"
help = "password expiration time"
}
option = {
long = "hist-kvno-diff-clnt"
type = "integer"
argument = "kvno diff"
help = "historic keys allowed for client"
default = "-1"
}
option = {
long = "hist-kvno-diff-svc"
type = "integer"
argument = "kvno diff"
help = "historic keys allowed for service"
default = "-1"
}
option = {
long = "use-defaults"
type = "flag"
help = "use default values"
}
option = {
long = "policy"
type = "string"
argument = "policy"
help = "policy name"
}
argument = "principal..."
min_args = "1"
help = "Adds a principal to the database."
@@ -210,6 +230,11 @@ command = {
type = "string"
help = "DES key in hex"
}
option = {
long = "keepold"
type = "flag"
help = "keep old keys/password"
}
argument = "principal..."
min_args = "1"
help = "Changes the password of one or more principals matching the expressions."
@@ -353,6 +378,26 @@ command = {
argument = "subject dn"
help = "aliases"
}
option = {
long = "policy"
type = "string"
argument = "policy"
help = "policy name"
}
option = {
long = "hist-kvno-diff-clnt"
type = "integer"
argument = "kvno diff"
help = "historic keys allowed for client"
default = "-1"
}
option = {
long = "hist-kvno-diff-svc"
type = "integer"
argument = "kvno diff"
help = "historic keys allowed for service"
default = "-1"
}
argument = "principal"
min_args = "1"
max_args = "1"
@@ -414,6 +459,22 @@ command = {
max_args = "1"
help = "Check the realm (if not given, the default realm) for configuration errors."
}
command = {
name = "lock"
function = "lock"
argument = ""
min_args = "0"
max_args = "0"
help = "Lock the database for writing (use with care)."
}
command = {
name = "unlock"
function = "unlock"
argument = ""
min_args = "0"
max_args = "0"
help = "Unlock the database."
}
command = {
name = "help"
name = "?"

View File

@@ -146,7 +146,8 @@ enctypes.
.Oc
.Ar principal...
.Bd -ragged -offset indent
Creates a keytab with the keys of the specified principals.
Creates a keytab with the keys of the specified principals. Requires
get-keys rights.
.Ed
.Pp
.Nm get
@@ -228,6 +229,7 @@ kadmin -l modify -a -disallow-proxiable user
.Ed
.Pp
.Nm passwd
.Op Fl Fl keepold
.Op Fl r | Fl Fl random-key
.Op Fl Fl random-password
.Oo Fl p Ar string \*(Ba Xo
@@ -260,6 +262,7 @@ Lists the operations you are allowed to perform. These include
.Li delete ,
.Li del_enctype ,
.Li get ,
.Li get-keys ,
.Li list ,
and
.Li modify .

View File

@@ -112,6 +112,18 @@ exit_kadmin (void *opt, int argc, char **argv)
return 0;
}
int
lock(void *opt, int argc, char **argv)
{
return kadm5_lock(kadm_handle);
}
int
unlock(void *opt, int argc, char **argv)
{
return kadm5_unlock(kadm_handle);
}
static void
usage(int ret)
{

View File

@@ -109,6 +109,9 @@ int str2attributes(const char *, krb5_flags *);
int parse_attributes (const char *, krb5_flags *, int *, int);
int edit_attributes (const char *, krb5_flags *, int *, int);
int parse_policy (const char *, char **, int *, int);
int edit_policy (const char *, char **, int *, int);
void time_t2str(time_t, char *, size_t, int);
int str2time_t (const char *, time_t *);
int parse_timet (const char *, krb5_timestamp *, int *, int);
@@ -124,7 +127,7 @@ int edit_entry(kadm5_principal_ent_t, int *, kadm5_principal_ent_t, int);
void set_defaults(kadm5_principal_ent_t, int *, kadm5_principal_ent_t, int);
int set_entry(krb5_context, kadm5_principal_ent_t, int *,
const char *, const char *, const char *,
const char *, const char *);
const char *, const char *, const char *);
int
foreach_principal(const char *, int (*)(krb5_principal, void*),
const char *, void *);

View File

@@ -107,6 +107,8 @@ add
.It
get
.It
get-keys
.It
all
.El
.Pp
@@ -147,10 +149,11 @@ compiled in defaults:
.D1 Nm Fl Fl ports Ns Li "=\*[q]+ 4711\*[q] &"
.Pp
This acl file will grant Joe all rights, and allow Mallory to view and
add host principals.
add host principals, as well as extract host principal keys (e.g., into
keytabs).
.Bd -literal -offset indent
joe/admin@EXAMPLE.COM all
mallory/admin@EXAMPLE.COM add,get host/*@EXAMPLE.COM
mallory/admin@EXAMPLE.COM add,get-keys host/*@EXAMPLE.COM
.Ed
.\".Sh DIAGNOSTICS
.Sh SEE ALSO

View File

@@ -41,7 +41,7 @@ add_tl(kadm5_principal_ent_rec *princ, int type, krb5_data *data)
tl = ecalloc(1, sizeof(*tl));
tl->tl_data_next = NULL;
tl->tl_data_type = KRB5_TL_EXTENSION;
tl->tl_data_type = type;
tl->tl_data_length = data->length;
tl->tl_data_contents = data->data;
@@ -185,6 +185,37 @@ add_pkinit_acl(krb5_context contextp, kadm5_principal_ent_rec *princ,
add_tl(princ, KRB5_TL_EXTENSION, &buf);
}
static void
add_kvno_diff(krb5_context context, kadm5_principal_ent_rec *princ,
int is_svc_diff, krb5_kvno kvno_diff)
{
krb5_error_code ret;
HDB_extension ext;
krb5_data buf;
size_t size = 0;
if (kvno_diff < 0)
return;
if (kvno_diff > 2048)
kvno_diff = 2048;
if (is_svc_diff) {
ext.data.element = choice_HDB_extension_data_hist_kvno_diff_svc;
ext.data.u.hist_kvno_diff_svc = (unsigned int)kvno_diff;
} else {
ext.data.element = choice_HDB_extension_data_hist_kvno_diff_clnt;
ext.data.u.hist_kvno_diff_clnt = (unsigned int)kvno_diff;
}
ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
&ext, &size, ret);
if (ret)
abort();
if (buf.length != size)
abort();
add_tl(princ, KRB5_TL_EXTENSION, &buf);
}
static int
do_mod_entry(krb5_principal principal, void *data)
{
@@ -207,16 +238,20 @@ do_mod_entry(krb5_principal principal, void *data)
e->expiration_time_string ||
e->pw_expiration_time_string ||
e->attributes_string ||
e->policy_string ||
e->kvno_integer != -1 ||
e->constrained_delegation_strings.num_strings ||
e->alias_strings.num_strings ||
e->pkinit_acl_strings.num_strings) {
e->pkinit_acl_strings.num_strings ||
e->hist_kvno_diff_clnt_integer != -1 ||
e->hist_kvno_diff_svc_integer != -1) {
ret = set_entry(context, &princ, &mask,
e->max_ticket_life_string,
e->max_renewable_life_string,
e->expiration_time_string,
e->pw_expiration_time_string,
e->attributes_string);
e->attributes_string,
e->policy_string);
if(e->kvno_integer != -1) {
princ.kvno = e->kvno_integer;
mask |= KADM5_KVNO;
@@ -234,7 +269,14 @@ do_mod_entry(krb5_principal principal, void *data)
add_pkinit_acl(context, &princ, &e->pkinit_acl_strings);
mask |= KADM5_TL_DATA;
}
if (e->hist_kvno_diff_clnt_integer != -1) {
add_kvno_diff(context, &princ, 0, e->hist_kvno_diff_clnt_integer);
mask |= KADM5_TL_DATA;
}
if (e->hist_kvno_diff_svc_integer != -1) {
add_kvno_diff(context, &princ, 1, e->hist_kvno_diff_clnt_integer);
mask |= KADM5_TL_DATA;
}
} else
ret = edit_entry(&princ, &mask, NULL, 0);
if(ret == 0) {

View File

@@ -47,9 +47,13 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
kadm5_principal_ent_rec ent;
char *password, *expression;
krb5_keyblock *new_keys;
krb5_key_salt_tuple *ks_tuple = NULL;
krb5_boolean keepold = FALSE;
int n_ks_tuple = 0;
int n_keys;
char **princs;
int n_princs;
int keys_ok = 0;
krb5_storage *sp;
krb5_unparse_name_fixed(contextp->context, contextp->caller,
@@ -74,7 +78,11 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
mask |= KADM5_PRINCIPAL;
krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET_KEYS, princ);
if (ret == 0)
keys_ok = 1;
else
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
if(ret){
krb5_free_principal(contextp->context, princ);
goto fail;
@@ -84,7 +92,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
sp = krb5_storage_emem();
krb5_store_int32(sp, ret);
if(ret == 0){
kadm5_store_principal_ent(sp, &ent);
if (keys_ok)
kadm5_store_principal_ent(sp, &ent);
else
kadm5_store_principal_ent_nokeys(sp, &ent);
kadm5_free_principal_ent(kadm_handlep, &ent);
}
krb5_free_principal(contextp->context, princ);
@@ -207,10 +218,15 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
case kadm_chpass:{
op = "CHPASS";
ret = krb5_ret_principal(sp, &princ);
if(ret)
if (ret)
goto fail;
ret = krb5_ret_string(sp, &password);
if(ret){
if (ret) {
krb5_free_principal(contextp->context, princ);
goto fail;
}
ret = krb5_ret_int32(sp, &keepold);
if (ret && ret != HEIM_ERR_EOF) {
krb5_free_principal(contextp->context, princ);
goto fail;
}
@@ -251,7 +267,8 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
free(password);
goto fail;
}
ret = kadm5_chpass_principal(kadm_handlep, princ, password);
ret = kadm5_chpass_principal_3(kadm_handlep, princ, keepold, 0, NULL,
password);
krb5_free_principal(contextp->context, princ);
memset(password, 0, strlen(password));
free(password);
@@ -274,6 +291,11 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
krb5_free_principal(contextp->context, princ);
goto fail;
}
ret = krb5_ret_int32(sp, &keepold);
if (ret && ret != HEIM_ERR_EOF) {
krb5_free_principal(contextp->context, princ);
goto fail;
}
/* n_key_data will be squeezed into an int16_t below. */
if (n_key_data < 0 || n_key_data >= 1 << 16 ||
(size_t)n_key_data > UINT_MAX/sizeof(*key_data)) {
@@ -318,8 +340,8 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
krb5_free_principal(contextp->context, princ);
goto fail;
}
ret = kadm5_chpass_principal_with_key(kadm_handlep, princ,
n_key_data, key_data);
ret = kadm5_chpass_principal_with_key_3(kadm_handlep, princ, keepold,
n_key_data, key_data);
{
int16_t dummy = n_key_data;
kadm5_free_key_data (contextp, &dummy, key_data);
@@ -355,9 +377,54 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
krb5_free_principal(contextp->context, princ);
goto fail;
}
ret = kadm5_randkey_principal(kadm_handlep, princ,
&new_keys, &n_keys);
/*
* See comments in kadm5_c_randkey_principal() regarding the
* protocol.
*/
ret = krb5_ret_int32(sp, &keepold);
if (ret != 0 && ret != HEIM_ERR_EOF) {
krb5_free_principal(contextp->context, princ);
goto fail;
}
ret = krb5_ret_int32(sp, &n_ks_tuple);
if (ret != 0 && ret != HEIM_ERR_EOF) {
krb5_free_principal(contextp->context, princ);
goto fail;
} else if (ret == 0) {
size_t i;
if (n_ks_tuple < 0) {
ret = EOVERFLOW;
krb5_free_principal(contextp->context, princ);
goto fail;
}
if ((ks_tuple = calloc(n_ks_tuple, sizeof (*ks_tuple))) == NULL) {
ret = errno;
krb5_free_principal(contextp->context, princ);
goto fail;
}
for (i = 0; i < n_ks_tuple; i++) {
ret = krb5_ret_int32(sp, &ks_tuple[i].ks_enctype);
if (ret != 0) {
krb5_free_principal(contextp->context, princ);
goto fail;
}
ret = krb5_ret_int32(sp, &ks_tuple[i].ks_salttype);
if (ret != 0) {
krb5_free_principal(contextp->context, princ);
goto fail;
}
}
}
ret = kadm5_randkey_principal_3(kadm_handlep, princ, keepold,
n_ks_tuple, ks_tuple, &new_keys,
&n_keys);
krb5_free_principal(contextp->context, princ);
krb5_storage_free(sp);
sp = krb5_storage_emem();
krb5_store_int32(sp, ret);

View File

@@ -145,6 +145,61 @@ edit_attributes (const char *prompt, krb5_flags *attr, int *mask, int bit)
return 0;
}
/*
* try to parse the string `resp' into policy in `attr', also
* setting the `bit' in `mask' if attributes are given and valid.
*/
#define VALID_POLICY_NAME_CHARS \
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
int
parse_policy (const char *resp, char **policy, int *mask, int bit)
{
if (strspn(resp, VALID_POLICY_NAME_CHARS) == strlen(resp) &&
*resp != '\0') {
*policy = strdup(resp);
if (*policy == NULL) {
fprintf (stderr, "Out of memory");
return -1;
}
if (mask)
*mask |= bit;
return 0;
} else if(*resp == '?') {
print_flags_table (kdb_attrs, stderr);
} else {
fprintf (stderr, "Unable to parse \"%s\"\n", resp);
}
return -1;
}
/*
* allow the user to edit the attributes in `attr', prompting with `prompt'
*/
int
edit_policy (const char *prompt, char **policy, int *mask, int bit)
{
char buf[1024], resp[1024];
if (mask && (*mask & bit))
return 0;
buf[0] = '\0';
strlcpy(buf, "default", sizeof (buf));
for (;;) {
if(get_response("Policy", buf, resp, sizeof(resp)) != 0)
return 1;
if (resp[0] == '\0')
break;
if (parse_policy (resp, policy, mask, bit) == 0)
break;
}
return 0;
}
/*
* time_t
* the special value 0 means ``never''
@@ -391,6 +446,14 @@ set_defaults(kadm5_principal_ent_t ent, int *mask,
&& (default_mask & KADM5_ATTRIBUTES)
&& !(*mask & KADM5_ATTRIBUTES))
ent->attributes = default_ent->attributes & ~KRB5_KDB_DISALLOW_ALL_TIX;
if (default_ent
&& (default_mask & KADM5_POLICY)
&& !(*mask & KADM5_POLICY)) {
ent->policy = strdup(default_ent->policy);
if (ent->policy == NULL)
abort();
}
}
int
@@ -420,6 +483,10 @@ edit_entry(kadm5_principal_ent_t ent, int *mask,
KADM5_ATTRIBUTES) != 0)
return 1;
if(edit_policy ("Policy", &ent->policy, mask,
KADM5_POLICY) != 0)
return 1;
return 0;
}
@@ -437,7 +504,8 @@ set_entry(krb5_context contextp,
const char *max_renewable_life,
const char *expiration,
const char *pw_expiration,
const char *attributes)
const char *attributes,
const char *policy)
{
if (max_ticket_life != NULL) {
if (parse_deltat (max_ticket_life, &ent->max_life,
@@ -475,6 +543,13 @@ set_entry(krb5_context contextp,
return 1;
}
}
if (policy != NULL) {
if (parse_policy (policy, &ent->policy,
mask, KADM5_POLICY)) {
krb5_warnx (contextp, "unable to parse `%s'", attributes);
return 1;
}
}
return 0;
}

View File

@@ -401,7 +401,7 @@ change (krb5_auth_context auth_context,
tmp = pwd_data->data;
tmp[pwd_data->length - 1] = '\0';
ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, tmp);
ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, 1, tmp);
krb5_free_data (context, pwd_data);
pwd_data = NULL;
if (ret) {

View File

@@ -29,11 +29,13 @@ gen_files_hdb = \
asn1_HDB_Ext_Lan_Manager_OWF.x \
asn1_HDB_Ext_Password.x \
asn1_HDB_Ext_Aliases.x \
asn1_HDB_Ext_KeySet.x \
asn1_HDB_extension.x \
asn1_HDB_extensions.x \
asn1_hdb_entry.x \
asn1_hdb_entry_alias.x \
asn1_hdb_keyset.x
asn1_hdb_keyset.x \
asn1_Keys.x
CLEANFILES = $(BUILT_SOURCES) $(gen_files_hdb) \
hdb_asn1{,-priv}.h* hdb_asn1_files hdb_asn1-template.c*
@@ -121,7 +123,7 @@ $(srcdir)/hdb-private.h:
$(gen_files_hdb) hdb_asn1.hx hdb_asn1-priv.hx: hdb_asn1_files
hdb_asn1_files: $(ASN1_COMPILE_DEP) $(srcdir)/hdb.asn1
$(ASN1_COMPILE) $(srcdir)/hdb.asn1 hdb_asn1
$(ASN1_COMPILE) --sequence=HDB-Ext-KeySet --sequence=Keys $(srcdir)/hdb.asn1 hdb_asn1
test_dbinfo_LIBS = libhdb.la

View File

@@ -105,7 +105,6 @@ _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
krb5_principal enterprise_principal = NULL;
krb5_data key, value;
krb5_error_code ret;
int code;
if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
if (principal->name.name_string.len != 1) {
@@ -125,43 +124,74 @@ _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
hdb_principal2key(context, principal, &key);
if (enterprise_principal)
krb5_free_principal(context, enterprise_principal);
code = db->hdb__get(context, db, key, &value);
ret = db->hdb__get(context, db, key, &value);
krb5_data_free(&key);
if(code)
return code;
code = hdb_value2entry(context, &value, &entry->entry);
if (code == ASN1_BAD_ID && (flags & HDB_F_CANON) == 0) {
if(ret)
return ret;
ret = hdb_value2entry(context, &value, &entry->entry);
if (ret == ASN1_BAD_ID && (flags & HDB_F_CANON) == 0) {
krb5_data_free(&value);
return HDB_ERR_NOENTRY;
} else if (code == ASN1_BAD_ID) {
} else if (ret == ASN1_BAD_ID) {
hdb_entry_alias alias;
code = hdb_value2entry_alias(context, &value, &alias);
if (code) {
ret = hdb_value2entry_alias(context, &value, &alias);
if (ret) {
krb5_data_free(&value);
return code;
return ret;
}
hdb_principal2key(context, alias.principal, &key);
krb5_data_free(&value);
free_hdb_entry_alias(&alias);
code = db->hdb__get(context, db, key, &value);
ret = db->hdb__get(context, db, key, &value);
krb5_data_free(&key);
if (code)
return code;
code = hdb_value2entry(context, &value, &entry->entry);
if (code) {
if (ret)
return ret;
ret = hdb_value2entry(context, &value, &entry->entry);
if (ret) {
krb5_data_free(&value);
return code;
return ret;
}
}
krb5_data_free(&value);
if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
code = hdb_unseal_keys (context, db, &entry->entry);
if (code)
if ((flags & HDB_F_DECRYPT) && (flags & HDB_F_ALL_KVNOS)) {
/* Decrypt the current keys */
ret = hdb_unseal_keys(context, db, &entry->entry);
if (ret) {
hdb_free_entry(context, entry);
return ret;
}
/* Decrypt the key history too */
ret = hdb_unseal_keys_kvno(context, db, 0, flags, &entry->entry);
if (ret) {
hdb_free_entry(context, entry);
return ret;
}
} else if ((flags & HDB_F_DECRYPT)) {
if ((flags & HDB_F_KVNO_SPECIFIED) == 0 || kvno == entry->entry.kvno) {
/* Decrypt the current keys */
ret = hdb_unseal_keys(context, db, &entry->entry);
if (ret) {
hdb_free_entry(context, entry);
return ret;
}
} else {
if ((flags & HDB_F_ALL_KVNOS))
kvno = 0;
/*
* Find and decrypt the keys from the history that we want,
* and swap them with the current keys
*/
ret = hdb_unseal_keys_kvno(context, db, kvno, flags, &entry->entry);
if (ret) {
hdb_free_entry(context, entry);
return ret;
}
}
}
return code;
return 0;
}
static krb5_error_code
@@ -284,6 +314,8 @@ _hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
krb5_data key, value;
int code;
if (entry->entry.flags.do_not_store)
return HDB_ERR_MISUSE;
/* check if new aliases already is used */
code = hdb_check_aliases(context, db, entry);
if (code)

View File

@@ -65,12 +65,24 @@ DB_lock(krb5_context context, HDB *db, int operation)
{
DB *d = (DB*)db->hdb_db;
int fd = (*d->fd)(d);
krb5_error_code ret;
if (db->lock_count > 0) {
db->lock_count++;
if (db->lock_type == HDB_WLOCK || db->lock_type == operation)
return 0;
}
if(fd < 0) {
krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
"Can't lock database: %s", db->hdb_name);
return HDB_ERR_CANT_LOCK_DB;
}
return hdb_lock(fd, operation);
ret = hdb_lock(fd, operation);
if (ret)
return ret;
db->lock_count++;
return 0;
}
static krb5_error_code
@@ -78,6 +90,14 @@ DB_unlock(krb5_context context, HDB *db)
{
DB *d = (DB*)db->hdb_db;
int fd = (*d->fd)(d);
if (db->lock_count > 1) {
db->lock_count--;
return 0;
}
heim_assert(db->lock_count == 1, "HDB lock/unlock sequence does not match");
db->lock_count--;
if(fd < 0) {
krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
"Can't unlock database: %s", db->hdb_name);

View File

@@ -75,9 +75,21 @@ DB_lock(krb5_context context, HDB *db, int operation)
{
DB *d = (DB*)db->hdb_db;
int fd;
krb5_error_code ret;
if (db->lock_count > 1) {
db->lock_count++;
if (db->lock_count == HDB_WLOCK || db->lock_count == operation)
return 0;
}
if ((*d->fd)(d, &fd))
return HDB_ERR_CANT_LOCK_DB;
return hdb_lock(fd, operation);
ret = hdb_lock(fd, operation);
if (ret)
return ret;
db->lock_count++;
return 0;
}
static krb5_error_code
@@ -85,6 +97,14 @@ DB_unlock(krb5_context context, HDB *db)
{
DB *d = (DB*)db->hdb_db;
int fd;
if (db->lock_count > 1) {
db->lock_count--;
return 0;
}
heim_assert(db->lock_count == 1, "HDB lock/unlock sequence does not match");
db->lock_count--;
if ((*d->fd)(d, &fd))
return HDB_ERR_CANT_LOCK_DB;
return hdb_unlock(fd);

View File

@@ -432,3 +432,67 @@ hdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a)
return 0;
}
unsigned int
hdb_entry_get_kvno_diff_clnt(const hdb_entry *entry)
{
const HDB_extension *ext;
ext = hdb_find_extension(entry,
choice_HDB_extension_data_hist_kvno_diff_clnt);
if (ext)
return ext->data.u.hist_kvno_diff_clnt;
return 1;
}
krb5_error_code
hdb_entry_set_kvno_diff_clnt(krb5_context context, hdb_entry *entry,
unsigned int diff)
{
HDB_extension ext;
if (diff > 16384)
return EINVAL;
ext.data.element = choice_HDB_extension_data_hist_kvno_diff_clnt;
ext.data.u.hist_kvno_diff_clnt = diff;
return hdb_replace_extension(context, entry, &ext);
}
krb5_error_code
hdb_entry_clear_kvno_diff_clnt(krb5_context context, hdb_entry *entry)
{
return hdb_clear_extension(context, entry,
choice_HDB_extension_data_hist_kvno_diff_clnt);
}
unsigned int
hdb_entry_get_kvno_diff_svc(const hdb_entry *entry)
{
const HDB_extension *ext;
ext = hdb_find_extension(entry,
choice_HDB_extension_data_hist_kvno_diff_svc);
if (ext)
return ext->data.u.hist_kvno_diff_svc;
return 1024; /* max_life effectively provides a better default */
}
krb5_error_code
hdb_entry_set_kvno_diff_svc(krb5_context context, hdb_entry *entry,
unsigned int diff)
{
HDB_extension ext;
if (diff > 16384)
return EINVAL;
ext.data.element = choice_HDB_extension_data_hist_kvno_diff_svc;
ext.data.u.hist_kvno_diff_svc = diff;
return hdb_replace_extension(context, entry, &ext);
}
krb5_error_code
hdb_entry_clear_kvno_diff_svc(krb5_context context, hdb_entry *entry)
{
return hdb_clear_extension(context, entry,
choice_HDB_extension_data_hist_kvno_diff_svc);
}

View File

@@ -196,12 +196,226 @@ fix_salt(krb5_context context, hdb_entry *ent, int key_num)
return 0;
}
/**
* This function outputs a pointer to a Key or array of @key_count Keys
* where the caller may place Keys.
*
* @param context Context
* @param entry HDB entry
* @param kvno kvno of the keys to be added
* @param is_hist Whether the keys will be historical keys or current keys
* @param key_count Size of array of keys to set. MUST be zero if !is_hist.
* @param out Pointer to Key * variable where to put the resulting Key *
*
* See three call sites below for more information.
*/
static krb5_error_code
get_entry_key_location(krb5_context context, hdb_entry *entry, krb5_kvno kvno,
krb5_boolean is_hist, size_t key_count, Key **out)
{
HDB_extension ext;
HDB_Ext_KeySet *hist_keys;
hdb_keyset *keyset = NULL;
size_t keyset_count = 0;
Key *k = NULL;
size_t i;
krb5_error_code ret;
*out = NULL;
if (!is_hist) {
Key *tmp;
/* Extend current keyset */
tmp = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1));
if (tmp == NULL) {
ret = ENOMEM;
goto out;
}
entry->keys.val = tmp;
/* k points to current Key */
k = &entry->keys.val[entry->keys.len];
memset(k, 0, sizeof(*k));
entry->keys.len += 1;
goto done;
}
/* Find a history keyset and extend it or extend the history keyset */
memset(&ext, 0, sizeof (ext));
ext.data.element = choice_HDB_extension_data_hist_keys;
hist_keys = &ext.data.u.hist_keys;
/* hdb_replace_extension() makes a copy of ext */
ret = hdb_replace_extension(context, entry, &ext);
if (ret)
return ret;
for (i = 0; i < hist_keys->len; i++) {
if (hist_keys->val[i].kvno == kvno) {
/* We're adding a key to an existing history keyset */
keyset = &hist_keys->val[i];
if ((keyset->keys.len % 8) == 0) {
Key *tmp;
/* We're adding the 9th, 17th, ... key to the set */
tmp = realloc(keyset->keys.val,
(keyset->keys.len + 8) * sizeof (*tmp));
if (tmp == NULL) {
ret = ENOMEM;
goto out;
}
}
break;
}
}
if (keyset == NULL) {
/* We're adding the first key of a new history keyset */
if (hist_keys->val == NULL) {
if (key_count == 0)
keyset_count = 8; /* There's not that many enctypes */
else
keyset_count = key_count;
hist_keys->val = calloc(keyset_count,
sizeof (*hist_keys->val));
if (hist_keys->val == NULL) {
ret = ENOMEM;
goto out;
}
keyset = &hist_keys->val[0];
} else if (hist_keys->len == keyset_count) {
hdb_keyset *tmp;
keyset_count *= 2;
tmp = realloc(hist_keys->val,
keyset_count * sizeof (*hist_keys->val));
if (tmp == NULL) {
ret = ENOMEM;
goto out;
}
hist_keys->val = tmp;
}
}
k = &keyset->keys.val[keyset->keys.len];
if (key_count != 0)
keyset->keys.len += key_count;
done:
memset(k, 0, sizeof (*k));
k->mkvno = malloc(sizeof(*k->mkvno));
if (k->mkvno == NULL) {
ret = ENOMEM;
goto out;
}
*k->mkvno = 1;
*out = k;
out:
if (ret && !is_hist)
entry->keys.len--;
if (is_hist)
free_HDB_extension(&ext);
return ret;
}
/**
* This function takes a key from a krb5_storage from an MIT KDB encoded
* entry and places it in the given Key object.
*
* @param context Context
* @param entry HDB entry
* @param sp krb5_storage with current offset set to the beginning of a
* key
* @param version See comments in caller body for the backstory on this
* @param k Key * to load the key into
*/
static krb5_error_code
mdb_keyvalue2key(krb5_context context, hdb_entry *entry, krb5_storage *sp, uint16_t version, Key *k)
{
size_t i;
uint16_t u16, type;
krb5_error_code ret;
for (i = 0; i < version; i++) {
CHECK(ret = krb5_ret_uint16(sp, &type));
CHECK(ret = krb5_ret_uint16(sp, &u16));
if (i == 0) {
/* This "version" means we have a key */
k->key.keytype = type;
if (u16 < 2) {
ret = EINVAL;
goto out;
}
/*
* MIT stores keys encrypted keys as {16-bit length
* of plaintext key, {encrypted key}}. The reason
* for this is that the Kerberos cryptosystem is not
* length-preserving. Heimdal's approach is to
* truncate the plaintext to the expected length of
* the key given its enctype, so we ignore this
* 16-bit length-of-plaintext-key field.
*/
krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */
k->key.keyvalue.length = u16 - 2; /* adjust cipher len */
k->key.keyvalue.data = malloc(k->key.keyvalue.length);
krb5_storage_read(sp, k->key.keyvalue.data,
k->key.keyvalue.length);
} else if (i == 1) {
/* This "version" means we have a salt */
k->salt = calloc(1, sizeof(*k->salt));
if (k->salt == NULL) {
ret = ENOMEM;
goto out;
}
k->salt->type = type;
if (u16 != 0) {
k->salt->salt.data = malloc(u16);
if (k->salt->salt.data == NULL) {
ret = ENOMEM;
goto out;
}
k->salt->salt.length = u16;
krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length);
}
fix_salt(context, entry, entry->keys.len - 1);
} else {
/*
* Whatever this "version" might be, we skip it
*
* XXX A krb5.conf parameter requesting that we log
* about strangeness like this, or return an error
* from here, might be nice.
*/
krb5_storage_seek(sp, u16, SEEK_CUR);
}
}
return 0;
out:
free_Key(k);
memset(k, 0, sizeof (*k));
return ret;
}
/**
* This function parses an MIT krb5 encoded KDB entry and fills in the
* given HDB entry with it.
*/
static krb5_error_code
mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry *entry)
{
krb5_error_code ret;
krb5_storage *sp;
Key *k;
krb5_kvno key_kvno;
uint32_t u32;
uint16_t u16, num_keys, num_tl;
size_t i, j;
@@ -328,125 +542,63 @@ mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry
for (i = 0; i < num_keys; i++) {
int keep = 0;
uint16_t version;
void *ptr;
CHECK(ret = krb5_ret_uint16(sp, &u16));
version = u16;
CHECK(ret = krb5_ret_uint16(sp, &u16));
key_kvno = u16;
/*
* First time through, and until we find one matching key,
* entry->kvno == 0.
*/
if ((entry->kvno < u16) && (kvno == 0 || kvno == u16)) {
keep = 1;
entry->kvno = u16;
if ((entry->kvno < key_kvno) && (kvno == 0 || kvno == key_kvno)) {
/*
* Found a higher kvno than earlier, so free the old highest
* kvno keys.
*
* XXX Of course, we actually want to extract the old kvnos
* as well, for some of the kadm5 APIs. We shouldn't free
* these keys, but keep them elsewhere.
* Found a higher kvno than earlier, we aren't looking for
* any particular kvno, so save the previously saved keys as
* historical keys.
*/
keep = 1;
/* Get an array of Keys to save the current keyset into */
ret = get_entry_key_location(context, entry, entry->kvno, TRUE,
entry->keys.len, &k);
for (j = 0; j < entry->keys.len; j++)
copy_Key(&entry->keys.val[j], &k[j]);
/* Change the entry's current kvno */
entry->kvno = key_kvno;
for (j = 0; j < entry->keys.len; j++)
free_Key(&entry->keys.val[j]);
free(entry->keys.val);
entry->keys.len = 0;
entry->keys.val = NULL;
} else if (entry->kvno == u16)
} else if (entry->kvno == key_kvno)
/* Accumulate keys */
keep = 1;
if (keep) {
Key *k;
ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1));
if (ptr == NULL) {
ret = ENOMEM;
ret = get_entry_key_location(context, entry, key_kvno,
FALSE, 0, &k);
if (ret)
goto out;
}
entry->keys.val = ptr;
/* k points to current Key */
k = &entry->keys.val[entry->keys.len];
memset(k, 0, sizeof(*k));
entry->keys.len += 1;
k->mkvno = malloc(sizeof(*k->mkvno));
if (k->mkvno == NULL) {
ret = ENOMEM;
ret = mdb_keyvalue2key(context, entry, sp, version, k);
if (ret)
goto out;
}
*k->mkvno = 1;
for (j = 0; j < version; j++) {
uint16_t type;
CHECK(ret = krb5_ret_uint16(sp, &type));
CHECK(ret = krb5_ret_uint16(sp, &u16));
if (j == 0) {
/* This "version" means we have a key */
k->key.keytype = type;
if (u16 < 2) {
ret = EINVAL;
goto out;
}
/*
* MIT stores keys encrypted keys as {16-bit length
* of plaintext key, {encrypted key}}. The reason
* for this is that the Kerberos cryptosystem is not
* length-preserving. Heimdal's approach is to
* truncate the plaintext to the expected length of
* the key given its enctype, so we ignore this
* 16-bit length-of-plaintext-key field.
*/
krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */
k->key.keyvalue.length = u16 - 2; /* adjust cipher len */
k->key.keyvalue.data = malloc(k->key.keyvalue.length);
krb5_storage_read(sp, k->key.keyvalue.data,
k->key.keyvalue.length);
} else if (j == 1) {
/* This "version" means we have a salt */
k->salt = calloc(1, sizeof(*k->salt));
if (k->salt == NULL) {
ret = ENOMEM;
goto out;
}
k->salt->type = type;
if (u16 != 0) {
k->salt->salt.data = malloc(u16);
if (k->salt->salt.data == NULL) {
ret = ENOMEM;
goto out;
}
k->salt->salt.length = u16;
krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length);
}
fix_salt(context, entry, entry->keys.len - 1);
} else {
/*
* Whatever this "version" might be, we skip it
*
* XXX A krb5.conf parameter requesting that we log
* about strangeness like this, or return an error
* from here, might be nice.
*/
krb5_storage_seek(sp, u16, SEEK_CUR);
}
}
} else {
/*
* XXX For now we skip older kvnos, but we should extract
* them...
* them... XXX Finish.
*/
for (j = 0; j < version; j++) {
/* enctype */
CHECK(ret = krb5_ret_uint16(sp, &u16));
/* encrypted key (or plaintext salt) */
CHECK(ret = krb5_ret_uint16(sp, &u16));
krb5_storage_seek(sp, u16, SEEK_CUR);
}
ret = get_entry_key_location(context, entry, key_kvno, TRUE, 0, &k);
if (ret)
goto out;
ret = mdb_keyvalue2key(context, entry, sp, version, k);
if (ret)
goto out;
}
}
@@ -496,12 +648,24 @@ mdb_lock(krb5_context context, HDB *db, int operation)
{
DB *d = (DB*)db->hdb_db;
int fd = (*d->fd)(d);
krb5_error_code ret;
if (db->lock_count > 1) {
db->lock_count++;
if (db->lock_type == HDB_WLOCK || db->lock_count == operation)
return 0;
}
if(fd < 0) {
krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
"Can't lock database: %s", db->hdb_name);
return HDB_ERR_CANT_LOCK_DB;
}
return hdb_lock(fd, operation);
ret = hdb_lock(fd, operation);
if (ret)
return ret;
db->lock_count++;
return 0;
}
static krb5_error_code
@@ -509,6 +673,14 @@ mdb_unlock(krb5_context context, HDB *db)
{
DB *d = (DB*)db->hdb_db;
int fd = (*d->fd)(d);
if (db->lock_count > 1) {
db->lock_count--;
return 0;
}
heim_assert(db->lock_count == 1, "HDB lock/unlock sequence does not match");
db->lock_count--;
if(fd < 0) {
krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
"Can't unlock database: %s", db->hdb_name);
@@ -581,19 +753,25 @@ static krb5_error_code
mdb_rename(krb5_context context, HDB *db, const char *new_name)
{
int ret;
char *old, *new;
char *old = NULL;
char *new = NULL;
asprintf(&old, "%s.db", db->hdb_name);
asprintf(&new, "%s.db", new_name);
if (asprintf(&old, "%s.db", db->hdb_name) < 0)
goto out;
if (asprintf(&new, "%s.db", new_name) < 0)
goto out;
ret = rename(old, new);
free(old);
free(new);
if(ret)
return errno;
goto out;
free(db->hdb_name);
db->hdb_name = strdup(new_name);
return 0;
errno = 0;
out:
free(old);
free(new);
return errno;
}
static krb5_error_code
@@ -732,8 +910,7 @@ mdb_open(krb5_context context, HDB *db, int flags, mode_t mode)
char *fn;
krb5_error_code ret;
asprintf(&fn, "%s.db", db->hdb_name);
if (fn == NULL) {
if (asprintf(&fn, "%s.db", db->hdb_name) < 0) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}

View File

@@ -46,8 +46,9 @@ HDBFlags ::= BIT STRING {
trusted-for-delegation(14), -- Trusted to print forwardabled tickets
allow-kerberos4(15), -- Allow Kerberos 4 requests
allow-digest(16), -- Allow digest requests
locked-out(17) -- Account is locked out,
locked-out(17), -- Account is locked out,
-- authentication will be denied
do-not-store(31) -- Not to be modified and stored in HDB
}
GENERATION ::= SEQUENCE {
@@ -87,6 +88,17 @@ HDB-Ext-Aliases ::= SEQUENCE {
aliases[1] SEQUENCE OF Principal -- all names, inc primary
}
Keys ::= SEQUENCE OF Key
hdb_keyset ::= SEQUENCE {
kvno[0] INTEGER (0..4294967295),
keys[1] Keys,
set-time[2] KerberosTime OPTIONAL, -- time this keyset was created/set
...
}
HDB-Ext-KeySet ::= SEQUENCE OF hdb_keyset
HDB-extension ::= SEQUENCE {
mandatory[0] BOOLEAN, -- kdc MUST understand this extension,
@@ -102,6 +114,10 @@ HDB-extension ::= SEQUENCE {
aliases[6] HDB-Ext-Aliases,
last-pw-change[7] KerberosTime,
pkinit-cert[8] HDB-Ext-PKINIT-cert,
hist-keys[9] HDB-Ext-KeySet,
hist-kvno-diff-clnt[10] INTEGER (0..4294967295),
hist-kvno-diff-svc[11] INTEGER (0..4294967295),
policy[12] UTF8String,
...
},
...
@@ -109,16 +125,11 @@ HDB-extension ::= SEQUENCE {
HDB-extensions ::= SEQUENCE OF HDB-extension
hdb_keyset ::= SEQUENCE {
kvno[1] INTEGER (0..4294967295),
keys[0] SEQUENCE OF Key
}
hdb_entry ::= SEQUENCE {
principal[0] Principal OPTIONAL, -- this is optional only
-- for compatibility with libkrb5
kvno[1] INTEGER (0..4294967295),
keys[2] SEQUENCE OF Key,
keys[2] Keys,
created-by[3] Event,
modified-by[4] Event OPTIONAL,
valid-start[5] KerberosTime OPTIONAL,

View File

@@ -168,13 +168,14 @@ hdb_unlock(int fd)
void
hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
{
Key *k;
size_t i;
if (ent->free_entry)
(*ent->free_entry)(context, ent);
for(i = 0; i < ent->entry.keys.len; ++i) {
Key *k = &ent->entry.keys.val[i];
for(i = 0; i < ent->entry.keys.len; i++) {
k = &ent->entry.keys.val[i];
memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
}

View File

@@ -57,6 +57,10 @@ enum hdb_lockop{ HDB_RLOCK, HDB_WLOCK };
#define HDB_F_CANON 32 /* want canonicalition */
#define HDB_F_ADMIN_DATA 64 /* want data that kdc don't use */
#define HDB_F_KVNO_SPECIFIED 128 /* we want a particular KVNO */
#define HDB_F_CURRENT_KVNO 256 /* we want the current KVNO */
#define HDB_F_LIVE_CLNT_KVNOS 512 /* we want all live keys for pre-auth */
#define HDB_F_LIVE_SVC_KVNOS 1024 /* we want all live keys for tix */
#define HDB_F_ALL_KVNOS 2048 /* we want all the keys, live or not */
/* hdb_capability_flags */
#define HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL 1
@@ -102,6 +106,8 @@ typedef struct HDB{
hdb_master_key hdb_master_key;
int hdb_openp;
int hdb_capability_flags;
int lock_count;
int lock_type;
/**
* Open (or create) the a Kerberos database.
*

View File

@@ -26,5 +26,6 @@ error_code NO_MKEY, "No correct master key"
error_code MANDATORY_OPTION, "Entry contains unknown mandatory extension"
error_code NO_WRITE_SUPPORT, "HDB backend doesn't contain write support"
error_code NOT_FOUND_HERE, "The secret for this entry is not replicated to this database"
error_code MISUSE, "Incorrect use of the API"
end

View File

@@ -36,6 +36,9 @@
#ifndef __HDB_LOCL_H__
#define __HDB_LOCL_H__
#include <assert.h>
#include <heimbase.h>
#include <config.h>
#include <stdio.h>

View File

@@ -39,9 +39,9 @@
*/
void
hdb_free_keys (krb5_context context, int len, Key *keys)
hdb_free_keys(krb5_context context, int len, Key *keys)
{
int i;
size_t i;
for (i = 0; i < len; i++) {
free(keys[i].mkvno);
@@ -196,6 +196,81 @@ parse_key_set(krb5_context context, const char *key,
return 0;
}
/**
* This function adds an HDB entry's current keyset to the entry's key
* history. The current keyset is left alone; the caller is responsible
* for freeing it.
*
* @param context Context
* @param entry HDB entry
*/
krb5_error_code
hdb_add_current_keys_to_history(krb5_context context, hdb_entry *entry)
{
krb5_error_code ret;
HDB_extension *ext;
HDB_Ext_KeySet *hist_keys;
hdb_keyset *tmp_keysets;
size_t i;
size_t replace = 0;
ext = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys);
if (ext != NULL) {
hist_keys = &ext->data.u.hist_keys;
tmp_keysets = realloc(hist_keys->val,
sizeof (*hist_keys->val) * (hist_keys->len + 1));
if (tmp_keysets == NULL)
return ENOMEM;
hist_keys->val = tmp_keysets;
memmove(&hist_keys->val[1], hist_keys->val,
sizeof (*hist_keys->val) * hist_keys->len++);
} else {
replace = 1;
ext = calloc(1, sizeof (*ext));
if (ext == NULL)
return ENOMEM;
ext->data.element = choice_HDB_extension_data_hist_keys;
hist_keys = &ext->data.u.hist_keys;
hist_keys->val = calloc(1, sizeof (*hist_keys->val));
if (hist_keys->val == NULL) {
free(hist_keys);
return ENOMEM;
}
hist_keys->len = 1;
}
hist_keys->val[0].keys.len = 0;
hist_keys->val[0].keys.val = calloc(entry->keys.len,
sizeof (*hist_keys->val[0].keys.val));
for (i = 0; i < entry->keys.len; i++, hist_keys->val[0].keys.len++) {
ret = copy_Key(&entry->keys.val[i], &hist_keys->val[0].keys.val[i]);
if (ret) {
free_HDB_extension(ext);
return ret;
}
}
hist_keys->val[0].kvno = entry->kvno;
hist_keys->val[0].set_time = malloc(sizeof (*hist_keys->val[0].set_time));
if (hist_keys->val[0].set_time == NULL) {
free_HDB_extension(ext);
return ENOMEM;
}
(void) hdb_entry_get_pw_change_time(entry, hist_keys->val[0].set_time);
if (replace) {
/* hdb_replace_extension() deep-copies ext; what a waste */
ret = hdb_replace_extension(context, entry, ext);
if (ret) {
free_HDB_extension(ext);
return ret;
}
free_HDB_extension(ext);
}
return 0;
}
static krb5_error_code
add_enctype_to_key_set(Key **key_set, size_t *nkeyset,
krb5_enctype enctype, krb5_salt *salt)
@@ -243,6 +318,50 @@ add_enctype_to_key_set(Key **key_set, size_t *nkeyset,
}
static
krb5_error_code
ks_tuple2str(krb5_context context, int n_ks_tuple,
krb5_key_salt_tuple *ks_tuple, char ***ks_tuple_strs)
{
size_t i;
char **ksnames;
char *ename, *sname;
krb5_error_code rc = KRB5_PROG_ETYPE_NOSUPP;
*ks_tuple_strs = NULL;
if (n_ks_tuple < 1)
return 0;
if ((ksnames = calloc(n_ks_tuple, sizeof (*ksnames))) == NULL)
return (errno);
for (i = 0; i < n_ks_tuple; i++) {
if (krb5_enctype_to_string(context, ks_tuple[i].ks_enctype, &ename))
goto out;
if (krb5_salttype_to_string(context, ks_tuple[i].ks_enctype,
ks_tuple[i].ks_salttype, &sname))
goto out;
if (asprintf(&ksnames[i], "%s:%s", ename, sname) == -1) {
rc = errno;
free(ename);
free(sname);
goto out;
}
free(ename);
free(sname);
}
*ks_tuple_strs = ksnames;
rc = 0;
out:
for (i = 0; i < n_ks_tuple; i++)
free(ksnames[i]);
free(ksnames);
return (rc);
}
/*
* Generate the `key_set' from the [kadmin]default_keys statement. If
* `no_salt' is set, salt is not important (and will not be set) since
@@ -251,12 +370,15 @@ add_enctype_to_key_set(Key **key_set, size_t *nkeyset,
krb5_error_code
hdb_generate_key_set(krb5_context context, krb5_principal principal,
int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
Key **ret_key_set, size_t *nkeyset, int no_salt)
{
char **ktypes, **kp;
char **ktypes = NULL;
char **kp;
krb5_error_code ret;
Key *k, *key_set;
size_t i, j;
char **ks_tuple_strs;
static const char *default_keytypes[] = {
"aes256-cts-hmac-sha1-96:pw-salt",
"des3-cbc-sha1:pw-salt",
@@ -264,16 +386,18 @@ hdb_generate_key_set(krb5_context context, krb5_principal principal,
NULL
};
ktypes = krb5_config_get_strings(context, NULL, "kadmin",
"default_keys", NULL);
if ((ret = ks_tuple2str(context, n_ks_tuple, ks_tuple, &ks_tuple_strs)))
return ret;
if (ks_tuple_strs == NULL)
ktypes = krb5_config_get_strings(context, NULL, "kadmin",
"default_keys", NULL);
if (ktypes == NULL)
ktypes = (char **)(intptr_t)default_keytypes;
*ret_key_set = key_set = NULL;
*nkeyset = 0;
ret = 0;
for(kp = ktypes; kp && *kp; kp++) {
const char *p;
krb5_salt salt;
@@ -366,7 +490,7 @@ hdb_generate_key_set_password(krb5_context context,
krb5_error_code ret;
size_t i;
ret = hdb_generate_key_set(context, principal,
ret = hdb_generate_key_set(context, principal, 0, NULL,
keys, num_keys, 0);
if (ret)
return ret;

View File

@@ -479,6 +479,131 @@ hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
}
krb5_error_code
hdb_unseal_keys_kvno(krb5_context context, HDB *db, krb5_kvno kvno,
unsigned flags, hdb_entry *ent)
{
krb5_error_code ret = HDB_ERR_NOENTRY;
HDB_extension *ext;
HDB_Ext_KeySet *hist_keys;
Key *tmp_val;
time_t tmp_set_time;
unsigned int tmp_len;
unsigned int kvno_diff = 0;
krb5_kvno tmp_kvno;
size_t i, k;
int exclude_dead = 0;
KerberosTime now = 0;
time_t *set_time;
if (kvno == 0)
ret = 0;
if ((flags & HDB_F_LIVE_CLNT_KVNOS) || (flags & HDB_F_LIVE_SVC_KVNOS)) {
exclude_dead = 1;
now = time(NULL);
if (HDB_F_LIVE_CLNT_KVNOS)
kvno_diff = hdb_entry_get_kvno_diff_clnt(ent);
else
kvno_diff = hdb_entry_get_kvno_diff_svc(ent);
}
ext = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
if (ext == NULL)
return ret;
/* For swapping; see below */
tmp_len = ent->keys.len;
tmp_val = ent->keys.val;
tmp_kvno = ent->kvno;
(void) hdb_entry_get_pw_change_time(ent, &tmp_set_time);
hist_keys = &ext->data.u.hist_keys;
for (i = 0; i < hist_keys->len; i++) {
if (kvno != 0 && hist_keys->val[i].kvno != kvno)
continue;
if (exclude_dead &&
((ent->max_life != NULL &&
hist_keys->val[i].set_time != NULL &&
(*hist_keys->val[i].set_time) < (now - (*ent->max_life))) ||
(hist_keys->val[i].kvno < kvno &&
(kvno - hist_keys->val[i].kvno) > kvno_diff)))
/*
* The KDC may want to to check for this keyset's set_time
* is within the TGS principal's max_life, say. But we stop
* here.
*/
continue;
/* Either the keys we want, or all the keys */
for (k = 0; k < hist_keys->val[i].keys.len; k++) {
ret = hdb_unseal_key_mkey(context,
&hist_keys->val[i].keys.val[k],
db->hdb_master_key);
/*
* If kvno == 0 we might not want to bail here! E.g., if we
* no longer have the right master key, so just ignore this.
*
* We could filter out keys that we can't decrypt here
* because of HDB_ERR_NO_MKEY. However, it seems safest to
* filter them out only where necessary, say, in kadm5.
*/
if (ret && kvno != 0)
return ret;
if (ret && ret != HDB_ERR_NO_MKEY)
return (ret);
}
if (kvno == 0)
continue;
/*
* What follows is a bit of a hack.
*
* This is the keyset we're being asked for, but it's not the
* current keyset. So we add the current keyset to the history,
* leave the one we were asked for in the history, and pretend
* the one we were asked for is also the current keyset.
*
* This is a bit of a defensive hack in case an entry fetched
* this way ever gets modified then stored: if the keyset is not
* changed we can detect this and put things back, else we won't
* drop any keysets from history by accident.
*
* Note too that we only ever get called with a non-zero kvno
* either in the KDC or in cases where we aren't changing the
* HDB entry anyways, which is why this is just a defensive
* hack. We also don't fetch specific kvnos in the dump case,
* so there's no danger that we'll dump this entry and load it
* again, repeatedly causing the history to grow boundelessly.
*/
set_time = malloc(sizeof (*set_time));
if (set_time == NULL)
return ENOMEM;
/* Swap key sets */
ent->kvno = hist_keys->val[i].kvno;
ent->keys.val = hist_keys->val[i].keys.val;
ent->keys.len = hist_keys->val[i].keys.len;
if (hist_keys->val[i].set_time != NULL)
/* Sloppy, but the callers we expect won't care */
(void) hdb_entry_set_pw_change_time(context, ent,
*hist_keys->val[i].set_time);
hist_keys->val[i].kvno = tmp_kvno;
hist_keys->val[i].keys.val = tmp_val;
hist_keys->val[i].keys.len = tmp_len;
if (hist_keys->val[i].set_time != NULL)
/* Sloppy, but the callers we expect won't care */
*hist_keys->val[i].set_time = tmp_set_time;
return 0;
}
return (ret);
}
krb5_error_code
hdb_unseal_key(krb5_context context, HDB *db, Key *k)
{
@@ -526,14 +651,31 @@ hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
krb5_error_code
hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
{
size_t i;
for(i = 0; i < ent->keys.len; i++){
krb5_error_code ret;
HDB_extension *ext;
HDB_Ext_KeySet *hist_keys;
size_t i, k;
krb5_error_code ret;
for(i = 0; i < ent->keys.len; i++){
ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey);
if (ret)
return ret;
}
ext = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
if (ext == NULL)
return 0;
hist_keys = &ext->data.u.hist_keys;
for (i = 0; i < hist_keys->len; i++) {
for (k = 0; k < hist_keys->val[i].keys.len; k++) {
ret = hdb_seal_key_mkey(context, &hist_keys->val[i].keys.val[k],
mkey);
if (ret)
return ret;
}
}
return 0;
}

View File

@@ -62,11 +62,12 @@ append_string(krb5_context context, krb5_storage *sp, const char *fmt, ...)
{
krb5_error_code ret;
char *s;
int rc;
va_list ap;
va_start(ap, fmt);
vasprintf(&s, fmt, ap);
rc = vasprintf(&s, fmt, ap);
va_end(ap);
if(s == NULL) {
if(rc < 0) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
@@ -234,7 +235,6 @@ entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent)
} else
append_string(context, sp, "-");
return 0;
}

View File

@@ -88,6 +88,10 @@ main(int argc, char **argv)
memset(&keyset, 0, sizeof(keyset));
keyset.kvno = kvno_integer;
keyset.set_time = malloc(sizeof (*keyset.set_time));
if (keyset.set_time == NULL)
errx(1, "couldn't allocate set_time field of keyset");
*keyset.set_time = time(NULL);
ret = hdb_generate_key_set_password(context, principal, password_str,
&keyset.keys.val, &len);

View File

@@ -4,6 +4,7 @@ HEIMDAL_HDB_1.0 {
global:
encode_hdb_keyset;
hdb_add_master_key;
hdb_add_current_keys_to_history;
hdb_check_db_format;
hdb_clear_extension;
hdb_clear_master_key;
@@ -74,33 +75,42 @@ HEIMDAL_HDB_1.0 {
hdb_kt_ops;
# some random bits needed for libkadm
HDBFlags2int;
add_HDB_Ext_KeySet;
add_Keys;
asn1_HDBFlags_units;
copy_Event;
copy_HDB_extensions;
copy_Key;
copy_Keys;
copy_Salt;
decode_HDB_Ext_Aliases;
decode_HDB_Ext_PKINIT_acl;
decode_HDB_extension;
decode_HDB_Ext_PKINIT_acl;
decode_Key;
decode_Keys;
encode_HDB_Ext_Aliases;
encode_HDB_Ext_PKINIT_acl;
encode_HDB_extension;
encode_HDB_Ext_PKINIT_acl;
encode_Key;
encode_Keys;
free_Event;
free_hdb_entry;
free_HDB_Ext_Aliases;
free_HDB_Ext_PKINIT_acl;
free_HDB_extension;
free_HDB_extensions;
free_HDB_Ext_PKINIT_acl;
free_hdb_keyset;
free_Key;
free_Keys;
free_Salt;
free_hdb_entry;
HDBFlags2int;
int2HDBFlags;
length_HDB_Ext_Aliases;
length_HDB_Ext_PKINIT_acl;
length_HDB_extension;
length_HDB_Ext_PKINIT_acl;
length_Key;
length_Keys;
remove_Keys;
local:
*;

View File

@@ -44,6 +44,7 @@ static struct units acl_units[] = {
{ "modify", KADM5_PRIV_MODIFY },
{ "add", KADM5_PRIV_ADD },
{ "get", KADM5_PRIV_GET },
{ "get-keys", KADM5_PRIV_GET_KEYS },
{ NULL, 0 }
};
@@ -177,6 +178,8 @@ check_flags (unsigned op,
if(res & KADM5_PRIV_GET)
return KADM5_AUTH_GET;
if(res & KADM5_PRIV_GET_KEYS)
return KADM5_AUTH_GET_KEYS;
if(res & KADM5_PRIV_ADD)
return KADM5_AUTH_ADD;
if(res & KADM5_PRIV_MODIFY)

View File

@@ -508,6 +508,7 @@ ad_get_cred(kadm5_ad_context *context, const char *password)
static kadm5_ret_t
kadm5_ad_chpass_principal(void *server_handle,
krb5_principal principal,
int keepold,
const char *password)
{
kadm5_ad_context *context = server_handle;
@@ -515,6 +516,9 @@ kadm5_ad_chpass_principal(void *server_handle,
int result_code;
kadm5_ret_t ret;
if (keepold)
return KADM5_KEEPOLD_NOSUPP;
ret = ad_get_cred(context, NULL);
if (ret)
return ret;
@@ -1224,14 +1228,21 @@ kadm5_ad_modify_principal(void *server_handle,
#endif
}
/*ARGSUSED*/
static kadm5_ret_t
kadm5_ad_randkey_principal(void *server_handle,
krb5_principal principal,
krb5_boolean keepold,
int n_ks_tuple,
krb5_key_salt_tuple *ks_tuple,
krb5_keyblock **keys,
int *n_keys)
{
kadm5_ad_context *context = server_handle;
if (keepold)
return KADM5_KEEPOLD_NOSUPP;
/*
* random key
*/
@@ -1321,6 +1332,7 @@ kadm5_ad_rename_principal(void *server_handle,
static kadm5_ret_t
kadm5_ad_chpass_principal_with_key(void *server_handle,
krb5_principal princ,
int keepold,
int n_key_data,
krb5_key_data *key_data)
{
@@ -1329,6 +1341,18 @@ kadm5_ad_chpass_principal_with_key(void *server_handle,
return KADM5_RPC_ERROR;
}
static kadm5_ret_t
kadm5_ad_lock(void *server_handle)
{
return ENOTSUP;
}
static kadm5_ret_t
kadm5_ad_unlock(void *server_handle)
{
return ENOTSUP;
}
static void
set_funcs(kadm5_ad_context *c)
{
@@ -1345,6 +1369,8 @@ set_funcs(kadm5_ad_context *c)
SET(c, modify_principal);
SET(c, randkey_principal);
SET(c, rename_principal);
SET(c, lock);
SET(c, unlock);
}
kadm5_ret_t

View File

@@ -112,6 +112,11 @@ typedef struct {
void* key_data_contents[2];/* Array of pointers */
} krb5_key_data;
typedef struct _krb5_keysalt {
int16_t type;
krb5_data data; /* Length, data */
} krb5_keysalt;
typedef struct _krb5_tl_data {
struct _krb5_tl_data* tl_data_next;
int16_t tl_data_type;
@@ -129,6 +134,8 @@ typedef struct _krb5_tl_data {
#define KRB5_TL_EXTENSION 0x0008
#define KRB5_TL_PKINIT_ACL 0x0009
#define KRB5_TL_ALIASES 0x000a
#define KRB5_TL_HIST_KVNO_DIFF_CLNT 0x000b
#define KRB5_TL_HIST_KVNO_DIFF_SVC 0x000c
typedef struct _kadm5_principal_ent_t {
krb5_principal principal;
@@ -193,12 +200,9 @@ typedef struct _kadm5_policy_ent_t {
#define KADM5_PRIV_DELETE (1 << 3)
#define KADM5_PRIV_LIST (1 << 4)
#define KADM5_PRIV_CPW (1 << 5)
#define KADM5_PRIV_GET_KEYS (1 << 6)
#define KADM5_PRIV_ALL (KADM5_PRIV_GET | KADM5_PRIV_ADD | KADM5_PRIV_MODIFY | KADM5_PRIV_DELETE | KADM5_PRIV_LIST | KADM5_PRIV_CPW)
typedef struct {
int XXX;
}krb5_key_salt_tuple;
typedef struct _kadm5_config_params {
uint32_t mask;
@@ -221,38 +225,4 @@ typedef krb5_error_code kadm5_ret_t;
#include "kadm5-protos.h"
#if 0
/* unimplemented functions */
kadm5_ret_t
kadm5_decrypt_key(void *server_handle,
kadm5_principal_ent_t entry, int32_t
ktype, int32_t stype, int32_t
kvno, krb5_keyblock *keyblock,
krb5_keysalt *keysalt, int *kvnop);
kadm5_ret_t
kadm5_create_policy(void *server_handle,
kadm5_policy_ent_t policy, uint32_t mask);
kadm5_ret_t
kadm5_delete_policy(void *server_handle, char *policy);
kadm5_ret_t
kadm5_modify_policy(void *server_handle,
kadm5_policy_ent_t policy,
uint32_t mask);
kadm5_ret_t
kadm5_get_policy(void *server_handle, char *policy, kadm5_policy_ent_t ent);
kadm5_ret_t
kadm5_get_policies(void *server_handle, char *exp,
char ***pols, int *count);
void
kadm5_free_policy_ent(kadm5_policy_ent_t policy);
#endif
#endif /* __KADM5_ADMIN_H__ */

View File

@@ -38,6 +38,7 @@ RCSID("$Id$");
kadm5_ret_t
kadm5_c_chpass_principal(void *server_handle,
krb5_principal princ,
int keepold,
const char *password)
{
kadm5_client_context *context = server_handle;
@@ -59,6 +60,7 @@ kadm5_c_chpass_principal(void *server_handle,
krb5_store_int32(sp, kadm_chpass);
krb5_store_principal(sp, princ);
krb5_store_string(sp, password);
krb5_store_int32(sp, keepold); /* extension */
ret = _kadm5_client_send(context, sp);
krb5_storage_free(sp);
if (ret)
@@ -82,6 +84,7 @@ kadm5_c_chpass_principal(void *server_handle,
kadm5_ret_t
kadm5_c_chpass_principal_with_key(void *server_handle,
krb5_principal princ,
int keepold,
int n_key_data,
krb5_key_data *key_data)
{
@@ -107,6 +110,7 @@ kadm5_c_chpass_principal_with_key(void *server_handle,
krb5_store_int32(sp, n_key_data);
for (i = 0; i < n_key_data; ++i)
kadm5_store_key_data (sp, &key_data[i]);
krb5_store_int32(sp, keepold); /* extension */
ret = _kadm5_client_send(context, sp);
krb5_storage_free(sp);
if (ret)

View File

@@ -38,6 +38,7 @@ RCSID("$Id$");
static kadm5_ret_t
change(void *server_handle,
krb5_principal princ,
int keepold,
const char *password,
int cond)
{
@@ -49,15 +50,27 @@ change(void *server_handle,
int existsp = 0;
memset(&ent, 0, sizeof(ent));
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
return ret;
if (!context->keep_open) {
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
return ret;
}
ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
if(ret)
goto out;
if (keepold || cond) {
/*
* We save these for now so we can handle password history checking;
* we handle keepold further below.
*/
ret = hdb_add_current_keys_to_history(context->context, &ent.entry);
if (ret)
goto out;
}
if (context->db->hdb_capability_flags & HDB_CAP_F_HANDLE_PASSWORDS) {
ret = context->db->hdb_password(context->context, context->db,
&ent, password, cond);
@@ -73,15 +86,20 @@ change(void *server_handle,
ret = _kadm5_set_keys(context, &ent.entry, password);
if(ret) {
_kadm5_free_keys (context->context, num_keys, keys);
_kadm5_free_keys(context->context, num_keys, keys);
goto out2;
}
_kadm5_free_keys(context->context, num_keys, keys);
if (cond)
existsp = _kadm5_exists_keys (ent.entry.keys.val,
ent.entry.keys.len,
keys, num_keys);
_kadm5_free_keys (context->context, num_keys, keys);
if (cond) {
HDB_extension *ext;
ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_hist_keys);
if (ext != NULL)
existsp = _kadm5_exists_keys_hist(ent.entry.keys.val,
ent.entry.keys.len,
&ext->data.u.hist_keys);
}
if (existsp) {
ret = KADM5_PASS_REUSE;
@@ -89,12 +107,23 @@ change(void *server_handle,
"Password reuse forbidden");
goto out2;
}
}
ent.entry.kvno++;
if (keepold) {
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
if (ret)
goto out2;
} else {
HDB_extension ext;
ext.data.element = choice_HDB_extension_data_hist_keys;
ext.data.u.hist_keys.len = 0;
ext.data.u.hist_keys.val = NULL;
ret = hdb_replace_extension(context->context, &ent.entry, &ext);
if (ret)
goto out2;
}
ent.entry.kvno++;
ret = _kadm5_set_modifier(context, &ent.entry);
if(ret)
@@ -118,7 +147,8 @@ change(void *server_handle,
out2:
hdb_free_entry(context->context, &ent);
out:
context->db->hdb_close(context->context, context->db);
if (!context->keep_open)
context->db->hdb_close(context->context, context->db);
return _kadm5_error_code(ret);
}
@@ -131,9 +161,10 @@ out:
kadm5_ret_t
kadm5_s_chpass_principal_cond(void *server_handle,
krb5_principal princ,
int keepold,
const char *password)
{
return change (server_handle, princ, password, 1);
return change (server_handle, princ, keepold, password, 1);
}
/*
@@ -143,9 +174,10 @@ kadm5_s_chpass_principal_cond(void *server_handle,
kadm5_ret_t
kadm5_s_chpass_principal(void *server_handle,
krb5_principal princ,
int keepold,
const char *password)
{
return change (server_handle, princ, password, 0);
return change (server_handle, princ, keepold, password, 0);
}
/*
@@ -155,6 +187,7 @@ kadm5_s_chpass_principal(void *server_handle,
kadm5_ret_t
kadm5_s_chpass_principal_with_key(void *server_handle,
krb5_principal princ,
int keepold,
int n_key_data,
krb5_key_data *key_data)
{
@@ -163,13 +196,20 @@ kadm5_s_chpass_principal_with_key(void *server_handle,
kadm5_ret_t ret;
memset(&ent, 0, sizeof(ent));
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
return ret;
if (!context->keep_open) {
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
return ret;
}
ret = context->db->hdb_fetch_kvno(context->context, context->db, princ, 0,
HDB_F_GET_ANY|HDB_F_ADMIN_DATA, &ent);
if(ret == HDB_ERR_NOENTRY)
goto out;
if (keepold) {
ret = hdb_add_current_keys_to_history(context->context, &ent.entry);
if (ret)
goto out2;
}
ret = _kadm5_set_keys2(context, &ent.entry, n_key_data, key_data);
if(ret)
goto out2;
@@ -181,9 +221,19 @@ kadm5_s_chpass_principal_with_key(void *server_handle,
if (ret)
goto out2;
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
if (ret)
goto out2;
if (keepold) {
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
if (ret)
goto out2;
} else {
HDB_extension ext;
ext.data.element = choice_HDB_extension_data_hist_keys;
ext.data.u.hist_keys.len = 0;
ext.data.u.hist_keys.val = NULL;
hdb_replace_extension(context->context, &ent.entry, &ext);
}
ret = context->db->hdb_store(context->context, context->db,
HDB_F_REPLACE, &ent);
@@ -199,6 +249,7 @@ kadm5_s_chpass_principal_with_key(void *server_handle,
out2:
hdb_free_entry(context->context, &ent);
out:
context->db->hdb_close(context->context, context->db);
if (!context->keep_open)
context->db->hdb_close(context->context, context->db);
return _kadm5_error_code(ret);
}

View File

@@ -42,7 +42,25 @@ kadm5_chpass_principal(void *server_handle,
krb5_principal princ,
const char *password)
{
return __CALL(chpass_principal, (server_handle, princ, password));
return __CALL(chpass_principal, (server_handle, princ, 0, password));
}
kadm5_ret_t
kadm5_chpass_principal_3(void *server_handle,
krb5_principal princ,
krb5_boolean keepold,
int n_ks_tuple,
krb5_key_salt_tuple *ks_tuple,
const char *password)
{
/*
* We should get around to implementing this... This can be useful
* for, e.g., x-realm principals. For now we need the _3() to get
* certain applications written to the kadm5 API to build and run.
*/
if (n_ks_tuple > 0)
return KADM5_KS_TUPLE_NOSUPP;
return __CALL(chpass_principal, (server_handle, princ, keepold, password));
}
kadm5_ret_t
@@ -52,7 +70,18 @@ kadm5_chpass_principal_with_key(void *server_handle,
krb5_key_data *key_data)
{
return __CALL(chpass_principal_with_key,
(server_handle, princ, n_key_data, key_data));
(server_handle, princ, 0, n_key_data, key_data));
}
kadm5_ret_t
kadm5_chpass_principal_with_key_3(void *server_handle,
krb5_principal princ,
int keepold,
int n_key_data,
krb5_key_data *key_data)
{
return __CALL(chpass_principal_with_key,
(server_handle, princ, keepold, n_key_data, key_data));
}
kadm5_ret_t
@@ -92,6 +121,49 @@ kadm5_get_principal(void *server_handle,
return __CALL(get_principal, (server_handle, princ, out, mask));
}
/**
* Extract decrypted keys from kadm5_principal_ent_t object. Mostly a
* no-op for Heimdal because we fetch the entry with decrypted keys.
* Sadly this is not fully a no-op, as we have to allocate a copy.
*
* @server_handle is the kadm5 handle
* @entry is the HDB entry for the principal in question
* @ktype is the enctype to get a key for, or -1 to get the first one
* @stype is the salttype to get a key for, or -1 to get the first match
* @kvno is the kvno to search for, or -1 to get the first match (highest kvno)
* @keyblock is where the key will be placed
* @keysalt, if not NULL, is where the salt will be placed
* @kvnop, if not NULL, is where the selected kvno will be placed
*/
kadm5_ret_t
kadm5_decrypt_key(void *server_handle,
kadm5_principal_ent_t entry,
int32_t ktype, int32_t stype,
int32_t kvno, krb5_keyblock *keyblock,
krb5_keysalt *keysalt, int *kvnop)
{
size_t i;
if (kvno < 1 || stype != -1)
return KADM5_DECRYPT_USAGE_NOSUPP;
for (i = 0; i < entry->n_key_data; i++) {
if (ktype != entry->key_data[i].key_data_kvno)
continue;
keyblock->keytype = ktype;
keyblock->keyvalue.length = entry->key_data[i].key_data_length[0];
keyblock->keyvalue.data = malloc(keyblock->keyvalue.length);
if (keyblock->keyvalue.data == NULL)
return ENOMEM;
memcpy(keyblock->keyvalue.data,
entry->key_data[i].key_data_contents[0],
keyblock->keyvalue.length);
}
return 0;
}
kadm5_ret_t
kadm5_modify_principal(void *server_handle,
kadm5_principal_ent_t princ,
@@ -106,7 +178,21 @@ kadm5_randkey_principal(void *server_handle,
krb5_keyblock **new_keys,
int *n_keys)
{
return __CALL(randkey_principal, (server_handle, princ, new_keys, n_keys));
return __CALL(randkey_principal, (server_handle, princ, FALSE, 0, NULL,
new_keys, n_keys));
}
kadm5_ret_t
kadm5_randkey_principal_3(void *server_handle,
krb5_principal princ,
krb5_boolean keepold,
int n_ks_tuple,
krb5_key_salt_tuple *ks_tuple,
krb5_keyblock **new_keys,
int *n_keys)
{
return __CALL(randkey_principal, (server_handle, princ, keepold,
n_ks_tuple, ks_tuple, new_keys, n_keys));
}
kadm5_ret_t
@@ -132,3 +218,188 @@ kadm5_get_privs(void *server_handle,
{
return __CALL(get_privs, (server_handle, privs));
}
/**
* This function is allows the caller to set new keys for a principal.
* This is a trivial wrapper around kadm5_setkey_principal_3().
*/
kadm5_ret_t
kadm5_setkey_principal(void *server_handle,
krb5_principal princ,
krb5_keyblock *new_keys,
int n_keys)
{
return kadm5_setkey_principal_3(server_handle, princ, 0, 0, NULL,
new_keys, n_keys);
}
/**
* This function is allows the caller to set new keys for a principal.
* This is a simple wrapper around kadm5_get_principal() and
* kadm5_modify_principal().
*/
kadm5_ret_t
kadm5_setkey_principal_3(void *server_handle,
krb5_principal princ,
krb5_boolean keepold,
int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
krb5_keyblock *keyblocks,
int n_keys)
{
kadm5_principal_ent_rec princ_ent;
kadm5_ret_t ret;
krb5_key_data *new_key_data = NULL;
size_t i;
if (n_keys < 1)
return EINVAL;
if (n_ks_tuple > 0 && n_ks_tuple != n_keys)
return KADM5_SETKEY3_ETYPE_MISMATCH;
ret = kadm5_get_principal(server_handle, princ, &princ_ent,
KADM5_KVNO | KADM5_PRINCIPAL | KADM5_KEY_DATA);
if (ret)
return ret;
if (keepold) {
new_key_data = malloc((n_keys + princ_ent.n_key_data) * sizeof(*new_key_data));
if (new_key_data == NULL) {
ret = ENOMEM;
goto out;
}
memcpy(&new_key_data[n_keys], &princ_ent.key_data[0],
princ_ent.n_key_data * sizeof (princ_ent.key_data[0]));
} else {
new_key_data = malloc(n_keys * sizeof(*new_key_data));
if (new_key_data == NULL) {
ret = ENOMEM;
goto out;
}
}
princ_ent.kvno++;
for (i = 0; i < n_keys; i++) {
new_key_data[i].key_data_ver = 2;
/* Key */
new_key_data[i].key_data_kvno = princ_ent.kvno;
new_key_data[i].key_data_type[0] = keyblocks[i].keytype;
new_key_data[i].key_data_length[0] = keyblocks[i].keyvalue.length;
new_key_data[i].key_data_contents[0] =
malloc(keyblocks[i].keyvalue.length);
if (new_key_data[i].key_data_contents[0] == NULL) {
ret = ENOMEM;
goto out;
}
memcpy(new_key_data[i].key_data_contents[0],
keyblocks[i].keyvalue.data,
keyblocks[i].keyvalue.length);
/*
* Salt (but there's no salt, just salttype, which is kinda
* silly -- what's the point of setkey_3() then, besides
* keepold?!)
*/
new_key_data[i].key_data_type[1] = 0;
if (n_ks_tuple > 0) {
if (ks_tuple[i].ks_enctype != keyblocks[i].keytype)
return KADM5_SETKEY3_ETYPE_MISMATCH;
new_key_data[i].key_data_type[1] = ks_tuple[i].ks_salttype;
}
new_key_data[i].key_data_length[1] = 0;
new_key_data[i].key_data_contents[1] = NULL;
}
/* Free old keys */
if (!keepold) {
for (i = 0; i < princ_ent.n_key_data; i++) {
free(princ_ent.key_data[i].key_data_contents[0]);
free(princ_ent.key_data[i].key_data_contents[1]);
}
}
free(princ_ent.key_data);
princ_ent.key_data = new_key_data;
princ_ent.n_key_data = n_keys + (keepold ? princ_ent.n_key_data : 0);
new_key_data = NULL;
/* Modify the principal */
ret = kadm5_modify_principal(server_handle, &princ_ent, KADM5_KVNO | KADM5_KEY_DATA);
out:
if (new_key_data != NULL) {
for (i = 0; i < n_keys; i++) {
free(new_key_data[i].key_data_contents[0]);
free(new_key_data[i].key_data_contents[1]);
}
free(new_key_data);
}
kadm5_free_principal_ent(server_handle, &princ_ent);
return ret;
}
kadm5_ret_t
kadm5_lock(void *server_handle)
{
return __CALL(lock, (server_handle));
}
kadm5_ret_t
kadm5_unlock(void *server_handle)
{
return __CALL(unlock, (server_handle));
}
kadm5_ret_t
kadm5_create_policy(void *server_handle,
kadm5_policy_ent_t policy, long mask)
{
return KADM5_POLICY_OP_NOSUPP;
}
kadm5_ret_t
kadm5_delete_policy(void *server_handle, char *name)
{
return KADM5_POLICY_OP_NOSUPP;
}
kadm5_ret_t
kadm5_modify_policy(void *server_handle, kadm5_policy_ent_t policy,
uint32_t mask)
{
return KADM5_POLICY_OP_NOSUPP;
}
kadm5_ret_t
kadm5_get_policy(void *server_handle, char *policy, kadm5_policy_ent_t ent)
{
memset(ent, 0, sizeof (*ent));
return KADM5_POLICY_OP_NOSUPP;
}
kadm5_ret_t
kadm5_get_policies(void *server_handle, char *exp, char ***pols, int *count)
{
*count = 0;
*pols = NULL;
return KADM5_POLICY_OP_NOSUPP;
}
kadm5_ret_t
kadm5_free_policy_ent(kadm5_policy_ent_t ent)
{
if (ent->policy)
free(ent->policy);
/*
* Not clear if we should free ent or not. It might be an automatic
* struct, so we don't free it for now, just in case.
*/
return 0;
}

View File

@@ -35,6 +35,53 @@
RCSID("$Id$");
static kadm5_ret_t
kadm5_s_lock(void *server_handle)
{
kadm5_server_context *context = server_handle;
kadm5_ret_t ret;
if (context->keep_open) {
/*
* We open/close around every operation, but we retain the DB
* open if the DB was locked with a prior call to kadm5_lock(),
* so if it's open here that must be because the DB is locked.
*/
heim_assert(context->db->lock_count > 0,
"Internal error in tracking HDB locks");
return KADM5_ALREADY_LOCKED;
}
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if (ret)
return ret;
ret = context->db->hdb_lock(context->context, context->db, HDB_WLOCK);
if (ret)
return ret;
context->keep_open = 1;
return 0;
}
static kadm5_ret_t
kadm5_s_unlock(void *server_handle)
{
kadm5_server_context *context = server_handle;
kadm5_ret_t ret;
if (!context->keep_open)
return KADM5_NOT_LOCKED;
(void) context->db->hdb_close(context->context, context->db);
context->keep_open = 0;
ret = context->db->hdb_unlock(context->context, context->db);
if (ret)
return ret;
return 0;
}
static void
set_funcs(kadm5_server_context *c)
{
@@ -51,6 +98,8 @@ set_funcs(kadm5_server_context *c)
SET(c, modify_principal);
SET(c, randkey_principal);
SET(c, rename_principal);
SET(c, lock);
SET(c, unlock);
}
#ifndef NO_UNIX_SOCKETS

View File

@@ -65,6 +65,7 @@ create_principal(kadm5_server_context *context,
kadm5_principal_ent_rec defrec, *defent;
uint32_t def_mask;
memset(ent, 0, sizeof(*ent));
if((mask & required_mask) != required_mask)
return KADM5_BAD_MASK;
if((mask & forbidden_mask))
@@ -72,7 +73,6 @@ create_principal(kadm5_server_context *context,
if((mask & KADM5_POLICY) && strcmp(princ->policy, "default"))
/* XXX no real policies for now */
return KADM5_UNK_POLICY;
memset(ent, 0, sizeof(*ent));
ret = krb5_copy_principal(context->context, princ->principal,
&ent->entry.principal);
if(ret)
@@ -111,6 +111,12 @@ kadm5_s_create_principal_with_key(void *server_handle,
hdb_entry_ex ent;
kadm5_server_context *context = server_handle;
if ((mask & KADM5_KVNO) == 0) {
/* create_principal() through _kadm5_setup_entry(), will need this */
princ->kvno = 1;
mask |= KADM5_KVNO;
}
ret = create_principal(context, princ, mask, &ent,
KADM5_PRINCIPAL | KADM5_KEY_DATA,
KADM5_LAST_PWD_CHANGE | KADM5_MOD_TIME
@@ -121,18 +127,18 @@ kadm5_s_create_principal_with_key(void *server_handle,
if(ret)
goto out;
if ((mask & KADM5_KVNO) == 0)
ent.entry.kvno = 1;
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
if (ret)
goto out;
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
goto out;
if (!context->keep_open) {
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
goto out;
}
ret = context->db->hdb_store(context->context, context->db, 0, &ent);
context->db->hdb_close(context->context, context->db);
if (!context->keep_open)
context->db->hdb_close(context->context, context->db);
if (ret)
goto out;
kadm5_log_create (context, &ent.entry);
@@ -153,6 +159,12 @@ kadm5_s_create_principal(void *server_handle,
hdb_entry_ex ent;
kadm5_server_context *context = server_handle;
if ((mask & KADM5_KVNO) == 0) {
/* create_principal() through _kadm5_setup_entry(), will need this */
princ->kvno = 1;
mask |= KADM5_KVNO;
}
ret = create_principal(context, princ, mask, &ent,
KADM5_PRINCIPAL,
KADM5_LAST_PWD_CHANGE | KADM5_MOD_TIME
@@ -163,9 +175,6 @@ kadm5_s_create_principal(void *server_handle,
if(ret)
goto out;
if ((mask & KADM5_KVNO) == 0)
ent.entry.kvno = 1;
ent.entry.keys.len = 0;
ent.entry.keys.val = NULL;
@@ -177,11 +186,14 @@ kadm5_s_create_principal(void *server_handle,
if (ret)
goto out;
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
goto out;
if (!context->keep_open) {
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
goto out;
}
ret = context->db->hdb_store(context->context, context->db, 0, &ent);
context->db->hdb_close(context->context, context->db);
if (!context->keep_open)
context->db->hdb_close(context->context, context->db);
if (ret)
goto out;

View File

@@ -85,7 +85,8 @@ parse_file(krb5_context context, krb5_principal principal, int no_salt)
size_t nkeys;
Key *keys;
ret = hdb_generate_key_set(context, principal, &keys, &nkeys, no_salt);
ret = hdb_generate_key_set(context, principal, 0, NULL, &keys, &nkeys,
no_salt);
if (ret)
krb5_err(context, 1, ret, "hdb_generate_key_set");

View File

@@ -43,10 +43,12 @@ kadm5_s_delete_principal(void *server_handle, krb5_principal princ)
hdb_entry_ex ent;
memset(&ent, 0, sizeof(ent));
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret) {
krb5_warn(context->context, ret, "opening database");
return ret;
if (!context->keep_open) {
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret) {
krb5_warn(context->context, ret, "opening database");
return ret;
}
}
ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
@@ -70,6 +72,7 @@ kadm5_s_delete_principal(void *server_handle, krb5_principal princ)
out2:
hdb_free_entry(context->context, &ent);
out:
context->db->hdb_close(context->context, context->db);
if (!context->keep_open)
context->db->hdb_close(context->context, context->db);
return _kadm5_error_code(ret);
}

View File

@@ -178,8 +178,27 @@ _kadm5_setup_entry(kadm5_server_context *context,
}
}
if(mask & KADM5_KVNO
&& princ_mask & KADM5_KVNO)
ent->entry.kvno = princ->kvno;
&& princ_mask & KADM5_KVNO) {
/*
* For some reason kadmin's ank changes the kvno after calling
* randkey. Now that we have key history, what are we to do
* when we update kvno but not keys?!
*
* For now just clear the key history if the kvno changes.
* Eventually we may want to search the key history for matching
* keys and use those to replace the current key set (putting
* the old current keyset in the history keysets list?!).
*/
if (ent->entry.kvno != princ->kvno &&
(mask & princ_mask & KADM5_KEY_DATA)) {
hdb_clear_extension(context->context, &ent->entry,
choice_HDB_extension_data_hist_keys);
princ->kvno = ent->entry.kvno;
} else {
/* _kadm5_set_keys2() expects this to have been done here */
ent->entry.kvno = princ->kvno;
}
}
if(mask & KADM5_MAX_RLIFE) {
if(princ_mask & KADM5_MAX_RLIFE) {
if(princ->max_renewable_life)

View File

@@ -85,10 +85,13 @@ kadm5_s_get_principals(void *server_handle,
struct foreach_data d;
kadm5_server_context *context = server_handle;
kadm5_ret_t ret;
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret) {
krb5_warn(context->context, ret, "opening database");
return ret;
if (!context->keep_open) {
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret) {
krb5_warn(context->context, ret, "opening database");
return ret;
}
}
d.exp = expression;
{
@@ -100,7 +103,8 @@ kadm5_s_get_principals(void *server_handle,
d.princs = NULL;
d.count = 0;
ret = hdb_foreach(context->context, context->db, HDB_F_ADMIN_DATA, foreach, &d);
context->db->hdb_close(context->context, context->db);
if (!context->keep_open)
context->db->hdb_close(context->context, context->db);
if(ret == 0)
ret = add_princ(&d, NULL);
if(ret == 0){

View File

@@ -32,6 +32,7 @@
*/
#include "kadm5_locl.h"
#include <assert.h>
RCSID("$Id$");
@@ -64,6 +65,57 @@ add_tl_data(kadm5_principal_ent_t ent, int16_t type,
KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
_krb5_put_int(void *buffer, unsigned long value, size_t size); /* XXX */
static
krb5_error_code
copy_keyset_to_kadm5(kadm5_server_context *context, krb5_kvno kvno,
size_t n_keys, Key *keys, krb5_salt *salt,
kadm5_principal_ent_t out)
{
size_t i;
Key *key;
krb5_key_data *kd;
krb5_data *sp;
krb5_error_code ret = 0;
for (i = 0; i < n_keys; i++) {
key = &keys[i];
kd = &out->key_data[out->n_key_data];
kd->key_data_ver = 2;
kd->key_data_kvno = kvno;
kd->key_data_type[0] = key->key.keytype;
if(key->salt)
kd->key_data_type[1] = key->salt->type;
else
kd->key_data_type[1] = KRB5_PADATA_PW_SALT;
/* setup key */
kd->key_data_length[0] = key->key.keyvalue.length;
kd->key_data_contents[0] = malloc(kd->key_data_length[0]);
if(kd->key_data_contents[0] == NULL && kd->key_data_length[0] != 0){
ret = ENOMEM;
break;
}
memcpy(kd->key_data_contents[0], key->key.keyvalue.data,
kd->key_data_length[0]);
/* setup salt */
if(key->salt)
sp = &key->salt->salt;
else
sp = &salt->saltvalue;
kd->key_data_length[1] = sp->length;
kd->key_data_contents[1] = malloc(kd->key_data_length[1]);
if(kd->key_data_length[1] != 0
&& kd->key_data_contents[1] == NULL) {
memset(kd->key_data_contents[0], 0, kd->key_data_length[0]);
ret = ENOMEM;
break;
}
memcpy(kd->key_data_contents[1], sp->data, kd->key_data_length[1]);
out->n_key_data++;
}
return ret;
}
kadm5_ret_t
kadm5_s_get_principal(void *server_handle,
krb5_principal princ,
@@ -75,12 +127,17 @@ kadm5_s_get_principal(void *server_handle,
hdb_entry_ex ent;
memset(&ent, 0, sizeof(ent));
ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0);
if(ret)
return ret;
if (!context->keep_open) {
ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0);
if(ret)
return ret;
}
ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
context->db->hdb_close(context->context, context->db);
HDB_F_DECRYPT|HDB_F_ALL_KVNOS|
HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
if (!context->keep_open)
context->db->hdb_close(context->context, context->db);
if(ret)
return _kadm5_error_code(ret);
@@ -160,8 +217,21 @@ kadm5_s_get_principal(void *server_handle,
if(mask & KADM5_FAIL_AUTH_COUNT)
;
#endif
if(mask & KADM5_POLICY)
out->policy = NULL;
if(mask & KADM5_POLICY) {
HDB_extension *ext;
ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_policy);
if (ext == NULL) {
out->policy = strdup("default");
/* It's OK if we retun NULL instead of "default" */
} else {
out->policy = strdup(ext->data.u.policy);
if (out->policy == NULL) {
ret = ENOMEM;
goto out;
}
}
}
if(mask & KADM5_MAX_RLIFE) {
if(ent.entry.max_renew)
out->max_renewable_life = *ent.entry.max_renew;
@@ -170,52 +240,38 @@ kadm5_s_get_principal(void *server_handle,
}
if(mask & KADM5_KEY_DATA){
size_t i;
Key *key;
krb5_key_data *kd;
size_t n_keys = ent.entry.keys.len;
krb5_salt salt;
krb5_data *sp;
HDB_extension *ext;
HDB_Ext_KeySet *hist_keys = NULL;
ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_hist_keys);
if (ext != NULL)
hist_keys = &ext->data.u.hist_keys;
krb5_get_pw_salt(context->context, ent.entry.principal, &salt);
out->key_data = malloc(ent.entry.keys.len * sizeof(*out->key_data));
if (out->key_data == NULL && ent.entry.keys.len != 0) {
for (i = 0; hist_keys != NULL && i < hist_keys->len; i++)
n_keys += hist_keys->val[i].keys.len;
out->key_data = malloc(n_keys * sizeof(*out->key_data));
if (out->key_data == NULL && n_keys != 0) {
ret = ENOMEM;
goto out;
}
for(i = 0; i < ent.entry.keys.len; i++){
key = &ent.entry.keys.val[i];
kd = &out->key_data[i];
kd->key_data_ver = 2;
kd->key_data_kvno = ent.entry.kvno;
kd->key_data_type[0] = key->key.keytype;
if(key->salt)
kd->key_data_type[1] = key->salt->type;
else
kd->key_data_type[1] = KRB5_PADATA_PW_SALT;
/* setup key */
kd->key_data_length[0] = key->key.keyvalue.length;
kd->key_data_contents[0] = malloc(kd->key_data_length[0]);
if(kd->key_data_contents[0] == NULL && kd->key_data_length[0] != 0){
ret = ENOMEM;
break;
}
memcpy(kd->key_data_contents[0], key->key.keyvalue.data,
kd->key_data_length[0]);
/* setup salt */
if(key->salt)
sp = &key->salt->salt;
else
sp = &salt.saltvalue;
kd->key_data_length[1] = sp->length;
kd->key_data_contents[1] = malloc(kd->key_data_length[1]);
if(kd->key_data_length[1] != 0
&& kd->key_data_contents[1] == NULL) {
memset(kd->key_data_contents[0], 0, kd->key_data_length[0]);
ret = ENOMEM;
break;
}
memcpy(kd->key_data_contents[1], sp->data, kd->key_data_length[1]);
out->n_key_data = i + 1;
out->n_key_data = 0;
ret = copy_keyset_to_kadm5(context, ent.entry.kvno, ent.entry.keys.len,
ent.entry.keys.val, &salt, out);
if (ret)
goto out;
for (i = 0; hist_keys != NULL && i < hist_keys->len; i++) {
ret = copy_keyset_to_kadm5(context, hist_keys->val[i].kvno,
hist_keys->val[i].keys.len,
hist_keys->val[i].keys.val,
&salt, out);
if (ret)
goto out;
}
krb5_free_salt(context->context, salt);
assert( out->n_key_data == n_keys );
}
if(ret){
kadm5_free_principal_ent(context, out);

View File

@@ -45,6 +45,18 @@
RCSID("$Id$");
static kadm5_ret_t
kadm5_c_lock(void *server_handle)
{
return ENOTSUP;
}
static kadm5_ret_t
kadm5_c_unlock(void *server_handle)
{
return ENOTSUP;
}
static void
set_funcs(kadm5_client_context *c)
{
@@ -61,6 +73,8 @@ set_funcs(kadm5_client_context *c)
SET(c, modify_principal);
SET(c, randkey_principal);
SET(c, rename_principal);
SET(c, lock);
SET(c, unlock);
}
kadm5_ret_t

View File

@@ -57,3 +57,11 @@ error_code AUTH_CHANGEPW, "Operation requires `change-password' privilege"
error_code BAD_TL_TYPE, "Invalid tagged data list element type"
error_code MISSING_CONF_PARAMS, "Required parameters in kdc.conf missing"
error_code BAD_SERVER_NAME, "Bad krb5 admin server hostname"
error_code KS_TUPLE_NOSUPP, "Key/salt tuples not supported by this function"
error_code SETKEY3_ETYPE_MISMATCH, "Key/salt tuples don't match keys"
error_code DECRYPT_USAGE_NOSUPP, "Given usage of kadm5_decrypt() not supported"
error_code POLICY_OP_NOSUPP, "Policy operations not supported"
error_code KEEPOLD_NOSUPP, "Keep old keys option not supported"
error_code AUTH_GET_KEYS, "Operation requires `get-keys' privilege"
error_code ALREADY_LOCKED, "Database already locked"
error_code NOT_LOCKED, "Database not locked"

View File

@@ -38,6 +38,7 @@
#include <config.h>
#include <roken.h>
#include <heimbase.h>
#include <stdio.h>
#include <stdlib.h>

View File

@@ -63,16 +63,18 @@ _kadm5_init_keys (Key *keys, int len)
}
}
/*
* return 1 if any key in `keys1, len1' exists in `keys2, len2'
*/
int
static int
_kadm5_exists_keys(Key *keys1, int len1, Key *keys2, int len2)
{
int i, j;
size_t i, j;
size_t optimize;
for (i = 0; i < len1; ++i) {
optimize = 0;
for (j = 0; j < len2; j++) {
if ((keys1[i].salt != NULL && keys2[j].salt == NULL)
|| (keys1[i].salt == NULL && keys2[j].salt != NULL))
@@ -89,6 +91,7 @@ _kadm5_exists_keys(Key *keys1, int len1, Key *keys2, int len2)
}
if (keys1[i].key.keytype != keys2[j].key.keytype)
continue;
optimize = 1;
if (keys1[i].key.keyvalue.length != keys2[j].key.keyvalue.length)
continue;
if (memcmp (keys1[i].key.keyvalue.data, keys2[j].key.keyvalue.data,
@@ -97,6 +100,33 @@ _kadm5_exists_keys(Key *keys1, int len1, Key *keys2, int len2)
return 1;
}
/*
* Optimization: no need to check all of keys1[] if one there
* was one key in keys2[] with matching enctype and salt but not
* matching key. Assumption: all keys in keys1[] and keys2[]
* are output by string2key.
*/
if (optimize)
return 0;
}
return 0;
}
/*
* return 1 if any key in `keys1, len1' exists in hist_keys
*/
int
_kadm5_exists_keys_hist(Key *keys1, int len1, HDB_Ext_KeySet *hist_keys)
{
size_t i;
for (i = 0; i < hist_keys->len; i++) {
if (_kadm5_exists_keys(keys1, len1,
hist_keys->val[i].keys.val,
hist_keys->val[i].keys.len))
return 1;
}
return 0;
}

View File

@@ -585,7 +585,8 @@ kadm5_log_replay_modify (kadm5_server_context *context,
memset(&ent, 0, sizeof(ent));
ret = context->db->hdb_fetch_kvno(context->context, context->db,
log_ent.entry.principal,
HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
HDB_F_DECRYPT|HDB_F_ALL_KVNOS|
HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
if (ret)
goto out;
if (mask & KADM5_PRINC_EXPIRE_TIME) {
@@ -698,6 +699,29 @@ kadm5_log_replay_modify (kadm5_server_context *context,
size_t num;
size_t i;
/*
* We don't need to do anything about key history here because
* we always log KADM5_TL_DATA when we change keys/passwords, so
* the code below this will handle key history implicitly.
* However, if we had to, the code to handle key history here
* would look like this:
*
* HDB_extension *ext;
* ...
* ext = hdb_find_extension(&log_ent.entry,
* choice_HDB_extension_data_hist_keys);
* if (ext);
* ret = hdb_replace_extension(context->context, &ent.entry, ext);
* else
* ret = hdb_clear_extension(context->context, &ent.entry,
* choice_HDB_extension_data_hist_keys);
*
* Maybe we should do this here anyways, wasteful as it would
* be, as a defensive programming measure? For now we heim_assert().
*/
heim_assert((mask & KADM5_TL_DATA),
"Wouldn't log and replay key history");
for (i = 0; i < ent.entry.keys.len; ++i)
free_Key(&ent.entry.keys.val[i]);
free (ent.entry.keys.val);

View File

@@ -53,6 +53,38 @@ kadm5_store_key_data(krb5_storage *sp,
return 0;
}
kadm5_ret_t
kadm5_store_fake_key_data(krb5_storage *sp,
krb5_key_data *key)
{
char buf[4] = {0};
krb5_data c;
krb5_store_int32(sp, key->key_data_ver);
krb5_store_int32(sp, key->key_data_kvno);
krb5_store_int32(sp, key->key_data_type[0]);
/*
* This is the key contents. We want it to be obvious to the client
* (if it really did want the keys) that the key won't work.
* 32-bit keys are no good for any enctype, so that should do.
* Clients that didn't need keys will ignore this, and clients that
* did want keys will either fail or they'll, say, create bogus
* keytab entries that will subsequently fail to be useful.
*/
c.length = sizeof (buf);
c.data = buf;
memset(buf, 0xdeadcee5, sizeof (buf)); /* bad bad hexspeak for deadkeys */
krb5_store_data(sp, c);
/* This is the salt -- no need to send garbage */
krb5_store_int32(sp, key->key_data_type[1]);
c.length = key->key_data_length[1];
c.data = key->key_data_contents[1];
krb5_store_data(sp, c);
return 0;
}
kadm5_ret_t
kadm5_ret_key_data(krb5_storage *sp,
krb5_key_data *key)
@@ -105,7 +137,7 @@ kadm5_ret_tl_data(krb5_storage *sp,
static kadm5_ret_t
store_principal_ent(krb5_storage *sp,
kadm5_principal_ent_t princ,
uint32_t mask)
uint32_t mask, int wkeys)
{
int i;
@@ -149,8 +181,12 @@ store_principal_ent(krb5_storage *sp,
krb5_store_int32(sp, princ->fail_auth_count);
if (mask & KADM5_KEY_DATA) {
krb5_store_int32(sp, princ->n_key_data);
for(i = 0; i < princ->n_key_data; i++)
kadm5_store_key_data(sp, &princ->key_data[i]);
for(i = 0; i < princ->n_key_data; i++) {
if (wkeys)
kadm5_store_key_data(sp, &princ->key_data[i]);
else
kadm5_store_fake_key_data(sp, &princ->key_data[i]);
}
}
if (mask & KADM5_TL_DATA) {
krb5_tl_data *tp;
@@ -167,7 +203,14 @@ kadm5_ret_t
kadm5_store_principal_ent(krb5_storage *sp,
kadm5_principal_ent_t princ)
{
return store_principal_ent (sp, princ, ~0);
return store_principal_ent (sp, princ, ~0, 1);
}
kadm5_ret_t
kadm5_store_principal_ent_nokeys(krb5_storage *sp,
kadm5_principal_ent_t princ)
{
return store_principal_ent (sp, princ, ~0, 0);
}
kadm5_ret_t
@@ -176,7 +219,7 @@ kadm5_store_principal_ent_mask(krb5_storage *sp,
uint32_t mask)
{
krb5_store_int32(sp, mask);
return store_principal_ent (sp, princ, mask);
return store_principal_ent (sp, princ, mask, 1);
}
static kadm5_ret_t

View File

@@ -44,15 +44,19 @@ modify_principal(void *server_handle,
kadm5_server_context *context = server_handle;
hdb_entry_ex ent;
kadm5_ret_t ret;
memset(&ent, 0, sizeof(ent));
if((mask & forbidden_mask))
return KADM5_BAD_MASK;
if((mask & KADM5_POLICY) && strcmp(princ->policy, "default"))
return KADM5_UNK_POLICY;
memset(&ent, 0, sizeof(ent));
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
return ret;
if (!context->keep_open) {
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
return ret;
}
ret = context->db->hdb_fetch_kvno(context->context, context->db,
princ->principal, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
if(ret)
@@ -68,6 +72,21 @@ modify_principal(void *server_handle,
if (ret)
goto out2;
if((mask & KADM5_POLICY)) {
HDB_extension ext;
ext.data.element = choice_HDB_extension_data_policy;
ext.data.u.policy = strdup(princ->policy);
if (ext.data.u.policy == NULL) {
ret = ENOMEM;
goto out2;
}
/* This calls free_HDB_extension(), freeing ext.data.u.policy */
ret = hdb_replace_extension(context->context, &ent.entry, &ext);
if (ret)
goto out2;
}
ret = context->db->hdb_store(context->context, context->db,
HDB_F_REPLACE, &ent);
if (ret)
@@ -80,7 +99,8 @@ modify_principal(void *server_handle,
out2:
hdb_free_entry(context->context, &ent);
out:
context->db->hdb_close(context->context, context->db);
if (!context->keep_open)
context->db->hdb_close(context->context, context->db);
return _kadm5_error_code(ret);
}

View File

@@ -37,7 +37,7 @@
#define __kadm5_privatex_h__
struct kadm_func {
kadm5_ret_t (*chpass_principal) (void *, krb5_principal, const char*);
kadm5_ret_t (*chpass_principal) (void *, krb5_principal, int, const char*);
kadm5_ret_t (*create_principal) (void*, kadm5_principal_ent_t,
uint32_t, const char*);
kadm5_ret_t (*delete_principal) (void*, krb5_principal);
@@ -48,11 +48,14 @@ struct kadm_func {
kadm5_ret_t (*get_principals) (void*, const char*, char***, int*);
kadm5_ret_t (*get_privs) (void*, uint32_t*);
kadm5_ret_t (*modify_principal) (void*, kadm5_principal_ent_t, uint32_t);
kadm5_ret_t (*randkey_principal) (void*, krb5_principal,
krb5_keyblock**, int*);
kadm5_ret_t (*randkey_principal) (void*, krb5_principal, krb5_boolean, int,
krb5_key_salt_tuple*, krb5_keyblock**,
int*);
kadm5_ret_t (*rename_principal) (void*, krb5_principal, krb5_principal);
kadm5_ret_t (*chpass_principal_with_key) (void *, krb5_principal,
kadm5_ret_t (*chpass_principal_with_key) (void *, krb5_principal, int,
int, krb5_key_data *);
kadm5_ret_t (*lock) (void *);
kadm5_ret_t (*unlock) (void *);
};
/* XXX should be integrated */
@@ -89,6 +92,7 @@ typedef struct kadm5_server_context {
/* */
kadm5_config_params config;
HDB *db;
int keep_open;
krb5_principal caller;
unsigned acl_flags;
kadm5_log_context log_context;

View File

@@ -38,14 +38,18 @@ RCSID("$Id$");
kadm5_ret_t
kadm5_c_randkey_principal(void *server_handle,
krb5_principal princ,
krb5_boolean keepold,
int n_ks_tuple,
krb5_key_salt_tuple *ks_tuple,
krb5_keyblock **new_keys,
int *n_keys)
{
kadm5_client_context *context = server_handle;
kadm5_ret_t ret;
krb5_storage *sp;
unsigned char buf[1024];
unsigned char buf[1536];
int32_t tmp;
size_t i;
krb5_data reply;
ret = _kadm5_connect(server_handle);
@@ -57,8 +61,37 @@ kadm5_c_randkey_principal(void *server_handle,
krb5_clear_error_message(context->context);
return ENOMEM;
}
/*
* NOTE WELL: This message is extensible. It currently consists of:
*
* - opcode (kadm_randkey)
* - principal name (princ)
*
* followed by optional items, each of which must be present if
* there are any items following them that are also present:
*
* - keepold boolean (whether to delete old kvnos)
* - number of key/salt type tuples
* - array of {enctype, salttype}
*
* Eventually we may add:
*
* - opaque string2key parameters (salt, rounds, ...)
*/
krb5_store_int32(sp, kadm_randkey);
krb5_store_principal(sp, princ);
if (keepold == TRUE || n_ks_tuple > 0)
krb5_store_uint32(sp, keepold);
if (n_ks_tuple > 0)
krb5_store_uint32(sp, n_ks_tuple);
for (i = 0; i < n_ks_tuple; i++) {
krb5_store_int32(sp, ks_tuple[i].ks_enctype);
krb5_store_int32(sp, ks_tuple[i].ks_salttype);
}
/* Future extensions go here */
ret = _kadm5_client_send(context, sp);
krb5_storage_free(sp);
if (ret)
@@ -80,6 +113,10 @@ kadm5_c_randkey_principal(void *server_handle,
int i;
krb5_ret_int32(sp, &tmp);
if (tmp < 0) {
ret = EOVERFLOW;
goto out;
}
k = malloc(tmp * sizeof(*k));
if (k == NULL) {
ret = ENOMEM;
@@ -87,8 +124,10 @@ kadm5_c_randkey_principal(void *server_handle,
}
for(i = 0; i < tmp; i++)
krb5_ret_keyblock(sp, &k[i]);
*n_keys = tmp;
*new_keys = k;
if (n_keys && new_keys) {
*n_keys = tmp;
*new_keys = k;
}
}
out:
krb5_storage_free(sp);

View File

@@ -43,6 +43,9 @@ RCSID("$Id$");
kadm5_ret_t
kadm5_s_randkey_principal(void *server_handle,
krb5_principal princ,
krb5_boolean keepold,
int n_ks_tuple,
krb5_key_salt_tuple *ks_tuple,
krb5_keyblock **new_keys,
int *n_keys)
{
@@ -51,16 +54,26 @@ kadm5_s_randkey_principal(void *server_handle,
kadm5_ret_t ret;
memset(&ent, 0, sizeof(ent));
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
return ret;
if (!context->keep_open) {
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
return ret;
}
ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
if(ret)
goto out;
if (keepold) {
ret = hdb_add_current_keys_to_history(context->context, &ent.entry);
if (ret)
goto out2;
}
ret = _kadm5_set_keys_randomly (context,
&ent.entry,
n_ks_tuple,
ks_tuple,
new_keys,
n_keys);
if (ret)
@@ -74,9 +87,18 @@ kadm5_s_randkey_principal(void *server_handle,
if (ret)
goto out2;
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
if (ret)
goto out2;
if (keepold) {
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
if (ret)
goto out2;
} else {
HDB_extension ext;
ext.data.element = choice_HDB_extension_data_hist_keys;
ext.data.u.hist_keys.len = 0;
ext.data.u.hist_keys.val = NULL;
hdb_replace_extension(context->context, &ent.entry, &ext);
}
ret = context->db->hdb_store(context->context, context->db,
HDB_F_REPLACE, &ent);
@@ -102,6 +124,7 @@ out3:
out2:
hdb_free_entry(context->context, &ent);
out:
context->db->hdb_close(context->context, context->db);
if (!context->keep_open)
context->db->hdb_close(context->context, context->db);
return _kadm5_error_code(ret);
}

View File

@@ -48,13 +48,16 @@ kadm5_s_rename_principal(void *server_handle,
memset(&ent, 0, sizeof(ent));
if(krb5_principal_compare(context->context, source, target))
return KADM5_DUP; /* XXX is this right? */
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
return ret;
if (!context->keep_open) {
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
if(ret)
return ret;
}
ret = context->db->hdb_fetch_kvno(context->context, context->db,
source, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
if(ret){
context->db->hdb_close(context->context, context->db);
if (!context->keep_open)
context->db->hdb_close(context->context, context->db);
goto out;
}
ret = _kadm5_set_modifier(context, &ent.entry);
@@ -103,7 +106,8 @@ kadm5_s_rename_principal(void *server_handle,
ret = context->db->hdb_remove(context->context, context->db, oldname);
ent.entry.principal = oldname;
out2:
context->db->hdb_close(context->context, context->db);
if (!context->keep_open)
context->db->hdb_close(context->context, context->db);
hdb_free_entry(context->context, &ent);
out:
return _kadm5_error_code(ret);

View File

@@ -72,6 +72,23 @@ _kadm5_set_keys(kadm5_server_context *context,
return 0;
}
static void
setup_Key(Key *k, Salt *s, krb5_key_data *kd, size_t kd_offset)
{
memset(k, 0, sizeof (*k)); /* sets mkvno and salt */
k->key.keytype = kd[kd_offset].key_data_type[0];
k->key.keyvalue.length = kd[kd_offset].key_data_length[0];
k->key.keyvalue.data = kd[kd_offset].key_data_contents[0];
if(kd[kd_offset].key_data_ver == 2) {
memset(s, 0, sizeof (*s));
s->type = kd[kd_offset].key_data_type[1];
s->salt.length = kd[kd_offset].key_data_length[1];
s->salt.data = kd[kd_offset].key_data_contents[1];
k->salt = s;
}
}
/*
* Set the keys of `ent' to (`n_key_data', `key_data')
*/
@@ -83,51 +100,161 @@ _kadm5_set_keys2(kadm5_server_context *context,
krb5_key_data *key_data)
{
krb5_error_code ret;
int i;
unsigned len;
Key *keys;
size_t i, k;
HDB_extension ext;
HDB_extension *extp = NULL;
HDB_Ext_KeySet *hist_keys = &ext.data.u.hist_keys;
Key key;
Salt salt;
Keys keys;
hdb_keyset hkset;
krb5_kvno kvno = -1;
int one_key_set = 1;
int replace_hist_keys = 0;
len = n_key_data;
keys = malloc (len * sizeof(*keys));
if (keys == NULL && len != 0)
return ENOMEM;
_kadm5_init_keys (keys, len);
for(i = 0; i < n_key_data; i++) {
keys[i].mkvno = NULL;
keys[i].key.keytype = key_data[i].key_data_type[0];
ret = krb5_data_copy(&keys[i].key.keyvalue,
key_data[i].key_data_contents[0],
key_data[i].key_data_length[0]);
if(ret)
goto out;
if(key_data[i].key_data_ver == 2) {
Salt *salt;
salt = calloc(1, sizeof(*salt));
if(salt == NULL) {
ret = ENOMEM;
goto out;
}
keys[i].salt = salt;
salt->type = key_data[i].key_data_type[1];
krb5_data_copy(&salt->salt,
key_data[i].key_data_contents[1],
key_data[i].key_data_length[1]);
} else
keys[i].salt = NULL;
if (n_key_data == 0) {
/* Clear all keys! */
ret = hdb_clear_extension(context->context, ent,
choice_HDB_extension_data_hist_keys);
if (ret)
return ret;
free_Keys(&ent->keys);
return 0;
}
_kadm5_free_keys (context->context, ent->keys.len, ent->keys.val);
ent->keys.len = len;
ent->keys.val = keys;
memset(&keys, 0, sizeof (keys));
memset(&hkset, 0, sizeof (hkset)); /* set set_time */
ext.data.element = choice_HDB_extension_data_hist_keys;
memset(hist_keys, 0, sizeof (*hist_keys));
for (i = 0; i < n_key_data; i++) {
if (kvno != -1 && kvno != key_data[i].key_data_kvno) {
one_key_set = 0;
break;
}
kvno = key_data[i].key_data_kvno;
}
if (one_key_set) {
/*
* If we're updating KADM5_KEY_DATA with a single keyset then we
* assume we must be setting the principal's kvno as well!
*
* Just have to be careful about old clients that might have
* sent 0 as the kvno... This may seem ugly, but it's the price
* of backwards compatibility with pre-multi-kvno kadmin clients
* (besides, who's to say that updating KADM5_KEY_DATA requires
* updating the entry's kvno?)
*
* Note that we do nothing special for the case where multiple
* keysets are given but the entry's kvno is not set and not in
* the given set of keysets. If this happens we'll just update
* the key history only and leave the current keyset alone.
*/
if (kvno == 0) {
/* Force kvno to 1 if it was 0; (ank would do this anyways) */
if (ent->kvno == 0)
ent->kvno = 1;
/* Below we need key_data[*].kvno to be reasonable */
for (i = 0; i < n_key_data; i++)
key_data[i].key_data_kvno = ent->kvno;
} else {
/*
* Or force the entry's kvno to match the one from the new,
* singular keyset
*/
ent->kvno = kvno;
}
}
for (i = 0; i < n_key_data; i++) {
if (key_data[i].key_data_kvno == ent->kvno) {
/* A current key; add to current key set */
setup_Key(&key, &salt, key_data, i);
ret = add_Keys(&keys, &key);
continue;
}
/*
* This kvno is historical. Build an hdb_keyset for keys of
* this enctype and add them to the new key history.
*/
for (k = 0; k < hist_keys->len; k++) {
if (hist_keys->val[k].kvno == key_data[i].key_data_kvno)
break;
}
if (hist_keys->len > k &&
hist_keys->val[k].kvno == key_data[i].key_data_kvno)
/* We've added all keys of this kvno already (see below) */
continue;
memset(&hkset, 0, sizeof (hkset)); /* set set_time */
hkset.kvno = key_data[i].key_data_kvno;
for (k = 0; k < n_key_data; k++) {
/* Find all keys of this kvno and add them to the new keyset */
if (key_data[k].key_data_kvno != hkset.kvno)
continue;
setup_Key(&key, &salt, key_data, k);
ret = add_Keys(&hkset.keys, &key);
if (ret)
goto out;
}
ret = add_HDB_Ext_KeySet(hist_keys, &hkset);
if (ret)
goto out;
replace_hist_keys = 1;
}
if (replace_hist_keys)
/* No key history given -> leave it alone */
extp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
if (extp != NULL) {
HDB_Ext_KeySet *old_hist_keys;
/*
* Try to keep the very useful set_time values from the old hist
* keys. kadm5 loses this info, so this heuristic is the best we
* can do.
*/
old_hist_keys = &extp->data.u.hist_keys;
for (i = 0; i < old_hist_keys->len; i++) {
if (old_hist_keys->val[i].set_time == NULL)
continue;
for (k = 0; k < hist_keys->len; k++) {
if (hist_keys->val[k].kvno != old_hist_keys->val[k].kvno)
continue;
hist_keys->val[k].set_time = old_hist_keys->val[k].set_time;
old_hist_keys->val[k].set_time = NULL;
}
}
}
if (replace_hist_keys) {
/* If hist keys not given in key_data then don't blow away hist_keys */
ret = hdb_replace_extension(context->context, ent, &ext);
if (ret)
goto out;
}
/*
* A structure copy is more efficient here than this would be:
*
* copy_Keys(&keys, &ent->keys);
* free_Keys(&keys);
*
* Of course, the above hdb_replace_extension() is not at all efficient...
*/
free_Keys(&ent->keys);
ent->keys = keys;
hdb_entry_set_pw_change_time(context->context, ent, 0);
hdb_entry_clear_password(context->context, ent);
return 0;
out:
_kadm5_free_keys (context->context, len, keys);
out:
free_Keys(&keys);
free_hdb_keyset(&hkset);
free_HDB_extension(&ext);
return ret;
}
@@ -196,6 +323,8 @@ is_des_key_p(int keytype)
kadm5_ret_t
_kadm5_set_keys_randomly (kadm5_server_context *context,
hdb_entry *ent,
int n_ks_tuple,
krb5_key_salt_tuple *ks_tuple,
krb5_keyblock **new_keys,
int *n_keys)
{
@@ -206,7 +335,7 @@ _kadm5_set_keys_randomly (kadm5_server_context *context,
Key *keys;
ret = hdb_generate_key_set(context->context, ent->principal,
&keys, &num_keys, 1);
n_ks_tuple, ks_tuple, &keys, &num_keys, 1);
if (ret)
return ret;
@@ -263,8 +392,10 @@ out:
_kadm5_free_keys (context->context, ent->keys.len, ent->keys.val);
ent->keys.val = keys;
ent->keys.len = num_keys;
*new_keys = kblock;
*n_keys = num_keys;
if (n_keys && new_keys) {
*new_keys = kblock;
*n_keys = num_keys;
}
hdb_entry_set_pw_change_time(context->context, ent, 0);
hdb_entry_clear_password(context->context, ent);

View File

@@ -7,14 +7,22 @@ HEIMDAL_KAMD5_SERVER_1.0 {
kadm5_add_passwd_quality_verifier;
kadm5_check_password_quality;
kadm5_chpass_principal;
kadm5_chpass_principal_3;
kadm5_chpass_principal_with_key;
kadm5_chpass_principal_with_key_3;
kadm5_create_policy;
kadm5_create_principal;
kadm5_delete_principal;
kadm5_destroy;
kadm5_decrypt_key;
kadm5_delete_policy;
kadm5_flush;
kadm5_free_key_data;
kadm5_free_name_list;
kadm5_free_policy_ent;
kadm5_free_principal_ent;
kadm5_get_policy;
kadm5_get_policies;
kadm5_get_principal;
kadm5_get_principals;
kadm5_get_privs;
@@ -24,18 +32,25 @@ HEIMDAL_KAMD5_SERVER_1.0 {
kadm5_init_with_password_ctx;
kadm5_init_with_skey;
kadm5_init_with_skey_ctx;
kadm5_lock;
kadm5_modify_principal;
kadm5_modify_policy;
kadm5_randkey_principal;
kadm5_randkey_principal_3;
kadm5_rename_principal;
kadm5_ret_key_data;
kadm5_ret_principal_ent;
kadm5_ret_principal_ent_mask;
kadm5_ret_tl_data;
kadm5_setup_passwd_quality_check;
kadm5_setkey_principal;
kadm5_setkey_principal_3;
kadm5_store_key_data;
kadm5_store_principal_ent;
kadm5_store_principal_ent_mask;
kadm5_store_principal_ent_nokeys;
kadm5_store_tl_data;
kadm5_unlock;
kadm5_s_init_with_password_ctx;
kadm5_s_init_with_password;
kadm5_s_init_with_skey_ctx;

View File

@@ -878,6 +878,11 @@ typedef struct {
typedef krb5_error_code
(KRB5_CALLCONV * krb5_gic_process_last_req)(krb5_context, krb5_last_req_entry **, void *);
typedef struct {
krb5_enctype ks_enctype;
krb5int32 ks_salttype;
}krb5_key_salt_tuple;
/*
*
*/

View File

@@ -187,6 +187,8 @@ ec=0
echo "Getting client initial tickets"; > messages.log
${kinit} --password-file=${objdir}/foopassword foo@$R || \
{ ec=1 ; eval "${testfailed}"; }
echo "Doing krbtgt key rollover"; > messages.log
${kadmin} cpw -r --keepold krbtgt/${R}@${R} || exit 1
echo "Getting tickets"; > messages.log
${kgetcred} ${server}@${R} || { ec=1 ; eval "${testfailed}"; }
echo "Listing tickets"; > messages.log