int mit_prop_dump(void *arg, const char *file) { krb5_error_code ret; char line [2048]; FILE *f; int lineno = 0; struct hdb_entry_ex ent; struct prop_data *pd = arg; f = fopen(file, "r"); 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; int tmp; lineno++; memset(&ent, 0, sizeof(ent)); q = nexttoken(&p); if(strcmp(q, "kdb5_util") == 0) { int major; q = nexttoken(&p); /* load_dump */ if(strcmp(q, "load_dump")) errx(1, "line %d: unknown version", lineno); q = nexttoken(&p); /* load_dump */ if(strcmp(q, "version")) errx(1, "line %d: unknown version", lineno); q = nexttoken(&p); /* x.0 */ if(sscanf(q, "%d", &major) != 1) errx(1, "line %d: unknown version", lineno); 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) { continue; } else if(strcmp(q, "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); } fclose(f); return 0; }
static krb5_error_code mdb_value2entry(krb5_context context, krb5_data *data, hdb_entry *entry) { krb5_error_code ret; krb5_storage *sp; uint32_t u32; uint16_t u16, num_keys, num_tl; size_t i, j; char *p = NULL; sp = krb5_storage_from_data(data); if (sp == NULL) { krb5_set_error_message(context, ENOMEM, "out of memory"); return ENOMEM; } krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE); /* 16: baselength */ CHECK(ret = krb5_ret_uint16(sp, &u16)); if (u16 != KDB_V1_BASE_LENGTH) { ret = EINVAL; goto out; } /* 32: attributes */ CHECK(ret = krb5_ret_uint32(sp, &u32)); entry->flags.postdate = !(u16 & KRB5_KDB_DISALLOW_POSTDATED); entry->flags.forwardable = !(u16 & KRB5_KDB_DISALLOW_FORWARDABLE); entry->flags.initial = !!(u16 & KRB5_KDB_DISALLOW_TGT_BASED); entry->flags.renewable = !(u16 & KRB5_KDB_DISALLOW_RENEWABLE); entry->flags.proxiable = !(u16 & KRB5_KDB_DISALLOW_PROXIABLE); /* DUP_SKEY */ entry->flags.invalid = !!(u16 & KRB5_KDB_DISALLOW_ALL_TIX); entry->flags.require_preauth =!!(u16 & KRB5_KDB_REQUIRES_PRE_AUTH); entry->flags.require_hwauth =!!(u16 & KRB5_KDB_REQUIRES_HW_AUTH); entry->flags.server = !(u16 & KRB5_KDB_DISALLOW_SVR); entry->flags.change_pw = !!(u16 & KRB5_KDB_PWCHANGE_SERVICE); entry->flags.client = 1; /* XXX */ /* 32: max time */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->max_life = malloc(sizeof(*entry->max_life)); *entry->max_life = u32; } /* 32: max renewable time */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->max_renew = malloc(sizeof(*entry->max_renew)); *entry->max_renew = u32; } /* 32: client expire */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->valid_end = malloc(sizeof(*entry->valid_end)); *entry->valid_end = u32; } /* 32: passwd expire */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->pw_end = malloc(sizeof(*entry->pw_end)); *entry->pw_end = u32; } /* 32: last successful passwd */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 32: last failed attempt */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 32: num of failed attempts */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 16: num tl data */ CHECK(ret = krb5_ret_uint16(sp, &u16)); num_tl = u16; /* 16: num key data */ CHECK(ret = krb5_ret_uint16(sp, &u16)); num_keys = u16; /* 16: principal length */ CHECK(ret = krb5_ret_uint16(sp, &u16)); /* length: principal */ { p = malloc(u16 + 1); krb5_storage_read(sp, p, u16); p[u16] = '\0'; CHECK(ret = krb5_parse_name(context, p, &entry->principal)); free(p); p = NULL; } /* for num tl data times 16: tl data type 16: tl data length length: length */ for (i = 0; i < num_tl; i++) { CHECK(ret = krb5_ret_uint16(sp, &u16)); CHECK(ret = krb5_ret_uint16(sp, &u16)); krb5_storage_seek(sp, u16, SEEK_CUR); } /* for num key data times 16: version (num keyblocks) 16: kvno for version times: 16: type 16: length length: keydata */ for (i = 0; i < num_keys; i++) { int keep = 0; uint16_t version; void *ptr; CHECK(ret = krb5_ret_uint16(sp, &u16)); version = u16; CHECK(ret = krb5_ret_uint16(sp, &u16)); if (entry->kvno < u16) { keep = 1; entry->kvno = u16; for (j = 0; j < entry->keys.len; j++) { free_Key(&entry->keys.val[j]); free(entry->keys.val); entry->keys.len = 0; entry->keys.val = NULL; } } else if (entry->kvno == u16) keep = 1; if (keep) { Key *k; ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1)); if (ptr == NULL) { ret = ENOMEM; goto out; } entry->keys.val = ptr; /* k points to current Key */ k = &entry->keys.val[entry->keys.len]; memset(k, 0, sizeof(*k)); entry->keys.len += 1; entry->keys.val[i].mkvno = malloc(sizeof(*entry->keys.val[i].mkvno)); if (entry->keys.val[i].mkvno == NULL) { ret = ENOMEM; goto out; } *entry->keys.val[i].mkvno = 1; for (j = 0; j < version; j++) { uint16_t type; CHECK(ret = krb5_ret_uint16(sp, &type)); CHECK(ret = krb5_ret_uint16(sp, &u16)); if (j == 0) { /* key */ k->key.keytype = type; if (u16 < 2) { ret = EINVAL; goto out; } krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */ k->key.keyvalue.length = u16 - 2; k->key.keyvalue.data = malloc(k->key.keyvalue.length); krb5_storage_read(sp, k->key.keyvalue.data, k->key.keyvalue.length); } else if (j == 1) { /* salt */ k->salt = calloc(1, sizeof(*k->salt)); if (k->salt == NULL) { ret = ENOMEM; goto out; } k->salt->type = type; if (u16 != 0) { k->salt->salt.data = malloc(u16); if (k->salt->salt.data == NULL) { ret = ENOMEM; goto out; } k->salt->salt.length = u16; krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length); } fix_salt(context, entry, entry->keys.len - 1); } else { krb5_storage_seek(sp, u16, SEEK_CUR); } } } else { /* skip */ for (j = 0; j < version; j++) { CHECK(ret = krb5_ret_uint16(sp, &u16)); CHECK(ret = krb5_ret_uint16(sp, &u16)); krb5_storage_seek(sp, u16, u16); } } } return 0; out: if (p) free(p); free_hdb_entry(entry); return ret; }
int mit_prop_dump(void *arg, const char *file) { krb5_error_code ret; char line [2048]; FILE *f; int lineno = 0; struct hdb_entry_ex ent; struct prop_data *pd = arg; f = fopen(file, "r"); 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 extra_data_length; int attributes; int tmp; lineno++; memset(&ent, 0, sizeof(ent)); q = nexttoken(&p); if(strcmp(q, "kdb5_util") == 0) { int major; q = nexttoken(&p); /* load_dump */ if(strcmp(q, "load_dump")) errx(1, "line %d: unknown version", lineno); q = nexttoken(&p); /* load_dump */ if(strcmp(q, "version")) errx(1, "line %d: unknown version", lineno); q = nexttoken(&p); /* x.0 */ if(sscanf(q, "%d", &major) != 1) errx(1, "line %d: unknown version", lineno); if(major != 4) errx(1, "unknown dump file format, got %d, expected 4", major); continue; } else if(strcmp(q, "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; } q = nexttoken(&p); /* length of principal */ num_tl_data = getint(&p); /* number of tl-data */ num_key_data = getint(&p); /* number of key-data */ extra_data_length = 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; } q = nexttoken(&p); /* last auth */ q = nexttoken(&p); /* last failed auth */ q = 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_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); 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); for(i = 0; i < num_key_data; i++) { int key_versions; key_versions = getint(&p); /* key data version */ ent.entry.kvno = getint(&p); /* XXX kvno */ ALLOC(ent.entry.keys.val[i].mkvno); *ent.entry.keys.val[i].mkvno = 0; /* 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; tmp = getint(&p); /* -1, if no data. */ } fix_salt(pd->context, &ent.entry, i); } } q = nexttoken(&p); /* extra data */ v5_prop(pd->context, NULL, &ent, arg); } fclose(f); return 0; }
static krb5_error_code mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry *entry) { krb5_error_code ret; krb5_storage *sp; uint32_t u32; uint16_t u16, num_keys, num_tl; size_t i, j; char *p; sp = krb5_storage_from_data(data); if (sp == NULL) { krb5_set_error_message(context, ENOMEM, "out of memory"); return ENOMEM; } krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE); /* * 16: baselength * * The story here is that these 16 bits have to be a constant: * KDB_V1_BASE_LENGTH. Once upon a time a different value here * would have been used to indicate the presence of "extra data" * between the "base" contents and the {principal name, TL data, * keys} that follow it. Nothing supports such "extra data" * nowadays, so neither do we here. * * XXX But... surely we ought to log about this extra data, or skip * it, or something, in case anyone has MIT KDBs with ancient * entries in them... Logging would allow the admin to know which * entries to dump with MIT krb5's kdb5_util. */ CHECK(ret = krb5_ret_uint16(sp, &u16)); 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 */ /* 32: max time */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->max_life = malloc(sizeof(*entry->max_life)); *entry->max_life = u32; } /* 32: max renewable time */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->max_renew = malloc(sizeof(*entry->max_renew)); *entry->max_renew = u32; } /* 32: client expire */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->valid_end = malloc(sizeof(*entry->valid_end)); *entry->valid_end = u32; } /* 32: passwd expire */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->pw_end = malloc(sizeof(*entry->pw_end)); *entry->pw_end = u32; } /* 32: last successful passwd */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 32: last failed attempt */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 32: num of failed attempts */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 16: num tl data */ CHECK(ret = krb5_ret_uint16(sp, &u16)); num_tl = u16; /* 16: num key data */ CHECK(ret = krb5_ret_uint16(sp, &u16)); num_keys = u16; /* 16: principal length */ CHECK(ret = krb5_ret_uint16(sp, &u16)); /* length: principal */ { /* * Note that the principal name includes the NUL in the entry, * but we don't want to take chances, so we add an extra NUL. */ p = malloc(u16 + 1); if (p == NULL) { ret = ENOMEM; goto out; } krb5_storage_read(sp, p, u16); p[u16] = '\0'; CHECK(ret = krb5_parse_name(context, p, &entry->principal)); free(p); } /* for num tl data times 16: tl data type 16: tl data length length: length */ for (i = 0; i < num_tl; i++) { /* 16: TL data type */ CHECK(ret = krb5_ret_uint16(sp, &u16)); /* 16: TL data length */ CHECK(ret = krb5_ret_uint16(sp, &u16)); krb5_storage_seek(sp, u16, SEEK_CUR); } /* * for num key data times * 16: "version" * 16: kvno * for version times: * 16: type * 16: length * length: keydata * * "version" here is really 1 or 2, the first meaning there's only * keys for this kvno, the second meaning there's keys and salt[s?]. * That's right... hold that gag reflex, you can do it. */ for (i = 0; i < num_keys; i++) { int keep = 0; uint16_t version; void *ptr; CHECK(ret = krb5_ret_uint16(sp, &u16)); version = u16; CHECK(ret = krb5_ret_uint16(sp, &u16)); /* * First time through, and until we find one matching key, * entry->kvno == 0. */ if ((entry->kvno < u16) && (kvno == 0 || kvno == u16)) { keep = 1; entry->kvno = u16; /* * Found a higher kvno than earlier, so free the old highest * kvno keys. * * XXX Of course, we actually want to extract the old kvnos * as well, for some of the kadm5 APIs. We shouldn't free * these keys, but keep them elsewhere. */ for (j = 0; j < entry->keys.len; j++) free_Key(&entry->keys.val[j]); free(entry->keys.val); entry->keys.len = 0; entry->keys.val = NULL; } else if (entry->kvno == u16) /* Accumulate keys */ keep = 1; if (keep) { Key *k; ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1)); if (ptr == NULL) { ret = ENOMEM; goto out; } entry->keys.val = ptr; /* k points to current Key */ k = &entry->keys.val[entry->keys.len]; memset(k, 0, sizeof(*k)); entry->keys.len += 1; k->mkvno = malloc(sizeof(*k->mkvno)); if (k->mkvno == NULL) { ret = ENOMEM; goto out; } *k->mkvno = 1; for (j = 0; j < version; j++) { uint16_t type; CHECK(ret = krb5_ret_uint16(sp, &type)); CHECK(ret = krb5_ret_uint16(sp, &u16)); if (j == 0) { /* This "version" means we have a key */ k->key.keytype = type; if (u16 < 2) { ret = EINVAL; goto out; } /* * MIT stores keys encrypted keys as {16-bit length * of plaintext key, {encrypted key}}. The reason * for this is that the Kerberos cryptosystem is not * length-preserving. Heimdal's approach is to * truncate the plaintext to the expected length of * the key given its enctype, so we ignore this * 16-bit length-of-plaintext-key field. */ krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */ k->key.keyvalue.length = u16 - 2; /* adjust cipher len */ k->key.keyvalue.data = malloc(k->key.keyvalue.length); krb5_storage_read(sp, k->key.keyvalue.data, k->key.keyvalue.length); } else if (j == 1) { /* This "version" means we have a salt */ k->salt = calloc(1, sizeof(*k->salt)); if (k->salt == NULL) { ret = ENOMEM; goto out; } k->salt->type = type; if (u16 != 0) { k->salt->salt.data = malloc(u16); if (k->salt->salt.data == NULL) { ret = ENOMEM; goto out; } k->salt->salt.length = u16; krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length); } fix_salt(context, entry, entry->keys.len - 1); } else { /* * Whatever this "version" might be, we skip it * * XXX A krb5.conf parameter requesting that we log * about strangeness like this, or return an error * from here, might be nice. */ krb5_storage_seek(sp, u16, SEEK_CUR); } } } else { /* * XXX For now we skip older kvnos, but we should extract * them... */ for (j = 0; j < version; j++) { /* enctype */ CHECK(ret = krb5_ret_uint16(sp, &u16)); /* encrypted key (or plaintext salt) */ CHECK(ret = krb5_ret_uint16(sp, &u16)); krb5_storage_seek(sp, u16, SEEK_CUR); } } } if (entry->kvno == 0 && kvno != 0) { ret = HDB_ERR_NOT_FOUND_HERE; goto out; } return 0; out: if (ret == HEIM_ERR_EOF) /* Better error code than "end of file" */ ret = HEIM_ERR_BAD_HDBENT_ENCODING; return ret; }