1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved 4 * 5 * $Header$ 6 */ 7 8 /* 9 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 10 * Use is subject to license terms. 11 */ 12 13 #include "k5-int.h" 14 #include <kadm5/admin.h> 15 #include "server_internal.h" 16 17 krb5_principal master_princ; 18 krb5_keyblock master_keyblock; /* local mkey */ 19 krb5_db_entry master_db; 20 21 krb5_principal hist_princ; 22 23 /* much of this code is stolen from the kdc. there should be some 24 library code to deal with this. */ 25 26 krb5_error_code kdb_init_master(kadm5_server_handle_t handle, 27 char *r, int from_keyboard) 28 { 29 int ret = 0; 30 char *realm; 31 krb5_boolean from_kbd = FALSE; 32 krb5_kvno mkvno = IGNORE_VNO; 33 34 if (from_keyboard) 35 from_kbd = TRUE; 36 37 if (r == NULL) { 38 if ((ret = krb5_get_default_realm(handle->context, &realm))) 39 return ret; 40 } else { 41 realm = r; 42 } 43 44 krb5_free_principal(handle->context, master_princ); 45 master_princ = NULL; 46 if ((ret = krb5_db_setup_mkey_name(handle->context, 47 handle->params.mkey_name, 48 realm, NULL, &master_princ))) 49 goto done; 50 51 krb5_free_keyblock_contents(handle->context, &master_keyblock); 52 master_keyblock.enctype = handle->params.enctype; 53 54 /* 55 * Fetch the local mkey, may not be the latest but that's okay because we 56 * really want the list of all mkeys and those can be retrieved with any 57 * valid mkey. 58 */ 59 ret = krb5_db_fetch_mkey(handle->context, master_princ, 60 master_keyblock.enctype, from_kbd, 61 FALSE /* only prompt once */, 62 handle->params.stash_file, 63 &mkvno /* get the kvno of the returned mkey */, 64 NULL /* I'm not sure about this, 65 but it's what the kdc does --marc */, 66 &master_keyblock); 67 if (ret) 68 goto done; 69 70 if ((ret = krb5_db_fetch_mkey_list(handle->context, master_princ, 71 &master_keyblock))) { 72 krb5_db_fini(handle->context); 73 return (ret); 74 } 75 76 done: 77 if (r == NULL) 78 free(realm); 79 80 return(ret); 81 } 82 83 /* Fetch the currently active master key version number and keyblock. */ 84 krb5_error_code 85 kdb_get_active_mkey(kadm5_server_handle_t handle, krb5_kvno *act_kvno_out, 86 krb5_keyblock **act_mkey_out) 87 { 88 krb5_error_code ret; 89 krb5_actkvno_node *active_mkey_list; 90 91 ret = krb5_dbe_fetch_act_key_list(handle->context, master_princ, 92 &active_mkey_list); 93 if (ret) 94 return ret; 95 ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, 96 act_kvno_out, act_mkey_out); 97 krb5_dbe_free_actkvno_list(handle->context, active_mkey_list); 98 return ret; 99 } 100 101 /* 102 * Function: kdb_init_hist 103 * 104 * Purpose: Initializes the hist_princ variable. 105 * 106 * Arguments: 107 * 108 * handle (r) kadm5 api server handle 109 * r (r) realm of history principal to use, or NULL 110 * 111 * Effects: This function sets the value of the hist_princ global variable. 112 */ 113 krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r) 114 { 115 int ret = 0; 116 char *realm, *hist_name; 117 118 if (r == NULL) { 119 if ((ret = krb5_get_default_realm(handle->context, &realm))) 120 return ret; 121 } else { 122 realm = r; 123 } 124 125 if (asprintf(&hist_name, "%s@%s", KADM5_HIST_PRINCIPAL, realm) < 0) { 126 hist_name = NULL; 127 goto done; 128 } 129 130 krb5_free_principal(handle->context, hist_princ); 131 hist_princ = NULL; 132 if ((ret = krb5_parse_name(handle->context, hist_name, &hist_princ))) 133 goto done; 134 135 done: 136 free(hist_name); 137 if (r == NULL) 138 free(realm); 139 return ret; 140 } 141 142 static krb5_error_code 143 create_hist(kadm5_server_handle_t handle) 144 { 145 kadm5_ret_t ret; 146 krb5_key_salt_tuple ks[1]; 147 kadm5_principal_ent_rec ent; 148 long mask = KADM5_PRINCIPAL | KADM5_MAX_LIFE | KADM5_ATTRIBUTES; 149 150 /* Create the history principal. */ 151 memset(&ent, 0, sizeof(ent)); 152 ent.principal = hist_princ; 153 ent.max_life = KRB5_KDB_DISALLOW_ALL_TIX; 154 ent.attributes = 0; 155 ks[0].ks_enctype = handle->params.enctype; 156 ks[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL; 157 ret = kadm5_create_principal_3(handle, &ent, mask, 1, ks, NULL); 158 if (ret) 159 return ret; 160 161 /* For better compatibility with pre-1.8 libkadm5 code, we want the 162 * initial history kvno to be 2, so re-randomize it. */ 163 return kadm5_randkey_principal_3(handle, ent.principal, 0, 1, ks, 164 NULL, NULL); 165 } 166 167 /* 168 * Fetch the current history key(s), creating the history principal if 169 * necessary. Database created since krb5 1.3 will have only one key, but 170 * databases created before that may have multiple keys (of the same kvno) 171 * and we need to try them all. History keys will be returned in a list 172 * terminated by an entry with enctype 0. 173 */ 174 krb5_error_code 175 kdb_get_hist_key(kadm5_server_handle_t handle, krb5_keyblock **keyblocks_out, 176 krb5_kvno *kvno_out) 177 { 178 krb5_error_code ret; 179 krb5_db_entry *kdb; 180 krb5_keyblock *mkey, *kblist = NULL; 181 krb5_int16 i; 182 183 /* Fetch the history principal, creating it if necessary. */ 184 ret = kdb_get_entry(handle, hist_princ, &kdb, NULL); 185 if (ret == KADM5_UNK_PRINC) { 186 ret = create_hist(handle); 187 if (ret) 188 return ret; 189 ret = kdb_get_entry(handle, hist_princ, &kdb, NULL); 190 } 191 if (ret) 192 return ret; 193 194 if (kdb->n_key_data <= 0) { 195 ret = KRB5_KDB_NO_MATCHING_KEY; 196 k5_setmsg(handle->context, ret, 197 _("History entry contains no key data")); 198 goto done; 199 } 200 201 ret = krb5_dbe_find_mkey(handle->context, kdb, &mkey); 202 if (ret) 203 goto done; 204 205 kblist = k5calloc(kdb->n_key_data + 1, sizeof(*kblist), &ret); 206 if (kblist == NULL) 207 goto done; 208 for (i = 0; i < kdb->n_key_data; i++) { 209 ret = krb5_dbe_decrypt_key_data(handle->context, mkey, 210 &kdb->key_data[i], &kblist[i], 211 NULL); 212 if (ret) 213 goto done; 214 } 215 216 *keyblocks_out = kblist; 217 kblist = NULL; 218 *kvno_out = kdb->key_data[0].key_data_kvno; 219 220 done: 221 kdb_free_entry(handle, kdb, NULL); 222 kdb_free_keyblocks(handle, kblist); 223 return ret; 224 } 225 226 /* Free all keyblocks in a list (terminated by a keyblock with enctype 0). */ 227 void 228 kdb_free_keyblocks(kadm5_server_handle_t handle, krb5_keyblock *keyblocks) 229 { 230 krb5_keyblock *kb; 231 232 if (keyblocks == NULL) 233 return; 234 for (kb = keyblocks; kb->enctype != 0; kb++) 235 krb5_free_keyblock_contents(handle->context, kb); 236 free(keyblocks); 237 } 238 239 /* 240 * Function: kdb_get_entry 241 * 242 * Purpose: Gets an entry from the kerberos database and breaks 243 * it out into a krb5_db_entry and an osa_princ_ent_t. 244 * 245 * Arguments: 246 * 247 * handle (r) the server_handle 248 * principal (r) the principal to get 249 * kdb (w) krb5_db_entry to create 250 * adb (w) osa_princ_ent_rec to fill in 251 * 252 * when the caller is done with kdb and adb, kdb_free_entry must be 253 * called to release them. The adb record is filled in with the 254 * contents of the KRB5_TL_KADM_DATA record; if that record doesn't 255 * exist, an empty but valid adb record is returned. 256 */ 257 krb5_error_code 258 kdb_get_entry(kadm5_server_handle_t handle, 259 krb5_principal principal, krb5_db_entry **kdb_ptr, 260 osa_princ_ent_rec *adb) 261 { 262 krb5_error_code ret; 263 krb5_tl_data tl_data; 264 XDR xdrs; 265 krb5_db_entry *kdb; 266 267 *kdb_ptr = NULL; 268 269 ret = krb5_db_get_principal(handle->context, principal, 0, &kdb); 270 if (ret == KRB5_KDB_NOENTRY) 271 return(KADM5_UNK_PRINC); 272 if (ret) 273 return(ret); 274 275 if (adb) { 276 memset(adb, 0, sizeof(*adb)); 277 278 tl_data.tl_data_type = KRB5_TL_KADM_DATA; 279 /* 280 * XXX Currently, lookup_tl_data always returns zero; it sets 281 * tl_data->tl_data_length to zero if the type isn't found. 282 * This should be fixed... 283 */ 284 if ((ret = krb5_dbe_lookup_tl_data(handle->context, kdb, &tl_data)) 285 || (tl_data.tl_data_length == 0)) { 286 /* there's no admin data. this can happen, if the admin 287 server is put into production after some principals 288 are created. In this case, return valid admin 289 data (which is all zeros with the hist_kvno filled 290 in), and when the entry is written, the admin 291 data will get stored correctly. */ 292 293 adb->admin_history_kvno = INITIAL_HIST_KVNO; 294 *kdb_ptr = kdb; 295 return(ret); 296 } 297 298 xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents, 299 tl_data.tl_data_length, XDR_DECODE); 300 if (! xdr_osa_princ_ent_rec(&xdrs, adb)) { 301 xdr_destroy(&xdrs); 302 krb5_db_free_principal(handle->context, kdb); 303 return(KADM5_XDR_FAILURE); 304 } 305 xdr_destroy(&xdrs); 306 } 307 308 *kdb_ptr = kdb; 309 return(0); 310 } 311 312 /* 313 * Function: kdb_free_entry 314 * 315 * Purpose: frees the resources allocated by kdb_get_entry 316 * 317 * Arguments: 318 * 319 * handle (r) the server_handle 320 * kdb (w) krb5_db_entry to fill in 321 * adb (w) osa_princ_ent_rec to fill in 322 * 323 * when the caller is done with kdb and adb, kdb_free_entry must be 324 * called to release them. 325 */ 326 327 krb5_error_code 328 kdb_free_entry(kadm5_server_handle_t handle, 329 krb5_db_entry *kdb, osa_princ_ent_rec *adb) 330 { 331 XDR xdrs; 332 333 334 if (kdb) 335 krb5_db_free_principal(handle->context, kdb); 336 337 if (adb) { 338 xdrmem_create(&xdrs, NULL, 0, XDR_FREE); 339 xdr_osa_princ_ent_rec(&xdrs, adb); 340 xdr_destroy(&xdrs); 341 } 342 343 return(0); 344 } 345 346 /* 347 * Function: kdb_put_entry 348 * 349 * Purpose: Stores the osa_princ_ent_t and krb5_db_entry into to 350 * database. 351 * 352 * Arguments: 353 * 354 * handle (r) the server_handle 355 * kdb (r/w) the krb5_db_entry to store 356 * adb (r) the osa_princ_db_ent to store 357 * 358 * Effects: 359 * 360 * The last modifier field of the kdb is set to the caller at now. 361 * adb is encoded with xdr_osa_princ_ent_ret and stored in kbd as 362 * KRB5_TL_KADM_DATA. kdb is then written to the database. 363 */ 364 krb5_error_code 365 kdb_put_entry(kadm5_server_handle_t handle, 366 krb5_db_entry *kdb, osa_princ_ent_rec *adb) 367 { 368 krb5_error_code ret; 369 krb5_timestamp now; 370 XDR xdrs; 371 krb5_tl_data tl_data; 372 373 ret = krb5_timeofday(handle->context, &now); 374 if (ret) 375 return(ret); 376 377 ret = krb5_dbe_update_mod_princ_data(handle->context, kdb, now, 378 handle->current_caller); 379 if (ret) 380 return(ret); 381 382 xdralloc_create(&xdrs, XDR_ENCODE); 383 if(! xdr_osa_princ_ent_rec(&xdrs, adb)) { 384 xdr_destroy(&xdrs); 385 return(KADM5_XDR_FAILURE); 386 } 387 tl_data.tl_data_type = KRB5_TL_KADM_DATA; 388 tl_data.tl_data_length = xdr_getpos(&xdrs); 389 tl_data.tl_data_contents = (krb5_octet *)xdralloc_getdata(&xdrs); 390 391 ret = krb5_dbe_update_tl_data(handle->context, kdb, &tl_data); 392 393 xdr_destroy(&xdrs); 394 395 if (ret) 396 return(ret); 397 398 /* we are always updating TL data */ 399 kdb->mask |= KADM5_TL_DATA; 400 401 ret = krb5_db_put_principal(handle->context, kdb); 402 if (ret) 403 return(ret); 404 405 return(0); 406 } 407 408 krb5_error_code 409 kdb_delete_entry(kadm5_server_handle_t handle, krb5_principal name) 410 { 411 krb5_error_code ret; 412 413 ret = krb5_db_delete_principal(handle->context, name); 414 if (ret == KRB5_KDB_NOENTRY) 415 ret = 0; 416 return ret; 417 } 418 419 typedef struct _iter_data { 420 void (*func)(void *, krb5_principal); 421 void *data; 422 } iter_data; 423 424 static krb5_error_code 425 kdb_iter_func(krb5_pointer data, krb5_db_entry *kdb) 426 { 427 iter_data *id = (iter_data *) data; 428 429 (*(id->func))(id->data, kdb->princ); 430 431 return(0); 432 } 433 434 krb5_error_code 435 kdb_iter_entry(kadm5_server_handle_t handle, char *match_entry, 436 void (*iter_fct)(void *, krb5_principal), void *data) 437 { 438 iter_data id; 439 krb5_error_code ret; 440 441 id.func = iter_fct; 442 id.data = data; 443 444 ret = krb5_db_iterate(handle->context, match_entry, kdb_iter_func, &id, 0); 445 if (ret) 446 return(ret); 447 448 return(0); 449 } 450