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 38 struct hdb_data { 39 char *dbname; 40 char *mkey; 41 }; 42 43 struct 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 55 static krb5_error_code KRB5_CALLCONV 56 hdb_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 102 static krb5_error_code KRB5_CALLCONV 103 hdb_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 113 static krb5_error_code KRB5_CALLCONV 114 hdb_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 133 static krb5_error_code 134 find_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 172 static krb5_error_code KRB5_CALLCONV 173 hdb_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 free(fdbname); 251 free(fmkey); 252 return ret; 253 } 254 255 /* 256 * find the keytab entry in `id' for `principal, kvno, enctype' and return 257 * it in `entry'. return 0 or an error code 258 */ 259 260 static krb5_error_code KRB5_CALLCONV 261 hdb_start_seq_get(krb5_context context, 262 krb5_keytab id, 263 krb5_kt_cursor *cursor) 264 { 265 krb5_error_code ret; 266 struct hdb_cursor *c; 267 struct hdb_data *d = id->data; 268 const char *dbname = d->dbname; 269 const char *mkey = d->mkey; 270 HDB *db; 271 272 if (dbname == NULL) { 273 /* 274 * We don't support enumerating without being told what 275 * backend to enumerate on 276 */ 277 ret = KRB5_KT_NOTFOUND; 278 return ret; 279 } 280 281 ret = hdb_create (context, &db, dbname); 282 if (ret) 283 return ret; 284 ret = hdb_set_master_keyfile (context, db, mkey); 285 if (ret) { 286 (*db->hdb_destroy)(context, db); 287 return ret; 288 } 289 290 ret = (*db->hdb_open)(context, db, O_RDONLY, 0); 291 if (ret) { 292 (*db->hdb_destroy)(context, db); 293 return ret; 294 } 295 296 cursor->data = c = malloc (sizeof(*c)); 297 if(c == NULL){ 298 (*db->hdb_close)(context, db); 299 (*db->hdb_destroy)(context, db); 300 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 301 return ENOMEM; 302 } 303 304 c->db = db; 305 c->first = TRUE; 306 c->next = TRUE; 307 c->key_idx = 0; 308 309 cursor->data = c; 310 return ret; 311 } 312 313 static int KRB5_CALLCONV 314 hdb_next_entry(krb5_context context, 315 krb5_keytab id, 316 krb5_keytab_entry *entry, 317 krb5_kt_cursor *cursor) 318 { 319 struct hdb_cursor *c = cursor->data; 320 krb5_error_code ret; 321 322 memset(entry, 0, sizeof(*entry)); 323 324 if (c->first) { 325 c->first = FALSE; 326 ret = (c->db->hdb_firstkey)(context, c->db, 327 HDB_F_DECRYPT| 328 HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 329 &c->hdb_entry); 330 if (ret == HDB_ERR_NOENTRY) 331 return KRB5_KT_END; 332 else if (ret) 333 return ret; 334 335 if (c->hdb_entry.entry.keys.len == 0) 336 hdb_free_entry(context, &c->hdb_entry); 337 else 338 c->next = FALSE; 339 } 340 341 while (c->next) { 342 ret = (c->db->hdb_nextkey)(context, c->db, 343 HDB_F_DECRYPT| 344 HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 345 &c->hdb_entry); 346 if (ret == HDB_ERR_NOENTRY) 347 return KRB5_KT_END; 348 else if (ret) 349 return ret; 350 351 /* If no keys on this entry, try again */ 352 if (c->hdb_entry.entry.keys.len == 0) 353 hdb_free_entry(context, &c->hdb_entry); 354 else 355 c->next = FALSE; 356 } 357 358 /* 359 * Return next enc type (keytabs are one slot per key, while 360 * hdb is one record per principal. 361 */ 362 363 ret = krb5_copy_principal(context, 364 c->hdb_entry.entry.principal, 365 &entry->principal); 366 if (ret) 367 return ret; 368 369 entry->vno = c->hdb_entry.entry.kvno; 370 ret = krb5_copy_keyblock_contents(context, 371 &c->hdb_entry.entry.keys.val[c->key_idx].key, 372 &entry->keyblock); 373 if (ret) { 374 krb5_free_principal(context, entry->principal); 375 memset(entry, 0, sizeof(*entry)); 376 return ret; 377 } 378 c->key_idx++; 379 380 /* 381 * Once we get to the end of the list, signal that we want the 382 * next entry 383 */ 384 385 if ((size_t)c->key_idx == c->hdb_entry.entry.keys.len) { 386 hdb_free_entry(context, &c->hdb_entry); 387 c->next = TRUE; 388 c->key_idx = 0; 389 } 390 391 return 0; 392 } 393 394 395 static int KRB5_CALLCONV 396 hdb_end_seq_get(krb5_context context, 397 krb5_keytab id, 398 krb5_kt_cursor *cursor) 399 { 400 struct hdb_cursor *c = cursor->data; 401 402 if (!c->next) 403 hdb_free_entry(context, &c->hdb_entry); 404 405 (c->db->hdb_close)(context, c->db); 406 (c->db->hdb_destroy)(context, c->db); 407 408 free(c); 409 return 0; 410 } 411 412 krb5_kt_ops hdb_kt_ops = { 413 "HDB", 414 hdb_resolve, 415 hdb_get_name, 416 hdb_close, 417 NULL, /* destroy */ 418 hdb_get_entry, 419 hdb_start_seq_get, 420 hdb_next_entry, 421 hdb_end_seq_get, 422 NULL, /* add */ 423 NULL /* remove */ 424 }; 425