mirror of
https://git.freebsd.org/src.git
synced 2026-01-11 19:57:22 +00:00
heimdal-kadmin: Add support for the -f dump option
The "-f" dump option allows a dump of the Heimdal
KDC in a format that the MIT kdb5_util command can
load into a MIT KDC's database.
This makes transitioning from the Heimdal KDC to
the current MIT one feasible without having to
re-create the KDC database from scratch.
glebius@ did the initial work, cherry picking these
commits from the Heimdal sources on github and then doing
extensive merge conflict resolution and other fixes so
that it would build.
Heimdal commit fca5399 authored by Nico Williams:
Initial commit for second approach for multiple kvno. NOT TESTED!
Heimdal commit 57f1545 authored by Nico Williams:
Add support for writing to KDB and dumping HDB to MIT KDB dump format
Before this change Heimdal could read KDBs. Now it can write to
them too.
Heimdal can now also dump HDBs (including KDBs) in MIT format,
which can then be imported with kdb5_util load.
This is intended to help in migrations from MIT to Heimdal by
allowing migrations from Heimdal to MIT so that it is possible
to rollback from Heimdal to MIT should there be any issues. The
idea is to allow a) running Heimdal kdc/kadmind with a KDB, or
b) running Heimdal with an HDB converted from a KDB and then
rollback by dumping the HDB and loading a KDB.
Note that not all TL data types are supported, only two: last
password change and modify-by. This is the minimum necessary.
PKINIT users may need to add support for KRB5_TL_USER_CERTIFICATE,
and for databases with K/M history we may need to add KRB5_TL_MKVNO
support.
This resulted in a Heimdal kadmin that would dump
the KDC database in MIT format. However, there
were issues when this dump was loaded into the
current MIT KDC in FreeBSD current/15.0.
The changes I did to make the dump more useful are listed below:
When "-f MIT" is used for "kadmin -l dump" it writes
the dump out in MIT format. This dump format is understood
by the MIT kdb5_util command. The patch modifies the above
so that the MIT KDC's master key keytab file can be provided
as the argument to "-f" so that the principals are re-encrypted in
it. This allows any principal with at least one strong encryption
type key to work without needing a change_password.
The strong encryption types supported by the Heimdal KDC are:
aes256-cts-hmac-sha1-96
aes128-cts-hmac-sha1-96
The issues my changes address are:
- If there are weak encryption keys in a principal's entry,
MIT's kadmin.local will report that the principcal's entry
is incomplete or corrupted.
- The keys are encrypted in Heimdal's master key. The
"-d" option can be used on the "kadmin -l dump" to
de-encrypt them, but the passwords will not work on the
current MIT KDC.
To try and deal with the above issues, this patch modied the above to:
- Does not dump the weak keys.
- Re-encrypts the strong keys in MIT's master key if the argument
to "-f" is actually a filename which holds the MIT KDC's
master key keytab and not "MIT".
- For principals that only have weak keys, it generates
a fake strong key. This key will not work on the MIT
KDC, but the principal entry will work once a
change_password is done to it.
- It always generates a "modified_by" entry, faking one if
not already present in the Heimdal KDC database.
This was necessary, since the MIT kadmin will
report that the principal entry is "incomplete or
corrupted" without one.
It also fixed a problem where "get principal" no longer
worked after the initial patch was applied.
A man page update will be done as a separate commit.
I believe this commit is acceptable since the Heimdal
sources are now essentially deprecated in favor of the
MIT sources and that this new "-f" patch simplifies
the transition to the MIT KDC.
Discussed with: glebius, cy
MFC after: 3 days
This commit is contained in:
parent
ccb600906f
commit
5000d023a4
21 changed files with 1373 additions and 467 deletions
|
|
@ -42,32 +42,51 @@ dump(struct dump_options *opt, int argc, char **argv)
|
|||
{
|
||||
krb5_error_code ret;
|
||||
FILE *f;
|
||||
struct hdb_print_entry_arg parg;
|
||||
HDB *db = NULL;
|
||||
|
||||
if(!local_flag) {
|
||||
if (!local_flag) {
|
||||
krb5_warnx(context, "dump is only available in local (-l) mode");
|
||||
return 0;
|
||||
}
|
||||
|
||||
db = _kadm5_s_get_db(kadm_handle);
|
||||
|
||||
if(argc == 0)
|
||||
if (argc == 0)
|
||||
f = stdout;
|
||||
else
|
||||
f = fopen(argv[0], "w");
|
||||
|
||||
if(f == NULL) {
|
||||
if (f == NULL) {
|
||||
krb5_warn(context, errno, "open: %s", argv[0]);
|
||||
goto out;
|
||||
}
|
||||
ret = db->hdb_open(context, db, O_RDONLY, 0600);
|
||||
if(ret) {
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "hdb_open");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!opt->format_string || strcmp(opt->format_string, "Heimdal") == 0) {
|
||||
parg.fmt = HDB_DUMP_HEIMDAL;
|
||||
} else if (opt->format_string && strcmp(opt->format_string, "MIT") == 0) {
|
||||
parg.fmt = HDB_DUMP_MIT;
|
||||
fprintf(f, "kdb5_util load_dump version 5\n"); /* 5||6, either way */
|
||||
} else if (opt->format_string) {
|
||||
/* Open the format string as a MIT mkey file. */
|
||||
ret = hdb_read_master_key(context, opt->format_string, &db->hdb_mit_key);
|
||||
if (ret)
|
||||
krb5_errx(context, 1, "Cannot open MIT mkey file");
|
||||
db->hdb_mit_key_set = 1;
|
||||
parg.fmt = HDB_DUMP_MIT;
|
||||
opt->decrypt_flag = 1;
|
||||
fprintf(f, "kdb5_util load_dump version 5\n"); /* 5||6, either way */
|
||||
} else {
|
||||
krb5_errx(context, 1, "Supported dump formats: Heimdal and MIT");
|
||||
}
|
||||
parg.out = f;
|
||||
hdb_foreach(context, db, opt->decrypt_flag ? HDB_F_DECRYPT : 0,
|
||||
hdb_print_entry, f);
|
||||
hdb_print_entry, &parg);
|
||||
|
||||
db->hdb_close(context, db);
|
||||
out:
|
||||
|
|
|
|||
|
|
@ -76,6 +76,12 @@ command = {
|
|||
type = "flag"
|
||||
help = "decrypt keys"
|
||||
}
|
||||
option = {
|
||||
long = "format"
|
||||
short = "f"
|
||||
type = "string"
|
||||
help = "dump format, mit or heimdal (default: heimdal)"
|
||||
}
|
||||
argument = "[dump-file]"
|
||||
min_args = "0"
|
||||
max_args = "1"
|
||||
|
|
|
|||
|
|
@ -85,23 +85,23 @@ main(int argc, char **argv)
|
|||
setprogname(argv[0]);
|
||||
|
||||
ret = krb5_init_context(&context);
|
||||
if(ret)
|
||||
if (ret)
|
||||
exit(1);
|
||||
|
||||
ret = krb5_openlog(context, "hpropd", &fac);
|
||||
if(ret)
|
||||
if (ret)
|
||||
errx(1, "krb5_openlog");
|
||||
krb5_set_warn_dest(context, fac);
|
||||
|
||||
if(getarg(args, num_args, argc, argv, &optidx))
|
||||
if (getarg(args, num_args, argc, argv, &optidx))
|
||||
usage(1);
|
||||
|
||||
if(local_realm != NULL)
|
||||
if (local_realm != NULL)
|
||||
krb5_set_default_realm(context, local_realm);
|
||||
|
||||
if(help_flag)
|
||||
if (help_flag)
|
||||
usage(0);
|
||||
if(version_flag) {
|
||||
if (version_flag) {
|
||||
print_version(NULL);
|
||||
exit(0);
|
||||
}
|
||||
|
|
@ -117,7 +117,7 @@ main(int argc, char **argv)
|
|||
if (database == NULL)
|
||||
database = hdb_default_db(context);
|
||||
|
||||
if(from_stdin) {
|
||||
if (from_stdin) {
|
||||
sock = STDIN_FILENO;
|
||||
} else {
|
||||
struct sockaddr_storage ss;
|
||||
|
|
@ -145,7 +145,7 @@ main(int argc, char **argv)
|
|||
HPROP_PORT), &sock);
|
||||
}
|
||||
sin_len = sizeof(ss);
|
||||
if(getpeername(sock, sa, &sin_len) < 0)
|
||||
if (getpeername(sock, sa, &sin_len) < 0)
|
||||
krb5_err(context, 1, errno, "getpeername");
|
||||
|
||||
if (inet_ntop(sa->sa_family,
|
||||
|
|
@ -158,7 +158,7 @@ main(int argc, char **argv)
|
|||
krb5_log(context, fac, 0, "Connection from %s", addr_name);
|
||||
|
||||
ret = krb5_kt_register(context, &hdb_kt_ops);
|
||||
if(ret)
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "krb5_kt_register");
|
||||
|
||||
if (ktname != NULL) {
|
||||
|
|
@ -173,7 +173,7 @@ main(int argc, char **argv)
|
|||
|
||||
ret = krb5_recvauth(context, &ac, &sock, HPROP_VERSION, NULL,
|
||||
0, keytab, &ticket);
|
||||
if(ret)
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "krb5_recvauth");
|
||||
|
||||
ret = krb5_unparse_name(context, ticket->server, &server);
|
||||
|
|
@ -186,15 +186,15 @@ main(int argc, char **argv)
|
|||
krb5_free_ticket (context, ticket);
|
||||
|
||||
ret = krb5_auth_con_getauthenticator(context, ac, &authent);
|
||||
if(ret)
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "krb5_auth_con_getauthenticator");
|
||||
|
||||
ret = krb5_make_principal(context, &c1, NULL, "kadmin", "hprop", NULL);
|
||||
if(ret)
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "krb5_make_principal");
|
||||
_krb5_principalname2krb5_principal(context, &c2,
|
||||
authent->cname, authent->crealm);
|
||||
if(!krb5_principal_compare(context, c1, c2)) {
|
||||
if (!krb5_principal_compare(context, c1, c2)) {
|
||||
char *s;
|
||||
ret = krb5_unparse_name(context, c2, &s);
|
||||
if (ret)
|
||||
|
|
@ -205,48 +205,48 @@ main(int argc, char **argv)
|
|||
krb5_free_principal(context, c2);
|
||||
|
||||
ret = krb5_kt_close(context, keytab);
|
||||
if(ret)
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "krb5_kt_close");
|
||||
}
|
||||
|
||||
if(!print_dump) {
|
||||
if (!print_dump) {
|
||||
asprintf(&tmp_db, "%s~", database);
|
||||
|
||||
ret = hdb_create(context, &db, tmp_db);
|
||||
if(ret)
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "hdb_create(%s)", tmp_db);
|
||||
ret = db->hdb_open(context, db, O_RDWR | O_CREAT | O_TRUNC, 0600);
|
||||
if(ret)
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "hdb_open(%s)", tmp_db);
|
||||
}
|
||||
|
||||
nprincs = 0;
|
||||
while(1){
|
||||
while (1){
|
||||
krb5_data data;
|
||||
hdb_entry_ex entry;
|
||||
|
||||
if(from_stdin) {
|
||||
if (from_stdin) {
|
||||
ret = krb5_read_message(context, &sock, &data);
|
||||
if(ret != 0 && ret != HEIM_ERR_EOF)
|
||||
if (ret != 0 && ret != HEIM_ERR_EOF)
|
||||
krb5_err(context, 1, ret, "krb5_read_message");
|
||||
} else {
|
||||
ret = krb5_read_priv_message(context, ac, &sock, &data);
|
||||
if(ret)
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "krb5_read_priv_message");
|
||||
}
|
||||
|
||||
if(ret == HEIM_ERR_EOF || data.length == 0) {
|
||||
if(!from_stdin) {
|
||||
if (ret == HEIM_ERR_EOF || data.length == 0) {
|
||||
if (!from_stdin) {
|
||||
data.data = NULL;
|
||||
data.length = 0;
|
||||
krb5_write_priv_message(context, ac, &sock, &data);
|
||||
}
|
||||
if(!print_dump) {
|
||||
if (!print_dump) {
|
||||
ret = db->hdb_close(context, db);
|
||||
if(ret)
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "db_close");
|
||||
ret = db->hdb_rename(context, db, database);
|
||||
if(ret)
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "db_rename");
|
||||
}
|
||||
break;
|
||||
|
|
@ -254,20 +254,24 @@ main(int argc, char **argv)
|
|||
memset(&entry, 0, sizeof(entry));
|
||||
ret = hdb_value2entry(context, &data, &entry.entry);
|
||||
krb5_data_free(&data);
|
||||
if(ret)
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "hdb_value2entry");
|
||||
if(print_dump)
|
||||
hdb_print_entry(context, db, &entry, stdout);
|
||||
else {
|
||||
if (print_dump) {
|
||||
struct hdb_print_entry_arg parg;
|
||||
|
||||
parg.out = stdout;
|
||||
parg.fmt = HDB_DUMP_HEIMDAL;
|
||||
hdb_print_entry(context, db, &entry, &parg);
|
||||
} else {
|
||||
ret = db->hdb_store(context, db, 0, &entry);
|
||||
if(ret == HDB_ERR_EXISTS) {
|
||||
if (ret == HDB_ERR_EXISTS) {
|
||||
char *s;
|
||||
ret = krb5_unparse_name(context, entry.entry.principal, &s);
|
||||
if (ret)
|
||||
s = strdup(unparseable_name);
|
||||
krb5_warnx(context, "Entry exists: %s", s);
|
||||
free(s);
|
||||
} else if(ret)
|
||||
} else if (ret)
|
||||
krb5_err(context, 1, ret, "db_store");
|
||||
else
|
||||
nprincs++;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,17 @@
|
|||
|
||||
#include "hprop.h"
|
||||
|
||||
extern krb5_error_code _hdb_mdb_value2entry(krb5_context context,
|
||||
krb5_data *data,
|
||||
krb5_kvno target_kvno,
|
||||
hdb_entry *entry);
|
||||
|
||||
extern int _hdb_mit_dump2mitdb_entry(krb5_context context,
|
||||
char *line,
|
||||
krb5_storage *sp);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
can have any number of princ stanzas.
|
||||
format is as follows (only \n indicates newlines)
|
||||
|
|
@ -74,19 +85,6 @@ unless no extra data
|
|||
|
||||
*/
|
||||
|
||||
static int
|
||||
hex_to_octet_string(const char *ptr, krb5_data *data)
|
||||
{
|
||||
size_t i;
|
||||
unsigned int v;
|
||||
for(i = 0; i < data->length; i++) {
|
||||
if(sscanf(ptr + 2 * i, "%02x", &v) != 1)
|
||||
return -1;
|
||||
((unsigned char*)data->data)[i] = v;
|
||||
}
|
||||
return 2 * i;
|
||||
}
|
||||
|
||||
static char *
|
||||
nexttoken(char **p)
|
||||
{
|
||||
|
|
@ -97,321 +95,116 @@ nexttoken(char **p)
|
|||
return q;
|
||||
}
|
||||
|
||||
static size_t
|
||||
getdata(char **p, unsigned char *buf, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
int v;
|
||||
char *q = nexttoken(p);
|
||||
i = 0;
|
||||
while(*q && i < len) {
|
||||
if(sscanf(q, "%02x", &v) != 1)
|
||||
break;
|
||||
buf[i++] = v;
|
||||
q += 2;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static int
|
||||
getint(char **p)
|
||||
{
|
||||
int val;
|
||||
char *q = nexttoken(p);
|
||||
sscanf(q, "%d", &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
#include <kadm5/admin.h>
|
||||
|
||||
static void
|
||||
attr_to_flags(unsigned attr, HDBFlags *flags)
|
||||
static int
|
||||
my_fgetln(FILE *f, char **buf, size_t *sz, size_t *len)
|
||||
{
|
||||
flags->postdate = !(attr & KRB5_KDB_DISALLOW_POSTDATED);
|
||||
flags->forwardable = !(attr & KRB5_KDB_DISALLOW_FORWARDABLE);
|
||||
flags->initial = !!(attr & KRB5_KDB_DISALLOW_TGT_BASED);
|
||||
flags->renewable = !(attr & KRB5_KDB_DISALLOW_RENEWABLE);
|
||||
flags->proxiable = !(attr & KRB5_KDB_DISALLOW_PROXIABLE);
|
||||
/* DUP_SKEY */
|
||||
flags->invalid = !!(attr & KRB5_KDB_DISALLOW_ALL_TIX);
|
||||
flags->require_preauth = !!(attr & KRB5_KDB_REQUIRES_PRE_AUTH);
|
||||
flags->require_hwauth = !!(attr & KRB5_KDB_REQUIRES_HW_AUTH);
|
||||
flags->server = !(attr & KRB5_KDB_DISALLOW_SVR);
|
||||
flags->change_pw = !!(attr & KRB5_KDB_PWCHANGE_SERVICE);
|
||||
flags->client = 1; /* XXX */
|
||||
}
|
||||
char *p, *n;
|
||||
|
||||
#define KRB5_KDB_SALTTYPE_NORMAL 0
|
||||
#define KRB5_KDB_SALTTYPE_V4 1
|
||||
#define KRB5_KDB_SALTTYPE_NOREALM 2
|
||||
#define KRB5_KDB_SALTTYPE_ONLYREALM 3
|
||||
#define KRB5_KDB_SALTTYPE_SPECIAL 4
|
||||
#define KRB5_KDB_SALTTYPE_AFS3 5
|
||||
|
||||
static krb5_error_code
|
||||
fix_salt(krb5_context context, hdb_entry *ent, int key_num)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
Salt *salt = ent->keys.val[key_num].salt;
|
||||
/* fix salt type */
|
||||
switch((int)salt->type) {
|
||||
case KRB5_KDB_SALTTYPE_NORMAL:
|
||||
salt->type = KRB5_PADATA_PW_SALT;
|
||||
break;
|
||||
case KRB5_KDB_SALTTYPE_V4:
|
||||
krb5_data_free(&salt->salt);
|
||||
salt->type = KRB5_PADATA_PW_SALT;
|
||||
break;
|
||||
case KRB5_KDB_SALTTYPE_NOREALM:
|
||||
{
|
||||
size_t len;
|
||||
size_t i;
|
||||
char *p;
|
||||
|
||||
len = 0;
|
||||
for (i = 0; i < ent->principal->name.name_string.len; ++i)
|
||||
len += strlen(ent->principal->name.name_string.val[i]);
|
||||
ret = krb5_data_alloc (&salt->salt, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
p = salt->salt.data;
|
||||
for (i = 0; i < ent->principal->name.name_string.len; ++i) {
|
||||
memcpy (p,
|
||||
ent->principal->name.name_string.val[i],
|
||||
strlen(ent->principal->name.name_string.val[i]));
|
||||
p += strlen(ent->principal->name.name_string.val[i]);
|
||||
}
|
||||
|
||||
salt->type = KRB5_PADATA_PW_SALT;
|
||||
break;
|
||||
if (!*buf) {
|
||||
*buf = malloc(*sz ? *sz : 2048);
|
||||
if (!*buf)
|
||||
return ENOMEM;
|
||||
if (!*sz)
|
||||
*sz = 2048;
|
||||
}
|
||||
case KRB5_KDB_SALTTYPE_ONLYREALM:
|
||||
krb5_data_free(&salt->salt);
|
||||
ret = krb5_data_copy(&salt->salt,
|
||||
ent->principal->realm,
|
||||
strlen(ent->principal->realm));
|
||||
if(ret)
|
||||
return ret;
|
||||
salt->type = KRB5_PADATA_PW_SALT;
|
||||
break;
|
||||
case KRB5_KDB_SALTTYPE_SPECIAL:
|
||||
salt->type = KRB5_PADATA_PW_SALT;
|
||||
break;
|
||||
case KRB5_KDB_SALTTYPE_AFS3:
|
||||
krb5_data_free(&salt->salt);
|
||||
ret = krb5_data_copy(&salt->salt,
|
||||
ent->principal->realm,
|
||||
strlen(ent->principal->realm));
|
||||
if(ret)
|
||||
return ret;
|
||||
salt->type = KRB5_PADATA_AFS3_SALT;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
*len = 0;
|
||||
while ((p = fgets(&(*buf)[*len], *sz, f))) {
|
||||
if (strcspn(*buf, "\r\n") || feof(f)) {
|
||||
*len = strlen(*buf);
|
||||
return 0;
|
||||
}
|
||||
*len += strlen(&(*buf)[*len]); /* *len should be == *sz */
|
||||
n = realloc(buf, *sz + (*sz >> 1));
|
||||
if (!n) {
|
||||
free(*buf);
|
||||
*buf = NULL;
|
||||
*sz = 0;
|
||||
*len = 0;
|
||||
return ENOMEM;
|
||||
}
|
||||
*buf = n;
|
||||
*sz += *sz >> 1;
|
||||
}
|
||||
return 0;
|
||||
return 0; /* *len == 0 || no EOL -> EOF */
|
||||
}
|
||||
|
||||
int
|
||||
mit_prop_dump(void *arg, const char *file)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char line [2048];
|
||||
FILE *f;
|
||||
size_t line_bufsz = 0;
|
||||
size_t line_len = 0;
|
||||
char *line = NULL;
|
||||
int lineno = 0;
|
||||
FILE *f;
|
||||
struct hdb_entry_ex ent;
|
||||
|
||||
struct prop_data *pd = arg;
|
||||
krb5_storage *sp = NULL;
|
||||
krb5_data kdb_ent;
|
||||
|
||||
memset(&ent, 0, sizeof (ent));
|
||||
f = fopen(file, "r");
|
||||
if(f == NULL)
|
||||
if (f == NULL)
|
||||
return errno;
|
||||
|
||||
while(fgets(line, sizeof(line), f)) {
|
||||
char *p = line, *q;
|
||||
ret = ENOMEM;
|
||||
sp = krb5_storage_emem();
|
||||
if (!sp)
|
||||
goto out;
|
||||
while ((ret = my_fgetln(f, &line, &line_bufsz, &line_len)) == 0) {
|
||||
char *p = line;
|
||||
char *q;
|
||||
lineno++;
|
||||
|
||||
int i;
|
||||
|
||||
int num_tl_data;
|
||||
int num_key_data;
|
||||
int high_kvno;
|
||||
int attributes;
|
||||
|
||||
int tmp;
|
||||
|
||||
lineno++;
|
||||
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
|
||||
q = nexttoken(&p);
|
||||
if(strcmp(q, "kdb5_util") == 0) {
|
||||
if(strncmp(line, "kdb5_util", strlen("kdb5_util")) == 0) {
|
||||
int major;
|
||||
q = nexttoken(&p);
|
||||
if (strcmp(q, "kdb5_util"))
|
||||
errx(1, "line %d: unknown version", lineno);
|
||||
q = nexttoken(&p); /* load_dump */
|
||||
if(strcmp(q, "load_dump"))
|
||||
if (strcmp(q, "load_dump"))
|
||||
errx(1, "line %d: unknown version", lineno);
|
||||
q = nexttoken(&p); /* load_dump */
|
||||
if(strcmp(q, "version"))
|
||||
if (strcmp(q, "version"))
|
||||
errx(1, "line %d: unknown version", lineno);
|
||||
q = nexttoken(&p); /* x.0 */
|
||||
if(sscanf(q, "%d", &major) != 1)
|
||||
if (sscanf(q, "%d", &major) != 1)
|
||||
errx(1, "line %d: unknown version", lineno);
|
||||
if(major != 4 && major != 5 && major != 6)
|
||||
if (major != 4 && major != 5 && major != 6)
|
||||
errx(1, "unknown dump file format, got %d, expected 4-6",
|
||||
major);
|
||||
continue;
|
||||
} else if(strcmp(q, "policy") == 0) {
|
||||
} else if(strncmp(p, "policy", strlen("policy")) == 0) {
|
||||
warnx("line: %d: ignoring policy (not supported)", lineno);
|
||||
continue;
|
||||
} else if(strcmp(q, "princ") != 0) {
|
||||
} else if(strncmp(p, "princ", strlen("princ")) != 0) {
|
||||
warnx("line %d: not a principal", lineno);
|
||||
continue;
|
||||
}
|
||||
tmp = getint(&p);
|
||||
if(tmp != 38) {
|
||||
warnx("line %d: bad base length %d != 38", lineno, tmp);
|
||||
continue;
|
||||
}
|
||||
nexttoken(&p); /* length of principal */
|
||||
num_tl_data = getint(&p); /* number of tl-data */
|
||||
num_key_data = getint(&p); /* number of key-data */
|
||||
getint(&p); /* length of extra data */
|
||||
q = nexttoken(&p); /* principal name */
|
||||
krb5_parse_name(pd->context, q, &ent.entry.principal);
|
||||
attributes = getint(&p); /* attributes */
|
||||
attr_to_flags(attributes, &ent.entry.flags);
|
||||
tmp = getint(&p); /* max life */
|
||||
if(tmp != 0) {
|
||||
ALLOC(ent.entry.max_life);
|
||||
*ent.entry.max_life = tmp;
|
||||
}
|
||||
tmp = getint(&p); /* max renewable life */
|
||||
if(tmp != 0) {
|
||||
ALLOC(ent.entry.max_renew);
|
||||
*ent.entry.max_renew = tmp;
|
||||
}
|
||||
tmp = getint(&p); /* expiration */
|
||||
if(tmp != 0 && tmp != 2145830400) {
|
||||
ALLOC(ent.entry.valid_end);
|
||||
*ent.entry.valid_end = tmp;
|
||||
}
|
||||
tmp = getint(&p); /* pw expiration */
|
||||
if(tmp != 0) {
|
||||
ALLOC(ent.entry.pw_end);
|
||||
*ent.entry.pw_end = tmp;
|
||||
}
|
||||
nexttoken(&p); /* last auth */
|
||||
nexttoken(&p); /* last failed auth */
|
||||
nexttoken(&p); /* fail auth count */
|
||||
for(i = 0; i < num_tl_data; i++) {
|
||||
unsigned long val;
|
||||
int tl_type, tl_length;
|
||||
unsigned char *buf;
|
||||
krb5_principal princ;
|
||||
|
||||
tl_type = getint(&p); /* data type */
|
||||
tl_length = getint(&p); /* data length */
|
||||
|
||||
#define mit_KRB5_TL_LAST_PWD_CHANGE 1
|
||||
#define mit_KRB5_TL_MOD_PRINC 2
|
||||
switch(tl_type) {
|
||||
case mit_KRB5_TL_LAST_PWD_CHANGE:
|
||||
buf = malloc(tl_length);
|
||||
if (buf == NULL)
|
||||
errx(ENOMEM, "malloc");
|
||||
getdata(&p, buf, tl_length); /* data itself */
|
||||
val = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
||||
free(buf);
|
||||
ALLOC(ent.entry.extensions);
|
||||
ALLOC_SEQ(ent.entry.extensions, 1);
|
||||
ent.entry.extensions->val[0].mandatory = 0;
|
||||
ent.entry.extensions->val[0].data.element
|
||||
= choice_HDB_extension_data_last_pw_change;
|
||||
ent.entry.extensions->val[0].data.u.last_pw_change = val;
|
||||
break;
|
||||
case mit_KRB5_TL_MOD_PRINC:
|
||||
buf = malloc(tl_length);
|
||||
if (buf == NULL)
|
||||
errx(ENOMEM, "malloc");
|
||||
getdata(&p, buf, tl_length); /* data itself */
|
||||
val = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
||||
ret = krb5_parse_name(pd->context, (char *)buf + 4, &princ);
|
||||
if (ret)
|
||||
krb5_err(pd->context, 1, ret,
|
||||
"parse_name: %s", (char *)buf + 4);
|
||||
free(buf);
|
||||
ALLOC(ent.entry.modified_by);
|
||||
ent.entry.modified_by->time = val;
|
||||
ent.entry.modified_by->principal = princ;
|
||||
break;
|
||||
default:
|
||||
nexttoken(&p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ALLOC_SEQ(&ent.entry.keys, num_key_data);
|
||||
high_kvno = -1;
|
||||
for(i = 0; i < num_key_data; i++) {
|
||||
int key_versions;
|
||||
int kvno;
|
||||
key_versions = getint(&p); /* key data version */
|
||||
kvno = getint(&p);
|
||||
|
||||
/*
|
||||
* An MIT dump file may contain multiple sets of keys with
|
||||
* different kvnos. Since the Heimdal database can only represent
|
||||
* one kvno per principal, we only want the highest set. Assume
|
||||
* that set will be given first, and discard all keys with lower
|
||||
* kvnos.
|
||||
*/
|
||||
if (kvno > high_kvno && high_kvno != -1)
|
||||
errx(1, "line %d: high kvno keys given after low kvno keys",
|
||||
lineno);
|
||||
else if (kvno < high_kvno) {
|
||||
nexttoken(&p); /* key type */
|
||||
nexttoken(&p); /* key length */
|
||||
nexttoken(&p); /* key */
|
||||
if (key_versions > 1) {
|
||||
nexttoken(&p); /* salt type */
|
||||
nexttoken(&p); /* salt length */
|
||||
nexttoken(&p); /* salt */
|
||||
}
|
||||
ent.entry.keys.len--;
|
||||
continue;
|
||||
}
|
||||
ent.entry.kvno = kvno;
|
||||
high_kvno = kvno;
|
||||
ALLOC(ent.entry.keys.val[i].mkvno);
|
||||
*ent.entry.keys.val[i].mkvno = 1;
|
||||
|
||||
/* key version 0 -- actual key */
|
||||
ent.entry.keys.val[i].key.keytype = getint(&p); /* key type */
|
||||
tmp = getint(&p); /* key length */
|
||||
/* the first two bytes of the key is the key length --
|
||||
skip it */
|
||||
krb5_data_alloc(&ent.entry.keys.val[i].key.keyvalue, tmp - 2);
|
||||
q = nexttoken(&p); /* key itself */
|
||||
hex_to_octet_string(q + 4, &ent.entry.keys.val[i].key.keyvalue);
|
||||
|
||||
if(key_versions > 1) {
|
||||
/* key version 1 -- optional salt */
|
||||
ALLOC(ent.entry.keys.val[i].salt);
|
||||
ent.entry.keys.val[i].salt->type = getint(&p); /* salt type */
|
||||
tmp = getint(&p); /* salt length */
|
||||
if(tmp > 0) {
|
||||
krb5_data_alloc(&ent.entry.keys.val[i].salt->salt, tmp - 2);
|
||||
q = nexttoken(&p); /* salt itself */
|
||||
hex_to_octet_string(q + 4,
|
||||
&ent.entry.keys.val[i].salt->salt);
|
||||
} else {
|
||||
ent.entry.keys.val[i].salt->salt.length = 0;
|
||||
ent.entry.keys.val[i].salt->salt.data = NULL;
|
||||
getint(&p); /* -1, if no data. */
|
||||
}
|
||||
fix_salt(pd->context, &ent.entry, i);
|
||||
}
|
||||
}
|
||||
nexttoken(&p); /* extra data */
|
||||
v5_prop(pd->context, NULL, &ent, arg);
|
||||
krb5_storage_truncate(sp, 0);
|
||||
ret = _hdb_mit_dump2mitdb_entry(pd->context, line, sp);
|
||||
if (ret) break;
|
||||
ret = krb5_storage_to_data(sp, &kdb_ent);
|
||||
if (ret) break;
|
||||
ret = _hdb_mdb_value2entry(pd->context, &kdb_ent, 0, &ent.entry);
|
||||
krb5_data_free(&kdb_ent);
|
||||
if (ret) break;
|
||||
ret = v5_prop(pd->context, NULL, &ent, arg);
|
||||
hdb_free_entry(pd->context, &ent);
|
||||
if (ret) break;
|
||||
}
|
||||
|
||||
out:
|
||||
fclose(f);
|
||||
return 0;
|
||||
free(line);
|
||||
if (sp)
|
||||
krb5_storage_free(sp);
|
||||
if (ret && ret == ENOMEM)
|
||||
errx(1, "out of memory");
|
||||
if (ret)
|
||||
errx(1, "line %d: problem parsing dump line", lineno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ 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 \
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
#ifdef notnow
|
||||
if ((flags & HDB_F_KVNO_SPECIFIED) == 0 &&
|
||||
(flags & HDB_F_CURRENT_KVNO) == 0) {
|
||||
|
||||
/*
|
||||
* Decrypt all the old keys too, since we don't know which
|
||||
* the caller will need.
|
||||
*/
|
||||
ret = hdb_unseal_keys_kvno(context, db, 0, &entry->entry);
|
||||
if (ret) {
|
||||
hdb_free_entry(context, entry);
|
||||
return ret;
|
||||
}
|
||||
} else if ((flags & HDB_F_KVNO_SPECIFIED) != 0 &&
|
||||
kvno != entry->entry.kvno &&
|
||||
kvno < entry->entry.kvno &&
|
||||
kvno > 0) {
|
||||
|
||||
/* Decrypt the keys we were asked for, if not the current ones */
|
||||
ret = hdb_unseal_keys_kvno(context, db, kvno, &entry->entry);
|
||||
if (ret) {
|
||||
hdb_free_entry(context, entry);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Always decrypt the current keys too */
|
||||
ret = hdb_unseal_keys(context, db, &entry->entry);
|
||||
if (ret) {
|
||||
hdb_free_entry(context, entry);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return code;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
|
|
|
|||
|
|
@ -432,3 +432,34 @@ hdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_set_last_modified_by(krb5_context context, hdb_entry *entry,
|
||||
krb5_principal modby, time_t modtime)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
Event *old_ev;
|
||||
Event *ev;
|
||||
|
||||
old_ev = entry->modified_by;
|
||||
|
||||
ev = calloc(1, sizeof (*ev));
|
||||
if (!ev)
|
||||
return ENOMEM;
|
||||
if (modby)
|
||||
ret = krb5_copy_principal(context, modby, &ev->principal);
|
||||
else
|
||||
ret = krb5_parse_name(context, "root/admin", &ev->principal);
|
||||
if (ret) {
|
||||
free(ev);
|
||||
return ret;
|
||||
}
|
||||
ev->time = modtime;
|
||||
if (!ev->time)
|
||||
time(&ev->time);
|
||||
|
||||
entry->modified_by = ev;
|
||||
if (old_ev)
|
||||
free_Event(old_ev);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,18 +91,28 @@ salt:
|
|||
|
||||
#include "hdb_locl.h"
|
||||
|
||||
static void
|
||||
attr_to_flags(unsigned attr, HDBFlags *flags)
|
||||
{
|
||||
flags->postdate = !(attr & KRB5_KDB_DISALLOW_POSTDATED);
|
||||
flags->forwardable = !(attr & KRB5_KDB_DISALLOW_FORWARDABLE);
|
||||
flags->initial = !!(attr & KRB5_KDB_DISALLOW_TGT_BASED);
|
||||
flags->renewable = !(attr & KRB5_KDB_DISALLOW_RENEWABLE);
|
||||
flags->proxiable = !(attr & KRB5_KDB_DISALLOW_PROXIABLE);
|
||||
/* DUP_SKEY */
|
||||
flags->invalid = !!(attr & KRB5_KDB_DISALLOW_ALL_TIX);
|
||||
flags->require_preauth = !!(attr & KRB5_KDB_REQUIRES_PRE_AUTH);
|
||||
flags->require_hwauth = !!(attr & KRB5_KDB_REQUIRES_HW_AUTH);
|
||||
flags->server = !(attr & KRB5_KDB_DISALLOW_SVR);
|
||||
flags->change_pw = !!(attr & KRB5_KDB_PWCHANGE_SERVICE);
|
||||
flags->client = 1; /* XXX */
|
||||
}
|
||||
|
||||
#define KDB_V1_BASE_LENGTH 38
|
||||
|
||||
#if HAVE_DB1
|
||||
|
||||
#if defined(HAVE_DB_185_H)
|
||||
#include <db_185.h>
|
||||
#elif defined(HAVE_DB_H)
|
||||
#include <db.h>
|
||||
#endif
|
||||
|
||||
#define CHECK(x) do { if ((x)) goto out; } while(0)
|
||||
|
||||
#ifdef HAVE_DB1
|
||||
static krb5_error_code
|
||||
mdb_principal2key(krb5_context context,
|
||||
krb5_const_principal principal,
|
||||
|
|
@ -118,6 +128,7 @@ mdb_principal2key(krb5_context context,
|
|||
key->length = strlen(str) + 1;
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_DB1 */
|
||||
|
||||
#define KRB5_KDB_SALTTYPE_NORMAL 0
|
||||
#define KRB5_KDB_SALTTYPE_V4 1
|
||||
|
|
@ -197,13 +208,15 @@ fix_salt(krb5_context context, hdb_entry *ent, int key_num)
|
|||
}
|
||||
|
||||
|
||||
static krb5_error_code
|
||||
mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry *entry)
|
||||
krb5_error_code
|
||||
_hdb_mdb_value2entry(krb5_context context, krb5_data *data,
|
||||
krb5_kvno kvno, hdb_entry *entry)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_storage *sp;
|
||||
uint32_t u32;
|
||||
uint16_t u16, num_keys, num_tl;
|
||||
ssize_t sz;
|
||||
size_t i, j;
|
||||
char *p;
|
||||
|
||||
|
|
@ -234,18 +247,7 @@ mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry
|
|||
if (u16 != KDB_V1_BASE_LENGTH) { ret = EINVAL; goto out; }
|
||||
/* 32: attributes */
|
||||
CHECK(ret = krb5_ret_uint32(sp, &u32));
|
||||
entry->flags.postdate = !(u32 & KRB5_KDB_DISALLOW_POSTDATED);
|
||||
entry->flags.forwardable = !(u32 & KRB5_KDB_DISALLOW_FORWARDABLE);
|
||||
entry->flags.initial = !!(u32 & KRB5_KDB_DISALLOW_TGT_BASED);
|
||||
entry->flags.renewable = !(u32 & KRB5_KDB_DISALLOW_RENEWABLE);
|
||||
entry->flags.proxiable = !(u32 & KRB5_KDB_DISALLOW_PROXIABLE);
|
||||
/* DUP_SKEY */
|
||||
entry->flags.invalid = !!(u32 & KRB5_KDB_DISALLOW_ALL_TIX);
|
||||
entry->flags.require_preauth =!!(u32 & KRB5_KDB_REQUIRES_PRE_AUTH);
|
||||
entry->flags.require_hwauth =!!(u32 & KRB5_KDB_REQUIRES_HW_AUTH);
|
||||
entry->flags.server = !(u32 & KRB5_KDB_DISALLOW_SVR);
|
||||
entry->flags.change_pw = !!(u32 & KRB5_KDB_PWCHANGE_SERVICE);
|
||||
entry->flags.client = 1; /* XXX */
|
||||
attr_to_flags(u32, &entry->flags);
|
||||
|
||||
/* 32: max time */
|
||||
CHECK(ret = krb5_ret_uint32(sp, &u32));
|
||||
|
|
@ -296,7 +298,11 @@ mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry
|
|||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
krb5_storage_read(sp, p, u16);
|
||||
sz = krb5_storage_read(sp, p, u16);
|
||||
if (sz != u16) {
|
||||
ret = EINVAL; /* XXX */
|
||||
goto out;
|
||||
}
|
||||
p[u16] = '\0';
|
||||
CHECK(ret = krb5_parse_name(context, p, &entry->principal));
|
||||
free(p);
|
||||
|
|
@ -305,12 +311,53 @@ mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry
|
|||
16: tl data type
|
||||
16: tl data length
|
||||
length: length */
|
||||
#define mit_KRB5_TL_LAST_PWD_CHANGE 1
|
||||
#define mit_KRB5_TL_MOD_PRINC 2
|
||||
for (i = 0; i < num_tl; i++) {
|
||||
int tl_type;
|
||||
krb5_principal modby;
|
||||
/* 16: TL data type */
|
||||
CHECK(ret = krb5_ret_uint16(sp, &u16));
|
||||
tl_type = u16;
|
||||
/* 16: TL data length */
|
||||
CHECK(ret = krb5_ret_uint16(sp, &u16));
|
||||
krb5_storage_seek(sp, u16, SEEK_CUR);
|
||||
/*
|
||||
* For rollback to MIT purposes we really must understand some
|
||||
* TL data!
|
||||
*
|
||||
* XXX Move all this to separate functions, one per-TL type.
|
||||
*/
|
||||
switch (tl_type) {
|
||||
case mit_KRB5_TL_LAST_PWD_CHANGE:
|
||||
CHECK(ret = krb5_ret_uint32(sp, &u32));
|
||||
CHECK(ret = hdb_entry_set_pw_change_time(context, entry, u32));
|
||||
break;
|
||||
case mit_KRB5_TL_MOD_PRINC:
|
||||
if (u16 < 5) {
|
||||
ret = EINVAL; /* XXX */
|
||||
goto out;
|
||||
}
|
||||
CHECK(ret = krb5_ret_uint32(sp, &u32)); /* mod time */
|
||||
p = malloc(u16 - 4 + 1);
|
||||
if (!p) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
p[u16 - 4] = '\0';
|
||||
sz = krb5_storage_read(sp, p, u16 - 4);
|
||||
if (sz != u16 - 4) {
|
||||
ret = EINVAL; /* XXX */
|
||||
goto out;
|
||||
}
|
||||
CHECK(ret = krb5_parse_name(context, p, &modby));
|
||||
ret = hdb_set_last_modified_by(context, entry, modby, u32);
|
||||
krb5_free_principal(context, modby);
|
||||
free(p);
|
||||
break;
|
||||
default:
|
||||
krb5_storage_seek(sp, u16, SEEK_CUR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* for num key data times
|
||||
|
|
@ -471,6 +518,14 @@ mdb_entry2value(krb5_context context, hdb_entry *entry, krb5_data *data)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_DB1
|
||||
|
||||
#if defined(HAVE_DB_185_H)
|
||||
#include <db_185.h>
|
||||
#elif defined(HAVE_DB_H)
|
||||
#include <db.h>
|
||||
#endif
|
||||
|
||||
|
||||
static krb5_error_code
|
||||
mdb_close(krb5_context context, HDB *db)
|
||||
|
|
@ -551,7 +606,7 @@ mdb_seq(krb5_context context, HDB *db,
|
|||
data.length = value.size;
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
|
||||
if (mdb_value2entry(context, &data, 0, &entry->entry))
|
||||
if (_hdb_mdb_value2entry(context, &data, 0, &entry->entry))
|
||||
return mdb_seq(context, db, flags, entry, R_NEXT);
|
||||
|
||||
if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
|
||||
|
|
@ -684,24 +739,26 @@ mdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
|
|||
unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry)
|
||||
{
|
||||
krb5_data key, value;
|
||||
krb5_error_code code;
|
||||
krb5_error_code ret;
|
||||
|
||||
code = mdb_principal2key(context, principal, &key);
|
||||
if (code)
|
||||
return code;
|
||||
code = db->hdb__get(context, db, key, &value);
|
||||
ret = mdb_principal2key(context, principal, &key);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = db->hdb__get(context, db, key, &value);
|
||||
krb5_data_free(&key);
|
||||
if(code)
|
||||
return code;
|
||||
code = mdb_value2entry(context, &value, kvno, &entry->entry);
|
||||
if(ret)
|
||||
return ret;
|
||||
ret = _hdb_mdb_value2entry(context, &value, kvno, &entry->entry);
|
||||
krb5_data_free(&value);
|
||||
if (code)
|
||||
return code;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
|
||||
code = hdb_unseal_keys (context, db, &entry->entry);
|
||||
if (code)
|
||||
ret = hdb_unseal_keys (context, db, &entry->entry);
|
||||
if (ret) {
|
||||
hdb_free_entry(context, entry);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -710,8 +767,48 @@ mdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
|
|||
static krb5_error_code
|
||||
mdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
|
||||
{
|
||||
krb5_set_error_message(context, EINVAL, "can't set principal in mdb");
|
||||
return EINVAL;
|
||||
krb5_error_code ret;
|
||||
krb5_storage *sp = NULL;
|
||||
krb5_storage *spent = NULL;
|
||||
krb5_data line = { 0, 0 };
|
||||
krb5_data kdb_ent = { 0, 0 };
|
||||
krb5_data key = { 0, 0 };
|
||||
ssize_t sz;
|
||||
|
||||
sp = krb5_storage_emem();
|
||||
if (!sp) return ENOMEM;
|
||||
ret = _hdb_set_master_key_usage(context, db, 0); /* MIT KDB uses KU 0 */
|
||||
ret = hdb_seal_keys(context, db, &entry->entry);
|
||||
if (ret) return ret;
|
||||
ret = entry2mit_string_int(context, sp, &entry->entry);
|
||||
if (ret) goto out;
|
||||
sz = krb5_storage_write(sp, "\n", 2); /* NUL-terminate */
|
||||
ret = ENOMEM;
|
||||
if (sz == -1) goto out;
|
||||
ret = krb5_storage_to_data(sp, &line);
|
||||
if (ret) goto out;
|
||||
|
||||
ret = ENOMEM;
|
||||
spent = krb5_storage_emem();
|
||||
if (!spent) goto out;
|
||||
ret = _hdb_mit_dump2mitdb_entry(context, line.data, spent);
|
||||
if (ret) goto out;
|
||||
ret = krb5_storage_to_data(spent, &kdb_ent);
|
||||
if (ret) goto out;
|
||||
ret = mdb_principal2key(context, entry->entry.principal, &key);
|
||||
if (ret) goto out;
|
||||
ret = mdb__put(context, db, 1, key, kdb_ent);
|
||||
|
||||
out:
|
||||
if (sp)
|
||||
krb5_storage_free(sp);
|
||||
if (spent)
|
||||
krb5_storage_free(spent);
|
||||
krb5_data_free(&line);
|
||||
krb5_data_free(&kdb_ent);
|
||||
krb5_data_free(&key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
|
|
@ -729,25 +826,31 @@ static krb5_error_code
|
|||
mdb_open(krb5_context context, HDB *db, int flags, mode_t mode)
|
||||
{
|
||||
char *fn;
|
||||
char *actual_fn;
|
||||
krb5_error_code ret;
|
||||
struct stat st;
|
||||
|
||||
asprintf(&fn, "%s.db", db->hdb_name);
|
||||
if (fn == NULL) {
|
||||
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
|
||||
return ENOMEM;
|
||||
}
|
||||
db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
|
||||
free(fn);
|
||||
|
||||
if (stat(fn, &st) == 0)
|
||||
actual_fn = fn;
|
||||
else
|
||||
actual_fn = db->hdb_name;
|
||||
db->hdb_db = dbopen(actual_fn, flags, mode, DB_BTREE, NULL);
|
||||
if (db->hdb_db == NULL) {
|
||||
switch (errno) {
|
||||
#ifdef EFTYPE
|
||||
case EFTYPE:
|
||||
#endif
|
||||
case EINVAL:
|
||||
db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
|
||||
db->hdb_db = dbopen(actual_fn, flags, mode, DB_BTREE, NULL);
|
||||
}
|
||||
}
|
||||
free(fn);
|
||||
|
||||
/* try to open without .db extension */
|
||||
if(db->hdb_db == NULL && errno == ENOENT)
|
||||
|
|
@ -758,11 +861,16 @@ mdb_open(krb5_context context, HDB *db, int flags, mode_t mode)
|
|||
db->hdb_name, strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
if((flags & O_ACCMODE) == O_RDONLY)
|
||||
ret = hdb_check_db_format(context, db);
|
||||
else
|
||||
#if 0
|
||||
/*
|
||||
* Don't do this -- MIT won't be able to handle the
|
||||
* HDB_DB_FORMAT_ENTRY key.
|
||||
*/
|
||||
if ((flags & O_ACCMODE) != O_RDONLY)
|
||||
ret = hdb_init_db(context, db);
|
||||
if(ret == HDB_ERR_NOENTRY) {
|
||||
#endif
|
||||
ret = hdb_check_db_format(context, db);
|
||||
if (ret == HDB_ERR_NOENTRY) {
|
||||
krb5_clear_error_message(context);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -815,3 +923,280 @@ hdb_mdb_create(krb5_context context, HDB **db,
|
|||
}
|
||||
|
||||
#endif /* HAVE_DB1 */
|
||||
|
||||
/*
|
||||
can have any number of princ stanzas.
|
||||
format is as follows (only \n indicates newlines)
|
||||
princ\t%d\t (%d is KRB5_KDB_V1_BASE_LENGTH, always 38)
|
||||
%d\t (strlen of principal e.g. shadow/foo@ANDREW.CMU.EDU)
|
||||
%d\t (number of tl_data)
|
||||
%d\t (number of key data, e.g. how many keys for this user)
|
||||
%d\t (extra data length)
|
||||
%s\t (principal name)
|
||||
%d\t (attributes)
|
||||
%d\t (max lifetime, seconds)
|
||||
%d\t (max renewable life, seconds)
|
||||
%d\t (expiration, seconds since epoch or 2145830400 for never)
|
||||
%d\t (password expiration, seconds, 0 for never)
|
||||
%d\t (last successful auth, seconds since epoch)
|
||||
%d\t (last failed auth, per above)
|
||||
%d\t (failed auth count)
|
||||
foreach tl_data 0 to number of tl_data - 1 as above
|
||||
%d\t%d\t (data type, data length)
|
||||
foreach tl_data 0 to length-1
|
||||
%02x (tl data contents[element n])
|
||||
except if tl_data length is 0
|
||||
%d (always -1)
|
||||
\t
|
||||
foreach key 0 to number of keys - 1 as above
|
||||
%d\t%d\t (key data version, kvno)
|
||||
foreach version 0 to key data version - 1 (a key or a salt)
|
||||
%d\t%d\t(data type for this key, data length for this key)
|
||||
foreach key data length 0 to length-1
|
||||
%02x (key data contents[element n])
|
||||
except if key_data length is 0
|
||||
%d (always -1)
|
||||
\t
|
||||
foreach extra data length 0 to length - 1
|
||||
%02x (extra data part)
|
||||
unless no extra data
|
||||
%d (always -1)
|
||||
;\n
|
||||
|
||||
*/
|
||||
|
||||
static char *
|
||||
nexttoken(char **p)
|
||||
{
|
||||
char *q;
|
||||
do {
|
||||
q = strsep(p, " \t");
|
||||
} while(q && *q == '\0');
|
||||
return q;
|
||||
}
|
||||
|
||||
static size_t
|
||||
getdata(char **p, unsigned char *buf, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
int v;
|
||||
char *q = nexttoken(p);
|
||||
i = 0;
|
||||
while(*q && i < len) {
|
||||
if(sscanf(q, "%02x", &v) != 1)
|
||||
break;
|
||||
buf[i++] = v;
|
||||
q += 2;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static int
|
||||
getint(char **p)
|
||||
{
|
||||
int val;
|
||||
char *q = nexttoken(p);
|
||||
sscanf(q, "%d", &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
getuint(char **p)
|
||||
{
|
||||
int val;
|
||||
char *q = nexttoken(p);
|
||||
sscanf(q, "%u", &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
#define KRB5_KDB_SALTTYPE_NORMAL 0
|
||||
#define KRB5_KDB_SALTTYPE_V4 1
|
||||
#define KRB5_KDB_SALTTYPE_NOREALM 2
|
||||
#define KRB5_KDB_SALTTYPE_ONLYREALM 3
|
||||
#define KRB5_KDB_SALTTYPE_SPECIAL 4
|
||||
#define KRB5_KDB_SALTTYPE_AFS3 5
|
||||
|
||||
#define CHECK_UINT(num) \
|
||||
if ((num) < 0 || (num) > INT_MAX) return EINVAL
|
||||
#define CHECK_UINT16(num) \
|
||||
if ((num) < 0 || (num) > 1<<15) return EINVAL
|
||||
#define CHECK_NUM(num, maxv) \
|
||||
if ((num) > (maxv)) return EINVAL
|
||||
|
||||
/*
|
||||
* This utility function converts an MIT dump entry to an MIT on-disk
|
||||
* encoded entry, which can then be decoded with _hdb_mdb_value2entry().
|
||||
* This allows us to have a single decoding function (_hdb_mdb_value2entry),
|
||||
* which makes the code cleaner (less code duplication), if a bit less
|
||||
* efficient. It also will allow us to have a function to dump an HDB
|
||||
* entry in MIT format so we can dump HDB into MIT format for rollback
|
||||
* purposes. And that will allow us to write to MIT KDBs, again
|
||||
* somewhat inefficiently, also for migration/rollback purposes.
|
||||
*/
|
||||
int
|
||||
_hdb_mit_dump2mitdb_entry(krb5_context context, char *line, krb5_storage *sp)
|
||||
{
|
||||
krb5_error_code ret = EINVAL;
|
||||
char *p = line, *q;
|
||||
char *princ;
|
||||
ssize_t sz;
|
||||
size_t i;
|
||||
size_t princ_len;
|
||||
unsigned int num_tl_data;
|
||||
size_t num_key_data;
|
||||
unsigned int attributes;
|
||||
int tmp;
|
||||
|
||||
krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
|
||||
|
||||
q = nexttoken(&p);
|
||||
if (strcmp(q, "kdb5_util") == 0 || strcmp(q, "policy") == 0 ||
|
||||
strcmp(q, "princ") != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (getint(&p) != 38)
|
||||
return EINVAL;
|
||||
#define KDB_V1_BASE_LENGTH 38
|
||||
ret = krb5_store_int16(sp, KDB_V1_BASE_LENGTH);
|
||||
if (ret) return ret;
|
||||
|
||||
nexttoken(&p); /* length of principal */
|
||||
num_tl_data = getuint(&p); /* number of tl-data */
|
||||
num_key_data = getuint(&p); /* number of key-data */
|
||||
getint(&p); /* length of extra data */
|
||||
princ = nexttoken(&p); /* principal name */
|
||||
|
||||
attributes = getuint(&p); /* attributes */
|
||||
ret = krb5_store_uint32(sp, attributes);
|
||||
if (ret) return ret;
|
||||
|
||||
tmp = getint(&p); /* max life */
|
||||
CHECK_UINT(tmp);
|
||||
ret = krb5_store_uint32(sp, tmp);
|
||||
if (ret) return ret;
|
||||
|
||||
tmp = getint(&p); /* max renewable life */
|
||||
CHECK_UINT(tmp);
|
||||
ret = krb5_store_uint32(sp, tmp);
|
||||
if (ret) return ret;
|
||||
|
||||
tmp = getint(&p); /* expiration */
|
||||
CHECK_UINT(tmp);
|
||||
ret = krb5_store_uint32(sp, tmp);
|
||||
if (ret) return ret;
|
||||
|
||||
tmp = getint(&p); /* pw expiration */
|
||||
CHECK_UINT(tmp);
|
||||
ret = krb5_store_uint32(sp, tmp);
|
||||
if (ret) return ret;
|
||||
|
||||
tmp = getint(&p); /* last auth */
|
||||
CHECK_UINT(tmp);
|
||||
ret = krb5_store_uint32(sp, tmp);
|
||||
if (ret) return ret;
|
||||
|
||||
tmp = getint(&p); /* last failed auth */
|
||||
CHECK_UINT(tmp);
|
||||
ret = krb5_store_uint32(sp, tmp);
|
||||
if (ret) return ret;
|
||||
|
||||
tmp = getint(&p); /* fail auth count */
|
||||
CHECK_UINT(tmp);
|
||||
ret = krb5_store_uint32(sp, tmp);
|
||||
if (ret) return ret;
|
||||
|
||||
/* add TL data count */
|
||||
CHECK_NUM(num_tl_data, 1023);
|
||||
ret = krb5_store_uint16(sp, num_tl_data);
|
||||
if (ret) return ret;
|
||||
|
||||
/* add key count */
|
||||
CHECK_NUM(num_key_data, 1023);
|
||||
ret = krb5_store_uint16(sp, num_key_data);
|
||||
if (ret) return ret;
|
||||
|
||||
/* add principal unparsed name length and unparsed name */
|
||||
princ_len = strlen(princ);
|
||||
if (princ_len > (1<<15) - 1) return EINVAL;
|
||||
princ_len++; /* must count and write the NUL in the on-disk encoding */
|
||||
ret = krb5_store_uint16(sp, princ_len);
|
||||
if (ret) return ret;
|
||||
sz = krb5_storage_write(sp, princ, princ_len);
|
||||
if (sz == -1) return ENOMEM;
|
||||
|
||||
/* scan and write TL data */
|
||||
for (i = 0; i < num_tl_data; i++) {
|
||||
int tl_type, tl_length;
|
||||
unsigned char *buf;
|
||||
|
||||
tl_type = getint(&p); /* data type */
|
||||
tl_length = getint(&p); /* data length */
|
||||
|
||||
CHECK_UINT16(tl_type);
|
||||
ret = krb5_store_uint16(sp, tl_type);
|
||||
if (ret) return ret;
|
||||
CHECK_UINT16(tl_length);
|
||||
ret = krb5_store_uint16(sp, tl_length);
|
||||
if (ret) return ret;
|
||||
|
||||
if (tl_length) {
|
||||
buf = malloc(tl_length);
|
||||
if (!buf) return ENOMEM;
|
||||
if (getdata(&p, buf, tl_length) != tl_length) return EINVAL;
|
||||
sz = krb5_storage_write(sp, buf, tl_length);
|
||||
free(buf);
|
||||
if (sz == -1) return ENOMEM;
|
||||
} else {
|
||||
if (strcmp(nexttoken(&p), "-1") != 0) return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < num_key_data; i++) {
|
||||
unsigned char *buf;
|
||||
int key_versions;
|
||||
int kvno;
|
||||
int keytype;
|
||||
int keylen;
|
||||
size_t k;
|
||||
|
||||
key_versions = getint(&p); /* key data version */
|
||||
CHECK_UINT16(key_versions);
|
||||
ret = krb5_store_int16(sp, key_versions);
|
||||
if (ret) return ret;
|
||||
|
||||
kvno = getint(&p);
|
||||
CHECK_UINT16(kvno);
|
||||
ret = krb5_store_int16(sp, kvno);
|
||||
if (ret) return ret;
|
||||
|
||||
for (k = 0; k < key_versions; k++) {
|
||||
keytype = getint(&p);
|
||||
CHECK_UINT16(keytype);
|
||||
ret = krb5_store_int16(sp, keytype);
|
||||
if (ret) return ret;
|
||||
|
||||
keylen = getint(&p);
|
||||
CHECK_UINT16(keylen);
|
||||
ret = krb5_store_int16(sp, keylen);
|
||||
if (ret) return ret;
|
||||
|
||||
if (keylen) {
|
||||
buf = malloc(keylen);
|
||||
if (!buf) return ENOMEM;
|
||||
if (getdata(&p, buf, keylen) != keylen) return EINVAL;
|
||||
sz = krb5_storage_write(sp, buf, keylen);
|
||||
free(buf);
|
||||
if (sz == -1) return ENOMEM;
|
||||
} else {
|
||||
if (strcmp(nexttoken(&p), "-1") != 0) return EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The rest is "extra data", but there's never any and we wouldn't
|
||||
* know what to do with it.
|
||||
*/
|
||||
/* nexttoken(&p); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,17 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
krb5_error_code
|
||||
entry2mit_string_int (
|
||||
krb5_context /*context*/,
|
||||
krb5_storage */*sp*/,
|
||||
hdb_entry */*ent*/);
|
||||
|
||||
krb5_error_code
|
||||
hdb_add_current_keys_to_history (
|
||||
krb5_context /*context*/,
|
||||
hdb_entry */*entry*/);
|
||||
|
||||
krb5_error_code
|
||||
hdb_add_master_key (
|
||||
krb5_context /*context*/,
|
||||
|
|
@ -346,6 +357,13 @@ hdb_seal_keys_mkey (
|
|||
hdb_entry */*ent*/,
|
||||
hdb_master_key /*mkey*/);
|
||||
|
||||
krb5_error_code
|
||||
hdb_set_last_modified_by (
|
||||
krb5_context /*context*/,
|
||||
hdb_entry */*entry*/,
|
||||
krb5_principal /*modby*/,
|
||||
time_t /*modtime*/);
|
||||
|
||||
krb5_error_code
|
||||
hdb_set_master_key (
|
||||
krb5_context /*context*/,
|
||||
|
|
@ -385,6 +403,13 @@ hdb_unseal_keys (
|
|||
HDB */*db*/,
|
||||
hdb_entry */*ent*/);
|
||||
|
||||
krb5_error_code
|
||||
hdb_unseal_keys_kvno (
|
||||
krb5_context /*context*/,
|
||||
HDB */*db*/,
|
||||
krb5_kvno /*kvno*/,
|
||||
hdb_entry */*ent*/);
|
||||
|
||||
krb5_error_code
|
||||
hdb_unseal_keys_mkey (
|
||||
krb5_context /*context*/,
|
||||
|
|
|
|||
|
|
@ -87,6 +87,14 @@ HDB-Ext-Aliases ::= SEQUENCE {
|
|||
aliases[1] SEQUENCE OF Principal -- all names, inc primary
|
||||
}
|
||||
|
||||
hdb_keyset ::= SEQUENCE {
|
||||
kvno[0] INTEGER (0..4294967295),
|
||||
replace-time[1] KerberosTime, -- time this key was replaced
|
||||
keys[2] SEQUENCE OF Key
|
||||
}
|
||||
|
||||
HDB-Ext-KeySet ::= SEQUENCE OF hdb_keyset
|
||||
|
||||
|
||||
HDB-extension ::= SEQUENCE {
|
||||
mandatory[0] BOOLEAN, -- kdc MUST understand this extension,
|
||||
|
|
@ -102,6 +110,7 @@ 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,
|
||||
...
|
||||
},
|
||||
...
|
||||
|
|
@ -109,11 +118,6 @@ 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
|
||||
|
|
|
|||
|
|
@ -168,13 +168,14 @@ hdb_unlock(int fd)
|
|||
void
|
||||
hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
|
||||
{
|
||||
size_t i;
|
||||
Key *k;
|
||||
int 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ typedef struct hdb_entry_ex {
|
|||
* query the backend database when talking about principals.
|
||||
*/
|
||||
|
||||
typedef struct HDB{
|
||||
typedef struct HDB {
|
||||
void *hdb_db;
|
||||
void *hdb_dbc; /** don't use, only for DB3 */
|
||||
char *hdb_name;
|
||||
|
|
@ -256,6 +256,8 @@ typedef struct HDB{
|
|||
* Check if s4u2self is allowed from this client to this server
|
||||
*/
|
||||
krb5_error_code (*hdb_check_s4u2self)(krb5_context, struct HDB *, hdb_entry_ex *, krb5_const_principal);
|
||||
int hdb_mit_key_set;
|
||||
hdb_master_key hdb_mit_key;
|
||||
}HDB;
|
||||
|
||||
#define HDB_INTERFACE_VERSION 7
|
||||
|
|
@ -266,6 +268,17 @@ struct hdb_so_method {
|
|||
krb5_error_code (*create)(krb5_context, HDB **, const char *filename);
|
||||
};
|
||||
|
||||
/* dump entry format, for hdb_print_entry() */
|
||||
typedef enum hdb_dump_format {
|
||||
HDB_DUMP_HEIMDAL = 0,
|
||||
HDB_DUMP_MIT = 1,
|
||||
} hdb_dump_format_t;
|
||||
|
||||
struct hdb_print_entry_arg {
|
||||
FILE *out;
|
||||
hdb_dump_format_t fmt;
|
||||
};
|
||||
|
||||
typedef krb5_error_code (*hdb_foreach_func_t)(krb5_context, HDB*,
|
||||
hdb_entry_ex*, void*);
|
||||
extern krb5_kt_ops hdb_kt_ops;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@
|
|||
|
||||
#include <config.h>
|
||||
|
||||
#include <heimbase.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -67,4 +69,9 @@
|
|||
#define HDB_DEFAULT_DB HDB_DB_DIR "/heimdal"
|
||||
#define HDB_DB_FORMAT_ENTRY "hdb/db-format"
|
||||
|
||||
/* Test for strong key etypes accepted by MIT's KDC. */
|
||||
#define mit_strong_etype(t) \
|
||||
((t) == ETYPE_AES128_CTS_HMAC_SHA1_96 || \
|
||||
(t) == ETYPE_AES256_CTS_HMAC_SHA1_96)
|
||||
|
||||
#endif /* __HDB_LOCL_H__ */
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
*/
|
||||
|
||||
void
|
||||
hdb_free_keys (krb5_context context, int len, Key *keys)
|
||||
hdb_free_keys(krb5_context context, int len, Key *keys)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
|
@ -56,6 +56,19 @@ hdb_free_keys (krb5_context context, int len, Key *keys)
|
|||
free (keys);
|
||||
}
|
||||
|
||||
void
|
||||
hdb_free_keysets(krb5_context context, int len, hdb_keyset *keysets)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
hdb_free_keys(context, keysets[i].keys.len, keysets[i].keys.val);
|
||||
keysets[i].keys.val = NULL;
|
||||
keysets[i].keys.len = 0;
|
||||
}
|
||||
free (keysets);
|
||||
}
|
||||
|
||||
/*
|
||||
* for each entry in `default_keys' try to parse it as a sequence
|
||||
* of etype:salttype:salt, syntax of this if something like:
|
||||
|
|
@ -196,6 +209,60 @@ parse_key_set(krb5_context context, const char *key,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
int add = 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 {
|
||||
add = 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.val = entry->keys.val;
|
||||
hist_keys->val[0].keys.len = entry->keys.len;
|
||||
hist_keys->val[0].kvno = entry->kvno;
|
||||
hist_keys->val[0].replace_time = time(NULL);
|
||||
|
||||
if (add) {
|
||||
ret = hdb_replace_extension(context, entry, ext);
|
||||
if (ret) {
|
||||
free_HDB_extension(ext);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* hdb_replace_extension() copies ext, so we have to free it */
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
*/
|
||||
|
||||
#include "hdb_locl.h"
|
||||
#include <assert.h>
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
|
@ -40,6 +41,7 @@ struct hdb_master_key_data {
|
|||
krb5_keytab_entry keytab;
|
||||
krb5_crypto crypto;
|
||||
struct hdb_master_key_data *next;
|
||||
unsigned int key_usage;
|
||||
};
|
||||
|
||||
void
|
||||
|
|
@ -68,6 +70,7 @@ hdb_process_master_key(krb5_context context,
|
|||
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
|
||||
return ENOMEM;
|
||||
}
|
||||
(*mkey)->key_usage = HDB_KU_MKEY;
|
||||
(*mkey)->keytab.vno = kvno;
|
||||
ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal);
|
||||
if(ret)
|
||||
|
|
@ -362,6 +365,15 @@ hdb_write_master_key(krb5_context context, const char *filename,
|
|||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_hdb_set_master_key_usage(krb5_context context, HDB *db, unsigned int key_usage)
|
||||
{
|
||||
if (db->hdb_master_key_set == 0)
|
||||
return HDB_ERR_NO_MKEY;
|
||||
db->hdb_master_key->key_usage = key_usage;
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdb_master_key
|
||||
_hdb_find_master_key(uint32_t *mkvno, hdb_master_key mkey)
|
||||
{
|
||||
|
|
@ -403,15 +415,20 @@ _hdb_mkey_encrypt(krb5_context context, hdb_master_key key,
|
|||
ptr, size, res);
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
|
||||
/*
|
||||
* Unseal and optionally reseal the key in the MIT KDC master key.
|
||||
* If mit_key != NULL, the key is sealed using this key.
|
||||
*/
|
||||
static krb5_error_code
|
||||
_hdb_reseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey,
|
||||
hdb_master_key mit_key)
|
||||
{
|
||||
|
||||
krb5_error_code ret;
|
||||
krb5_data res;
|
||||
krb5_data mitres, res;
|
||||
size_t keysize;
|
||||
|
||||
hdb_master_key key;
|
||||
hdb_master_key key, mitkey;
|
||||
|
||||
if(k->mkvno == NULL)
|
||||
return 0;
|
||||
|
|
@ -428,9 +445,9 @@ hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
|
|||
if(ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
|
||||
/* try to decrypt with MIT key usage */
|
||||
ret = _hdb_mkey_decrypt(context, key, 0,
|
||||
k->key.keyvalue.data,
|
||||
k->key.keyvalue.length,
|
||||
&res);
|
||||
k->key.keyvalue.data,
|
||||
k->key.keyvalue.length,
|
||||
&res);
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -446,45 +463,196 @@ hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
|
|||
return KRB5_BAD_KEYSIZE;
|
||||
}
|
||||
|
||||
memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
|
||||
free(k->key.keyvalue.data);
|
||||
k->key.keyvalue = res;
|
||||
k->key.keyvalue.length = keysize;
|
||||
free(k->mkvno);
|
||||
k->mkvno = NULL;
|
||||
/* For mit_key != NULL, re-encrypt the key using the mitkey. */
|
||||
if (mit_key != NULL) {
|
||||
mitkey = _hdb_find_master_key(NULL, mit_key);
|
||||
if (mitkey == NULL) {
|
||||
krb5_data_free(&res);
|
||||
return HDB_ERR_NO_MKEY;
|
||||
}
|
||||
|
||||
ret = _hdb_mkey_encrypt(context, mitkey, 0,
|
||||
res.data,
|
||||
keysize,
|
||||
&mitres);
|
||||
krb5_data_free(&res);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_data_free(&k->key.keyvalue);
|
||||
if (mit_key == NULL) {
|
||||
k->key.keyvalue = res;
|
||||
k->key.keyvalue.length = keysize;
|
||||
free(k->mkvno);
|
||||
k->mkvno = NULL;
|
||||
} else {
|
||||
k->key.keyvalue = mitres;
|
||||
*k->mkvno = mitkey->keytab.vno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
|
||||
hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
|
||||
{
|
||||
|
||||
krb5_error_code ret;
|
||||
|
||||
ret = _hdb_reseal_key_mkey(context, k, mkey, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
_hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey,
|
||||
hdb_master_key mitkey)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
size_t i;
|
||||
int got_one = 0;
|
||||
|
||||
for(i = 0; i < ent->keys.len; i++){
|
||||
krb5_error_code ret;
|
||||
if (mitkey == NULL || mit_strong_etype(ent->keys.val[i].key.keytype)) {
|
||||
ret = _hdb_reseal_key_mkey(context, &ent->keys.val[i], mkey,
|
||||
mitkey);
|
||||
if (ret)
|
||||
return ret;
|
||||
got_one = 1;
|
||||
}
|
||||
}
|
||||
|
||||
ret = hdb_unseal_key_mkey(context, &ent->keys.val[i], mkey);
|
||||
/*
|
||||
* If none of the keys were string enough, create a strong key,
|
||||
* but one that is not encrypted in the MIT master key. As such,
|
||||
* it will require a "change_password" once in the MIT KDC to
|
||||
* make it work.
|
||||
*/
|
||||
if (got_one == 0 && mitkey != NULL && ent->keys.len > 0) {
|
||||
krb5_keyblock key;
|
||||
krb5_salt salt;
|
||||
|
||||
krb5_free_keyblock_contents(context, &ent->keys.val[0].key);
|
||||
salt.salttype = KRB5_PW_SALT;
|
||||
salt.saltvalue.data = NULL;
|
||||
salt.saltvalue.length = 0;
|
||||
ret = krb5_string_to_key_salt(context, ETYPE_AES256_CTS_HMAC_SHA1_96,
|
||||
"XXXX", salt, &ent->keys.val[0].key);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
ret = _hdb_unseal_keys_mkey(context, ent, mkey, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
|
||||
{
|
||||
if (db->hdb_master_key_set == 0)
|
||||
return 0;
|
||||
return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
|
||||
if (db->hdb_mit_key_set != 0)
|
||||
return _hdb_unseal_keys_mkey(context, ent, db->hdb_master_key,
|
||||
db->hdb_mit_key);
|
||||
else
|
||||
return _hdb_unseal_keys_mkey(context, ent, db->hdb_master_key,
|
||||
NULL);
|
||||
}
|
||||
|
||||
#ifdef notnow
|
||||
krb5_error_code
|
||||
hdb_unseal_keys_kvno(krb5_context context, HDB *db, krb5_kvno kvno,
|
||||
hdb_entry *ent)
|
||||
{
|
||||
krb5_error_code ret = KRB5KRB_AP_ERR_NOKEY; /* XXX need a better code? */
|
||||
HDB_extension *tmp;
|
||||
HDB_Ext_KeySet *hist_keys;
|
||||
hdb_keyset *tmp_keys;
|
||||
Key *tmp_val;
|
||||
unsigned int tmp_len;
|
||||
krb5_kvno tmp_kvno;
|
||||
int i, k;
|
||||
|
||||
assert(kvno == 0 || kvno < ent->kvno);
|
||||
|
||||
tmp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
|
||||
if (tmp == NULL)
|
||||
return ret;
|
||||
|
||||
tmp_len = ent->keys.len;
|
||||
tmp_val = ent->keys.val;
|
||||
tmp_kvno = ent->kvno;
|
||||
|
||||
hist_keys = &tmp->data.u.hist_keys;
|
||||
|
||||
for (i = hist_keys->len - 1; i >= 0; i++) {
|
||||
if (kvno != 0 && hist_keys->val[i].kvno != kvno)
|
||||
continue;
|
||||
for (k = 0; k < hist_keys->val[i].keys.len; k++) {
|
||||
ret = _hdb_reseal_key_mkey(context,
|
||||
&hist_keys->val[i].keys.val[k],
|
||||
db->hdb_master_key, NULL);
|
||||
if (ret)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
if (kvno == 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* NOTE: What follows is a bit of an ugly hack.
|
||||
*
|
||||
* This is the keyset we're being asked for, 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.
|
||||
*/
|
||||
tmp_keys = realloc(hist_keys->val,
|
||||
sizeof (*hist_keys->val) * (hist_keys->len + 1));
|
||||
if (tmp_keys == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
memmove(&tmp_keys[1], tmp_keys,
|
||||
sizeof (*hist_keys->val) * hist_keys->len++);
|
||||
tmp_keys[0].keys.len = ent->keys.len;
|
||||
tmp_keys[0].keys.val = ent->keys.val;
|
||||
tmp_keys[0].kvno = ent->kvno;
|
||||
tmp_keys[0].replace_time = time(NULL);
|
||||
i++;
|
||||
ent->keys.len = hist_keys->val[i].keys.len;
|
||||
ent->keys.val = hist_keys->val[i].keys.val;
|
||||
ent->kvno = kvno;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
krb5_error_code
|
||||
hdb_unseal_key(krb5_context context, HDB *db, Key *k)
|
||||
{
|
||||
if (db->hdb_master_key_set == 0)
|
||||
return 0;
|
||||
return hdb_unseal_key_mkey(context, k, db->hdb_master_key);
|
||||
return _hdb_reseal_key_mkey(context, k, db->hdb_master_key, NULL);
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
|
|
@ -556,9 +724,9 @@ hdb_seal_key(krb5_context context, HDB *db, Key *k)
|
|||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_set_master_key (krb5_context context,
|
||||
HDB *db,
|
||||
krb5_keyblock *key)
|
||||
hdb_set_master_key(krb5_context context,
|
||||
HDB *db,
|
||||
krb5_keyblock *key)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
hdb_master_key mkey;
|
||||
|
|
@ -571,6 +739,7 @@ hdb_set_master_key (krb5_context context,
|
|||
des_set_random_generator_seed(key.keyvalue.data);
|
||||
#endif
|
||||
db->hdb_master_key_set = 1;
|
||||
db->hdb_master_key->key_usage = HDB_KU_MKEY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,44 +57,57 @@
|
|||
generation number
|
||||
*/
|
||||
|
||||
static krb5_error_code
|
||||
/*
|
||||
* These utility functions return the number of bytes written or -1, and
|
||||
* they set an error in the context.
|
||||
*/
|
||||
static ssize_t
|
||||
append_string(krb5_context context, krb5_storage *sp, const char *fmt, ...)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
ssize_t sz;
|
||||
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;
|
||||
return -1;
|
||||
}
|
||||
ret = krb5_storage_write(sp, s, strlen(s));
|
||||
sz = krb5_storage_write(sp, s, strlen(s));
|
||||
free(s);
|
||||
return ret;
|
||||
return sz;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
append_hex(krb5_context context, krb5_storage *sp, krb5_data *data)
|
||||
append_hex(krb5_context context, krb5_storage *sp,
|
||||
int always_encode, int lower, krb5_data *data)
|
||||
{
|
||||
ssize_t sz;
|
||||
int printable = 1;
|
||||
size_t i;
|
||||
char *p;
|
||||
|
||||
p = data->data;
|
||||
for(i = 0; i < data->length; i++)
|
||||
if(!isalnum((unsigned char)p[i]) && p[i] != '.'){
|
||||
printable = 0;
|
||||
break;
|
||||
}
|
||||
if(printable)
|
||||
if (!always_encode) {
|
||||
for (i = 0; i < data->length; i++) {
|
||||
if (!isalnum((unsigned char)p[i]) && p[i] != '.'){
|
||||
printable = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (printable && !always_encode)
|
||||
return append_string(context, sp, "\"%.*s\"",
|
||||
data->length, data->data);
|
||||
hex_encode(data->data, data->length, &p);
|
||||
append_string(context, sp, "%s", p);
|
||||
sz = hex_encode(data->data, data->length, &p);
|
||||
if (sz == -1) return sz;
|
||||
if (lower)
|
||||
strlwr(p);
|
||||
sz = append_string(context, sp, "%s", p);
|
||||
free(p);
|
||||
return 0;
|
||||
return sz;
|
||||
}
|
||||
|
||||
static char *
|
||||
|
|
@ -105,29 +118,97 @@ time2str(time_t t)
|
|||
return buf;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
static ssize_t
|
||||
append_event(krb5_context context, krb5_storage *sp, Event *ev)
|
||||
{
|
||||
char *pr = NULL;
|
||||
krb5_error_code ret;
|
||||
ssize_t sz;
|
||||
char *pr = NULL;
|
||||
if(ev == NULL)
|
||||
return append_string(context, sp, "- ");
|
||||
if (ev->principal != NULL) {
|
||||
ret = krb5_unparse_name(context, ev->principal, &pr);
|
||||
if(ret)
|
||||
return ret;
|
||||
if (ret) return -1; /* krb5_unparse_name() sets error info */
|
||||
}
|
||||
ret = append_string(context, sp, "%s:%s ",
|
||||
time2str(ev->time), pr ? pr : "UNKNOWN");
|
||||
sz = append_string(context, sp, "%s:%s ", time2str(ev->time),
|
||||
pr ? pr : "UNKNOWN");
|
||||
free(pr);
|
||||
return ret;
|
||||
return sz;
|
||||
}
|
||||
|
||||
#define KRB5_KDB_SALTTYPE_NORMAL 0
|
||||
#define KRB5_KDB_SALTTYPE_V4 1
|
||||
#define KRB5_KDB_SALTTYPE_NOREALM 2
|
||||
#define KRB5_KDB_SALTTYPE_ONLYREALM 3
|
||||
#define KRB5_KDB_SALTTYPE_SPECIAL 4
|
||||
#define KRB5_KDB_SALTTYPE_AFS3 5
|
||||
|
||||
static ssize_t
|
||||
append_mit_key(krb5_context context, krb5_storage *sp,
|
||||
krb5_const_principal princ,
|
||||
unsigned int kvno, Key *key)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
ssize_t sz;
|
||||
size_t key_versions = key->salt ? 2 : 1;
|
||||
size_t decrypted_key_length;
|
||||
char buf[2];
|
||||
krb5_data keylenbytes;
|
||||
unsigned int salttype;
|
||||
|
||||
sz = append_string(context, sp, "\t%u\t%u\t%d\t%d\t", key_versions, kvno,
|
||||
key->key.keytype, key->key.keyvalue.length + 2);
|
||||
if (sz == -1) return sz;
|
||||
ret = krb5_enctype_keysize(context, key->key.keytype, &decrypted_key_length);
|
||||
if (ret) return -1; /* XXX we lose the error code */
|
||||
buf[0] = decrypted_key_length & 0xff;
|
||||
buf[1] = (decrypted_key_length & 0xff00) >> 8;
|
||||
keylenbytes.data = buf;
|
||||
keylenbytes.length = sizeof (buf);
|
||||
sz = append_hex(context, sp, 1, 1, &keylenbytes);
|
||||
if (sz == -1) return sz;
|
||||
sz = append_hex(context, sp, 1, 1, &key->key.keyvalue);
|
||||
if (!key->salt)
|
||||
return sz;
|
||||
|
||||
/* Map salt to MIT KDB style */
|
||||
if (key->salt->type == KRB5_PADATA_PW_SALT) {
|
||||
krb5_salt k5salt;
|
||||
|
||||
/*
|
||||
* Compute normal salt and then see whether it matches the stored one
|
||||
*/
|
||||
ret = krb5_get_pw_salt(context, princ, &k5salt);
|
||||
if (ret) return -1;
|
||||
if (k5salt.saltvalue.length == key->salt->salt.length &&
|
||||
memcmp(k5salt.saltvalue.data, key->salt->salt.data,
|
||||
k5salt.saltvalue.length) == 0)
|
||||
salttype = KRB5_KDB_SALTTYPE_NORMAL; /* matches */
|
||||
else if (key->salt->salt.length == strlen(princ->realm) &&
|
||||
memcmp(key->salt->salt.data, princ->realm,
|
||||
key->salt->salt.length) == 0)
|
||||
salttype = KRB5_KDB_SALTTYPE_ONLYREALM; /* matches realm */
|
||||
else if (key->salt->salt.length == k5salt.saltvalue.length - strlen(princ->realm) &&
|
||||
memcmp((char *)k5salt.saltvalue.data + strlen(princ->realm),
|
||||
key->salt->salt.data, key->salt->salt.length) == 0)
|
||||
salttype = KRB5_KDB_SALTTYPE_NOREALM; /* matches w/o realm */
|
||||
else
|
||||
salttype = KRB5_KDB_SALTTYPE_NORMAL; /* hope for best */
|
||||
|
||||
} else if (key->salt->type == KRB5_PADATA_AFS3_SALT) {
|
||||
salttype = KRB5_KDB_SALTTYPE_AFS3;
|
||||
}
|
||||
sz = append_string(context, sp, "\t%u\t%u\t", salttype,
|
||||
key->salt->salt.length);
|
||||
if (sz == -1) return sz;
|
||||
return append_hex(context, sp, 1, 1, &key->salt->salt);
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent)
|
||||
{
|
||||
char *p;
|
||||
size_t i;
|
||||
int i;
|
||||
krb5_error_code ret;
|
||||
|
||||
/* --- principal */
|
||||
|
|
@ -149,12 +230,12 @@ entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent)
|
|||
append_string(context, sp, "::%d:",
|
||||
ent->keys.val[i].key.keytype);
|
||||
/* --- keydata */
|
||||
append_hex(context, sp, &ent->keys.val[i].key.keyvalue);
|
||||
append_hex(context, sp, 0, 0, &ent->keys.val[i].key.keyvalue);
|
||||
append_string(context, sp, ":");
|
||||
/* --- salt */
|
||||
if(ent->keys.val[i].salt){
|
||||
append_string(context, sp, "%u/", ent->keys.val[i].salt->type);
|
||||
append_hex(context, sp, &ent->keys.val[i].salt->salt);
|
||||
append_hex(context, sp, 0, 0, &ent->keys.val[i].salt->salt);
|
||||
}else
|
||||
append_string(context, sp, "-");
|
||||
}
|
||||
|
|
@ -234,25 +315,266 @@ entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent)
|
|||
} else
|
||||
append_string(context, sp, "-");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define KRB5_KDB_DISALLOW_POSTDATED 0x00000001
|
||||
#define KRB5_KDB_DISALLOW_FORWARDABLE 0x00000002
|
||||
#define KRB5_KDB_DISALLOW_TGT_BASED 0x00000004
|
||||
#define KRB5_KDB_DISALLOW_RENEWABLE 0x00000008
|
||||
#define KRB5_KDB_DISALLOW_PROXIABLE 0x00000010
|
||||
#define KRB5_KDB_DISALLOW_DUP_SKEY 0x00000020
|
||||
#define KRB5_KDB_DISALLOW_ALL_TIX 0x00000040
|
||||
#define KRB5_KDB_REQUIRES_PRE_AUTH 0x00000080
|
||||
#define KRB5_KDB_REQUIRES_HW_AUTH 0x00000100
|
||||
#define KRB5_KDB_REQUIRES_PWCHANGE 0x00000200
|
||||
#define KRB5_KDB_DISALLOW_SVR 0x00001000
|
||||
#define KRB5_KDB_PWCHANGE_SERVICE 0x00002000
|
||||
#define KRB5_KDB_SUPPORT_DESMD5 0x00004000
|
||||
#define KRB5_KDB_NEW_PRINC 0x00008000
|
||||
|
||||
static int
|
||||
flags_to_attr(HDBFlags flags)
|
||||
{
|
||||
int a = 0;
|
||||
|
||||
if (!flags.postdate)
|
||||
a |= KRB5_KDB_DISALLOW_POSTDATED;
|
||||
if (!flags.forwardable)
|
||||
a |= KRB5_KDB_DISALLOW_FORWARDABLE;
|
||||
if (flags.initial)
|
||||
a |= KRB5_KDB_DISALLOW_TGT_BASED;
|
||||
if (!flags.renewable)
|
||||
a |= KRB5_KDB_DISALLOW_RENEWABLE;
|
||||
if (!flags.proxiable)
|
||||
a |= KRB5_KDB_DISALLOW_PROXIABLE;
|
||||
if (flags.invalid)
|
||||
a |= KRB5_KDB_DISALLOW_ALL_TIX;
|
||||
if (flags.require_preauth)
|
||||
a |= KRB5_KDB_REQUIRES_PRE_AUTH;
|
||||
if (flags.require_hwauth)
|
||||
a |= KRB5_KDB_REQUIRES_HW_AUTH;
|
||||
if (!flags.server)
|
||||
a |= KRB5_KDB_DISALLOW_SVR;
|
||||
if (flags.change_pw)
|
||||
a |= KRB5_KDB_PWCHANGE_SERVICE;
|
||||
return a;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
entry2mit_string_int(krb5_context context, krb5_storage *sp, hdb_entry *ent)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
ssize_t sz;
|
||||
size_t i, k;
|
||||
size_t num_tl_data = 0;
|
||||
size_t num_key_data = 0;
|
||||
char *p;
|
||||
HDB_Ext_KeySet *hist_keys = NULL;
|
||||
HDB_extension *extp;
|
||||
time_t last_pw_chg = 0;
|
||||
time_t exp = 0;
|
||||
time_t pwexp = 0;
|
||||
unsigned int max_life = 0;
|
||||
unsigned int max_renew = 0;
|
||||
|
||||
/* Always create a modified_by entry. */
|
||||
num_tl_data++;
|
||||
|
||||
ret = hdb_entry_get_pw_change_time(ent, &last_pw_chg);
|
||||
if (ret) return ret;
|
||||
if (last_pw_chg)
|
||||
num_tl_data++;
|
||||
|
||||
extp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
|
||||
if (extp)
|
||||
hist_keys = &extp->data.u.hist_keys;
|
||||
|
||||
for (i = 0; i < ent->keys.len;i++) {
|
||||
if (!mit_strong_etype(ent->keys.val[i].key.keytype))
|
||||
continue;
|
||||
num_key_data++;
|
||||
}
|
||||
if (hist_keys) {
|
||||
for (i = 0; i < hist_keys->len; i++) {
|
||||
/*
|
||||
* MIT uses the highest kvno as the current kvno instead of
|
||||
* tracking kvno separately, so we can't dump keysets with kvno
|
||||
* higher than the entry's kvno.
|
||||
*/
|
||||
if (hist_keys->val[i].kvno >= ent->kvno)
|
||||
continue;
|
||||
for (k = 0; k < hist_keys->val[i].keys.len; k++) {
|
||||
if (ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD4 ||
|
||||
ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD5)
|
||||
continue;
|
||||
num_key_data++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = krb5_unparse_name(context, ent->principal, &p);
|
||||
if (ret) return ret;
|
||||
sz = append_string(context, sp, "princ\t38\t%u\t%u\t%u\t0\t%s\t%d",
|
||||
strlen(p), num_tl_data, num_key_data, p,
|
||||
flags_to_attr(ent->flags));
|
||||
if (sz == -1) {
|
||||
free(p);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (ent->max_life)
|
||||
max_life = *ent->max_life;
|
||||
if (ent->max_renew)
|
||||
max_renew = *ent->max_renew;
|
||||
if (ent->valid_end)
|
||||
exp = *ent->valid_end;
|
||||
if (ent->pw_end)
|
||||
pwexp = *ent->pw_end;
|
||||
|
||||
sz = append_string(context, sp, "\t%u\t%u\t%u\t%u\t0\t0\t0",
|
||||
max_life, max_renew, exp, pwexp);
|
||||
if (sz == -1) {
|
||||
free(p);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* Dump TL data we know: last pw chg and modified_by */
|
||||
#define mit_KRB5_TL_LAST_PWD_CHANGE 1
|
||||
#define mit_KRB5_TL_MOD_PRINC 2
|
||||
if (last_pw_chg) {
|
||||
krb5_data d;
|
||||
time_t val;
|
||||
unsigned char *ptr;
|
||||
|
||||
ptr = (unsigned char *)&last_pw_chg;
|
||||
val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
|
||||
d.data = &val;
|
||||
d.length = sizeof (last_pw_chg);
|
||||
sz = append_string(context, sp, "\t%u\t%u\t",
|
||||
mit_KRB5_TL_LAST_PWD_CHANGE, d.length);
|
||||
if (sz == -1) {
|
||||
free(p);
|
||||
return ENOMEM;
|
||||
}
|
||||
sz = append_hex(context, sp, 1, 1, &d);
|
||||
if (sz == -1) {
|
||||
free(p);
|
||||
return ENOMEM;
|
||||
}
|
||||
}
|
||||
if (ent->modified_by) {
|
||||
krb5_data d;
|
||||
unsigned int val;
|
||||
size_t plen;
|
||||
unsigned char *ptr;
|
||||
char *modby_p;
|
||||
|
||||
free(p);
|
||||
ptr = (unsigned char *)&ent->modified_by->time;
|
||||
val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
|
||||
d.data = &val;
|
||||
d.length = sizeof (ent->modified_by->time);
|
||||
ret = krb5_unparse_name(context, ent->modified_by->principal, &modby_p);
|
||||
if (ret) return ret;
|
||||
plen = strlen(modby_p);
|
||||
sz = append_string(context, sp, "\t%u\t%u\t",
|
||||
mit_KRB5_TL_MOD_PRINC,
|
||||
d.length + plen + 1 /* NULL counted */);
|
||||
if (sz == -1) {
|
||||
free(modby_p);
|
||||
return ENOMEM;
|
||||
}
|
||||
sz = append_hex(context, sp, 1, 1, &d);
|
||||
if (sz == -1) {
|
||||
free(modby_p);
|
||||
return ENOMEM;
|
||||
}
|
||||
d.data = modby_p;
|
||||
d.length = plen + 1;
|
||||
sz = append_hex(context, sp, 1, 1, &d);
|
||||
free(modby_p);
|
||||
if (sz == -1) return ENOMEM;
|
||||
} else {
|
||||
krb5_data d;
|
||||
unsigned int val;
|
||||
size_t plen;
|
||||
unsigned char *ptr;
|
||||
|
||||
/* Fake the entry to make MIT happy. */
|
||||
ptr = (unsigned char *)&last_pw_chg;
|
||||
val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
|
||||
d.data = &val;
|
||||
d.length = sizeof (last_pw_chg);
|
||||
plen = strlen(p);
|
||||
sz = append_string(context, sp, "\t%u\t%u\t",
|
||||
mit_KRB5_TL_MOD_PRINC,
|
||||
d.length + plen + 1 /* NULL counted */);
|
||||
if (sz == -1) {
|
||||
free(p);
|
||||
return ENOMEM;
|
||||
}
|
||||
sz = append_hex(context, sp, 1, 1, &d);
|
||||
if (sz == -1) {
|
||||
free(p);
|
||||
return ENOMEM;
|
||||
}
|
||||
d.data = p;
|
||||
d.length = plen + 1;
|
||||
sz = append_hex(context, sp, 1, 1, &d);
|
||||
free(p);
|
||||
if (sz == -1) return ENOMEM;
|
||||
}
|
||||
/*
|
||||
* Dump keys (remembering to not include any with kvno higher than
|
||||
* the entry's because MIT doesn't track entry kvno separately from
|
||||
* the entry's keys -- max kvno is it)
|
||||
*/
|
||||
for (i = 0; i < ent->keys.len; i++) {
|
||||
if (!mit_strong_etype(ent->keys.val[i].key.keytype))
|
||||
continue;
|
||||
sz = append_mit_key(context, sp, ent->principal, ent->kvno,
|
||||
&ent->keys.val[i]);
|
||||
if (sz == -1) return ENOMEM;
|
||||
}
|
||||
for (i = 0; hist_keys && i < ent->kvno; i++) {
|
||||
size_t m;
|
||||
|
||||
/* dump historical keys */
|
||||
for (k = 0; k < hist_keys->len; k++) {
|
||||
if (hist_keys->val[k].kvno != ent->kvno - i)
|
||||
continue;
|
||||
for (m = 0; m < hist_keys->val[k].keys.len; m++) {
|
||||
if (ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD4 ||
|
||||
ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD5)
|
||||
continue;
|
||||
sz = append_mit_key(context, sp, ent->principal,
|
||||
hist_keys->val[k].kvno,
|
||||
&hist_keys->val[k].keys.val[m]);
|
||||
if (sz == -1) return ENOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
sz = append_string(context, sp, "\t-1;"); /* "extra data" */
|
||||
if (sz == -1) return ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_entry2string (krb5_context context, hdb_entry *ent, char **str)
|
||||
hdb_entry2string(krb5_context context, hdb_entry *ent, char **str)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_data data;
|
||||
krb5_storage *sp;
|
||||
|
||||
sp = krb5_storage_emem();
|
||||
if(sp == NULL) {
|
||||
if (sp == NULL) {
|
||||
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = entry2string_int(context, sp, ent);
|
||||
if(ret) {
|
||||
if (ret) {
|
||||
krb5_storage_free(sp);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -267,22 +589,31 @@ hdb_entry2string (krb5_context context, hdb_entry *ent, char **str)
|
|||
/* print a hdb_entry to (FILE*)data; suitable for hdb_foreach */
|
||||
|
||||
krb5_error_code
|
||||
hdb_print_entry(krb5_context context, HDB *db, hdb_entry_ex *entry, void *data)
|
||||
hdb_print_entry(krb5_context context, HDB *db, hdb_entry_ex *entry,
|
||||
void *data)
|
||||
{
|
||||
struct hdb_print_entry_arg *parg = data;
|
||||
krb5_error_code ret;
|
||||
krb5_storage *sp;
|
||||
|
||||
FILE *f = data;
|
||||
|
||||
fflush(f);
|
||||
sp = krb5_storage_from_fd(fileno(f));
|
||||
if(sp == NULL) {
|
||||
fflush(parg->out);
|
||||
sp = krb5_storage_from_fd(fileno(parg->out));
|
||||
if (sp == NULL) {
|
||||
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = entry2string_int(context, sp, &entry->entry);
|
||||
if(ret) {
|
||||
switch (parg->fmt) {
|
||||
case HDB_DUMP_HEIMDAL:
|
||||
ret = entry2string_int(context, sp, &entry->entry);
|
||||
break;
|
||||
case HDB_DUMP_MIT:
|
||||
ret = entry2mit_string_int(context, sp, &entry->entry);
|
||||
break;
|
||||
default:
|
||||
heim_abort("Only two dump formats supported: Heimdal and MIT");
|
||||
}
|
||||
if (ret) {
|
||||
krb5_storage_free(sp);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -57,6 +58,7 @@ HEIMDAL_HDB_1.0 {
|
|||
hdb_seal_key_mkey;
|
||||
hdb_seal_keys;
|
||||
hdb_seal_keys_mkey;
|
||||
hdb_set_last_modified_by;
|
||||
hdb_set_master_key;
|
||||
hdb_set_master_keyfile;
|
||||
hdb_unlock;
|
||||
|
|
@ -71,6 +73,10 @@ HEIMDAL_HDB_1.0 {
|
|||
hdb_interface_version;
|
||||
initialize_hdb_error_table_r;
|
||||
|
||||
# MIT KDB related entries
|
||||
_hdb_mdb_value2entry;
|
||||
_hdb_mit_dump2mitdb_entry;
|
||||
|
||||
hdb_kt_ops;
|
||||
|
||||
# some random bits needed for libkadm
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ change(void *server_handle,
|
|||
if(ret)
|
||||
goto out;
|
||||
|
||||
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);
|
||||
|
|
@ -170,6 +174,9 @@ kadm5_s_chpass_principal_with_key(void *server_handle,
|
|||
HDB_F_GET_ANY|HDB_F_ADMIN_DATA, &ent);
|
||||
if(ret)
|
||||
goto out;
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -59,6 +59,10 @@ kadm5_s_randkey_principal(void *server_handle,
|
|||
if(ret)
|
||||
goto out;
|
||||
|
||||
ret = hdb_add_current_keys_to_history(context->context, &ent.entry);
|
||||
if (ret)
|
||||
goto out2;
|
||||
|
||||
ret = _kadm5_set_keys_randomly (context,
|
||||
&ent.entry,
|
||||
new_keys,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
PACKAGE= kerberos-lib
|
||||
|
||||
LIB= hdb
|
||||
LDFLAGS+= -Wl,--no-undefined ${LDAPLDFLAGS}
|
||||
VERSION_MAP= ${KRB5DIR}/lib/hdb/version-script.map
|
||||
LIBADD= asn1 com_err krb5 roken sqlite3
|
||||
LIBADD= asn1 com_err krb5 roken sqlite3 heimbase
|
||||
LDADD= ${LDAPLDADD}
|
||||
DPADD= ${LDAPDPADD}
|
||||
|
||||
|
|
@ -74,6 +76,7 @@ GEN= asn1_Salt.x \
|
|||
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 \
|
||||
|
|
|
|||
|
|
@ -425,7 +425,7 @@ _DP_kadm5clnt= com_err krb5 roken
|
|||
_DP_kadm5srv= com_err hdb krb5 roken
|
||||
_DP_heimntlm= crypto com_err krb5 roken
|
||||
_DP_hx509= asn1 com_err crypto roken wind
|
||||
_DP_hdb= asn1 com_err krb5 roken sqlite3
|
||||
_DP_hdb= asn1 com_err krb5 roken sqlite3 heimbase
|
||||
_DP_asn1= com_err roken
|
||||
_DP_kdc= roken hdb hx509 krb5 heimntlm asn1 crypto
|
||||
_DP_wind= com_err roken
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue