Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 5 Oct 2025 16:22:15 GMT
From:      Rick Macklem <rmacklem@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 5000d023a446 - main - heimdal-kadmin: Add support for the -f dump option
Message-ID:  <202510051622.595GMFbu032657@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=5000d023a446b81f6d45ed59aa379607ec814f01

commit 5000d023a446b81f6d45ed59aa379607ec814f01
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2025-10-05 16:18:35 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2025-10-05 16:18:35 +0000

    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
---
 crypto/heimdal/kadmin/dump.c              |  29 +-
 crypto/heimdal/kadmin/kadmin-commands.in  |   6 +
 crypto/heimdal/kdc/hpropd.c               |  68 +++--
 crypto/heimdal/kdc/mit_dump.c             | 383 ++++++------------------
 crypto/heimdal/lib/hdb/Makefile.am        |   1 +
 crypto/heimdal/lib/hdb/common.c           |  68 +++--
 crypto/heimdal/lib/hdb/ext.c              |  31 ++
 crypto/heimdal/lib/hdb/hdb-mitdb.c        | 477 +++++++++++++++++++++++++++---
 crypto/heimdal/lib/hdb/hdb-protos.h       |  25 ++
 crypto/heimdal/lib/hdb/hdb.asn1           |  14 +-
 crypto/heimdal/lib/hdb/hdb.c              |   7 +-
 crypto/heimdal/lib/hdb/hdb.h              |  15 +-
 crypto/heimdal/lib/hdb/hdb_locl.h         |   7 +
 crypto/heimdal/lib/hdb/keys.c             |  69 ++++-
 crypto/heimdal/lib/hdb/mkey.c             | 211 +++++++++++--
 crypto/heimdal/lib/hdb/print.c            | 407 ++++++++++++++++++++++---
 crypto/heimdal/lib/hdb/version-script.map |   6 +
 crypto/heimdal/lib/kadm5/chpass_s.c       |   7 +
 crypto/heimdal/lib/kadm5/randkey_s.c      |   4 +
 kerberos5/lib/libhdb/Makefile             |   5 +-
 share/mk/src.libnames.mk                  |   2 +-
 21 files changed, 1374 insertions(+), 468 deletions(-)

diff --git a/crypto/heimdal/kadmin/dump.c b/crypto/heimdal/kadmin/dump.c
index 91a5ada86607..2c302eced210 100644
--- a/crypto/heimdal/kadmin/dump.c
+++ b/crypto/heimdal/kadmin/dump.c
@@ -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:
diff --git a/crypto/heimdal/kadmin/kadmin-commands.in b/crypto/heimdal/kadmin/kadmin-commands.in
index 4396ff800441..dc36db4e0a00 100644
--- a/crypto/heimdal/kadmin/kadmin-commands.in
+++ b/crypto/heimdal/kadmin/kadmin-commands.in
@@ -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"
diff --git a/crypto/heimdal/kdc/hpropd.c b/crypto/heimdal/kdc/hpropd.c
index 1cfc688b2a6c..c76be0446705 100644
--- a/crypto/heimdal/kdc/hpropd.c
+++ b/crypto/heimdal/kdc/hpropd.c
@@ -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++;
diff --git a/crypto/heimdal/kdc/mit_dump.c b/crypto/heimdal/kdc/mit_dump.c
index f28e932b15b4..4397d1ad897d 100644
--- a/crypto/heimdal/kdc/mit_dump.c
+++ b/crypto/heimdal/kdc/mit_dump.c
@@ -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)
-{
-    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 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)
+static int
+my_fgetln(FILE *f, char **buf, size_t *sz, size_t *len)
 {
-    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;
+    char *p, *n;
+
+    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;
-
-	int i;
-
-	int num_tl_data;
-	int num_key_data;
-	int high_kvno;
-	int attributes;
+    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 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;
 }
+
diff --git a/crypto/heimdal/lib/hdb/Makefile.am b/crypto/heimdal/lib/hdb/Makefile.am
index b629f56258d2..fd009bd26867 100644
--- a/crypto/heimdal/lib/hdb/Makefile.am
+++ b/crypto/heimdal/lib/hdb/Makefile.am
@@ -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 \
diff --git a/crypto/heimdal/lib/hdb/common.c b/crypto/heimdal/lib/hdb/common.c
index 2715adf63dca..80482e7a4c1c 100644
--- a/crypto/heimdal/lib/hdb/common.c
+++ b/crypto/heimdal/lib/hdb/common.c
@@ -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
diff --git a/crypto/heimdal/lib/hdb/ext.c b/crypto/heimdal/lib/hdb/ext.c
index d2a4373b9b38..5f7a19a55e04 100644
--- a/crypto/heimdal/lib/hdb/ext.c
+++ b/crypto/heimdal/lib/hdb/ext.c
@@ -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;
+}
diff --git a/crypto/heimdal/lib/hdb/hdb-mitdb.c b/crypto/heimdal/lib/hdb/hdb-mitdb.c
index 02c575050fe2..1dfe7835cb4d 100644
--- a/crypto/heimdal/lib/hdb/hdb-mitdb.c
+++ b/crypto/heimdal/lib/hdb/hdb-mitdb.c
@@ -91,18 +91,28 @@ salt:
*** 1797 LINES SKIPPED ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202510051622.595GMFbu032657>