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:[database:file:mkey] 53 */ 54 55static krb5_error_code 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 = strchr(name, ':'); 68 if(mkey == NULL || mkey[1] == '\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 if((mkey - db) == 0) { 82 d->dbname = NULL; 83 } else { 84 d->dbname = malloc(mkey - db + 1); 85 if(d->dbname == NULL) { 86 free(d); 87 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 88 return ENOMEM; 89 } 90 memmove(d->dbname, db, mkey - db); 91 d->dbname[mkey - db] = '\0'; 92 } 93 d->mkey = strdup(mkey + 1); 94 if(d->mkey == NULL) { 95 free(d->dbname); 96 free(d); 97 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 98 return ENOMEM; 99 } 100 } 101 id->data = d; 102 return 0; 103} 104 105static krb5_error_code 106hdb_close(krb5_context context, krb5_keytab id) 107{ 108 struct hdb_data *d = id->data; 109 110 free(d->dbname); 111 free(d->mkey); 112 free(d); 113 return 0; 114} 115 116static krb5_error_code 117hdb_get_name(krb5_context context, 118 krb5_keytab id, 119 char *name, 120 size_t namesize) 121{ 122 struct hdb_data *d = id->data; 123 124 snprintf(name, namesize, "%s%s%s", 125 d->dbname ? d->dbname : "", 126 (d->dbname || d->mkey) ? ":" : "", 127 d->mkey ? d->mkey : ""); 128 return 0; 129} 130 131/* 132 * try to figure out the database (`dbname') and master-key (`mkey') 133 * that should be used for `principal'. 134 */ 135 136static krb5_error_code 137find_db (krb5_context context, 138 char **dbname, 139 char **mkey, 140 krb5_const_principal principal) 141{ 142 krb5_const_realm realm = krb5_principal_get_realm(context, principal); 143 krb5_error_code ret; 144 struct hdb_dbinfo *head, *dbinfo = NULL; 145 146 *dbname = *mkey = NULL; 147 148 ret = hdb_get_dbinfo(context, &head); 149 if (ret) 150 return ret; 151 152 while ((dbinfo = hdb_dbinfo_get_next(head, dbinfo)) != NULL) { 153 const char *p = hdb_dbinfo_get_realm(context, dbinfo); 154 if (p && strcmp (realm, p) == 0) { 155 p = hdb_dbinfo_get_dbname(context, dbinfo); 156 if (p) 157 *dbname = strdup(p); 158 p = hdb_dbinfo_get_mkey_file(context, dbinfo); 159 if (p) 160 *mkey = strdup(p); 161 break; 162 } 163 } 164 hdb_free_dbinfo(context, &head); 165 if (*dbname == NULL) 166 *dbname = strdup(HDB_DEFAULT_DB); 167 return 0; 168} 169 170/* 171 * find the keytab entry in `id' for `principal, kvno, enctype' and return 172 * it in `entry'. return 0 or an error code 173 */ 174 175static krb5_error_code 176hdb_get_entry(krb5_context context, 177 krb5_keytab id, 178 krb5_const_principal principal, 179 krb5_kvno kvno, 180 krb5_enctype enctype, 181 krb5_keytab_entry *entry) 182{ 183 hdb_entry_ex ent; 184 krb5_error_code ret; 185 struct hdb_data *d = id->data; 186 const char *dbname = d->dbname; 187 const char *mkey = d->mkey; 188 char *fdbname = NULL, *fmkey = NULL; 189 HDB *db; 190 int i; 191 192 memset(&ent, 0, sizeof(ent)); 193 194 if (dbname == NULL) { 195 ret = find_db(context, &fdbname, &fmkey, principal); 196 if (ret) 197 return ret; 198 dbname = fdbname; 199 mkey = fmkey; 200 } 201 202 ret = hdb_create (context, &db, dbname); 203 if (ret) 204 goto out2; 205 ret = hdb_set_master_keyfile (context, db, mkey); 206 if (ret) { 207 (*db->hdb_destroy)(context, db); 208 goto out2; 209 } 210 211 ret = (*db->hdb_open)(context, db, O_RDONLY, 0); 212 if (ret) { 213 (*db->hdb_destroy)(context, db); 214 goto out2; 215 } 216 ret = (*db->hdb_fetch)(context, db, principal, 217 HDB_F_DECRYPT| 218 HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 219 &ent); 220 221 if(ret == HDB_ERR_NOENTRY) { 222 ret = KRB5_KT_NOTFOUND; 223 goto out; 224 }else if(ret) 225 goto out; 226 227 if(kvno && ent.entry.kvno != kvno) { 228 hdb_free_entry(context, &ent); 229 ret = KRB5_KT_NOTFOUND; 230 goto out; 231 } 232 if(enctype == 0) 233 if(ent.entry.keys.len > 0) 234 enctype = ent.entry.keys.val[0].key.keytype; 235 ret = KRB5_KT_NOTFOUND; 236 for(i = 0; i < ent.entry.keys.len; i++) { 237 if(ent.entry.keys.val[i].key.keytype == enctype) { 238 krb5_copy_principal(context, principal, &entry->principal); 239 entry->vno = ent.entry.kvno; 240 krb5_copy_keyblock_contents(context, 241 &ent.entry.keys.val[i].key, 242 &entry->keyblock); 243 ret = 0; 244 break; 245 } 246 } 247 hdb_free_entry(context, &ent); 248 out: 249 (*db->hdb_close)(context, db); 250 (*db->hdb_destroy)(context, db); 251 out2: 252 free(fdbname); 253 free(fmkey); 254 return ret; 255} 256 257/* 258 * find the keytab entry in `id' for `principal, kvno, enctype' and return 259 * it in `entry'. return 0 or an error code 260 */ 261 262static krb5_error_code 263hdb_start_seq_get(krb5_context context, 264 krb5_keytab id, 265 krb5_kt_cursor *cursor) 266{ 267 krb5_error_code ret; 268 struct hdb_cursor *c; 269 struct hdb_data *d = id->data; 270 const char *dbname = d->dbname; 271 const char *mkey = d->mkey; 272 HDB *db; 273 274 if (dbname == NULL) { 275 /* 276 * We don't support enumerating without being told what 277 * backend to enumerate on 278 */ 279 ret = KRB5_KT_NOTFOUND; 280 return ret; 281 } 282 283 ret = hdb_create (context, &db, dbname); 284 if (ret) 285 return ret; 286 ret = hdb_set_master_keyfile (context, db, mkey); 287 if (ret) { 288 (*db->hdb_destroy)(context, db); 289 return ret; 290 } 291 292 ret = (*db->hdb_open)(context, db, O_RDONLY, 0); 293 if (ret) { 294 (*db->hdb_destroy)(context, db); 295 return ret; 296 } 297 298 cursor->data = c = malloc (sizeof(*c)); 299 if(c == NULL){ 300 (*db->hdb_close)(context, db); 301 (*db->hdb_destroy)(context, db); 302 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 303 return ENOMEM; 304 } 305 306 c->db = db; 307 c->first = TRUE; 308 c->next = TRUE; 309 c->key_idx = 0; 310 311 cursor->data = c; 312 return ret; 313} 314 315static int 316hdb_next_entry(krb5_context context, 317 krb5_keytab id, 318 krb5_keytab_entry *entry, 319 krb5_kt_cursor *cursor) 320{ 321 struct hdb_cursor *c = cursor->data; 322 krb5_error_code ret; 323 324 memset(entry, 0, sizeof(*entry)); 325 326 if (c->first) { 327 c->first = FALSE; 328 ret = (c->db->hdb_firstkey)(context, c->db, 329 HDB_F_DECRYPT| 330 HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 331 &c->hdb_entry); 332 if (ret == HDB_ERR_NOENTRY) 333 return KRB5_KT_END; 334 else if (ret) 335 return ret; 336 337 if (c->hdb_entry.entry.keys.len == 0) 338 hdb_free_entry(context, &c->hdb_entry); 339 else 340 c->next = FALSE; 341 } 342 343 while (c->next) { 344 ret = (c->db->hdb_nextkey)(context, c->db, 345 HDB_F_DECRYPT| 346 HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 347 &c->hdb_entry); 348 if (ret == HDB_ERR_NOENTRY) 349 return KRB5_KT_END; 350 else if (ret) 351 return ret; 352 353 /* If no keys on this entry, try again */ 354 if (c->hdb_entry.entry.keys.len == 0) 355 hdb_free_entry(context, &c->hdb_entry); 356 else 357 c->next = FALSE; 358 } 359 360 /* 361 * Return next enc type (keytabs are one slot per key, while 362 * hdb is one record per principal. 363 */ 364 365 ret = krb5_copy_principal(context, 366 c->hdb_entry.entry.principal, 367 &entry->principal); 368 if (ret) 369 return ret; 370 371 entry->vno = c->hdb_entry.entry.kvno; 372 ret = krb5_copy_keyblock_contents(context, 373 &c->hdb_entry.entry.keys.val[c->key_idx].key, 374 &entry->keyblock); 375 if (ret) { 376 krb5_free_principal(context, entry->principal); 377 memset(entry, 0, sizeof(*entry)); 378 return ret; 379 } 380 c->key_idx++; 381 382 /* 383 * Once we get to the end of the list, signal that we want the 384 * next entry 385 */ 386 387 if (c->key_idx == c->hdb_entry.entry.keys.len) { 388 hdb_free_entry(context, &c->hdb_entry); 389 c->next = TRUE; 390 c->key_idx = 0; 391 } 392 393 return 0; 394} 395 396 397static int 398hdb_end_seq_get(krb5_context context, 399 krb5_keytab id, 400 krb5_kt_cursor *cursor) 401{ 402 struct hdb_cursor *c = cursor->data; 403 404 (c->db->hdb_close)(context, c->db); 405 (c->db->hdb_destroy)(context, c->db); 406 407 if (!c->next) 408 hdb_free_entry(context, &c->hdb_entry); 409 410 free(c); 411 return 0; 412} 413 414krb5_kt_ops hdb_kt_ops = { 415 "HDB", 416 hdb_resolve, 417 hdb_get_name, 418 hdb_close, 419 NULL, /* destroy */ 420 hdb_get_entry, 421 hdb_start_seq_get, 422 hdb_next_entry, 423 hdb_end_seq_get, 424 NULL, /* add */ 425 NULL /* remove */ 426}; 427