155682Smarkm/* 2233294Sstas * Copyright (c) 1999 - 2002 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 555682Smarkm * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 955682Smarkm * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 1655682Smarkm * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 2055682Smarkm * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 3455682Smarkm#include "hdb_locl.h" 3555682Smarkm 3655682Smarkm/* keytab backend for HDB databases */ 3755682Smarkm 3855682Smarkmstruct hdb_data { 3955682Smarkm char *dbname; 4055682Smarkm char *mkey; 4155682Smarkm}; 4255682Smarkm 43233294Sstasstruct hdb_cursor { 44233294Sstas HDB *db; 45233294Sstas hdb_entry_ex hdb_entry; 46233294Sstas int first, next; 47233294Sstas int key_idx; 48233294Sstas}; 49233294Sstas 5072445Sassar/* 5172445Sassar * the format for HDB keytabs is: 52233294Sstas * HDB:[HDBFORMAT:database-specific-data[:mkey=mkey-file]] 5372445Sassar */ 5472445Sassar 55233294Sstasstatic krb5_error_code KRB5_CALLCONV 5655682Smarkmhdb_resolve(krb5_context context, const char *name, krb5_keytab id) 5755682Smarkm{ 5855682Smarkm struct hdb_data *d; 5955682Smarkm const char *db, *mkey; 6072445Sassar 6155682Smarkm d = malloc(sizeof(*d)); 6290926Snectar if(d == NULL) { 63233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 6455682Smarkm return ENOMEM; 6590926Snectar } 6655682Smarkm db = name; 67233294Sstas mkey = strstr(name, ":mkey="); 68233294Sstas if(mkey == NULL || mkey[5] == '\0') { 6955682Smarkm if(*name == '\0') 7055682Smarkm d->dbname = NULL; 7155682Smarkm else { 7255682Smarkm d->dbname = strdup(name); 7355682Smarkm if(d->dbname == NULL) { 7455682Smarkm free(d); 75233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 7655682Smarkm return ENOMEM; 7755682Smarkm } 7855682Smarkm } 7955682Smarkm d->mkey = NULL; 8055682Smarkm } else { 81233294Sstas d->dbname = malloc(mkey - db + 1); 82233294Sstas if(d->dbname == NULL) { 83233294Sstas free(d); 84233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 85233294Sstas return ENOMEM; 8655682Smarkm } 87233294Sstas memmove(d->dbname, db, mkey - db); 88233294Sstas d->dbname[mkey - db] = '\0'; 89233294Sstas 90233294Sstas d->mkey = strdup(mkey + 5); 9155682Smarkm if(d->mkey == NULL) { 9255682Smarkm free(d->dbname); 9355682Smarkm free(d); 94233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 9555682Smarkm return ENOMEM; 9655682Smarkm } 9755682Smarkm } 9855682Smarkm id->data = d; 9955682Smarkm return 0; 10055682Smarkm} 10155682Smarkm 102233294Sstasstatic krb5_error_code KRB5_CALLCONV 10355682Smarkmhdb_close(krb5_context context, krb5_keytab id) 10455682Smarkm{ 10555682Smarkm struct hdb_data *d = id->data; 10672445Sassar 10772445Sassar free(d->dbname); 10872445Sassar free(d->mkey); 10955682Smarkm free(d); 11055682Smarkm return 0; 11155682Smarkm} 11255682Smarkm 113233294Sstasstatic krb5_error_code KRB5_CALLCONV 114233294Sstashdb_get_name(krb5_context context, 115233294Sstas krb5_keytab id, 116233294Sstas char *name, 11755682Smarkm size_t namesize) 11855682Smarkm{ 11955682Smarkm struct hdb_data *d = id->data; 12072445Sassar 121233294Sstas snprintf(name, namesize, "%s%s%s", 12255682Smarkm d->dbname ? d->dbname : "", 12355682Smarkm (d->dbname || d->mkey) ? ":" : "", 12455682Smarkm d->mkey ? d->mkey : ""); 12555682Smarkm return 0; 12655682Smarkm} 12755682Smarkm 12872445Sassar/* 12972445Sassar * try to figure out the database (`dbname') and master-key (`mkey') 13072445Sassar * that should be used for `principal'. 13172445Sassar */ 13272445Sassar 133233294Sstasstatic krb5_error_code 13472445Sassarfind_db (krb5_context context, 135233294Sstas char **dbname, 136233294Sstas char **mkey, 13772445Sassar krb5_const_principal principal) 13872445Sassar{ 139233294Sstas krb5_const_realm realm = krb5_principal_get_realm(context, principal); 140233294Sstas krb5_error_code ret; 141233294Sstas struct hdb_dbinfo *head, *dbinfo = NULL; 14272445Sassar 14372445Sassar *dbname = *mkey = NULL; 14472445Sassar 145233294Sstas ret = hdb_get_dbinfo(context, &head); 146233294Sstas if (ret) 147233294Sstas return ret; 148233294Sstas 149233294Sstas while ((dbinfo = hdb_dbinfo_get_next(head, dbinfo)) != NULL) { 150233294Sstas const char *p = hdb_dbinfo_get_realm(context, dbinfo); 151233294Sstas if (p && strcmp (realm, p) == 0) { 152233294Sstas p = hdb_dbinfo_get_dbname(context, dbinfo); 153233294Sstas if (p) 154233294Sstas *dbname = strdup(p); 155233294Sstas p = hdb_dbinfo_get_mkey_file(context, dbinfo); 156233294Sstas if (p) 157233294Sstas *mkey = strdup(p); 15872445Sassar break; 15972445Sassar } 16072445Sassar } 161233294Sstas hdb_free_dbinfo(context, &head); 16272445Sassar if (*dbname == NULL) 163233294Sstas *dbname = strdup(HDB_DEFAULT_DB); 164233294Sstas return 0; 16572445Sassar} 16672445Sassar 16772445Sassar/* 16872445Sassar * find the keytab entry in `id' for `principal, kvno, enctype' and return 16972445Sassar * it in `entry'. return 0 or an error code 17072445Sassar */ 17172445Sassar 172233294Sstasstatic krb5_error_code KRB5_CALLCONV 17355682Smarkmhdb_get_entry(krb5_context context, 17455682Smarkm krb5_keytab id, 17555682Smarkm krb5_const_principal principal, 17655682Smarkm krb5_kvno kvno, 17755682Smarkm krb5_enctype enctype, 17855682Smarkm krb5_keytab_entry *entry) 17955682Smarkm{ 180178825Sdfr hdb_entry_ex ent; 18155682Smarkm krb5_error_code ret; 18255682Smarkm struct hdb_data *d = id->data; 18372445Sassar const char *dbname = d->dbname; 18472445Sassar const char *mkey = d->mkey; 185233294Sstas char *fdbname = NULL, *fmkey = NULL; 186233294Sstas HDB *db; 187233294Sstas size_t i; 18855682Smarkm 189178825Sdfr memset(&ent, 0, sizeof(ent)); 190178825Sdfr 191233294Sstas if (dbname == NULL) { 192233294Sstas ret = find_db(context, &fdbname, &fmkey, principal); 193233294Sstas if (ret) 194233294Sstas return ret; 195233294Sstas dbname = fdbname; 196233294Sstas mkey = fmkey; 197233294Sstas } 19872445Sassar 19972445Sassar ret = hdb_create (context, &db, dbname); 20055682Smarkm if (ret) 201233294Sstas goto out2; 20272445Sassar ret = hdb_set_master_keyfile (context, db, mkey); 20372445Sassar if (ret) { 204178825Sdfr (*db->hdb_destroy)(context, db); 205233294Sstas goto out2; 20672445Sassar } 207233294Sstas 208178825Sdfr ret = (*db->hdb_open)(context, db, O_RDONLY, 0); 20972445Sassar if (ret) { 210178825Sdfr (*db->hdb_destroy)(context, db); 211233294Sstas goto out2; 21272445Sassar } 21372445Sassar 214233294Sstas ret = (*db->hdb_fetch_kvno)(context, db, principal, 215233294Sstas HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED| 216233294Sstas HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 217233294Sstas kvno, &ent); 218233294Sstas 219178825Sdfr if(ret == HDB_ERR_NOENTRY) { 220178825Sdfr ret = KRB5_KT_NOTFOUND; 221178825Sdfr goto out; 222178825Sdfr }else if(ret) 223178825Sdfr goto out; 224178825Sdfr 225233294Sstas if(kvno && (krb5_kvno)ent.entry.kvno != kvno) { 22655682Smarkm hdb_free_entry(context, &ent); 227178825Sdfr ret = KRB5_KT_NOTFOUND; 228178825Sdfr goto out; 22955682Smarkm } 23055682Smarkm if(enctype == 0) 231178825Sdfr if(ent.entry.keys.len > 0) 232178825Sdfr enctype = ent.entry.keys.val[0].key.keytype; 23355682Smarkm ret = KRB5_KT_NOTFOUND; 234178825Sdfr for(i = 0; i < ent.entry.keys.len; i++) { 235178825Sdfr if(ent.entry.keys.val[i].key.keytype == enctype) { 23655682Smarkm krb5_copy_principal(context, principal, &entry->principal); 237178825Sdfr entry->vno = ent.entry.kvno; 238233294Sstas krb5_copy_keyblock_contents(context, 239233294Sstas &ent.entry.keys.val[i].key, 24055682Smarkm &entry->keyblock); 24155682Smarkm ret = 0; 24255682Smarkm break; 24355682Smarkm } 24455682Smarkm } 24555682Smarkm hdb_free_entry(context, &ent); 246233294Sstas out: 247178825Sdfr (*db->hdb_close)(context, db); 248178825Sdfr (*db->hdb_destroy)(context, db); 249233294Sstas out2: 250233294Sstas free(fdbname); 251233294Sstas free(fmkey); 25255682Smarkm return ret; 25355682Smarkm} 25455682Smarkm 255233294Sstas/* 256233294Sstas * find the keytab entry in `id' for `principal, kvno, enctype' and return 257233294Sstas * it in `entry'. return 0 or an error code 258233294Sstas */ 259233294Sstas 260233294Sstasstatic krb5_error_code KRB5_CALLCONV 261233294Sstashdb_start_seq_get(krb5_context context, 262233294Sstas krb5_keytab id, 263233294Sstas krb5_kt_cursor *cursor) 264233294Sstas{ 265233294Sstas krb5_error_code ret; 266233294Sstas struct hdb_cursor *c; 267233294Sstas struct hdb_data *d = id->data; 268233294Sstas const char *dbname = d->dbname; 269233294Sstas const char *mkey = d->mkey; 270233294Sstas HDB *db; 271233294Sstas 272233294Sstas if (dbname == NULL) { 273233294Sstas /* 274233294Sstas * We don't support enumerating without being told what 275233294Sstas * backend to enumerate on 276233294Sstas */ 277233294Sstas ret = KRB5_KT_NOTFOUND; 278233294Sstas return ret; 279233294Sstas } 280233294Sstas 281233294Sstas ret = hdb_create (context, &db, dbname); 282233294Sstas if (ret) 283233294Sstas return ret; 284233294Sstas ret = hdb_set_master_keyfile (context, db, mkey); 285233294Sstas if (ret) { 286233294Sstas (*db->hdb_destroy)(context, db); 287233294Sstas return ret; 288233294Sstas } 289233294Sstas 290233294Sstas ret = (*db->hdb_open)(context, db, O_RDONLY, 0); 291233294Sstas if (ret) { 292233294Sstas (*db->hdb_destroy)(context, db); 293233294Sstas return ret; 294233294Sstas } 295233294Sstas 296233294Sstas cursor->data = c = malloc (sizeof(*c)); 297233294Sstas if(c == NULL){ 298233294Sstas (*db->hdb_close)(context, db); 299233294Sstas (*db->hdb_destroy)(context, db); 300233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 301233294Sstas return ENOMEM; 302233294Sstas } 303233294Sstas 304233294Sstas c->db = db; 305233294Sstas c->first = TRUE; 306233294Sstas c->next = TRUE; 307233294Sstas c->key_idx = 0; 308233294Sstas 309233294Sstas cursor->data = c; 310233294Sstas return ret; 311233294Sstas} 312233294Sstas 313233294Sstasstatic int KRB5_CALLCONV 314233294Sstashdb_next_entry(krb5_context context, 315233294Sstas krb5_keytab id, 316233294Sstas krb5_keytab_entry *entry, 317233294Sstas krb5_kt_cursor *cursor) 318233294Sstas{ 319233294Sstas struct hdb_cursor *c = cursor->data; 320233294Sstas krb5_error_code ret; 321233294Sstas 322233294Sstas memset(entry, 0, sizeof(*entry)); 323233294Sstas 324233294Sstas if (c->first) { 325233294Sstas c->first = FALSE; 326233294Sstas ret = (c->db->hdb_firstkey)(context, c->db, 327233294Sstas HDB_F_DECRYPT| 328233294Sstas HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 329233294Sstas &c->hdb_entry); 330233294Sstas if (ret == HDB_ERR_NOENTRY) 331233294Sstas return KRB5_KT_END; 332233294Sstas else if (ret) 333233294Sstas return ret; 334233294Sstas 335233294Sstas if (c->hdb_entry.entry.keys.len == 0) 336233294Sstas hdb_free_entry(context, &c->hdb_entry); 337233294Sstas else 338233294Sstas c->next = FALSE; 339233294Sstas } 340233294Sstas 341233294Sstas while (c->next) { 342233294Sstas ret = (c->db->hdb_nextkey)(context, c->db, 343233294Sstas HDB_F_DECRYPT| 344233294Sstas HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 345233294Sstas &c->hdb_entry); 346233294Sstas if (ret == HDB_ERR_NOENTRY) 347233294Sstas return KRB5_KT_END; 348233294Sstas else if (ret) 349233294Sstas return ret; 350233294Sstas 351233294Sstas /* If no keys on this entry, try again */ 352233294Sstas if (c->hdb_entry.entry.keys.len == 0) 353233294Sstas hdb_free_entry(context, &c->hdb_entry); 354233294Sstas else 355233294Sstas c->next = FALSE; 356233294Sstas } 357233294Sstas 358233294Sstas /* 359233294Sstas * Return next enc type (keytabs are one slot per key, while 360233294Sstas * hdb is one record per principal. 361233294Sstas */ 362233294Sstas 363233294Sstas ret = krb5_copy_principal(context, 364233294Sstas c->hdb_entry.entry.principal, 365233294Sstas &entry->principal); 366233294Sstas if (ret) 367233294Sstas return ret; 368233294Sstas 369233294Sstas entry->vno = c->hdb_entry.entry.kvno; 370233294Sstas ret = krb5_copy_keyblock_contents(context, 371233294Sstas &c->hdb_entry.entry.keys.val[c->key_idx].key, 372233294Sstas &entry->keyblock); 373233294Sstas if (ret) { 374233294Sstas krb5_free_principal(context, entry->principal); 375233294Sstas memset(entry, 0, sizeof(*entry)); 376233294Sstas return ret; 377233294Sstas } 378233294Sstas c->key_idx++; 379233294Sstas 380233294Sstas /* 381233294Sstas * Once we get to the end of the list, signal that we want the 382233294Sstas * next entry 383233294Sstas */ 384233294Sstas 385233294Sstas if ((size_t)c->key_idx == c->hdb_entry.entry.keys.len) { 386233294Sstas hdb_free_entry(context, &c->hdb_entry); 387233294Sstas c->next = TRUE; 388233294Sstas c->key_idx = 0; 389233294Sstas } 390233294Sstas 391233294Sstas return 0; 392233294Sstas} 393233294Sstas 394233294Sstas 395233294Sstasstatic int KRB5_CALLCONV 396233294Sstashdb_end_seq_get(krb5_context context, 397233294Sstas krb5_keytab id, 398233294Sstas krb5_kt_cursor *cursor) 399233294Sstas{ 400233294Sstas struct hdb_cursor *c = cursor->data; 401233294Sstas 402233294Sstas if (!c->next) 403233294Sstas hdb_free_entry(context, &c->hdb_entry); 404233294Sstas 405233294Sstas (c->db->hdb_close)(context, c->db); 406233294Sstas (c->db->hdb_destroy)(context, c->db); 407233294Sstas 408233294Sstas free(c); 409233294Sstas return 0; 410233294Sstas} 411233294Sstas 41255682Smarkmkrb5_kt_ops hdb_kt_ops = { 41355682Smarkm "HDB", 41455682Smarkm hdb_resolve, 41555682Smarkm hdb_get_name, 41655682Smarkm hdb_close, 417233294Sstas NULL, /* destroy */ 41855682Smarkm hdb_get_entry, 419233294Sstas hdb_start_seq_get, 420233294Sstas hdb_next_entry, 421233294Sstas hdb_end_seq_get, 42255682Smarkm NULL, /* add */ 42355682Smarkm NULL /* remove */ 42455682Smarkm}; 425