keytab.c revision 233294
1118611Snjl/* 2118611Snjl * Copyright (c) 1999 - 2002 Kungliga Tekniska H��gskolan 3118611Snjl * (Royal Institute of Technology, Stockholm, Sweden). 4118611Snjl * All rights reserved. 5118611Snjl * 6118611Snjl * Redistribution and use in source and binary forms, with or without 7118611Snjl * modification, are permitted provided that the following conditions 8118611Snjl * are met: 9118611Snjl * 10118611Snjl * 1. Redistributions of source code must retain the above copyright 11118611Snjl * notice, this list of conditions and the following disclaimer. 12202771Sjkim * 13118611Snjl * 2. Redistributions in binary form must reproduce the above copyright 14118611Snjl * notice, this list of conditions and the following disclaimer in the 15118611Snjl * documentation and/or other materials provided with the distribution. 16118611Snjl * 17118611Snjl * 3. Neither the name of the Institute nor the names of its contributors 18118611Snjl * may be used to endorse or promote products derived from this software 19118611Snjl * without specific prior written permission. 20118611Snjl * 21118611Snjl * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22118611Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23118611Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24118611Snjl * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25118611Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26118611Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27118611Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28118611Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29118611Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30118611Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31118611Snjl * SUCH DAMAGE. 32118611Snjl */ 33118611Snjl 34118611Snjl#include "hdb_locl.h" 35118611Snjl 36118611Snjl/* keytab backend for HDB databases */ 37118611Snjl 38118611Snjlstruct hdb_data { 39118611Snjl char *dbname; 40118611Snjl char *mkey; 41118611Snjl}; 42118611Snjl 43118611Snjlstruct hdb_cursor { 44118611Snjl HDB *db; 45118611Snjl hdb_entry_ex hdb_entry; 46118611Snjl int first, next; 47118611Snjl int key_idx; 48118611Snjl}; 49118611Snjl 50118611Snjl/* 51118611Snjl * the format for HDB keytabs is: 52118611Snjl * HDB:[HDBFORMAT:database-specific-data[:mkey=mkey-file]] 53118611Snjl */ 54118611Snjl 55118611Snjlstatic krb5_error_code KRB5_CALLCONV 56118611Snjlhdb_resolve(krb5_context context, const char *name, krb5_keytab id) 57118611Snjl{ 58118611Snjl struct hdb_data *d; 59118611Snjl const char *db, *mkey; 60118611Snjl 61118611Snjl d = malloc(sizeof(*d)); 62118611Snjl if(d == NULL) { 63118611Snjl krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 64118611Snjl return ENOMEM; 65118611Snjl } 66118611Snjl db = name; 67118611Snjl mkey = strstr(name, ":mkey="); 68118611Snjl if(mkey == NULL || mkey[5] == '\0') { 69118611Snjl if(*name == '\0') 70118611Snjl d->dbname = NULL; 71118611Snjl else { 72118611Snjl d->dbname = strdup(name); 73118611Snjl if(d->dbname == NULL) { 74118611Snjl free(d); 75118611Snjl krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 76118611Snjl return ENOMEM; 77118611Snjl } 78118611Snjl } 79118611Snjl d->mkey = NULL; 80118611Snjl } else { 81118611Snjl d->dbname = malloc(mkey - db + 1); 82118611Snjl if(d->dbname == NULL) { 83118611Snjl free(d); 84118611Snjl krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 85118611Snjl return ENOMEM; 86118611Snjl } 87118611Snjl memmove(d->dbname, db, mkey - db); 88118611Snjl d->dbname[mkey - db] = '\0'; 89118611Snjl 90118611Snjl d->mkey = strdup(mkey + 5); 91118611Snjl if(d->mkey == NULL) { 92118611Snjl free(d->dbname); 93118611Snjl free(d); 94118611Snjl krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 95118611Snjl return ENOMEM; 96118611Snjl } 97118611Snjl } 98118611Snjl id->data = d; 99118611Snjl return 0; 100118611Snjl} 101118611Snjl 102118611Snjlstatic krb5_error_code KRB5_CALLCONV 103118611Snjlhdb_close(krb5_context context, krb5_keytab id) 104118611Snjl{ 105118611Snjl struct hdb_data *d = id->data; 106118611Snjl 107118611Snjl free(d->dbname); 108118611Snjl free(d->mkey); 109118611Snjl free(d); 110118611Snjl return 0; 111118611Snjl} 112118611Snjl 113118611Snjlstatic krb5_error_code KRB5_CALLCONV 114118611Snjlhdb_get_name(krb5_context context, 115118611Snjl krb5_keytab id, 116118611Snjl char *name, 117118611Snjl size_t namesize) 118151937Sjkim{ 119118611Snjl struct hdb_data *d = id->data; 120118611Snjl 121118611Snjl snprintf(name, namesize, "%s%s%s", 122118611Snjl d->dbname ? d->dbname : "", 123118611Snjl (d->dbname || d->mkey) ? ":" : "", 124151937Sjkim d->mkey ? d->mkey : ""); 125118611Snjl return 0; 126151937Sjkim} 127151937Sjkim 128151937Sjkim/* 129151937Sjkim * try to figure out the database (`dbname') and master-key (`mkey') 130151937Sjkim * that should be used for `principal'. 131151937Sjkim */ 132151937Sjkim 133151937Sjkimstatic krb5_error_code 134151937Sjkimfind_db (krb5_context context, 135151937Sjkim char **dbname, 136151937Sjkim char **mkey, 137151937Sjkim krb5_const_principal principal) 138151937Sjkim{ 139151937Sjkim krb5_const_realm realm = krb5_principal_get_realm(context, principal); 140151937Sjkim krb5_error_code ret; 141151937Sjkim struct hdb_dbinfo *head, *dbinfo = NULL; 142151937Sjkim 143151937Sjkim *dbname = *mkey = NULL; 144151937Sjkim 145151937Sjkim ret = hdb_get_dbinfo(context, &head); 146151937Sjkim if (ret) 147151937Sjkim return ret; 148151937Sjkim 149151937Sjkim while ((dbinfo = hdb_dbinfo_get_next(head, dbinfo)) != NULL) { 150151937Sjkim const char *p = hdb_dbinfo_get_realm(context, dbinfo); 151151937Sjkim if (p && strcmp (realm, p) == 0) { 152151937Sjkim p = hdb_dbinfo_get_dbname(context, dbinfo); 153151937Sjkim if (p) 154151937Sjkim *dbname = strdup(p); 155151937Sjkim p = hdb_dbinfo_get_mkey_file(context, dbinfo); 156151937Sjkim if (p) 157151937Sjkim *mkey = strdup(p); 158151937Sjkim break; 159151937Sjkim } 160151937Sjkim } 161151937Sjkim hdb_free_dbinfo(context, &head); 162151937Sjkim if (*dbname == NULL) 163151937Sjkim *dbname = strdup(HDB_DEFAULT_DB); 164118611Snjl return 0; 165118611Snjl} 166118611Snjl 167118611Snjl/* 168151937Sjkim * find the keytab entry in `id' for `principal, kvno, enctype' and return 169151937Sjkim * it in `entry'. return 0 or an error code 170151937Sjkim */ 171151937Sjkim 172118611Snjlstatic krb5_error_code KRB5_CALLCONV 173151937Sjkimhdb_get_entry(krb5_context context, 174118611Snjl krb5_keytab id, 175138287Smarks krb5_const_principal principal, 176151937Sjkim krb5_kvno kvno, 177118611Snjl krb5_enctype enctype, 178118611Snjl krb5_keytab_entry *entry) 179118611Snjl{ 180151937Sjkim hdb_entry_ex ent; 181118611Snjl krb5_error_code ret; 182151937Sjkim struct hdb_data *d = id->data; 183151937Sjkim const char *dbname = d->dbname; 184118611Snjl const char *mkey = d->mkey; 185118611Snjl char *fdbname = NULL, *fmkey = NULL; 186118611Snjl HDB *db; 187118611Snjl size_t i; 188151937Sjkim 189118611Snjl memset(&ent, 0, sizeof(ent)); 190118611Snjl 191151937Sjkim if (dbname == NULL) { 192151937Sjkim ret = find_db(context, &fdbname, &fmkey, principal); 193151937Sjkim if (ret) 194118611Snjl return ret; 195118611Snjl dbname = fdbname; 196151937Sjkim mkey = fmkey; 197138287Smarks } 198151937Sjkim 199138287Smarks ret = hdb_create (context, &db, dbname); 200151937Sjkim if (ret) 201138287Smarks goto out2; 202151937Sjkim ret = hdb_set_master_keyfile (context, db, mkey); 203138287Smarks if (ret) { 204151937Sjkim (*db->hdb_destroy)(context, db); 205138287Smarks goto out2; 206151937Sjkim } 207118611Snjl 208151937Sjkim ret = (*db->hdb_open)(context, db, O_RDONLY, 0); 209118611Snjl if (ret) { 210151937Sjkim (*db->hdb_destroy)(context, db); 211151937Sjkim goto out2; 212151937Sjkim } 213118611Snjl 214151937Sjkim ret = (*db->hdb_fetch_kvno)(context, db, principal, 215118611Snjl HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED| 216118611Snjl HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 217118611Snjl kvno, &ent); 218118611Snjl 219118611Snjl if(ret == HDB_ERR_NOENTRY) { 220118611Snjl ret = KRB5_KT_NOTFOUND; 221118611Snjl goto out; 222151937Sjkim }else if(ret) 223118611Snjl goto out; 224118611Snjl 225118611Snjl if(kvno && (krb5_kvno)ent.entry.kvno != kvno) { 226118611Snjl hdb_free_entry(context, &ent); 227118611Snjl ret = KRB5_KT_NOTFOUND; 228118611Snjl goto out; 229118611Snjl } 230118611Snjl if(enctype == 0) 231151937Sjkim if(ent.entry.keys.len > 0) 232118611Snjl enctype = ent.entry.keys.val[0].key.keytype; 233118611Snjl ret = KRB5_KT_NOTFOUND; 234118611Snjl for(i = 0; i < ent.entry.keys.len; i++) { 235118611Snjl if(ent.entry.keys.val[i].key.keytype == enctype) { 236118611Snjl krb5_copy_principal(context, principal, &entry->principal); 237118611Snjl entry->vno = ent.entry.kvno; 238118611Snjl krb5_copy_keyblock_contents(context, 239118611Snjl &ent.entry.keys.val[i].key, 240118611Snjl &entry->keyblock); 241118611Snjl ret = 0; 242118611Snjl break; 243118611Snjl } 244118611Snjl } 245118611Snjl hdb_free_entry(context, &ent); 246118611Snjl out: 247118611Snjl (*db->hdb_close)(context, db); 248118611Snjl (*db->hdb_destroy)(context, db); 249151937Sjkim out2: 250118611Snjl free(fdbname); 251118611Snjl free(fmkey); 252118611Snjl return ret; 253118611Snjl} 254118611Snjl 255118611Snjl/* 256118611Snjl * find the keytab entry in `id' for `principal, kvno, enctype' and return 257118611Snjl * it in `entry'. return 0 or an error code 258151937Sjkim */ 259118611Snjl 260118611Snjlstatic krb5_error_code KRB5_CALLCONV 261118611Snjlhdb_start_seq_get(krb5_context context, 262118611Snjl krb5_keytab id, 263118611Snjl krb5_kt_cursor *cursor) 264118611Snjl{ 265118611Snjl krb5_error_code ret; 266118611Snjl struct hdb_cursor *c; 267118611Snjl struct hdb_data *d = id->data; 268118611Snjl const char *dbname = d->dbname; 269118611Snjl const char *mkey = d->mkey; 270118611Snjl HDB *db; 271118611Snjl 272118611Snjl if (dbname == NULL) { 273151937Sjkim /* 274118611Snjl * We don't support enumerating without being told what 275118611Snjl * backend to enumerate on 276118611Snjl */ 277118611Snjl ret = KRB5_KT_NOTFOUND; 278118611Snjl return ret; 279118611Snjl } 280118611Snjl 281118611Snjl ret = hdb_create (context, &db, dbname); 282151937Sjkim if (ret) 283118611Snjl return ret; 284118611Snjl ret = hdb_set_master_keyfile (context, db, mkey); 285118611Snjl if (ret) { 286118611Snjl (*db->hdb_destroy)(context, db); 287118611Snjl return ret; 288118611Snjl } 289118611Snjl 290118611Snjl ret = (*db->hdb_open)(context, db, O_RDONLY, 0); 291118611Snjl if (ret) { 292118611Snjl (*db->hdb_destroy)(context, db); 293118611Snjl return ret; 294118611Snjl } 295118611Snjl 296118611Snjl cursor->data = c = malloc (sizeof(*c)); 297118611Snjl if(c == NULL){ 298118611Snjl (*db->hdb_close)(context, db); 299118611Snjl (*db->hdb_destroy)(context, db); 300118611Snjl krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 301118611Snjl return ENOMEM; 302118611Snjl } 303151937Sjkim 304118611Snjl c->db = db; 305118611Snjl c->first = TRUE; 306118611Snjl c->next = TRUE; 307118611Snjl c->key_idx = 0; 308118611Snjl 309118611Snjl cursor->data = c; 310118611Snjl return ret; 311118611Snjl} 312151937Sjkim 313118611Snjlstatic int KRB5_CALLCONV 314118611Snjlhdb_next_entry(krb5_context context, 315118611Snjl krb5_keytab id, 316118611Snjl krb5_keytab_entry *entry, 317118611Snjl krb5_kt_cursor *cursor) 318118611Snjl{ 319118611Snjl struct hdb_cursor *c = cursor->data; 320118611Snjl krb5_error_code ret; 321118611Snjl 322118611Snjl memset(entry, 0, sizeof(*entry)); 323118611Snjl 324118611Snjl if (c->first) { 325118611Snjl c->first = FALSE; 326118611Snjl ret = (c->db->hdb_firstkey)(context, c->db, 327118611Snjl HDB_F_DECRYPT| 328118611Snjl HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 329118611Snjl &c->hdb_entry); 330118611Snjl if (ret == HDB_ERR_NOENTRY) 331118611Snjl return KRB5_KT_END; 332118611Snjl else if (ret) 333118611Snjl return ret; 334118611Snjl 335118611Snjl if (c->hdb_entry.entry.keys.len == 0) 336118611Snjl hdb_free_entry(context, &c->hdb_entry); 337118611Snjl else 338118611Snjl c->next = FALSE; 339118611Snjl } 340118611Snjl 341118611Snjl while (c->next) { 342118611Snjl ret = (c->db->hdb_nextkey)(context, c->db, 343118611Snjl HDB_F_DECRYPT| 344118611Snjl HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 345118611Snjl &c->hdb_entry); 346118611Snjl if (ret == HDB_ERR_NOENTRY) 347118611Snjl return KRB5_KT_END; 348118611Snjl else if (ret) 349118611Snjl return ret; 350118611Snjl 351118611Snjl /* If no keys on this entry, try again */ 352118611Snjl if (c->hdb_entry.entry.keys.len == 0) 353118611Snjl hdb_free_entry(context, &c->hdb_entry); 354118611Snjl else 355118611Snjl c->next = FALSE; 356118611Snjl } 357118611Snjl 358118611Snjl /* 359118611Snjl * Return next enc type (keytabs are one slot per key, while 360118611Snjl * hdb is one record per principal. 361118611Snjl */ 362151937Sjkim 363118611Snjl ret = krb5_copy_principal(context, 364118611Snjl c->hdb_entry.entry.principal, 365118611Snjl &entry->principal); 366118611Snjl if (ret) 367118611Snjl return ret; 368118611Snjl 369118611Snjl entry->vno = c->hdb_entry.entry.kvno; 370118611Snjl ret = krb5_copy_keyblock_contents(context, 371118611Snjl &c->hdb_entry.entry.keys.val[c->key_idx].key, 372118611Snjl &entry->keyblock); 373118611Snjl if (ret) { 374118611Snjl krb5_free_principal(context, entry->principal); 375118611Snjl memset(entry, 0, sizeof(*entry)); 376118611Snjl return ret; 377118611Snjl } 378118611Snjl c->key_idx++; 379118611Snjl 380118611Snjl /* 381118611Snjl * Once we get to the end of the list, signal that we want the 382151937Sjkim * next entry 383151937Sjkim */ 384151937Sjkim 385151937Sjkim if ((size_t)c->key_idx == c->hdb_entry.entry.keys.len) { 386151937Sjkim hdb_free_entry(context, &c->hdb_entry); 387151937Sjkim c->next = TRUE; 388151937Sjkim c->key_idx = 0; 389151937Sjkim } 390151937Sjkim 391118611Snjl return 0; 392118611Snjl} 393118611Snjl 394118611Snjl 395118611Snjlstatic int KRB5_CALLCONV 396118611Snjlhdb_end_seq_get(krb5_context context, 397118611Snjl krb5_keytab id, 398118611Snjl krb5_kt_cursor *cursor) 399118611Snjl{ 400118611Snjl struct hdb_cursor *c = cursor->data; 401118611Snjl 402118611Snjl if (!c->next) 403118611Snjl hdb_free_entry(context, &c->hdb_entry); 404118611Snjl 405118611Snjl (c->db->hdb_close)(context, c->db); 406118611Snjl (c->db->hdb_destroy)(context, c->db); 407118611Snjl 408118611Snjl free(c); 409118611Snjl return 0; 410118611Snjl} 411118611Snjl 412151937Sjkimkrb5_kt_ops hdb_kt_ops = { 413118611Snjl "HDB", 414118611Snjl hdb_resolve, 415118611Snjl hdb_get_name, 416118611Snjl hdb_close, 417118611Snjl NULL, /* destroy */ 418118611Snjl hdb_get_entry, 419118611Snjl hdb_start_seq_get, 420118611Snjl hdb_next_entry, 421118611Snjl hdb_end_seq_get, 422118611Snjl NULL, /* add */ 423118611Snjl NULL /* remove */ 424151937Sjkim}; 425151937Sjkim