Path: blob/main/crypto/heimdal/lib/hdb/hdb-mitdb.c
105688 views
/*1* Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan2* (Royal Institute of Technology, Stockholm, Sweden).3* All rights reserved.4*5* Portions Copyright (c) 2009 Apple Inc. All rights reserved.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions9* are met:10*11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13*14* 2. Redistributions in binary form must reproduce the above copyright15* notice, this list of conditions and the following disclaimer in the16* documentation and/or other materials provided with the distribution.17*18* 3. Neither the name of the Institute nor the names of its contributors19* may be used to endorse or promote products derived from this software20* without specific prior written permission.21*22* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND23* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE24* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE25* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE26* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL27* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS28* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)29* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT30* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY31* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF32* SUCH DAMAGE.33*/3435#define KRB5_KDB_DISALLOW_POSTDATED 0x0000000136#define KRB5_KDB_DISALLOW_FORWARDABLE 0x0000000237#define KRB5_KDB_DISALLOW_TGT_BASED 0x0000000438#define KRB5_KDB_DISALLOW_RENEWABLE 0x0000000839#define KRB5_KDB_DISALLOW_PROXIABLE 0x0000001040#define KRB5_KDB_DISALLOW_DUP_SKEY 0x0000002041#define KRB5_KDB_DISALLOW_ALL_TIX 0x0000004042#define KRB5_KDB_REQUIRES_PRE_AUTH 0x0000008043#define KRB5_KDB_REQUIRES_HW_AUTH 0x0000010044#define KRB5_KDB_REQUIRES_PWCHANGE 0x0000020045#define KRB5_KDB_DISALLOW_SVR 0x0000100046#define KRB5_KDB_PWCHANGE_SERVICE 0x0000200047#define KRB5_KDB_SUPPORT_DESMD5 0x0000400048#define KRB5_KDB_NEW_PRINC 0x000080004950/*5152key: krb5_unparse_name + NUL535416: baselength5532: attributes5632: max time5732: max renewable time5832: client expire5932: passwd expire6032: last successful passwd6132: last failed attempt6232: num of failed attempts6316: num tl data6416: num data data6516: principal length66length: principal67for num tl data times6816: tl data type6916: tl data length70length: length71for num key data times7216: version (num keyblocks)7316: kvno74for version times:7516: type7616: length77length: keydata787980key_data_contents[0]8182int16: length83read-of-data: key-encrypted, key-usage 0, master-key8485salt:86version2 = salt in key_data->key_data_contents[1]87else default salt.8889*/9091#include "hdb_locl.h"9293static void94attr_to_flags(unsigned attr, HDBFlags *flags)95{96flags->postdate = !(attr & KRB5_KDB_DISALLOW_POSTDATED);97flags->forwardable = !(attr & KRB5_KDB_DISALLOW_FORWARDABLE);98flags->initial = !!(attr & KRB5_KDB_DISALLOW_TGT_BASED);99flags->renewable = !(attr & KRB5_KDB_DISALLOW_RENEWABLE);100flags->proxiable = !(attr & KRB5_KDB_DISALLOW_PROXIABLE);101/* DUP_SKEY */102flags->invalid = !!(attr & KRB5_KDB_DISALLOW_ALL_TIX);103flags->require_preauth = !!(attr & KRB5_KDB_REQUIRES_PRE_AUTH);104flags->require_hwauth = !!(attr & KRB5_KDB_REQUIRES_HW_AUTH);105flags->server = !(attr & KRB5_KDB_DISALLOW_SVR);106flags->change_pw = !!(attr & KRB5_KDB_PWCHANGE_SERVICE);107flags->client = 1; /* XXX */108}109110#define KDB_V1_BASE_LENGTH 38111112#define CHECK(x) do { if ((x)) goto out; } while(0)113114#ifdef HAVE_DB1115static krb5_error_code116mdb_principal2key(krb5_context context,117krb5_const_principal principal,118krb5_data *key)119{120krb5_error_code ret;121char *str;122123ret = krb5_unparse_name(context, principal, &str);124if (ret)125return ret;126key->data = str;127key->length = strlen(str) + 1;128return 0;129}130#endif /* HAVE_DB1 */131132#define KRB5_KDB_SALTTYPE_NORMAL 0133#define KRB5_KDB_SALTTYPE_V4 1134#define KRB5_KDB_SALTTYPE_NOREALM 2135#define KRB5_KDB_SALTTYPE_ONLYREALM 3136#define KRB5_KDB_SALTTYPE_SPECIAL 4137#define KRB5_KDB_SALTTYPE_AFS3 5138#define KRB5_KDB_SALTTYPE_CERTHASH 6139140static krb5_error_code141fix_salt(krb5_context context, hdb_entry *ent, int key_num)142{143krb5_error_code ret;144Salt *salt = ent->keys.val[key_num].salt;145/* fix salt type */146switch((int)salt->type) {147case KRB5_KDB_SALTTYPE_NORMAL:148salt->type = KRB5_PADATA_PW_SALT;149break;150case KRB5_KDB_SALTTYPE_V4:151krb5_data_free(&salt->salt);152salt->type = KRB5_PADATA_PW_SALT;153break;154case KRB5_KDB_SALTTYPE_NOREALM:155{156size_t len;157size_t i;158char *p;159160len = 0;161for (i = 0; i < ent->principal->name.name_string.len; ++i)162len += strlen(ent->principal->name.name_string.val[i]);163ret = krb5_data_alloc (&salt->salt, len);164if (ret)165return ret;166p = salt->salt.data;167for (i = 0; i < ent->principal->name.name_string.len; ++i) {168memcpy (p,169ent->principal->name.name_string.val[i],170strlen(ent->principal->name.name_string.val[i]));171p += strlen(ent->principal->name.name_string.val[i]);172}173174salt->type = KRB5_PADATA_PW_SALT;175break;176}177case KRB5_KDB_SALTTYPE_ONLYREALM:178krb5_data_free(&salt->salt);179ret = krb5_data_copy(&salt->salt,180ent->principal->realm,181strlen(ent->principal->realm));182if(ret)183return ret;184salt->type = KRB5_PADATA_PW_SALT;185break;186case KRB5_KDB_SALTTYPE_SPECIAL:187salt->type = KRB5_PADATA_PW_SALT;188break;189case KRB5_KDB_SALTTYPE_AFS3:190krb5_data_free(&salt->salt);191ret = krb5_data_copy(&salt->salt,192ent->principal->realm,193strlen(ent->principal->realm));194if(ret)195return ret;196salt->type = KRB5_PADATA_AFS3_SALT;197break;198case KRB5_KDB_SALTTYPE_CERTHASH:199krb5_data_free(&salt->salt);200free(ent->keys.val[key_num].salt);201ent->keys.val[key_num].salt = NULL;202break;203default:204abort();205}206return 0;207}208209210krb5_error_code211_hdb_mdb_value2entry(krb5_context context, krb5_data *data,212krb5_kvno kvno, hdb_entry *entry)213{214krb5_error_code ret;215krb5_storage *sp;216uint32_t u32;217uint16_t u16, num_keys, num_tl;218ssize_t sz;219size_t i, j;220char *p;221222sp = krb5_storage_from_data(data);223if (sp == NULL) {224krb5_set_error_message(context, ENOMEM, "out of memory");225return ENOMEM;226}227228krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);229230/*231* 16: baselength232*233* The story here is that these 16 bits have to be a constant:234* KDB_V1_BASE_LENGTH. Once upon a time a different value here235* would have been used to indicate the presence of "extra data"236* between the "base" contents and the {principal name, TL data,237* keys} that follow it. Nothing supports such "extra data"238* nowadays, so neither do we here.239*240* XXX But... surely we ought to log about this extra data, or skip241* it, or something, in case anyone has MIT KDBs with ancient242* entries in them... Logging would allow the admin to know which243* entries to dump with MIT krb5's kdb5_util.244*/245CHECK(ret = krb5_ret_uint16(sp, &u16));246if (u16 != KDB_V1_BASE_LENGTH) { ret = EINVAL; goto out; }247/* 32: attributes */248CHECK(ret = krb5_ret_uint32(sp, &u32));249attr_to_flags(u32, &entry->flags);250251/* 32: max time */252CHECK(ret = krb5_ret_uint32(sp, &u32));253if (u32) {254entry->max_life = malloc(sizeof(*entry->max_life));255*entry->max_life = u32;256}257/* 32: max renewable time */258CHECK(ret = krb5_ret_uint32(sp, &u32));259if (u32) {260entry->max_renew = malloc(sizeof(*entry->max_renew));261*entry->max_renew = u32;262}263/* 32: client expire */264CHECK(ret = krb5_ret_uint32(sp, &u32));265if (u32) {266entry->valid_end = malloc(sizeof(*entry->valid_end));267*entry->valid_end = u32;268}269/* 32: passwd expire */270CHECK(ret = krb5_ret_uint32(sp, &u32));271if (u32) {272entry->pw_end = malloc(sizeof(*entry->pw_end));273*entry->pw_end = u32;274}275/* 32: last successful passwd */276CHECK(ret = krb5_ret_uint32(sp, &u32));277/* 32: last failed attempt */278CHECK(ret = krb5_ret_uint32(sp, &u32));279/* 32: num of failed attempts */280CHECK(ret = krb5_ret_uint32(sp, &u32));281/* 16: num tl data */282CHECK(ret = krb5_ret_uint16(sp, &u16));283num_tl = u16;284/* 16: num key data */285CHECK(ret = krb5_ret_uint16(sp, &u16));286num_keys = u16;287/* 16: principal length */288CHECK(ret = krb5_ret_uint16(sp, &u16));289/* length: principal */290{291/*292* Note that the principal name includes the NUL in the entry,293* but we don't want to take chances, so we add an extra NUL.294*/295p = malloc(u16 + 1);296if (p == NULL) {297ret = ENOMEM;298goto out;299}300sz = krb5_storage_read(sp, p, u16);301if (sz != u16) {302ret = EINVAL; /* XXX */303goto out;304}305p[u16] = '\0';306CHECK(ret = krb5_parse_name(context, p, &entry->principal));307free(p);308}309/* for num tl data times31016: tl data type31116: tl data length312length: length */313#define mit_KRB5_TL_LAST_PWD_CHANGE 1314#define mit_KRB5_TL_MOD_PRINC 2315for (i = 0; i < num_tl; i++) {316int tl_type;317krb5_principal modby;318/* 16: TL data type */319CHECK(ret = krb5_ret_uint16(sp, &u16));320tl_type = u16;321/* 16: TL data length */322CHECK(ret = krb5_ret_uint16(sp, &u16));323/*324* For rollback to MIT purposes we really must understand some325* TL data!326*327* XXX Move all this to separate functions, one per-TL type.328*/329switch (tl_type) {330case mit_KRB5_TL_LAST_PWD_CHANGE:331CHECK(ret = krb5_ret_uint32(sp, &u32));332CHECK(ret = hdb_entry_set_pw_change_time(context, entry, u32));333break;334case mit_KRB5_TL_MOD_PRINC:335if (u16 < 5) {336ret = EINVAL; /* XXX */337goto out;338}339CHECK(ret = krb5_ret_uint32(sp, &u32)); /* mod time */340p = malloc(u16 - 4 + 1);341if (!p) {342ret = ENOMEM;343goto out;344}345p[u16 - 4] = '\0';346sz = krb5_storage_read(sp, p, u16 - 4);347if (sz != u16 - 4) {348ret = EINVAL; /* XXX */349goto out;350}351CHECK(ret = krb5_parse_name(context, p, &modby));352ret = hdb_set_last_modified_by(context, entry, modby, u32);353krb5_free_principal(context, modby);354free(p);355break;356default:357krb5_storage_seek(sp, u16, SEEK_CUR);358break;359}360}361/*362* for num key data times363* 16: "version"364* 16: kvno365* for version times:366* 16: type367* 16: length368* length: keydata369*370* "version" here is really 1 or 2, the first meaning there's only371* keys for this kvno, the second meaning there's keys and salt[s?].372* That's right... hold that gag reflex, you can do it.373*/374for (i = 0; i < num_keys; i++) {375int keep = 0;376uint16_t version;377void *ptr;378379CHECK(ret = krb5_ret_uint16(sp, &u16));380version = u16;381CHECK(ret = krb5_ret_uint16(sp, &u16));382383/*384* First time through, and until we find one matching key,385* entry->kvno == 0.386*/387if ((entry->kvno < u16) && (kvno == 0 || kvno == u16)) {388keep = 1;389entry->kvno = u16;390/*391* Found a higher kvno than earlier, so free the old highest392* kvno keys.393*394* XXX Of course, we actually want to extract the old kvnos395* as well, for some of the kadm5 APIs. We shouldn't free396* these keys, but keep them elsewhere.397*/398for (j = 0; j < entry->keys.len; j++)399free_Key(&entry->keys.val[j]);400free(entry->keys.val);401entry->keys.len = 0;402entry->keys.val = NULL;403} else if (entry->kvno == u16)404/* Accumulate keys */405keep = 1;406407if (keep) {408Key *k;409410ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1));411if (ptr == NULL) {412ret = ENOMEM;413goto out;414}415entry->keys.val = ptr;416417/* k points to current Key */418k = &entry->keys.val[entry->keys.len];419420memset(k, 0, sizeof(*k));421entry->keys.len += 1;422423k->mkvno = malloc(sizeof(*k->mkvno));424if (k->mkvno == NULL) {425ret = ENOMEM;426goto out;427}428*k->mkvno = 1;429430for (j = 0; j < version; j++) {431uint16_t type;432CHECK(ret = krb5_ret_uint16(sp, &type));433CHECK(ret = krb5_ret_uint16(sp, &u16));434if (j == 0) {435/* This "version" means we have a key */436k->key.keytype = type;437if (u16 < 2) {438ret = EINVAL;439goto out;440}441/*442* MIT stores keys encrypted keys as {16-bit length443* of plaintext key, {encrypted key}}. The reason444* for this is that the Kerberos cryptosystem is not445* length-preserving. Heimdal's approach is to446* truncate the plaintext to the expected length of447* the key given its enctype, so we ignore this448* 16-bit length-of-plaintext-key field.449*/450krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */451k->key.keyvalue.length = u16 - 2; /* adjust cipher len */452k->key.keyvalue.data = malloc(k->key.keyvalue.length);453krb5_storage_read(sp, k->key.keyvalue.data,454k->key.keyvalue.length);455} else if (j == 1) {456/* This "version" means we have a salt */457k->salt = calloc(1, sizeof(*k->salt));458if (k->salt == NULL) {459ret = ENOMEM;460goto out;461}462k->salt->type = type;463if (u16 != 0) {464k->salt->salt.data = malloc(u16);465if (k->salt->salt.data == NULL) {466ret = ENOMEM;467goto out;468}469k->salt->salt.length = u16;470krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length);471}472fix_salt(context, entry, entry->keys.len - 1);473} else {474/*475* Whatever this "version" might be, we skip it476*477* XXX A krb5.conf parameter requesting that we log478* about strangeness like this, or return an error479* from here, might be nice.480*/481krb5_storage_seek(sp, u16, SEEK_CUR);482}483}484} else {485/*486* XXX For now we skip older kvnos, but we should extract487* them...488*/489for (j = 0; j < version; j++) {490/* enctype */491CHECK(ret = krb5_ret_uint16(sp, &u16));492/* encrypted key (or plaintext salt) */493CHECK(ret = krb5_ret_uint16(sp, &u16));494krb5_storage_seek(sp, u16, SEEK_CUR);495}496}497}498499if (entry->kvno == 0 && kvno != 0) {500ret = HDB_ERR_NOT_FOUND_HERE;501goto out;502}503504return 0;505out:506if (ret == HEIM_ERR_EOF)507/* Better error code than "end of file" */508ret = HEIM_ERR_BAD_HDBENT_ENCODING;509return ret;510}511512#if 0513static krb5_error_code514mdb_entry2value(krb5_context context, hdb_entry *entry, krb5_data *data)515{516return EINVAL;517}518#endif519520#if HAVE_DB1521522#if defined(HAVE_DB_185_H)523#include <db_185.h>524#elif defined(HAVE_DB_H)525#include <db.h>526#endif527528529static krb5_error_code530mdb_close(krb5_context context, HDB *db)531{532DB *d = (DB*)db->hdb_db;533(*d->close)(d);534return 0;535}536537static krb5_error_code538mdb_destroy(krb5_context context, HDB *db)539{540krb5_error_code ret;541542ret = hdb_clear_master_key (context, db);543free(db->hdb_name);544free(db);545return ret;546}547548static krb5_error_code549mdb_lock(krb5_context context, HDB *db, int operation)550{551DB *d = (DB*)db->hdb_db;552int fd = (*d->fd)(d);553if(fd < 0) {554krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,555"Can't lock database: %s", db->hdb_name);556return HDB_ERR_CANT_LOCK_DB;557}558return hdb_lock(fd, operation);559}560561static krb5_error_code562mdb_unlock(krb5_context context, HDB *db)563{564DB *d = (DB*)db->hdb_db;565int fd = (*d->fd)(d);566if(fd < 0) {567krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,568"Can't unlock database: %s", db->hdb_name);569return HDB_ERR_CANT_LOCK_DB;570}571return hdb_unlock(fd);572}573574575static krb5_error_code576mdb_seq(krb5_context context, HDB *db,577unsigned flags, hdb_entry_ex *entry, int flag)578{579DB *d = (DB*)db->hdb_db;580DBT key, value;581krb5_data key_data, data;582int code;583584code = db->hdb_lock(context, db, HDB_RLOCK);585if(code == -1) {586krb5_set_error_message(context, HDB_ERR_DB_INUSE, "Database %s in use", db->hdb_name);587return HDB_ERR_DB_INUSE;588}589code = (*d->seq)(d, &key, &value, flag);590db->hdb_unlock(context, db); /* XXX check value */591if(code == -1) {592code = errno;593krb5_set_error_message(context, code, "Database %s seq error: %s",594db->hdb_name, strerror(code));595return code;596}597if(code == 1) {598krb5_clear_error_message(context);599return HDB_ERR_NOENTRY;600}601602key_data.data = key.data;603key_data.length = key.size;604data.data = value.data;605data.length = value.size;606memset(entry, 0, sizeof(*entry));607608if (_hdb_mdb_value2entry(context, &data, 0, &entry->entry))609return mdb_seq(context, db, flags, entry, R_NEXT);610611if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {612code = hdb_unseal_keys (context, db, &entry->entry);613if (code)614hdb_free_entry (context, entry);615}616617return code;618}619620621static krb5_error_code622mdb_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)623{624return mdb_seq(context, db, flags, entry, R_FIRST);625}626627628static krb5_error_code629mdb_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)630{631return mdb_seq(context, db, flags, entry, R_NEXT);632}633634static krb5_error_code635mdb_rename(krb5_context context, HDB *db, const char *new_name)636{637int ret;638char *old, *new;639640asprintf(&old, "%s.db", db->hdb_name);641asprintf(&new, "%s.db", new_name);642ret = rename(old, new);643free(old);644free(new);645if(ret)646return errno;647648free(db->hdb_name);649db->hdb_name = strdup(new_name);650return 0;651}652653static krb5_error_code654mdb__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)655{656DB *d = (DB*)db->hdb_db;657DBT k, v;658int code;659660k.data = key.data;661k.size = key.length;662code = db->hdb_lock(context, db, HDB_RLOCK);663if(code)664return code;665code = (*d->get)(d, &k, &v, 0);666db->hdb_unlock(context, db);667if(code < 0) {668code = errno;669krb5_set_error_message(context, code, "Database %s get error: %s",670db->hdb_name, strerror(code));671return code;672}673if(code == 1) {674krb5_clear_error_message(context);675return HDB_ERR_NOENTRY;676}677678krb5_data_copy(reply, v.data, v.size);679return 0;680}681682static krb5_error_code683mdb__put(krb5_context context, HDB *db, int replace,684krb5_data key, krb5_data value)685{686DB *d = (DB*)db->hdb_db;687DBT k, v;688int code;689690k.data = key.data;691k.size = key.length;692v.data = value.data;693v.size = value.length;694code = db->hdb_lock(context, db, HDB_WLOCK);695if(code)696return code;697code = (*d->put)(d, &k, &v, replace ? 0 : R_NOOVERWRITE);698db->hdb_unlock(context, db);699if(code < 0) {700code = errno;701krb5_set_error_message(context, code, "Database %s put error: %s",702db->hdb_name, strerror(code));703return code;704}705if(code == 1) {706krb5_clear_error_message(context);707return HDB_ERR_EXISTS;708}709return 0;710}711712static krb5_error_code713mdb__del(krb5_context context, HDB *db, krb5_data key)714{715DB *d = (DB*)db->hdb_db;716DBT k;717krb5_error_code code;718k.data = key.data;719k.size = key.length;720code = db->hdb_lock(context, db, HDB_WLOCK);721if(code)722return code;723code = (*d->del)(d, &k, 0);724db->hdb_unlock(context, db);725if(code == 1) {726code = errno;727krb5_set_error_message(context, code, "Database %s put error: %s",728db->hdb_name, strerror(code));729return code;730}731if(code < 0)732return errno;733return 0;734}735736static krb5_error_code737mdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,738unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry)739{740krb5_data key, value;741krb5_error_code ret;742743ret = mdb_principal2key(context, principal, &key);744if (ret)745return ret;746ret = db->hdb__get(context, db, key, &value);747krb5_data_free(&key);748if(ret)749return ret;750ret = _hdb_mdb_value2entry(context, &value, kvno, &entry->entry);751krb5_data_free(&value);752if (ret)753return ret;754755if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {756ret = hdb_unseal_keys (context, db, &entry->entry);757if (ret) {758hdb_free_entry(context, entry);759return ret;760}761}762763return 0;764}765766static krb5_error_code767mdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)768{769krb5_error_code ret;770krb5_storage *sp = NULL;771krb5_storage *spent = NULL;772krb5_data line = { 0, 0 };773krb5_data kdb_ent = { 0, 0 };774krb5_data key = { 0, 0 };775ssize_t sz;776777sp = krb5_storage_emem();778if (!sp) return ENOMEM;779ret = _hdb_set_master_key_usage(context, db, 0); /* MIT KDB uses KU 0 */780ret = hdb_seal_keys(context, db, &entry->entry);781if (ret) return ret;782ret = entry2mit_string_int(context, sp, &entry->entry);783if (ret) goto out;784sz = krb5_storage_write(sp, "\n", 2); /* NUL-terminate */785ret = ENOMEM;786if (sz == -1) goto out;787ret = krb5_storage_to_data(sp, &line);788if (ret) goto out;789790ret = ENOMEM;791spent = krb5_storage_emem();792if (!spent) goto out;793ret = _hdb_mit_dump2mitdb_entry(context, line.data, spent);794if (ret) goto out;795ret = krb5_storage_to_data(spent, &kdb_ent);796if (ret) goto out;797ret = mdb_principal2key(context, entry->entry.principal, &key);798if (ret) goto out;799ret = mdb__put(context, db, 1, key, kdb_ent);800801out:802if (sp)803krb5_storage_free(sp);804if (spent)805krb5_storage_free(spent);806krb5_data_free(&line);807krb5_data_free(&kdb_ent);808krb5_data_free(&key);809810return ret;811}812813static krb5_error_code814mdb_remove(krb5_context context, HDB *db, krb5_const_principal principal)815{816krb5_error_code code;817krb5_data key;818819code = db->hdb__del(context, db, key);820krb5_data_free(&key);821return code;822}823824static krb5_error_code825mdb_open(krb5_context context, HDB *db, int flags, mode_t mode)826{827char *fn;828char *actual_fn;829krb5_error_code ret;830struct stat st;831832asprintf(&fn, "%s.db", db->hdb_name);833if (fn == NULL) {834krb5_set_error_message(context, ENOMEM, "malloc: out of memory");835return ENOMEM;836}837838if (stat(fn, &st) == 0)839actual_fn = fn;840else841actual_fn = db->hdb_name;842db->hdb_db = dbopen(actual_fn, flags, mode, DB_BTREE, NULL);843if (db->hdb_db == NULL) {844switch (errno) {845#ifdef EFTYPE846case EFTYPE:847#endif848case EINVAL:849db->hdb_db = dbopen(actual_fn, flags, mode, DB_BTREE, NULL);850}851}852free(fn);853854/* try to open without .db extension */855if(db->hdb_db == NULL && errno == ENOENT)856db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL);857if(db->hdb_db == NULL) {858ret = errno;859krb5_set_error_message(context, ret, "dbopen (%s): %s",860db->hdb_name, strerror(ret));861return ret;862}863#if 0864/*865* Don't do this -- MIT won't be able to handle the866* HDB_DB_FORMAT_ENTRY key.867*/868if ((flags & O_ACCMODE) != O_RDONLY)869ret = hdb_init_db(context, db);870#endif871ret = hdb_check_db_format(context, db);872if (ret == HDB_ERR_NOENTRY) {873krb5_clear_error_message(context);874return 0;875}876if (ret) {877mdb_close(context, db);878krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",879(flags & O_ACCMODE) == O_RDONLY ?880"checking format of" : "initialize",881db->hdb_name);882}883return ret;884}885886krb5_error_code887hdb_mdb_create(krb5_context context, HDB **db,888const char *filename)889{890*db = calloc(1, sizeof(**db));891if (*db == NULL) {892krb5_set_error_message(context, ENOMEM, "malloc: out of memory");893return ENOMEM;894}895896(*db)->hdb_db = NULL;897(*db)->hdb_name = strdup(filename);898if ((*db)->hdb_name == NULL) {899free(*db);900*db = NULL;901krb5_set_error_message(context, ENOMEM, "malloc: out of memory");902return ENOMEM;903}904(*db)->hdb_master_key_set = 0;905(*db)->hdb_openp = 0;906(*db)->hdb_capability_flags = 0;907(*db)->hdb_open = mdb_open;908(*db)->hdb_close = mdb_close;909(*db)->hdb_fetch_kvno = mdb_fetch_kvno;910(*db)->hdb_store = mdb_store;911(*db)->hdb_remove = mdb_remove;912(*db)->hdb_firstkey = mdb_firstkey;913(*db)->hdb_nextkey= mdb_nextkey;914(*db)->hdb_lock = mdb_lock;915(*db)->hdb_unlock = mdb_unlock;916(*db)->hdb_rename = mdb_rename;917(*db)->hdb__get = mdb__get;918(*db)->hdb__put = mdb__put;919(*db)->hdb__del = mdb__del;920(*db)->hdb_destroy = mdb_destroy;921return 0;922}923924#endif /* HAVE_DB1 */925926/*927can have any number of princ stanzas.928format is as follows (only \n indicates newlines)929princ\t%d\t (%d is KRB5_KDB_V1_BASE_LENGTH, always 38)930%d\t (strlen of principal e.g. shadow/[email protected])931%d\t (number of tl_data)932%d\t (number of key data, e.g. how many keys for this user)933%d\t (extra data length)934%s\t (principal name)935%d\t (attributes)936%d\t (max lifetime, seconds)937%d\t (max renewable life, seconds)938%d\t (expiration, seconds since epoch or 2145830400 for never)939%d\t (password expiration, seconds, 0 for never)940%d\t (last successful auth, seconds since epoch)941%d\t (last failed auth, per above)942%d\t (failed auth count)943foreach tl_data 0 to number of tl_data - 1 as above944%d\t%d\t (data type, data length)945foreach tl_data 0 to length-1946%02x (tl data contents[element n])947except if tl_data length is 0948%d (always -1)949\t950foreach key 0 to number of keys - 1 as above951%d\t%d\t (key data version, kvno)952foreach version 0 to key data version - 1 (a key or a salt)953%d\t%d\t(data type for this key, data length for this key)954foreach key data length 0 to length-1955%02x (key data contents[element n])956except if key_data length is 0957%d (always -1)958\t959foreach extra data length 0 to length - 1960%02x (extra data part)961unless no extra data962%d (always -1)963;\n964965*/966967static char *968nexttoken(char **p)969{970char *q;971do {972q = strsep(p, " \t");973} while(q && *q == '\0');974return q;975}976977static size_t978getdata(char **p, unsigned char *buf, size_t len)979{980size_t i;981int v;982char *q = nexttoken(p);983i = 0;984while(*q && i < len) {985if(sscanf(q, "%02x", &v) != 1)986break;987buf[i++] = v;988q += 2;989}990return i;991}992993static int994getint(char **p)995{996int val;997char *q = nexttoken(p);998sscanf(q, "%d", &val);999return val;1000}10011002static unsigned int1003getuint(char **p)1004{1005int val;1006char *q = nexttoken(p);1007sscanf(q, "%u", &val);1008return val;1009}10101011#define KRB5_KDB_SALTTYPE_NORMAL 01012#define KRB5_KDB_SALTTYPE_V4 11013#define KRB5_KDB_SALTTYPE_NOREALM 21014#define KRB5_KDB_SALTTYPE_ONLYREALM 31015#define KRB5_KDB_SALTTYPE_SPECIAL 41016#define KRB5_KDB_SALTTYPE_AFS3 510171018#define CHECK_UINT(num) \1019if ((num) < 0 || (num) > INT_MAX) return EINVAL1020#define CHECK_UINT16(num) \1021if ((num) < 0 || (num) > 1<<15) return EINVAL1022#define CHECK_NUM(num, maxv) \1023if ((num) > (maxv)) return EINVAL10241025/*1026* This utility function converts an MIT dump entry to an MIT on-disk1027* encoded entry, which can then be decoded with _hdb_mdb_value2entry().1028* This allows us to have a single decoding function (_hdb_mdb_value2entry),1029* which makes the code cleaner (less code duplication), if a bit less1030* efficient. It also will allow us to have a function to dump an HDB1031* entry in MIT format so we can dump HDB into MIT format for rollback1032* purposes. And that will allow us to write to MIT KDBs, again1033* somewhat inefficiently, also for migration/rollback purposes.1034*/1035int1036_hdb_mit_dump2mitdb_entry(krb5_context context, char *line, krb5_storage *sp)1037{1038krb5_error_code ret = EINVAL;1039char *p = line, *q;1040char *princ;1041ssize_t sz;1042size_t i;1043size_t princ_len;1044unsigned int num_tl_data;1045size_t num_key_data;1046unsigned int attributes;1047int tmp;10481049krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);10501051q = nexttoken(&p);1052if (strcmp(q, "kdb5_util") == 0 || strcmp(q, "policy") == 0 ||1053strcmp(q, "princ") != 0) {1054return -1;1055}1056if (getint(&p) != 38)1057return EINVAL;1058#define KDB_V1_BASE_LENGTH 381059ret = krb5_store_int16(sp, KDB_V1_BASE_LENGTH);1060if (ret) return ret;10611062nexttoken(&p); /* length of principal */1063num_tl_data = getuint(&p); /* number of tl-data */1064num_key_data = getuint(&p); /* number of key-data */1065getint(&p); /* length of extra data */1066princ = nexttoken(&p); /* principal name */10671068attributes = getuint(&p); /* attributes */1069ret = krb5_store_uint32(sp, attributes);1070if (ret) return ret;10711072tmp = getint(&p); /* max life */1073CHECK_UINT(tmp);1074ret = krb5_store_uint32(sp, tmp);1075if (ret) return ret;10761077tmp = getint(&p); /* max renewable life */1078CHECK_UINT(tmp);1079ret = krb5_store_uint32(sp, tmp);1080if (ret) return ret;10811082tmp = getint(&p); /* expiration */1083CHECK_UINT(tmp);1084ret = krb5_store_uint32(sp, tmp);1085if (ret) return ret;10861087tmp = getint(&p); /* pw expiration */1088CHECK_UINT(tmp);1089ret = krb5_store_uint32(sp, tmp);1090if (ret) return ret;10911092tmp = getint(&p); /* last auth */1093CHECK_UINT(tmp);1094ret = krb5_store_uint32(sp, tmp);1095if (ret) return ret;10961097tmp = getint(&p); /* last failed auth */1098CHECK_UINT(tmp);1099ret = krb5_store_uint32(sp, tmp);1100if (ret) return ret;11011102tmp = getint(&p); /* fail auth count */1103CHECK_UINT(tmp);1104ret = krb5_store_uint32(sp, tmp);1105if (ret) return ret;11061107/* add TL data count */1108CHECK_NUM(num_tl_data, 1023);1109ret = krb5_store_uint16(sp, num_tl_data);1110if (ret) return ret;11111112/* add key count */1113CHECK_NUM(num_key_data, 1023);1114ret = krb5_store_uint16(sp, num_key_data);1115if (ret) return ret;11161117/* add principal unparsed name length and unparsed name */1118princ_len = strlen(princ);1119if (princ_len > (1<<15) - 1) return EINVAL;1120princ_len++; /* must count and write the NUL in the on-disk encoding */1121ret = krb5_store_uint16(sp, princ_len);1122if (ret) return ret;1123sz = krb5_storage_write(sp, princ, princ_len);1124if (sz == -1) return ENOMEM;11251126/* scan and write TL data */1127for (i = 0; i < num_tl_data; i++) {1128int tl_type, tl_length;1129unsigned char *buf;11301131tl_type = getint(&p); /* data type */1132tl_length = getint(&p); /* data length */11331134CHECK_UINT16(tl_type);1135ret = krb5_store_uint16(sp, tl_type);1136if (ret) return ret;1137CHECK_UINT16(tl_length);1138ret = krb5_store_uint16(sp, tl_length);1139if (ret) return ret;11401141if (tl_length) {1142buf = malloc(tl_length);1143if (!buf) return ENOMEM;1144if (getdata(&p, buf, tl_length) != tl_length) return EINVAL;1145sz = krb5_storage_write(sp, buf, tl_length);1146free(buf);1147if (sz == -1) return ENOMEM;1148} else {1149if (strcmp(nexttoken(&p), "-1") != 0) return EINVAL;1150}1151}11521153for (i = 0; i < num_key_data; i++) {1154unsigned char *buf;1155int key_versions;1156int kvno;1157int keytype;1158int keylen;1159size_t k;11601161key_versions = getint(&p); /* key data version */1162CHECK_UINT16(key_versions);1163ret = krb5_store_int16(sp, key_versions);1164if (ret) return ret;11651166kvno = getint(&p);1167CHECK_UINT16(kvno);1168ret = krb5_store_int16(sp, kvno);1169if (ret) return ret;11701171for (k = 0; k < key_versions; k++) {1172keytype = getint(&p);1173CHECK_UINT16(keytype);1174ret = krb5_store_int16(sp, keytype);1175if (ret) return ret;11761177keylen = getint(&p);1178CHECK_UINT16(keylen);1179ret = krb5_store_int16(sp, keylen);1180if (ret) return ret;11811182if (keylen) {1183buf = malloc(keylen);1184if (!buf) return ENOMEM;1185if (getdata(&p, buf, keylen) != keylen) return EINVAL;1186sz = krb5_storage_write(sp, buf, keylen);1187free(buf);1188if (sz == -1) return ENOMEM;1189} else {1190if (strcmp(nexttoken(&p), "-1") != 0) return EINVAL;1191}1192}1193}1194/*1195* The rest is "extra data", but there's never any and we wouldn't1196* know what to do with it.1197*/1198/* nexttoken(&p); */1199return 0;1200}1201120212031204