1 /* $NetBSD: keytab.c,v 1.3 2023/06/19 21:41:43 christos 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[6] == '\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 + 6); 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)) == NULL) { 166 free(*mkey); 167 *mkey = NULL; 168 return krb5_enomem(context); 169 } 170 return 0; 171 } 172 173 /* 174 * find the keytab entry in `id' for `principal, kvno, enctype' and return 175 * it in `entry'. return 0 or an error code 176 */ 177 178 static krb5_error_code KRB5_CALLCONV 179 hdb_get_entry(krb5_context context, 180 krb5_keytab id, 181 krb5_const_principal principal, 182 krb5_kvno kvno, 183 krb5_enctype enctype, 184 krb5_keytab_entry *entry) 185 { 186 hdb_entry_ex ent; 187 krb5_error_code ret; 188 struct hdb_data *d = id->data; 189 const char *dbname = d->dbname; 190 const char *mkey = d->mkey; 191 char *fdbname = NULL, *fmkey = NULL; 192 HDB *db; 193 size_t i; 194 195 memset(&ent, 0, sizeof(ent)); 196 197 if (dbname == NULL) { 198 ret = find_db(context, &fdbname, &fmkey, principal); 199 if (ret) 200 return ret; 201 dbname = fdbname; 202 mkey = fmkey; 203 } 204 205 ret = hdb_create (context, &db, dbname); 206 if (ret) 207 goto out2; 208 ret = hdb_set_master_keyfile (context, db, mkey); 209 if (ret) { 210 (*db->hdb_destroy)(context, db); 211 goto out2; 212 } 213 214 ret = (*db->hdb_open)(context, db, O_RDONLY, 0); 215 if (ret) { 216 (*db->hdb_destroy)(context, db); 217 goto out2; 218 } 219 220 ret = (*db->hdb_fetch_kvno)(context, db, principal, 221 HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED| 222 HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 223 kvno, &ent); 224 225 if(ret == HDB_ERR_NOENTRY) { 226 ret = KRB5_KT_NOTFOUND; 227 goto out; 228 }else if(ret) 229 goto out; 230 231 if(kvno && (krb5_kvno)ent.entry.kvno != kvno) { 232 hdb_free_entry(context, &ent); 233 ret = KRB5_KT_NOTFOUND; 234 goto out; 235 } 236 if(enctype == 0) 237 if(ent.entry.keys.len > 0) 238 enctype = ent.entry.keys.val[0].key.keytype; 239 ret = KRB5_KT_NOTFOUND; 240 for(i = 0; i < ent.entry.keys.len; i++) { 241 if(ent.entry.keys.val[i].key.keytype == enctype) { 242 krb5_copy_principal(context, principal, &entry->principal); 243 entry->vno = ent.entry.kvno; 244 krb5_copy_keyblock_contents(context, 245 &ent.entry.keys.val[i].key, 246 &entry->keyblock); 247 ret = 0; 248 break; 249 } 250 } 251 hdb_free_entry(context, &ent); 252 out: 253 (*db->hdb_close)(context, db); 254 (*db->hdb_destroy)(context, db); 255 out2: 256 free(fdbname); 257 free(fmkey); 258 return ret; 259 } 260 261 /* 262 * find the keytab entry in `id' for `principal, kvno, enctype' and return 263 * it in `entry'. return 0 or an error code 264 */ 265 266 static krb5_error_code KRB5_CALLCONV 267 hdb_start_seq_get(krb5_context context, 268 krb5_keytab id, 269 krb5_kt_cursor *cursor) 270 { 271 krb5_error_code ret; 272 struct hdb_cursor *c; 273 struct hdb_data *d = id->data; 274 const char *dbname = d->dbname; 275 const char *mkey = d->mkey; 276 HDB *db; 277 278 if (dbname == NULL) { 279 /* 280 * We don't support enumerating without being told what 281 * backend to enumerate on 282 */ 283 ret = KRB5_KT_NOTFOUND; 284 return ret; 285 } 286 287 ret = hdb_create (context, &db, dbname); 288 if (ret) 289 return ret; 290 ret = hdb_set_master_keyfile (context, db, mkey); 291 if (ret) { 292 (*db->hdb_destroy)(context, db); 293 return ret; 294 } 295 296 ret = (*db->hdb_open)(context, db, O_RDONLY, 0); 297 if (ret) { 298 (*db->hdb_destroy)(context, db); 299 return ret; 300 } 301 302 cursor->data = c = malloc (sizeof(*c)); 303 if(c == NULL){ 304 (*db->hdb_close)(context, db); 305 (*db->hdb_destroy)(context, db); 306 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 307 return ENOMEM; 308 } 309 310 c->db = db; 311 c->first = TRUE; 312 c->next = TRUE; 313 c->key_idx = 0; 314 315 cursor->data = c; 316 return ret; 317 } 318 319 static int KRB5_CALLCONV 320 hdb_next_entry(krb5_context context, 321 krb5_keytab id, 322 krb5_keytab_entry *entry, 323 krb5_kt_cursor *cursor) 324 { 325 struct hdb_cursor *c = cursor->data; 326 krb5_error_code ret; 327 328 memset(entry, 0, sizeof(*entry)); 329 330 if (c->first) { 331 c->first = FALSE; 332 ret = (c->db->hdb_firstkey)(context, c->db, 333 HDB_F_DECRYPT| 334 HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 335 &c->hdb_entry); 336 if (ret == HDB_ERR_NOENTRY) 337 return KRB5_KT_END; 338 else if (ret) 339 return ret; 340 341 if (c->hdb_entry.entry.keys.len == 0) 342 hdb_free_entry(context, &c->hdb_entry); 343 else 344 c->next = FALSE; 345 } 346 347 while (c->next) { 348 ret = (c->db->hdb_nextkey)(context, c->db, 349 HDB_F_DECRYPT| 350 HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, 351 &c->hdb_entry); 352 if (ret == HDB_ERR_NOENTRY) 353 return KRB5_KT_END; 354 else if (ret) 355 return ret; 356 357 /* If no keys on this entry, try again */ 358 if (c->hdb_entry.entry.keys.len == 0) 359 hdb_free_entry(context, &c->hdb_entry); 360 else 361 c->next = FALSE; 362 } 363 364 /* 365 * Return next enc type (keytabs are one slot per key, while 366 * hdb is one record per principal. 367 */ 368 369 ret = krb5_copy_principal(context, 370 c->hdb_entry.entry.principal, 371 &entry->principal); 372 if (ret) 373 return ret; 374 375 entry->vno = c->hdb_entry.entry.kvno; 376 ret = krb5_copy_keyblock_contents(context, 377 &c->hdb_entry.entry.keys.val[c->key_idx].key, 378 &entry->keyblock); 379 if (ret) { 380 krb5_free_principal(context, entry->principal); 381 memset(entry, 0, sizeof(*entry)); 382 return ret; 383 } 384 c->key_idx++; 385 386 /* 387 * Once we get to the end of the list, signal that we want the 388 * next entry 389 */ 390 391 if ((size_t)c->key_idx == c->hdb_entry.entry.keys.len) { 392 hdb_free_entry(context, &c->hdb_entry); 393 c->next = TRUE; 394 c->key_idx = 0; 395 } 396 397 return 0; 398 } 399 400 401 static int KRB5_CALLCONV 402 hdb_end_seq_get(krb5_context context, 403 krb5_keytab id, 404 krb5_kt_cursor *cursor) 405 { 406 struct hdb_cursor *c = cursor->data; 407 408 if (!c->next) 409 hdb_free_entry(context, &c->hdb_entry); 410 411 (c->db->hdb_close)(context, c->db); 412 (c->db->hdb_destroy)(context, c->db); 413 414 free(c); 415 return 0; 416 } 417 418 krb5_kt_ops hdb_kt_ops = { 419 "HDB", 420 hdb_resolve, 421 hdb_get_name, 422 hdb_close, 423 NULL, /* destroy */ 424 hdb_get_entry, 425 hdb_start_seq_get, 426 hdb_next_entry, 427 hdb_end_seq_get, 428 NULL, /* add */ 429 NULL, /* remove */ 430 NULL, 431 0 432 }; 433 434 krb5_kt_ops hdb_get_kt_ops = { 435 "HDBGET", 436 hdb_resolve, 437 hdb_get_name, 438 hdb_close, 439 NULL, 440 hdb_get_entry, 441 NULL, 442 NULL, 443 NULL, 444 NULL, 445 NULL, 446 NULL, 447 0 448 }; 449