diff options
42 files changed, 1463 insertions, 522 deletions
diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc index 36e8c048661e..3645cff43458 100644 --- a/ObsoleteFiles.inc +++ b/ObsoleteFiles.inc @@ -51,6 +51,9 @@ # xargs -n1 | sort | uniq -d; # done +# 20251006: Remove libnss_tacplus.a (it never should have been installed) +OLD_FILES+=usr/lib/libnss_tacplus.a + # 20251003: kgdb python scripts moved OLD_FILES+=usr/libexec/kgdb/acttrace.py OLD_DIRS+=usr/libexec/kgdb diff --git a/cddl/share/zfs/compatibility.d/Makefile b/cddl/share/zfs/compatibility.d/Makefile index 6cdb64b0fccb..6d3663081ae6 100644 --- a/cddl/share/zfs/compatibility.d/Makefile +++ b/cddl/share/zfs/compatibility.d/Makefile @@ -24,6 +24,7 @@ FILES= \ openzfs-2.1-linux \ openzfs-2.2 \ openzfs-2.3 \ + openzfs-2.4 \ zol-0.6.1 \ zol-0.6.4 \ zol-0.6.5 \ @@ -60,7 +61,9 @@ LINKS= \ ${FILESDIR}/openzfs-2.2 ${FILESDIR}/openzfs-2.2-linux \ ${FILESDIR}/openzfs-2.2 ${FILESDIR}/openzfs-2.2-freebsd \ ${FILESDIR}/openzfs-2.3 ${FILESDIR}/openzfs-2.3-linux \ - ${FILESDIR}/openzfs-2.3 ${FILESDIR}/openzfs-2.3-freebsd + ${FILESDIR}/openzfs-2.3 ${FILESDIR}/openzfs-2.3-freebsd \ + ${FILESDIR}/openzfs-2.4 ${FILESDIR}/openzfs-2.4-linux \ + ${FILESDIR}/openzfs-2.4 ${FILESDIR}/openzfs-2.4-freebsd LINKMODE= ${NOBINMODE} 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/kadmin/kadmin.8 b/crypto/heimdal/kadmin/kadmin.8 index bd2fd4e7363f..e4555cd529f4 100644 --- a/crypto/heimdal/kadmin/kadmin.8 +++ b/crypto/heimdal/kadmin/kadmin.8 @@ -31,7 +31,7 @@ .\" .\" $Id$ .\" -.Dd Feb 22, 2007 +.Dd October 5, 2025 .Dt KADMIN 8 .Os HEIMDAL .Sh NAME @@ -286,14 +286,39 @@ When running in local mode, the following commands can also be used: .Pp .Nm dump .Op Fl d | Fl Fl decrypt +.Op Fl f Ns Ar format | Fl Fl format= Ns Ar format .Op Ar dump-file .Bd -ragged -offset indent Writes the database in -.Dq human readable +.Dq machine readable text form to the specified file, or standard out. If the database is encrypted, the dump will also have encrypted keys, unless .Fl Fl decrypt is used. +.Pp +If +.Fl Fl format=MIT +is used then the dump will be in MIT format. +This option may be used if you require that all principal +passwords be changed after loading the dump into an MIT KDC database. +.Pp +If +.Fl Fl format=<keytab-file> +is used, the +.Dq <keytab-file> +should hold the master key for the +MIT KDC (usually a file called /var/db/krb5kdc/.k5.YOUR.REALM). +This will cause the keys to be re-encrypted in the MIT master +key as well as doing the dump in MIT format. +When this dump is loaded into the MIT KDC's database, +the principals that had at least one strong encryption type +key should work and any keytabs for those principals should still work. +The principcals with only weak encryption keys will require a +.Dq change_password +be done on the MIT KDC to get them working. +The +.Fl Fl decrypt +flag is meaningless for this case. .Ed .Pp .Nm init 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: #include "hdb_locl.h" -#define KDB_V1_BASE_LENGTH 38 - -#if HAVE_DB1 +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 */ +} -#if defined(HAVE_DB_185_H) -#include <db_185.h> -#elif defined(HAVE_DB_H) -#include <db.h> -#endif +#define KDB_V1_BASE_LENGTH 38 #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; +} + diff --git a/crypto/heimdal/lib/hdb/hdb-protos.h b/crypto/heimdal/lib/hdb/hdb-protos.h index 44a1bddc7625..2b692855c405 100644 --- a/crypto/heimdal/lib/hdb/hdb-protos.h +++ b/crypto/heimdal/lib/hdb/hdb-protos.h @@ -9,6 +9,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*/, krb5_keyblock */*key*/, @@ -347,6 +358,13 @@ hdb_seal_keys_mkey ( 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*/, HDB */*db*/, @@ -386,6 +404,13 @@ hdb_unseal_keys ( 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*/, hdb_entry */*ent*/, diff --git a/crypto/heimdal/lib/hdb/hdb.asn1 b/crypto/heimdal/lib/hdb/hdb.asn1 index a72851c9f201..d24737fbf6e8 100644 --- a/crypto/heimdal/lib/hdb/hdb.asn1 +++ b/crypto/heimdal/lib/hdb/hdb.asn1 @@ -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 diff --git a/crypto/heimdal/lib/hdb/hdb.c b/crypto/heimdal/lib/hdb/hdb.c index ca05cc4a1785..aff576a30d37 100644 --- a/crypto/heimdal/lib/hdb/hdb.c +++ b/crypto/heimdal/lib/hdb/hdb.c @@ -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); } diff --git a/crypto/heimdal/lib/hdb/hdb.h b/crypto/heimdal/lib/hdb/hdb.h index a1692ce82ca2..4c4c2c2b1a5c 100644 --- a/crypto/heimdal/lib/hdb/hdb.h +++ b/crypto/heimdal/lib/hdb/hdb.h @@ -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; diff --git a/crypto/heimdal/lib/hdb/hdb_locl.h b/crypto/heimdal/lib/hdb/hdb_locl.h index e896b5802575..5aad504589df 100644 --- a/crypto/heimdal/lib/hdb/hdb_locl.h +++ b/crypto/heimdal/lib/hdb/hdb_locl.h @@ -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__ */ diff --git a/crypto/heimdal/lib/hdb/keys.c b/crypto/heimdal/lib/hdb/keys.c index 3d0b9d7c1b31..9c0af5c47f23 100644 --- a/crypto/heimdal/lib/hdb/keys.c +++ b/crypto/heimdal/lib/hdb/keys.c @@ -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) diff --git a/crypto/heimdal/lib/hdb/mkey.c b/crypto/heimdal/lib/hdb/mkey.c index 9eb98fca32c0..78a9e51a132c 100644 --- a/crypto/heimdal/lib/hdb/mkey.c +++ b/crypto/heimdal/lib/hdb/mkey.c @@ -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,25 +463,81 @@ 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; } @@ -472,19 +545,114 @@ hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey) } 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; } diff --git a/crypto/heimdal/lib/hdb/print.c b/crypto/heimdal/lib/hdb/print.c index 697d32d2909c..d5359f538056 100644 --- a/crypto/heimdal/lib/hdb/print.c +++ b/crypto/heimdal/lib/hdb/print.c @@ -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; } diff --git a/crypto/heimdal/lib/hdb/version-script.map b/crypto/heimdal/lib/hdb/version-script.map index 50a36cec0aa9..42e2043b4b47 100644 --- a/crypto/heimdal/lib/hdb/version-script.map +++ b/crypto/heimdal/lib/hdb/version-script.map @@ -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 diff --git a/crypto/heimdal/lib/kadm5/chpass_s.c b/crypto/heimdal/lib/kadm5/chpass_s.c index 624293e5c3a3..22f65517fcc5 100644 --- a/crypto/heimdal/lib/kadm5/chpass_s.c +++ b/crypto/heimdal/lib/kadm5/chpass_s.c @@ -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; diff --git a/crypto/heimdal/lib/kadm5/randkey_s.c b/crypto/heimdal/lib/kadm5/randkey_s.c index dcb179aac40f..adb3564be719 100644 --- a/crypto/heimdal/lib/kadm5/randkey_s.c +++ b/crypto/heimdal/lib/kadm5/randkey_s.c @@ -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, diff --git a/kerberos5/lib/libhdb/Makefile b/kerberos5/lib/libhdb/Makefile index bcc3f75206bc..14fa242bc9e5 100644 --- a/kerberos5/lib/libhdb/Makefile +++ b/kerberos5/lib/libhdb/Makefile @@ -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 \ diff --git a/lib/nss_tacplus/Makefile b/lib/nss_tacplus/Makefile index a969cd85778d..c85297a7dbaa 100644 --- a/lib/nss_tacplus/Makefile +++ b/lib/nss_tacplus/Makefile @@ -3,7 +3,7 @@ SRCS= ${LIB}.c SHLIB_MAJOR= 1 SHLIB_NAME= ${LIB}.so.${SHLIB_MAJOR} LIBADD= tacplus -MK_INSTALLIB= no +MK_INSTALLLIB= no MAN= ${LIB}.8 .include <bsd.lib.mk> diff --git a/libexec/rc/rc.conf b/libexec/rc/rc.conf index 2589e2614c35..c776a815003c 100644 --- a/libexec/rc/rc.conf +++ b/libexec/rc/rc.conf @@ -501,7 +501,8 @@ netwait_timeout="60" # Total number of seconds to perform pings. #netwait_if="" # Wait for active link on each intf in this list. netwait_if_timeout="30" # Total number of seconds to monitor link state. netwait_dad="NO" # Wait for DAD to complete -netwait_dad_timeout="10" # Total number of seconds to wait for DAD. +netwait_dad_timeout="" # Total number of seconds to wait for DAD, zero + # or unset to autodetect ### Miscellaneous network options: ### icmp_bmcastecho="NO" # respond to broadcast ping packets diff --git a/libexec/rc/rc.d/netwait b/libexec/rc/rc.d/netwait index b609440a2e4e..05874552cf1c 100755 --- a/libexec/rc/rc.d/netwait +++ b/libexec/rc/rc.d/netwait @@ -36,13 +36,15 @@ netwait_start() err 1 "Nothing to wait for" fi - if ! [ "${netwait_if_timeout}" -ge 1 ]; then + if ! [ "${netwait_if_timeout:=0}" -ge 1 ]; then err 1 "netwait_if_timeout must be >= 1" fi - if ! [ "${netwait_dad_timeout}" -ge 1 ]; then - err 1 "netwait_dad_timeout must be >= 1" + if ! check_kern_features inet6; then + netwait_dad="NO" + elif ! [ "${netwait_dad_timeout:=0}" -ge 1 ]; then + netwait_dad_timeout=$(($(sysctl -n net.inet6.ip6.dad_count)+1)) fi - if ! [ "${netwait_timeout}" -ge 1 ]; then + if ! [ "${netwait_timeout:=0}" -ge 1 ]; then err 1 "netwait_timeout must be >= 1" fi diff --git a/release/packages/generate-set-ucl.lua b/release/packages/generate-set-ucl.lua index b1b70053b02a..3c8474bf8e68 100755 --- a/release/packages/generate-set-ucl.lua +++ b/release/packages/generate-set-ucl.lua @@ -1,7 +1,7 @@ #!/usr/libexec/flua --[[ usage: -generare-set-ucl.lua <template> [<variablename> <variablevalue>] +generate-set-ucl.lua <template> [<variablename> <variablevalue>] Generate the UCL for a set metapackage. The variables provided will be substituted as UCL variables. diff --git a/release/packages/generate-ucl.lua b/release/packages/generate-ucl.lua index ea3743894740..c61cbb251d8b 100755 --- a/release/packages/generate-ucl.lua +++ b/release/packages/generate-ucl.lua @@ -1,7 +1,7 @@ #!/usr/libexec/flua --[[ usage: -generare-ucl.lua [<variablename> <variablevalue>]... <sourceucl> <destucl> +generate-ucl.lua [<variablename> <variablevalue>]... <sourceucl> <destucl> Build a package's UCL configuration by loading the template UCL file <sourceucl>, replacing any $VARIABLES in the UCL based on the provided diff --git a/release/packages/ucl/ctf-all.ucl b/release/packages/ucl/ctf-all.ucl index 941990908373..970ac3ffb4fa 100644 --- a/release/packages/ucl/ctf-all.ucl +++ b/release/packages/ucl/ctf-all.ucl @@ -30,7 +30,7 @@ CTF is intended specifically for runtime program analyzers such as DTrace. This package provides the ctfconvert(1), ctfdump(1) and ctfmerge(1) utilities which are used to work with CTF data, and the libctf library which allows -application to access CTF debugging information programatically. +applications to access CTF debugging information programmatically. EOD licenses = [ "CDDL-1.0" ] diff --git a/release/packages/ucl/fetch-all.ucl b/release/packages/ucl/fetch-all.ucl index d14f22cb74a5..5a3c0be826fa 100644 --- a/release/packages/ucl/fetch-all.ucl +++ b/release/packages/ucl/fetch-all.ucl @@ -19,13 +19,13 @@ comment = "Retrieve a file from a remote URL" desc = <<EOD -The fetch(1) utility retieves one or more files from a remote URL via +The fetch(1) utility retrieves one or more files from a remote URL via HTTP or FTP. fetch(1) includes support for TLS, HTTP proxies, .netrc files, and includes a "mirror" mode which only downloads files which are newer on the remote site. Also provided is fetch(3), a library which allows applications to use -this functionality programatically. +this functionality programmatically. EOD annotations { diff --git a/release/packages/ucl/openssl-all.ucl b/release/packages/ucl/openssl-all.ucl index 24a83ab6b9ba..4c393efe3b89 100644 --- a/release/packages/ucl/openssl-all.ucl +++ b/release/packages/ucl/openssl-all.ucl @@ -26,7 +26,7 @@ protocols such as HTTP, SMTP and DNS. OpenSSL provides openssl(1), a command-line utility used for testing TLS clients and servers, managing certificate used in TLS authentication, and -performing various miscallenous cryptographic operations. +performing various miscellaneous cryptographic operations. Also provided are two libraries, libcrypto and libssl, which can be used by applications to provide generic cryptographic functionality, and to implement diff --git a/release/packages/ucl/ufs-all.ucl b/release/packages/ucl/ufs-all.ucl index d9302bd0e3f9..b35e0f3d1dc7 100644 --- a/release/packages/ucl/ufs-all.ucl +++ b/release/packages/ucl/ufs-all.ucl @@ -32,7 +32,7 @@ utilities used to check the consistency of an existing filesystem after an unclean shutdown or panic. The libufs library is also provided, which allows applications to access -UFS filesystems programatically. +UFS filesystems programmatically. EOD annotations { diff --git a/release/packages/ucl/xz-all.ucl b/release/packages/ucl/xz-all.ucl index 16da8b76f0ac..8a7c33bcce46 100644 --- a/release/packages/ucl/xz-all.ucl +++ b/release/packages/ucl/xz-all.ucl @@ -21,7 +21,7 @@ comment = "LZMA2 data compression" desc = <<EOD xz compresses data using the LZMA2 data compression algorithm. This package provides the front-end xz(1) utility, and the liblzma library which allows -applications to use this functionality programatically. +applications to use this functionality programmatically. EOD annotations { diff --git a/release/packages/ucl/zfs-all.ucl b/release/packages/ucl/zfs-all.ucl index 70ebcdacdb57..9d166452188d 100644 --- a/release/packages/ucl/zfs-all.ucl +++ b/release/packages/ucl/zfs-all.ucl @@ -39,7 +39,7 @@ This package provides the zfs(8) and zpool(8) utilities used to manage ZFS filesystems, the zfsd(8) self-healing daemon, and several other utilities, as well as the runtime libraries used internally by ZFS itself, and the libzfs_core library providing a stable interface for -managing ZFS programatically. +managing ZFS programmatically. EOD licenses = [ "CDDL-1.0" ] diff --git a/release/tools/vmimage.subr b/release/tools/vmimage.subr index 3b0519c21dbc..cae8a113871e 100644 --- a/release/tools/vmimage.subr +++ b/release/tools/vmimage.subr @@ -256,6 +256,7 @@ vm_extra_pkg_rmcache() { if [ -n "${NO_ROOT}" ]; then ${PKG_CMD} \ -o ASSUME_ALWAYS_YES=yes \ + -o INSTALL_AS_USER=yes \ -r ${DESTDIR} \ clean -y -a else diff --git a/share/man/man5/rc.conf.5 b/share/man/man5/rc.conf.5 index c0048f27c740..edbfa937f8df 100644 --- a/share/man/man5/rc.conf.5 +++ b/share/man/man5/rc.conf.5 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd October 2, 2025 +.Dd October 5, 2025 .Dt RC.CONF 5 .Os .Sh NAME @@ -4618,8 +4618,11 @@ Set to to enable waiting for DAD to complete. .It Va netwait_dad_timeout .Pq Vt int -Indicates the total number of seconds to wait for DAD to complete. -The default is 10. +Unset by default. +Indicates the maximum number of seconds to wait for DAD to complete. +If zero or unset, the timeout will be one more than the value of the +.Va net.inet6.ip6.dad_count +sysctl variable. .It Va rctl_enable .Pq Vt bool If set to diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk index 95cd3b02d8e4..3e108591251a 100644 --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -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 diff --git a/sys/kern/kern_mutex.c b/sys/kern/kern_mutex.c index b7316ea5f387..d67c70984528 100644 --- a/sys/kern/kern_mutex.c +++ b/sys/kern/kern_mutex.c @@ -503,8 +503,8 @@ _mtx_trylock_flags_(volatile uintptr_t *c, int opts, const char *file, int line) /* * __mtx_lock_sleep: the tougher part of acquiring an MTX_DEF lock. * - * We call this if the lock is either contested (i.e. we need to go to - * sleep waiting for it), or if we need to recurse on it. + * We get here if lock profiling is enabled, the lock is already held by + * someone else or we are recursing on it. */ #if LOCK_DEBUG > 0 void @@ -660,13 +660,8 @@ retry_turnstile: } #endif - /* - * If the mutex isn't already contested and a failure occurs - * setting the contested bit, the mutex was either released - * or the state of the MTX_RECURSED bit changed. - */ - if ((v & MTX_CONTESTED) == 0 && - !atomic_fcmpset_ptr(&m->mtx_lock, &v, v | MTX_CONTESTED)) { + if ((v & MTX_WAITERS) == 0 && + !atomic_fcmpset_ptr(&m->mtx_lock, &v, v | MTX_WAITERS)) { goto retry_turnstile; } @@ -1029,8 +1024,8 @@ thread_lock_set(struct thread *td, struct mtx *new) /* * __mtx_unlock_sleep: the tougher part of releasing an MTX_DEF lock. * - * We are only called here if the lock is recursed, contested (i.e. we - * need to wake up a blocked thread) or lockstat probe is active. + * We get here if lock profiling is enabled, the lock is already held by + * someone else or we are recursing on it. */ #if LOCK_DEBUG > 0 void @@ -1207,7 +1202,7 @@ _mtx_destroy(volatile uintptr_t *c) if (!mtx_owned(m)) MPASS(mtx_unowned(m)); else { - MPASS((m->mtx_lock & (MTX_RECURSED|MTX_CONTESTED)) == 0); + MPASS((m->mtx_lock & (MTX_RECURSED|MTX_WAITERS)) == 0); /* Perform the non-mtx related part of mtx_unlock_spin(). */ if (LOCK_CLASS(&m->lock_object) == &lock_class_mtx_spin) { @@ -1359,8 +1354,8 @@ db_show_mtx(const struct lock_object *lock) db_printf("DESTROYED"); else { db_printf("OWNED"); - if (m->mtx_lock & MTX_CONTESTED) - db_printf(", CONTESTED"); + if (m->mtx_lock & MTX_WAITERS) + db_printf(", WAITERS"); if (m->mtx_lock & MTX_RECURSED) db_printf(", RECURSED"); } diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index 3d4567b6ab1e..a53df50c06bd 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -806,9 +806,12 @@ file_v_lock(struct file *fp, short lock_bit, short lock_wait_bit) flagsp = &fp->f_vflags; state = atomic_load_16(flagsp); - if ((state & lock_bit) == 0 && - atomic_cmpset_acq_16(flagsp, state, state | lock_bit)) - return; + for (;;) { + if ((state & lock_bit) != 0) + break; + if (atomic_fcmpset_acq_16(flagsp, &state, state | lock_bit)) + return; + } sleepq_lock(flagsp); state = atomic_load_16(flagsp); @@ -842,9 +845,12 @@ file_v_unlock(struct file *fp, short lock_bit, short lock_wait_bit) flagsp = &fp->f_vflags; state = atomic_load_16(flagsp); - if ((state & lock_wait_bit) == 0 && - atomic_cmpset_rel_16(flagsp, state, state & ~lock_bit)) - return; + for (;;) { + if ((state & lock_wait_bit) != 0) + break; + if (atomic_fcmpset_rel_16(flagsp, &state, state & ~lock_bit)) + return; + } sleepq_lock(flagsp); MPASS((*flagsp & lock_bit) != 0); @@ -864,10 +870,6 @@ foffset_lock(struct file *fp, int flags) FILE_V_FOFFSET_LOCK_WAITING); } - /* - * According to McKusick the vn lock was protecting f_offset here. - * It is now protected by the FOFFSET_LOCKED flag. - */ return (atomic_load_long(&fp->f_offset)); } diff --git a/sys/netgraph/netflow/netflow.c b/sys/netgraph/netflow/netflow.c index 978d6fd0b54d..05c6062463be 100644 --- a/sys/netgraph/netflow/netflow.c +++ b/sys/netgraph/netflow/netflow.c @@ -960,7 +960,7 @@ struct ngnf_show_header *resp) list_id = 0; TAILQ_FOREACH(fle, &hsh->head, fle_hash) { - if (hsh->mtx.mtx_lock & MTX_CONTESTED) { + if (hsh->mtx.mtx_lock & MTX_WAITERS) { resp->hash_id = i; resp->list_id = list_id; mtx_unlock(&hsh->mtx); @@ -1111,7 +1111,7 @@ ng_netflow_expire(void *arg) * Interrupt thread wants this entry! * Quick! Quick! Bail out! */ - if (hsh->mtx.mtx_lock & MTX_CONTESTED) + if (hsh->mtx.mtx_lock & MTX_WAITERS) break; /* @@ -1150,7 +1150,7 @@ ng_netflow_expire(void *arg) * Interrupt thread wants this entry! * Quick! Quick! Bail out! */ - if (hsh->mtx.mtx_lock & MTX_CONTESTED) + if (hsh->mtx.mtx_lock & MTX_WAITERS) break; /* diff --git a/sys/netinet/sctp_lock_bsd.h b/sys/netinet/sctp_lock_bsd.h index ec66be0cf371..a60983cb30e3 100644 --- a/sys/netinet/sctp_lock_bsd.h +++ b/sys/netinet/sctp_lock_bsd.h @@ -263,10 +263,10 @@ } while (0) #define SCTP_INP_LOCK_CONTENDED(_inp) \ - ((_inp)->inp_mtx.mtx_lock & MTX_CONTESTED) + ((_inp)->inp_mtx.mtx_lock & MTX_WAITERS) #define SCTP_INP_READ_CONTENDED(_inp) \ - ((_inp)->inp_rdata_mtx.mtx_lock & MTX_CONTESTED) + ((_inp)->inp_rdata_mtx.mtx_lock & MTX_WAITERS) #ifdef SCTP_LOCK_LOGGING #define SCTP_INP_RLOCK(_inp) do { \ @@ -337,7 +337,7 @@ } while (0) #define SCTP_ASOC_CREATE_LOCK_CONTENDED(_inp) \ - ((_inp)->inp_create_mtx.mtx_lock & MTX_CONTESTED) + ((_inp)->inp_create_mtx.mtx_lock & MTX_WAITERS) /* * For the majority of things (once we have found the association) we will diff --git a/sys/sys/mutex.h b/sys/sys/mutex.h index 83300d4eb593..4f6b45d78a88 100644 --- a/sys/sys/mutex.h +++ b/sys/sys/mutex.h @@ -68,9 +68,9 @@ */ #define MTX_UNOWNED 0x00000000 /* Cookie for free mutex */ #define MTX_RECURSED 0x00000001 /* lock recursed (for MTX_DEF only) */ -#define MTX_CONTESTED 0x00000002 /* lock contested (for MTX_DEF only) */ +#define MTX_WAITERS 0x00000002 /* lock has waiters (for MTX_DEF only) */ #define MTX_DESTROYED 0x00000004 /* lock destroyed */ -#define MTX_FLAGMASK (MTX_RECURSED | MTX_CONTESTED | MTX_DESTROYED) +#define MTX_FLAGMASK (MTX_RECURSED | MTX_WAITERS | MTX_DESTROYED) /* * Prototypes @@ -217,7 +217,7 @@ void _thread_lock(struct thread *); #define _mtx_obtain_lock_fetch(mp, vp, tid) \ atomic_fcmpset_acq_ptr(&(mp)->mtx_lock, vp, (tid)) -/* Try to release mtx_lock if it is unrecursed and uncontested. */ +/* Try to release mtx_lock if it is unrecursed and without waiters. */ #define _mtx_release_lock(mp, tid) \ atomic_cmpset_rel_ptr(&(mp)->mtx_lock, (tid), MTX_UNOWNED) |