1/* 2 * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include "hdb_locl.h" 35 36/* keytab backend for HDB databases */ 37 38struct hdb_data { 39 char *dbname; 40 char *mkey; 41}; 42 43struct hdb_cursor { 44 HDB *db; 45 hdb_entry_ex hdb_entry; 46 int first, next; 47 int key_idx; 48}; 49 50/* 51 * the format for HDB keytabs is: 52 * HDB:[HDBFORMAT:database-specific-data[:mkey=mkey-file]] 53 */ 54 55static krb5_error_code KRB5_CALLCONV 56hdb_resolve(krb5_context context, const char *name, krb5_keytab id) 57{ 58 struct hdb_data *d; 59 const char *db, *mkey; 60 61 d = malloc(sizeof(*d)); 62 if(d == NULL) { 63 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 64 return ENOMEM; 65 } 66 db = name; 67 mkey = strstr(name, ":mkey="); 68 if(mkey == NULL || mkey[5] == '\0') { 69 if(*name == '\0') 70 d->dbname = NULL; 71 else { 72 d->dbname = strdup(name); 73 if(d->dbname == NULL) { 74 free(d); 75 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 76 return ENOMEM; 77 } 78 } 79 d->mkey = NULL; 80 } else { 81 d->dbname = malloc(mkey - db + 1); 82 if(d->dbname == NULL) { 83 free(d); 84 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 85 return ENOMEM; 86 } 87 memmove(d->dbname, db, mkey - db); 88 d->dbname[mkey - db] = '\0'; 89 90 d->mkey = strdup(mkey + 5); 91 if(d->mkey == NULL) { 92 free(d->dbname); 93 free(d); 94 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 95 return ENOMEM; 96 } 97 } 98 id->data = d; 99 return 0; 100} 101 102static krb5_error_code KRB5_CALLCONV 103hdb_close(krb5_context context, krb5_keytab id) 104{ 105 struct hdb_data *d = id->data; 106 107 free(d->dbname); 108 free(d->mkey); 109 free(d); 110 return 0; 111} 112 113static krb5_error_code KRB5_CALLCONV 114hdb_get_name(krb5_context context, 115 krb5_keytab id, 116 char *name, 117 size_t namesize) 118{ 119 struct hdb_data *d = id->data; 120 121 snprintf(name, namesize, "%s%s%s", 122 d->dbname ? d->dbname : "", 123 (d->dbname || d->mkey) ? ":" : "", 124 d->mkey ? d->mkey : ""); 125 return 0; 126} 127 128/* 129 * try to figure out the database (`dbname') and master-key (`mkey') 130 * that should be used for `principal'. 131 */ 132 133static krb5_error_code 134find_db (krb5_context context, 135 char **dbname, 136 char **mkey, 137 krb5_const_principal principal) 138{ 139 krb5_const_realm realm = krb5_principal_get_realm(context, principal); 140 krb5_error_code ret; 141 struct hdb_dbinfo *head, *dbinfo = NULL; 142 143 *dbname = *mkey = NULL; 144 145 ret = hdb_get_dbinfo(context, &head); 146 if (ret) 147 return ret; 148 149 while ((dbinfo = hdb_dbinfo_get_next(head, dbinfo)) != NULL) { 150 const char *p = hdb_dbinfo_get_realm(context, dbinfo); 151 if (p && strcmp (realm, p) == 0) { 152 p = hdb_dbinfo_get_dbname(context, dbinfo); 153 if (p) 154 *dbname = strdup(p); 155 p = hdb_dbinfo_get_mkey_file(context, dbinfo); 156 if (p) 157 *mkey = strdup(p); 158 break; 159 } 160 } 161 hdb_free_dbinfo(context, &head); 162 if (*dbname == NULL) 163 *dbname = strdup(HDB_DEFAULT_DB); 164 return 0; 165} 166 167/* 168 * find the keytab entry in `id' for `principal, kvno, enctype' and return 169 * it in `entry'. return 0 or an error code 170 */ 171 172static krb5_error_code KRB5_CALLCONV 173hdb_get_entry(krb5_context context, 174 krb5_keytab id, 175 krb5_const_principal principal, 176 krb5_kvno kvno, 177 krb5_enctype enctype, 178 krb5_keytab_entry *entry) 179{ 180 hdb_entry_ex ent; 181 krb5_error_code ret; 182 struct hdb_data *d = id->data; 183 const char *dbname = d->dbname; 184 const char *mkey = d->mkey; 185 char *fdbname = NULL, *fmkey = NULL; 186 HDB *db; 187 size_t i; 188 189 memset(&ent, 0, sizeof(ent)); 190 191 if (dbname == NULL) { 192 ret = find_db(context, &fdbname, &fmkey, principal); 193 if (ret) 194 return ret; 195 dbname = fdbname; 196 mkey = fmkey; 197 } 198 199 ret = hdb_create (context, &db, dbname); 200 if (ret) 201 goto out2; 202 ret = hdb_set_master_keyfile (context, db, mkey); 203 if (ret) { 204 (*db->hdb_destroy)(context, db); 205 goto out2; 206 } 207 208 ret = (*db->hdb_open)(context, db, O_RDONLY, 0); 209 if (ret) { 210 (*db->hdb_destroy)(context, db); 211 goto out2; 212 } 213 214 ret = (*db->hdb_fetch_kvno)(context, db, principal, 215 HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED| 216 HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 217 kvno, &ent); 218 219 if(ret == HDB_ERR_NOENTRY) { 220 ret = KRB5_KT_NOTFOUND; 221 goto out; 222 }else if(ret) 223 goto out; 224 225 if(kvno && (krb5_kvno)ent.entry.kvno != kvno) { 226 hdb_free_entry(context, &ent); 227 ret = KRB5_KT_NOTFOUND; 228 goto out; 229 } 230 if(enctype == 0) 231 if(ent.entry.keys.len > 0) 232 enctype = ent.entry.keys.val[0].key.keytype; 233 ret = KRB5_KT_NOTFOUND; 234 for(i = 0; i < ent.entry.keys.len; i++) { 235 if(ent.entry.keys.val[i].key.keytype == enctype) { 236 krb5_copy_principal(context, principal, &entry->principal); 237 entry->vno = ent.entry.kvno; 238 krb5_copy_keyblock_contents(context, 239 &ent.entry.keys.val[i].key, 240 &entry->keyblock); 241 ret = 0; 242 break; 243 } 244 } 245 hdb_free_entry(context, &ent); 246 out: 247 (*db->hdb_close)(context, db); 248 (*db->hdb_destroy)(context, db); 249 out2: 250 if (ret) { 251 krb5_error_code ret2; 252 char *p; 253 254 ret2 = krb5_unparse_name(context, principal, &p); 255 if (ret2 == 0) { 256 krb5_set_error_message(context, ret, 257 "Principal %s (kvno %d) not found " 258 "in the database %s", 259 p, kvno, dbname); 260 krb5_xfree(p); 261 } 262 } 263 264 free(fdbname); 265 free(fmkey); 266 return ret; 267} 268 269/* 270 * find the keytab entry in `id' for `principal, kvno, enctype' and return 271 * it in `entry'. return 0 or an error code 272 */ 273 274static krb5_error_code KRB5_CALLCONV 275hdb_start_seq_get(krb5_context context, 276 krb5_keytab id, 277 krb5_kt_cursor *cursor) 278{ 279 krb5_error_code ret; 280 struct hdb_cursor *c; 281 struct hdb_data *d = id->data; 282 const char *dbname = d->dbname; 283 const char *mkey = d->mkey; 284 HDB *db; 285 286 if (dbname == NULL) { 287 /* 288 * We don't support enumerating without being told what 289 * backend to enumerate on 290 */ 291 ret = KRB5_KT_NOTFOUND; 292 return ret; 293 } 294 295 ret = hdb_create (context, &db, dbname); 296 if (ret) 297 return ret; 298 ret = hdb_set_master_keyfile (context, db, mkey); 299 if (ret) { 300 (*db->hdb_destroy)(context, db); 301 return ret; 302 } 303 304 ret = (*db->hdb_open)(context, db, O_RDONLY, 0); 305 if (ret) { 306 (*db->hdb_destroy)(context, db); 307 return ret; 308 } 309 310 c = malloc (sizeof(*c)); 311 if(c == NULL){ 312 (*db->hdb_close)(context, db); 313 (*db->hdb_destroy)(context, db); 314 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 315 return ENOMEM; 316 } 317 318 c->db = db; 319 c->first = TRUE; 320 c->next = TRUE; 321 c->key_idx = 0; 322 323 cursor->data = c; 324 return ret; 325} 326 327static int KRB5_CALLCONV 328hdb_next_entry(krb5_context context, 329 krb5_keytab id, 330 krb5_keytab_entry *entry, 331 krb5_kt_cursor *cursor) 332{ 333 struct hdb_cursor *c = cursor->data; 334 krb5_error_code ret; 335 336 memset(entry, 0, sizeof(*entry)); 337 338 if (c->first) { 339 c->first = FALSE; 340 ret = (c->db->hdb_firstkey)(context, c->db, 341 HDB_F_DECRYPT| 342 HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 343 &c->hdb_entry); 344 if (ret == HDB_ERR_NOENTRY) 345 return KRB5_KT_END; 346 else if (ret) 347 return ret; 348 349 if (c->hdb_entry.entry.keys.len == 0) 350 hdb_free_entry(context, &c->hdb_entry); 351 else 352 c->next = FALSE; 353 } 354 355 while (c->next) { 356 ret = (c->db->hdb_nextkey)(context, c->db, 357 HDB_F_DECRYPT| 358 HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 359 &c->hdb_entry); 360 if (ret == HDB_ERR_NOENTRY) 361 return KRB5_KT_END; 362 else if (ret) 363 return ret; 364 365 /* If no keys on this entry, try again */ 366 if (c->hdb_entry.entry.keys.len == 0) 367 hdb_free_entry(context, &c->hdb_entry); 368 else 369 c->next = FALSE; 370 } 371 372 /* 373 * Return next enc type (keytabs are one slot per key, while 374 * hdb is one record per principal. 375 */ 376 377 ret = krb5_copy_principal(context, 378 c->hdb_entry.entry.principal, 379 &entry->principal); 380 if (ret) 381 return ret; 382 383 entry->vno = c->hdb_entry.entry.kvno; 384 ret = krb5_copy_keyblock_contents(context, 385 &c->hdb_entry.entry.keys.val[c->key_idx].key, 386 &entry->keyblock); 387 if (ret) { 388 krb5_free_principal(context, entry->principal); 389 memset(entry, 0, sizeof(*entry)); 390 return ret; 391 } 392 c->key_idx++; 393 394 /* 395 * Once we get to the end of the list, signal that we want the 396 * next entry 397 */ 398 399 if ((size_t)c->key_idx == c->hdb_entry.entry.keys.len) { 400 hdb_free_entry(context, &c->hdb_entry); 401 c->next = TRUE; 402 c->key_idx = 0; 403 } 404 405 return 0; 406} 407 408 409static int KRB5_CALLCONV 410hdb_end_seq_get(krb5_context context, 411 krb5_keytab id, 412 krb5_kt_cursor *cursor) 413{ 414 struct hdb_cursor *c = cursor->data; 415 416 if (!c->next) 417 hdb_free_entry(context, &c->hdb_entry); 418 419 (c->db->hdb_close)(context, c->db); 420 (c->db->hdb_destroy)(context, c->db); 421 422 free(c); 423 return 0; 424} 425 426krb5_kt_ops hdb_kt_ops = { 427 "HDB", 428 hdb_resolve, 429 hdb_get_name, 430 hdb_close, 431 NULL, /* destroy */ 432 hdb_get_entry, 433 hdb_start_seq_get, 434 hdb_next_entry, 435 hdb_end_seq_get, 436 NULL, /* add */ 437 NULL /* remove */ 438}; 439