1 /* $NetBSD: keytab.c,v 1.1.1.1 2011/04/13 18:14:42 elric Exp $ */ 2 3 /* 4 * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "hdb_locl.h" 37 38 /* keytab backend for HDB databases */ 39 40 struct hdb_data { 41 char *dbname; 42 char *mkey; 43 }; 44 45 struct hdb_cursor { 46 HDB *db; 47 hdb_entry_ex hdb_entry; 48 int first, next; 49 int key_idx; 50 }; 51 52 /* 53 * the format for HDB keytabs is: 54 * HDB:[HDBFORMAT:database-specific-data[:mkey=mkey-file]] 55 */ 56 57 static krb5_error_code KRB5_CALLCONV 58 hdb_resolve(krb5_context context, const char *name, krb5_keytab id) 59 { 60 struct hdb_data *d; 61 const char *db, *mkey; 62 63 d = malloc(sizeof(*d)); 64 if(d == NULL) { 65 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 66 return ENOMEM; 67 } 68 db = name; 69 mkey = strstr(name, ":mkey="); 70 if(mkey == NULL || mkey[5] == '\0') { 71 if(*name == '\0') 72 d->dbname = NULL; 73 else { 74 d->dbname = strdup(name); 75 if(d->dbname == NULL) { 76 free(d); 77 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 78 return ENOMEM; 79 } 80 } 81 d->mkey = NULL; 82 } else { 83 d->dbname = malloc(mkey - db + 1); 84 if(d->dbname == NULL) { 85 free(d); 86 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 87 return ENOMEM; 88 } 89 memmove(d->dbname, db, mkey - db); 90 d->dbname[mkey - db] = '\0'; 91 92 d->mkey = strdup(mkey + 5); 93 if(d->mkey == NULL) { 94 free(d->dbname); 95 free(d); 96 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 97 return ENOMEM; 98 } 99 } 100 id->data = d; 101 return 0; 102 } 103 104 static krb5_error_code KRB5_CALLCONV 105 hdb_close(krb5_context context, krb5_keytab id) 106 { 107 struct hdb_data *d = id->data; 108 109 free(d->dbname); 110 free(d->mkey); 111 free(d); 112 return 0; 113 } 114 115 static krb5_error_code KRB5_CALLCONV 116 hdb_get_name(krb5_context context, 117 krb5_keytab id, 118 char *name, 119 size_t namesize) 120 { 121 struct hdb_data *d = id->data; 122 123 snprintf(name, namesize, "%s%s%s", 124 d->dbname ? d->dbname : "", 125 (d->dbname || d->mkey) ? ":" : "", 126 d->mkey ? d->mkey : ""); 127 return 0; 128 } 129 130 /* 131 * try to figure out the database (`dbname') and master-key (`mkey') 132 * that should be used for `principal'. 133 */ 134 135 static krb5_error_code 136 find_db (krb5_context context, 137 char **dbname, 138 char **mkey, 139 krb5_const_principal principal) 140 { 141 krb5_const_realm realm = krb5_principal_get_realm(context, principal); 142 krb5_error_code ret; 143 struct hdb_dbinfo *head, *dbinfo = NULL; 144 145 *dbname = *mkey = NULL; 146 147 ret = hdb_get_dbinfo(context, &head); 148 if (ret) 149 return ret; 150 151 while ((dbinfo = hdb_dbinfo_get_next(head, dbinfo)) != NULL) { 152 const char *p = hdb_dbinfo_get_realm(context, dbinfo); 153 if (p && strcmp (realm, p) == 0) { 154 p = hdb_dbinfo_get_dbname(context, dbinfo); 155 if (p) 156 *dbname = strdup(p); 157 p = hdb_dbinfo_get_mkey_file(context, dbinfo); 158 if (p) 159 *mkey = strdup(p); 160 break; 161 } 162 } 163 hdb_free_dbinfo(context, &head); 164 if (*dbname == NULL) 165 *dbname = strdup(HDB_DEFAULT_DB); 166 return 0; 167 } 168 169 /* 170 * find the keytab entry in `id' for `principal, kvno, enctype' and return 171 * it in `entry'. return 0 or an error code 172 */ 173 174 static krb5_error_code KRB5_CALLCONV 175 hdb_get_entry(krb5_context context, 176 krb5_keytab id, 177 krb5_const_principal principal, 178 krb5_kvno kvno, 179 krb5_enctype enctype, 180 krb5_keytab_entry *entry) 181 { 182 hdb_entry_ex ent; 183 krb5_error_code ret; 184 struct hdb_data *d = id->data; 185 const char *dbname = d->dbname; 186 const char *mkey = d->mkey; 187 char *fdbname = NULL, *fmkey = NULL; 188 HDB *db; 189 int i; 190 191 memset(&ent, 0, sizeof(ent)); 192 193 if (dbname == NULL) { 194 ret = find_db(context, &fdbname, &fmkey, principal); 195 if (ret) 196 return ret; 197 dbname = fdbname; 198 mkey = fmkey; 199 } 200 201 ret = hdb_create (context, &db, dbname); 202 if (ret) 203 goto out2; 204 ret = hdb_set_master_keyfile (context, db, mkey); 205 if (ret) { 206 (*db->hdb_destroy)(context, db); 207 goto out2; 208 } 209 210 ret = (*db->hdb_open)(context, db, O_RDONLY, 0); 211 if (ret) { 212 (*db->hdb_destroy)(context, db); 213 goto out2; 214 } 215 216 ret = (*db->hdb_fetch_kvno)(context, db, principal, 217 HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED| 218 HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 219 kvno, &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 262 static krb5_error_code KRB5_CALLCONV 263 hdb_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 315 static int KRB5_CALLCONV 316 hdb_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 397 static int KRB5_CALLCONV 398 hdb_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 if (!c->next) 405 hdb_free_entry(context, &c->hdb_entry); 406 407 (c->db->hdb_close)(context, c->db); 408 (c->db->hdb_destroy)(context, c->db); 409 410 free(c); 411 return 0; 412 } 413 414 krb5_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