1226031Sstas/* 2226031Sstas * Copyright (c) 1997 - 2001 Kungliga Tekniska H��gskolan 3226031Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4226031Sstas * All rights reserved. 5226031Sstas * 6226031Sstas * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 7226031Sstas * 8226031Sstas * Redistribution and use in source and binary forms, with or without 9226031Sstas * modification, are permitted provided that the following conditions 10226031Sstas * are met: 11226031Sstas * 12226031Sstas * 1. Redistributions of source code must retain the above copyright 13226031Sstas * notice, this list of conditions and the following disclaimer. 14226031Sstas * 15226031Sstas * 2. Redistributions in binary form must reproduce the above copyright 16226031Sstas * notice, this list of conditions and the following disclaimer in the 17226031Sstas * documentation and/or other materials provided with the distribution. 18226031Sstas * 19226031Sstas * 3. Neither the name of the Institute nor the names of its contributors 20226031Sstas * may be used to endorse or promote products derived from this software 21226031Sstas * without specific prior written permission. 22226031Sstas * 23226031Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26226031Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33226031Sstas * SUCH DAMAGE. 34226031Sstas */ 35226031Sstas 36226031Sstas#define KRB5_KDB_DISALLOW_POSTDATED 0x00000001 37226031Sstas#define KRB5_KDB_DISALLOW_FORWARDABLE 0x00000002 38226031Sstas#define KRB5_KDB_DISALLOW_TGT_BASED 0x00000004 39226031Sstas#define KRB5_KDB_DISALLOW_RENEWABLE 0x00000008 40226031Sstas#define KRB5_KDB_DISALLOW_PROXIABLE 0x00000010 41226031Sstas#define KRB5_KDB_DISALLOW_DUP_SKEY 0x00000020 42226031Sstas#define KRB5_KDB_DISALLOW_ALL_TIX 0x00000040 43226031Sstas#define KRB5_KDB_REQUIRES_PRE_AUTH 0x00000080 44226031Sstas#define KRB5_KDB_REQUIRES_HW_AUTH 0x00000100 45226031Sstas#define KRB5_KDB_REQUIRES_PWCHANGE 0x00000200 46226031Sstas#define KRB5_KDB_DISALLOW_SVR 0x00001000 47226031Sstas#define KRB5_KDB_PWCHANGE_SERVICE 0x00002000 48226031Sstas#define KRB5_KDB_SUPPORT_DESMD5 0x00004000 49226031Sstas#define KRB5_KDB_NEW_PRINC 0x00008000 50226031Sstas 51226031Sstas/* 52226031Sstas 53226031Sstaskey: krb5_unparse_name + NUL 54226031Sstas 55226031Sstas 16: baselength 56226031Sstas 32: attributes 57226031Sstas 32: max time 58226031Sstas 32: max renewable time 59226031Sstas 32: client expire 60226031Sstas 32: passwd expire 61226031Sstas 32: last successful passwd 62226031Sstas 32: last failed attempt 63226031Sstas 32: num of failed attempts 64226031Sstas 16: num tl data 65226031Sstas 16: num data data 66226031Sstas 16: principal length 67226031Sstas length: principal 68226031Sstas for num tl data times 69226031Sstas 16: tl data type 70226031Sstas 16: tl data length 71226031Sstas length: length 72226031Sstas for num key data times 73226031Sstas 16: version (num keyblocks) 74226031Sstas 16: kvno 75226031Sstas for version times: 76226031Sstas 16: type 77226031Sstas 16: length 78226031Sstas length: keydata 79226031Sstas 80226031Sstas 81226031Sstaskey_data_contents[0] 82226031Sstas 83226031Sstas int16: length 84226031Sstas read-of-data: key-encrypted, key-usage 0, master-key 85226031Sstas 86226031Sstassalt: 87226031Sstas version2 = salt in key_data->key_data_contents[1] 88226031Sstas else default salt. 89226031Sstas 90226031Sstas*/ 91226031Sstas 92226031Sstas#include "hdb_locl.h" 93226031Sstas 94226031Sstas#define KDB_V1_BASE_LENGTH 38 95226031Sstas 96226031Sstas#if HAVE_DB1 97226031Sstas 98226031Sstas#if defined(HAVE_DB_185_H) 99226031Sstas#include <db_185.h> 100226031Sstas#elif defined(HAVE_DB_H) 101226031Sstas#include <db.h> 102226031Sstas#endif 103226031Sstas 104226031Sstas#define CHECK(x) do { if ((x)) goto out; } while(0) 105226031Sstas 106226031Sstasstatic krb5_error_code 107226031Sstasmdb_principal2key(krb5_context context, 108226031Sstas krb5_const_principal principal, 109226031Sstas krb5_data *key) 110226031Sstas{ 111226031Sstas krb5_error_code ret; 112226031Sstas char *str; 113226031Sstas 114226031Sstas ret = krb5_unparse_name(context, principal, &str); 115226031Sstas if (ret) 116226031Sstas return ret; 117226031Sstas key->data = str; 118226031Sstas key->length = strlen(str) + 1; 119226031Sstas return 0; 120226031Sstas} 121226031Sstas 122226031Sstas#define KRB5_KDB_SALTTYPE_NORMAL 0 123226031Sstas#define KRB5_KDB_SALTTYPE_V4 1 124226031Sstas#define KRB5_KDB_SALTTYPE_NOREALM 2 125226031Sstas#define KRB5_KDB_SALTTYPE_ONLYREALM 3 126226031Sstas#define KRB5_KDB_SALTTYPE_SPECIAL 4 127226031Sstas#define KRB5_KDB_SALTTYPE_AFS3 5 128226031Sstas#define KRB5_KDB_SALTTYPE_CERTHASH 6 129226031Sstas 130226031Sstasstatic krb5_error_code 131226031Sstasfix_salt(krb5_context context, hdb_entry *ent, int key_num) 132226031Sstas{ 133226031Sstas krb5_error_code ret; 134226031Sstas Salt *salt = ent->keys.val[key_num].salt; 135226031Sstas /* fix salt type */ 136226031Sstas switch((int)salt->type) { 137226031Sstas case KRB5_KDB_SALTTYPE_NORMAL: 138226031Sstas salt->type = KRB5_PADATA_PW_SALT; 139226031Sstas break; 140226031Sstas case KRB5_KDB_SALTTYPE_V4: 141226031Sstas krb5_data_free(&salt->salt); 142226031Sstas salt->type = KRB5_PADATA_PW_SALT; 143226031Sstas break; 144226031Sstas case KRB5_KDB_SALTTYPE_NOREALM: 145226031Sstas { 146226031Sstas size_t len; 147226031Sstas size_t i; 148226031Sstas char *p; 149226031Sstas 150226031Sstas len = 0; 151226031Sstas for (i = 0; i < ent->principal->name.name_string.len; ++i) 152226031Sstas len += strlen(ent->principal->name.name_string.val[i]); 153226031Sstas ret = krb5_data_alloc (&salt->salt, len); 154226031Sstas if (ret) 155226031Sstas return ret; 156226031Sstas p = salt->salt.data; 157226031Sstas for (i = 0; i < ent->principal->name.name_string.len; ++i) { 158226031Sstas memcpy (p, 159226031Sstas ent->principal->name.name_string.val[i], 160226031Sstas strlen(ent->principal->name.name_string.val[i])); 161226031Sstas p += strlen(ent->principal->name.name_string.val[i]); 162226031Sstas } 163226031Sstas 164226031Sstas salt->type = KRB5_PADATA_PW_SALT; 165226031Sstas break; 166226031Sstas } 167226031Sstas case KRB5_KDB_SALTTYPE_ONLYREALM: 168226031Sstas krb5_data_free(&salt->salt); 169226031Sstas ret = krb5_data_copy(&salt->salt, 170226031Sstas ent->principal->realm, 171226031Sstas strlen(ent->principal->realm)); 172226031Sstas if(ret) 173226031Sstas return ret; 174226031Sstas salt->type = KRB5_PADATA_PW_SALT; 175226031Sstas break; 176226031Sstas case KRB5_KDB_SALTTYPE_SPECIAL: 177226031Sstas salt->type = KRB5_PADATA_PW_SALT; 178226031Sstas break; 179226031Sstas case KRB5_KDB_SALTTYPE_AFS3: 180226031Sstas krb5_data_free(&salt->salt); 181226031Sstas ret = krb5_data_copy(&salt->salt, 182226031Sstas ent->principal->realm, 183226031Sstas strlen(ent->principal->realm)); 184226031Sstas if(ret) 185226031Sstas return ret; 186226031Sstas salt->type = KRB5_PADATA_AFS3_SALT; 187226031Sstas break; 188226031Sstas case KRB5_KDB_SALTTYPE_CERTHASH: 189226031Sstas krb5_data_free(&salt->salt); 190226031Sstas free(ent->keys.val[key_num].salt); 191226031Sstas ent->keys.val[key_num].salt = NULL; 192226031Sstas break; 193226031Sstas default: 194226031Sstas abort(); 195226031Sstas } 196226031Sstas return 0; 197226031Sstas} 198226031Sstas 199226031Sstas 200226031Sstasstatic krb5_error_code 201226031Sstasmdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry *entry) 202226031Sstas{ 203226031Sstas krb5_error_code ret; 204226031Sstas krb5_storage *sp; 205226031Sstas uint32_t u32; 206226031Sstas uint16_t u16, num_keys, num_tl; 207226031Sstas size_t i, j; 208226031Sstas char *p; 209226031Sstas 210226031Sstas sp = krb5_storage_from_data(data); 211226031Sstas if (sp == NULL) { 212226031Sstas krb5_set_error_message(context, ENOMEM, "out of memory"); 213226031Sstas return ENOMEM; 214226031Sstas } 215226031Sstas 216226031Sstas krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE); 217226031Sstas 218226031Sstas /* 219226031Sstas * 16: baselength 220226031Sstas * 221226031Sstas * The story here is that these 16 bits have to be a constant: 222226031Sstas * KDB_V1_BASE_LENGTH. Once upon a time a different value here 223226031Sstas * would have been used to indicate the presence of "extra data" 224226031Sstas * between the "base" contents and the {principal name, TL data, 225226031Sstas * keys} that follow it. Nothing supports such "extra data" 226226031Sstas * nowadays, so neither do we here. 227226031Sstas * 228226031Sstas * XXX But... surely we ought to log about this extra data, or skip 229226031Sstas * it, or something, in case anyone has MIT KDBs with ancient 230226031Sstas * entries in them... Logging would allow the admin to know which 231226031Sstas * entries to dump with MIT krb5's kdb5_util. 232226031Sstas */ 233226031Sstas CHECK(ret = krb5_ret_uint16(sp, &u16)); 234226031Sstas if (u16 != KDB_V1_BASE_LENGTH) { ret = EINVAL; goto out; } 235226031Sstas /* 32: attributes */ 236226031Sstas CHECK(ret = krb5_ret_uint32(sp, &u32)); 237226031Sstas entry->flags.postdate = !(u32 & KRB5_KDB_DISALLOW_POSTDATED); 238226031Sstas entry->flags.forwardable = !(u32 & KRB5_KDB_DISALLOW_FORWARDABLE); 239226031Sstas entry->flags.initial = !!(u32 & KRB5_KDB_DISALLOW_TGT_BASED); 240226031Sstas entry->flags.renewable = !(u32 & KRB5_KDB_DISALLOW_RENEWABLE); 241226031Sstas entry->flags.proxiable = !(u32 & KRB5_KDB_DISALLOW_PROXIABLE); 242226031Sstas /* DUP_SKEY */ 243226031Sstas entry->flags.invalid = !!(u32 & KRB5_KDB_DISALLOW_ALL_TIX); 244226031Sstas entry->flags.require_preauth =!!(u32 & KRB5_KDB_REQUIRES_PRE_AUTH); 245226031Sstas entry->flags.require_hwauth =!!(u32 & KRB5_KDB_REQUIRES_HW_AUTH); 246226031Sstas entry->flags.server = !(u32 & KRB5_KDB_DISALLOW_SVR); 247226031Sstas entry->flags.change_pw = !!(u32 & KRB5_KDB_PWCHANGE_SERVICE); 248226031Sstas entry->flags.client = 1; /* XXX */ 249226031Sstas 250226031Sstas /* 32: max time */ 251226031Sstas CHECK(ret = krb5_ret_uint32(sp, &u32)); 252226031Sstas if (u32) { 253226031Sstas entry->max_life = malloc(sizeof(*entry->max_life)); 254226031Sstas *entry->max_life = u32; 255226031Sstas } 256226031Sstas /* 32: max renewable time */ 257226031Sstas CHECK(ret = krb5_ret_uint32(sp, &u32)); 258226031Sstas if (u32) { 259226031Sstas entry->max_renew = malloc(sizeof(*entry->max_renew)); 260226031Sstas *entry->max_renew = u32; 261226031Sstas } 262226031Sstas /* 32: client expire */ 263226031Sstas CHECK(ret = krb5_ret_uint32(sp, &u32)); 264226031Sstas if (u32) { 265226031Sstas entry->valid_end = malloc(sizeof(*entry->valid_end)); 266226031Sstas *entry->valid_end = u32; 267226031Sstas } 268226031Sstas /* 32: passwd expire */ 269226031Sstas CHECK(ret = krb5_ret_uint32(sp, &u32)); 270226031Sstas if (u32) { 271226031Sstas entry->pw_end = malloc(sizeof(*entry->pw_end)); 272226031Sstas *entry->pw_end = u32; 273226031Sstas } 274226031Sstas /* 32: last successful passwd */ 275226031Sstas CHECK(ret = krb5_ret_uint32(sp, &u32)); 276226031Sstas /* 32: last failed attempt */ 277226031Sstas CHECK(ret = krb5_ret_uint32(sp, &u32)); 278226031Sstas /* 32: num of failed attempts */ 279226031Sstas CHECK(ret = krb5_ret_uint32(sp, &u32)); 280226031Sstas /* 16: num tl data */ 281226031Sstas CHECK(ret = krb5_ret_uint16(sp, &u16)); 282226031Sstas num_tl = u16; 283226031Sstas /* 16: num key data */ 284226031Sstas CHECK(ret = krb5_ret_uint16(sp, &u16)); 285226031Sstas num_keys = u16; 286226031Sstas /* 16: principal length */ 287226031Sstas CHECK(ret = krb5_ret_uint16(sp, &u16)); 288226031Sstas /* length: principal */ 289226031Sstas { 290226031Sstas /* 291226031Sstas * Note that the principal name includes the NUL in the entry, 292226031Sstas * but we don't want to take chances, so we add an extra NUL. 293226031Sstas */ 294226031Sstas p = malloc(u16 + 1); 295226031Sstas if (p == NULL) { 296226031Sstas ret = ENOMEM; 297226031Sstas goto out; 298226031Sstas } 299226031Sstas krb5_storage_read(sp, p, u16); 300226031Sstas p[u16] = '\0'; 301226031Sstas CHECK(ret = krb5_parse_name(context, p, &entry->principal)); 302226031Sstas free(p); 303226031Sstas } 304226031Sstas /* for num tl data times 305226031Sstas 16: tl data type 306226031Sstas 16: tl data length 307226031Sstas length: length */ 308226031Sstas for (i = 0; i < num_tl; i++) { 309226031Sstas /* 16: TL data type */ 310226031Sstas CHECK(ret = krb5_ret_uint16(sp, &u16)); 311226031Sstas /* 16: TL data length */ 312226031Sstas CHECK(ret = krb5_ret_uint16(sp, &u16)); 313226031Sstas krb5_storage_seek(sp, u16, SEEK_CUR); 314226031Sstas } 315226031Sstas /* 316226031Sstas * for num key data times 317226031Sstas * 16: "version" 318226031Sstas * 16: kvno 319226031Sstas * for version times: 320226031Sstas * 16: type 321226031Sstas * 16: length 322226031Sstas * length: keydata 323226031Sstas * 324226031Sstas * "version" here is really 1 or 2, the first meaning there's only 325226031Sstas * keys for this kvno, the second meaning there's keys and salt[s?]. 326226031Sstas * That's right... hold that gag reflex, you can do it. 327226031Sstas */ 328226031Sstas for (i = 0; i < num_keys; i++) { 329226031Sstas int keep = 0; 330226031Sstas uint16_t version; 331226031Sstas void *ptr; 332226031Sstas 333226031Sstas CHECK(ret = krb5_ret_uint16(sp, &u16)); 334226031Sstas version = u16; 335226031Sstas CHECK(ret = krb5_ret_uint16(sp, &u16)); 336226031Sstas 337226031Sstas /* 338226031Sstas * First time through, and until we find one matching key, 339226031Sstas * entry->kvno == 0. 340226031Sstas */ 341226031Sstas if ((entry->kvno < u16) && (kvno == 0 || kvno == u16)) { 342226031Sstas keep = 1; 343226031Sstas entry->kvno = u16; 344226031Sstas /* 345226031Sstas * Found a higher kvno than earlier, so free the old highest 346226031Sstas * kvno keys. 347226031Sstas * 348226031Sstas * XXX Of course, we actually want to extract the old kvnos 349226031Sstas * as well, for some of the kadm5 APIs. We shouldn't free 350226031Sstas * these keys, but keep them elsewhere. 351226031Sstas */ 352226031Sstas for (j = 0; j < entry->keys.len; j++) 353226031Sstas free_Key(&entry->keys.val[j]); 354226031Sstas free(entry->keys.val); 355226031Sstas entry->keys.len = 0; 356226031Sstas entry->keys.val = NULL; 357226031Sstas } else if (entry->kvno == u16) 358226031Sstas /* Accumulate keys */ 359226031Sstas keep = 1; 360226031Sstas 361226031Sstas if (keep) { 362226031Sstas Key *k; 363226031Sstas 364226031Sstas ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1)); 365226031Sstas if (ptr == NULL) { 366226031Sstas ret = ENOMEM; 367226031Sstas goto out; 368226031Sstas } 369226031Sstas entry->keys.val = ptr; 370226031Sstas 371226031Sstas /* k points to current Key */ 372226031Sstas k = &entry->keys.val[entry->keys.len]; 373226031Sstas 374226031Sstas memset(k, 0, sizeof(*k)); 375226031Sstas entry->keys.len += 1; 376226031Sstas 377226031Sstas k->mkvno = malloc(sizeof(*k->mkvno)); 378226031Sstas if (k->mkvno == NULL) { 379226031Sstas ret = ENOMEM; 380226031Sstas goto out; 381226031Sstas } 382226031Sstas *k->mkvno = 1; 383226031Sstas 384226031Sstas for (j = 0; j < version; j++) { 385226031Sstas uint16_t type; 386226031Sstas CHECK(ret = krb5_ret_uint16(sp, &type)); 387226031Sstas CHECK(ret = krb5_ret_uint16(sp, &u16)); 388226031Sstas if (j == 0) { 389226031Sstas /* This "version" means we have a key */ 390226031Sstas k->key.keytype = type; 391226031Sstas if (u16 < 2) { 392226031Sstas ret = EINVAL; 393226031Sstas goto out; 394226031Sstas } 395226031Sstas /* 396226031Sstas * MIT stores keys encrypted keys as {16-bit length 397226031Sstas * of plaintext key, {encrypted key}}. The reason 398226031Sstas * for this is that the Kerberos cryptosystem is not 399226031Sstas * length-preserving. Heimdal's approach is to 400226031Sstas * truncate the plaintext to the expected length of 401226031Sstas * the key given its enctype, so we ignore this 402226031Sstas * 16-bit length-of-plaintext-key field. 403226031Sstas */ 404226031Sstas krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */ 405226031Sstas k->key.keyvalue.length = u16 - 2; /* adjust cipher len */ 406226031Sstas k->key.keyvalue.data = malloc(k->key.keyvalue.length); 407226031Sstas krb5_storage_read(sp, k->key.keyvalue.data, 408226031Sstas k->key.keyvalue.length); 409226031Sstas } else if (j == 1) { 410226031Sstas /* This "version" means we have a salt */ 411226031Sstas k->salt = calloc(1, sizeof(*k->salt)); 412226031Sstas if (k->salt == NULL) { 413226031Sstas ret = ENOMEM; 414226031Sstas goto out; 415226031Sstas } 416226031Sstas k->salt->type = type; 417226031Sstas if (u16 != 0) { 418226031Sstas k->salt->salt.data = malloc(u16); 419226031Sstas if (k->salt->salt.data == NULL) { 420226031Sstas ret = ENOMEM; 421226031Sstas goto out; 422226031Sstas } 423226031Sstas k->salt->salt.length = u16; 424226031Sstas krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length); 425226031Sstas } 426226031Sstas fix_salt(context, entry, entry->keys.len - 1); 427226031Sstas } else { 428226031Sstas /* 429226031Sstas * Whatever this "version" might be, we skip it 430226031Sstas * 431226031Sstas * XXX A krb5.conf parameter requesting that we log 432226031Sstas * about strangeness like this, or return an error 433226031Sstas * from here, might be nice. 434226031Sstas */ 435226031Sstas krb5_storage_seek(sp, u16, SEEK_CUR); 436226031Sstas } 437226031Sstas } 438226031Sstas } else { 439226031Sstas /* 440226031Sstas * XXX For now we skip older kvnos, but we should extract 441226031Sstas * them... 442226031Sstas */ 443226031Sstas for (j = 0; j < version; j++) { 444226031Sstas /* enctype */ 445226031Sstas CHECK(ret = krb5_ret_uint16(sp, &u16)); 446226031Sstas /* encrypted key (or plaintext salt) */ 447226031Sstas CHECK(ret = krb5_ret_uint16(sp, &u16)); 448226031Sstas krb5_storage_seek(sp, u16, SEEK_CUR); 449226031Sstas } 450226031Sstas } 451226031Sstas } 452226031Sstas 453226031Sstas if (entry->kvno == 0 && kvno != 0) { 454226031Sstas ret = HDB_ERR_NOT_FOUND_HERE; 455226031Sstas goto out; 456226031Sstas } 457226031Sstas 458226031Sstas return 0; 459226031Sstas out: 460226031Sstas if (ret == HEIM_ERR_EOF) 461226031Sstas /* Better error code than "end of file" */ 462226031Sstas ret = HEIM_ERR_BAD_HDBENT_ENCODING; 463226031Sstas return ret; 464226031Sstas} 465226031Sstas 466226031Sstas#if 0 467226031Sstasstatic krb5_error_code 468226031Sstasmdb_entry2value(krb5_context context, hdb_entry *entry, krb5_data *data) 469226031Sstas{ 470226031Sstas return EINVAL; 471226031Sstas} 472226031Sstas#endif 473226031Sstas 474226031Sstas 475226031Sstasstatic krb5_error_code 476226031Sstasmdb_close(krb5_context context, HDB *db) 477226031Sstas{ 478226031Sstas DB *d = (DB*)db->hdb_db; 479226031Sstas (*d->close)(d); 480226031Sstas return 0; 481226031Sstas} 482226031Sstas 483226031Sstasstatic krb5_error_code 484226031Sstasmdb_destroy(krb5_context context, HDB *db) 485226031Sstas{ 486226031Sstas krb5_error_code ret; 487226031Sstas 488226031Sstas ret = hdb_clear_master_key (context, db); 489226031Sstas free(db->hdb_name); 490226031Sstas free(db); 491226031Sstas return ret; 492226031Sstas} 493226031Sstas 494226031Sstasstatic krb5_error_code 495226031Sstasmdb_lock(krb5_context context, HDB *db, int operation) 496226031Sstas{ 497226031Sstas DB *d = (DB*)db->hdb_db; 498226031Sstas int fd = (*d->fd)(d); 499226031Sstas if(fd < 0) { 500226031Sstas krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB, 501226031Sstas "Can't lock database: %s", db->hdb_name); 502226031Sstas return HDB_ERR_CANT_LOCK_DB; 503226031Sstas } 504226031Sstas return hdb_lock(fd, operation); 505226031Sstas} 506226031Sstas 507226031Sstasstatic krb5_error_code 508226031Sstasmdb_unlock(krb5_context context, HDB *db) 509226031Sstas{ 510226031Sstas DB *d = (DB*)db->hdb_db; 511226031Sstas int fd = (*d->fd)(d); 512226031Sstas if(fd < 0) { 513226031Sstas krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB, 514226031Sstas "Can't unlock database: %s", db->hdb_name); 515226031Sstas return HDB_ERR_CANT_LOCK_DB; 516226031Sstas } 517226031Sstas return hdb_unlock(fd); 518226031Sstas} 519226031Sstas 520226031Sstas 521226031Sstasstatic krb5_error_code 522226031Sstasmdb_seq(krb5_context context, HDB *db, 523226031Sstas unsigned flags, hdb_entry_ex *entry, int flag) 524226031Sstas{ 525226031Sstas DB *d = (DB*)db->hdb_db; 526226031Sstas DBT key, value; 527226031Sstas krb5_data key_data, data; 528226031Sstas int code; 529226031Sstas 530226031Sstas code = db->hdb_lock(context, db, HDB_RLOCK); 531226031Sstas if(code == -1) { 532226031Sstas krb5_set_error_message(context, HDB_ERR_DB_INUSE, "Database %s in use", db->hdb_name); 533226031Sstas return HDB_ERR_DB_INUSE; 534226031Sstas } 535226031Sstas code = (*d->seq)(d, &key, &value, flag); 536226031Sstas db->hdb_unlock(context, db); /* XXX check value */ 537226031Sstas if(code == -1) { 538226031Sstas code = errno; 539226031Sstas krb5_set_error_message(context, code, "Database %s seq error: %s", 540226031Sstas db->hdb_name, strerror(code)); 541226031Sstas return code; 542226031Sstas } 543226031Sstas if(code == 1) { 544226031Sstas krb5_clear_error_message(context); 545226031Sstas return HDB_ERR_NOENTRY; 546226031Sstas } 547226031Sstas 548226031Sstas key_data.data = key.data; 549226031Sstas key_data.length = key.size; 550226031Sstas data.data = value.data; 551226031Sstas data.length = value.size; 552226031Sstas memset(entry, 0, sizeof(*entry)); 553226031Sstas 554226031Sstas if (mdb_value2entry(context, &data, 0, &entry->entry)) 555226031Sstas return mdb_seq(context, db, flags, entry, R_NEXT); 556226031Sstas 557226031Sstas if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { 558226031Sstas code = hdb_unseal_keys (context, db, &entry->entry); 559226031Sstas if (code) 560226031Sstas hdb_free_entry (context, entry); 561226031Sstas } 562226031Sstas 563226031Sstas return code; 564226031Sstas} 565226031Sstas 566226031Sstas 567226031Sstasstatic krb5_error_code 568226031Sstasmdb_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) 569226031Sstas{ 570226031Sstas return mdb_seq(context, db, flags, entry, R_FIRST); 571226031Sstas} 572226031Sstas 573226031Sstas 574226031Sstasstatic krb5_error_code 575226031Sstasmdb_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) 576226031Sstas{ 577226031Sstas return mdb_seq(context, db, flags, entry, R_NEXT); 578226031Sstas} 579226031Sstas 580226031Sstasstatic krb5_error_code 581226031Sstasmdb_rename(krb5_context context, HDB *db, const char *new_name) 582226031Sstas{ 583226031Sstas int ret; 584226031Sstas char *old, *new; 585226031Sstas 586226031Sstas asprintf(&old, "%s.db", db->hdb_name); 587226031Sstas asprintf(&new, "%s.db", new_name); 588226031Sstas ret = rename(old, new); 589226031Sstas free(old); 590226031Sstas free(new); 591226031Sstas if(ret) 592226031Sstas return errno; 593226031Sstas 594226031Sstas free(db->hdb_name); 595226031Sstas db->hdb_name = strdup(new_name); 596226031Sstas return 0; 597226031Sstas} 598226031Sstas 599226031Sstasstatic krb5_error_code 600226031Sstasmdb__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply) 601226031Sstas{ 602226031Sstas DB *d = (DB*)db->hdb_db; 603226031Sstas DBT k, v; 604226031Sstas int code; 605226031Sstas 606226031Sstas k.data = key.data; 607226031Sstas k.size = key.length; 608226031Sstas code = db->hdb_lock(context, db, HDB_RLOCK); 609226031Sstas if(code) 610226031Sstas return code; 611226031Sstas code = (*d->get)(d, &k, &v, 0); 612226031Sstas db->hdb_unlock(context, db); 613226031Sstas if(code < 0) { 614226031Sstas code = errno; 615226031Sstas krb5_set_error_message(context, code, "Database %s get error: %s", 616226031Sstas db->hdb_name, strerror(code)); 617226031Sstas return code; 618226031Sstas } 619226031Sstas if(code == 1) { 620226031Sstas krb5_clear_error_message(context); 621226031Sstas return HDB_ERR_NOENTRY; 622226031Sstas } 623226031Sstas 624226031Sstas krb5_data_copy(reply, v.data, v.size); 625226031Sstas return 0; 626226031Sstas} 627226031Sstas 628226031Sstasstatic krb5_error_code 629226031Sstasmdb__put(krb5_context context, HDB *db, int replace, 630226031Sstas krb5_data key, krb5_data value) 631226031Sstas{ 632226031Sstas DB *d = (DB*)db->hdb_db; 633226031Sstas DBT k, v; 634226031Sstas int code; 635226031Sstas 636226031Sstas k.data = key.data; 637226031Sstas k.size = key.length; 638226031Sstas v.data = value.data; 639226031Sstas v.size = value.length; 640226031Sstas code = db->hdb_lock(context, db, HDB_WLOCK); 641226031Sstas if(code) 642226031Sstas return code; 643226031Sstas code = (*d->put)(d, &k, &v, replace ? 0 : R_NOOVERWRITE); 644226031Sstas db->hdb_unlock(context, db); 645226031Sstas if(code < 0) { 646226031Sstas code = errno; 647226031Sstas krb5_set_error_message(context, code, "Database %s put error: %s", 648226031Sstas db->hdb_name, strerror(code)); 649226031Sstas return code; 650226031Sstas } 651226031Sstas if(code == 1) { 652226031Sstas krb5_clear_error_message(context); 653226031Sstas return HDB_ERR_EXISTS; 654226031Sstas } 655226031Sstas return 0; 656226031Sstas} 657226031Sstas 658226031Sstasstatic krb5_error_code 659226031Sstasmdb__del(krb5_context context, HDB *db, krb5_data key) 660226031Sstas{ 661226031Sstas DB *d = (DB*)db->hdb_db; 662226031Sstas DBT k; 663226031Sstas krb5_error_code code; 664226031Sstas k.data = key.data; 665226031Sstas k.size = key.length; 666226031Sstas code = db->hdb_lock(context, db, HDB_WLOCK); 667226031Sstas if(code) 668226031Sstas return code; 669226031Sstas code = (*d->del)(d, &k, 0); 670226031Sstas db->hdb_unlock(context, db); 671226031Sstas if(code == 1) { 672226031Sstas code = errno; 673226031Sstas krb5_set_error_message(context, code, "Database %s put error: %s", 674226031Sstas db->hdb_name, strerror(code)); 675226031Sstas return code; 676226031Sstas } 677226031Sstas if(code < 0) 678226031Sstas return errno; 679226031Sstas return 0; 680226031Sstas} 681226031Sstas 682226031Sstasstatic krb5_error_code 683226031Sstasmdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal, 684226031Sstas unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry) 685226031Sstas{ 686226031Sstas krb5_data key, value; 687226031Sstas krb5_error_code code; 688226031Sstas 689226031Sstas code = mdb_principal2key(context, principal, &key); 690226031Sstas if (code) 691226031Sstas return code; 692226031Sstas code = db->hdb__get(context, db, key, &value); 693226031Sstas krb5_data_free(&key); 694226031Sstas if(code) 695226031Sstas return code; 696226031Sstas code = mdb_value2entry(context, &value, kvno, &entry->entry); 697226031Sstas krb5_data_free(&value); 698226031Sstas if (code) 699226031Sstas return code; 700226031Sstas 701226031Sstas if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { 702226031Sstas code = hdb_unseal_keys (context, db, &entry->entry); 703226031Sstas if (code) 704226031Sstas hdb_free_entry(context, entry); 705226031Sstas } 706226031Sstas 707226031Sstas return 0; 708226031Sstas} 709226031Sstas 710226031Sstasstatic krb5_error_code 711226031Sstasmdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) 712226031Sstas{ 713226031Sstas krb5_set_error_message(context, EINVAL, "can't set principal in mdb"); 714226031Sstas return EINVAL; 715226031Sstas} 716226031Sstas 717226031Sstasstatic krb5_error_code 718226031Sstasmdb_remove(krb5_context context, HDB *db, krb5_const_principal principal) 719226031Sstas{ 720226031Sstas krb5_error_code code; 721226031Sstas krb5_data key; 722226031Sstas 723226031Sstas mdb_principal2key(context, principal, &key); 724226031Sstas code = db->hdb__del(context, db, key); 725226031Sstas krb5_data_free(&key); 726226031Sstas return code; 727226031Sstas} 728226031Sstas 729226031Sstasstatic krb5_error_code 730226031Sstasmdb_open(krb5_context context, HDB *db, int flags, mode_t mode) 731226031Sstas{ 732226031Sstas char *fn; 733226031Sstas krb5_error_code ret; 734226031Sstas 735226031Sstas asprintf(&fn, "%s.db", db->hdb_name); 736226031Sstas if (fn == NULL) { 737226031Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 738226031Sstas return ENOMEM; 739226031Sstas } 740226031Sstas db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL); 741226031Sstas free(fn); 742226031Sstas 743226031Sstas if (db->hdb_db == NULL) { 744226031Sstas switch (errno) { 745226031Sstas#ifdef EFTYPE 746226031Sstas case EFTYPE: 747226031Sstas#endif 748226031Sstas case EINVAL: 749226031Sstas db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL); 750226031Sstas } 751226031Sstas } 752226031Sstas 753226031Sstas /* try to open without .db extension */ 754226031Sstas if(db->hdb_db == NULL && errno == ENOENT) 755226031Sstas db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL); 756226031Sstas if(db->hdb_db == NULL) { 757226031Sstas ret = errno; 758226031Sstas krb5_set_error_message(context, ret, "dbopen (%s): %s", 759226031Sstas db->hdb_name, strerror(ret)); 760226031Sstas return ret; 761226031Sstas } 762226031Sstas if((flags & O_ACCMODE) == O_RDONLY) 763226031Sstas ret = hdb_check_db_format(context, db); 764226031Sstas else 765226031Sstas ret = hdb_init_db(context, db); 766226031Sstas if(ret == HDB_ERR_NOENTRY) { 767226031Sstas krb5_clear_error_message(context); 768226031Sstas return 0; 769226031Sstas } 770226031Sstas if (ret) { 771226031Sstas mdb_close(context, db); 772226031Sstas krb5_set_error_message(context, ret, "hdb_open: failed %s database %s", 773226031Sstas (flags & O_ACCMODE) == O_RDONLY ? 774226031Sstas "checking format of" : "initialize", 775226031Sstas db->hdb_name); 776226031Sstas } 777226031Sstas return ret; 778226031Sstas} 779226031Sstas 780226031Sstaskrb5_error_code 781226031Sstashdb_mdb_create(krb5_context context, HDB **db, 782226031Sstas const char *filename) 783226031Sstas{ 784226031Sstas *db = calloc(1, sizeof(**db)); 785226031Sstas if (*db == NULL) { 786226031Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 787226031Sstas return ENOMEM; 788226031Sstas } 789226031Sstas 790226031Sstas (*db)->hdb_db = NULL; 791226031Sstas (*db)->hdb_name = strdup(filename); 792226031Sstas if ((*db)->hdb_name == NULL) { 793226031Sstas free(*db); 794226031Sstas *db = NULL; 795226031Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 796226031Sstas return ENOMEM; 797226031Sstas } 798226031Sstas (*db)->hdb_master_key_set = 0; 799226031Sstas (*db)->hdb_openp = 0; 800226031Sstas (*db)->hdb_capability_flags = 0; 801226031Sstas (*db)->hdb_open = mdb_open; 802226031Sstas (*db)->hdb_close = mdb_close; 803226031Sstas (*db)->hdb_fetch_kvno = mdb_fetch_kvno; 804226031Sstas (*db)->hdb_store = mdb_store; 805226031Sstas (*db)->hdb_remove = mdb_remove; 806226031Sstas (*db)->hdb_firstkey = mdb_firstkey; 807226031Sstas (*db)->hdb_nextkey= mdb_nextkey; 808226031Sstas (*db)->hdb_lock = mdb_lock; 809226031Sstas (*db)->hdb_unlock = mdb_unlock; 810226031Sstas (*db)->hdb_rename = mdb_rename; 811226031Sstas (*db)->hdb__get = mdb__get; 812226031Sstas (*db)->hdb__put = mdb__put; 813226031Sstas (*db)->hdb__del = mdb__del; 814226031Sstas (*db)->hdb_destroy = mdb_destroy; 815226031Sstas return 0; 816226031Sstas} 817226031Sstas 818226031Sstas#endif /* HAVE_DB1 */ 819