1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 8 * 9 * Openvision retains the copyright to derivative works of 10 * this source code. Do *NOT* create a derivative of this 11 * source code before consulting with your legal department. 12 * Do *NOT* integrate *ANY* of this source code into another 13 * product before consulting with your legal department. 14 * 15 * For further information, read the top-level Openvision 16 * copyright which is contained in the top-level MIT Kerberos 17 * copyright. 18 * 19 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 20 * 21 */ 22 23 24 /* 25 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved 26 * 27 * $Header$ 28 */ 29 30 #if !defined(lint) && !defined(__CODECENTER__) 31 static char *rcsid = "$Header$"; 32 #endif 33 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include "k5-int.h" 37 #include <kadm5/admin.h> 38 #include "server_internal.h" 39 40 extern caddr_t xdralloc_getdata(XDR *xdrs); 41 extern void xdralloc_create(XDR *xdrs, enum xdr_op op); 42 43 krb5_principal master_princ; 44 krb5_db_entry master_db; 45 46 krb5_principal hist_princ; 47 krb5_keyblock hist_key; 48 krb5_db_entry hist_db; 49 krb5_kvno hist_kvno; 50 51 /* much of this code is stolen from the kdc. there should be some 52 library code to deal with this. */ 53 54 krb5_error_code kdb_init_master(kadm5_server_handle_t handle, 55 char *r, int from_keyboard) 56 { 57 int ret = 0; 58 char *realm; 59 krb5_boolean from_kbd = FALSE; 60 61 if (from_keyboard) 62 from_kbd = TRUE; 63 64 if (r == NULL) { 65 if ((ret = krb5_get_default_realm(handle->context, &realm))) 66 return ret; 67 } else { 68 realm = r; 69 } 70 71 if ((ret = krb5_db_setup_mkey_name(handle->context, 72 handle->params.mkey_name, 73 realm, NULL, &master_princ))) 74 goto done; 75 /* Solaris Kerberos */ 76 #if 0 77 master_keyblock.enctype = handle->params.enctype; 78 #endif 79 80 /* Solaris Kerberos */ 81 ret = krb5_db_fetch_mkey(handle->context, master_princ, 82 handle->params.enctype, from_kbd, 83 FALSE /* only prompt once */, 84 handle->params.stash_file, 85 NULL /* I'm not sure about this, 86 but it's what the kdc does --marc */, 87 &handle->master_keyblock); 88 if (ret) 89 goto done; 90 91 /* Solaris Kerberos */ 92 if ((ret = krb5_db_verify_master_key(handle->context, master_princ, 93 &handle->master_keyblock))) { 94 krb5_db_fini(handle->context); 95 return ret; 96 } 97 98 done: 99 if (r == NULL) 100 free(realm); 101 102 return(ret); 103 } 104 105 /* 106 * Function: kdb_init_hist 107 * 108 * Purpose: Initializes the global history variables. 109 * 110 * Arguments: 111 * 112 * handle (r) kadm5 api server handle 113 * r (r) realm of history principal to use, or NULL 114 * 115 * Effects: This function sets the value of the following global 116 * variables: 117 * 118 * hist_princ krb5_principal holding the history principal 119 * hist_db krb5_db_entry of the history principal 120 * hist_key krb5_keyblock holding the history principal's key 121 * hist_encblock krb5_encrypt_block holding the procssed hist_key 122 * hist_kvno the version number of the history key 123 * 124 * If the history principal does not already exist, this function 125 * attempts to create it with kadm5_create_principal. WARNING! 126 * If the history principal is deleted and this function is executed 127 * (by kadmind, or kadmin.local, or anything else with permission), 128 * the principal will be assigned a new random key and all existing 129 * password history information will become useless. 130 */ 131 krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r) 132 { 133 int ret = 0; 134 char *realm, *hist_name; 135 krb5_key_data *key_data; 136 krb5_key_salt_tuple ks[1]; 137 138 if (r == NULL) { 139 if ((ret = krb5_get_default_realm(handle->context, &realm))) 140 return ret; 141 } else { 142 realm = r; 143 } 144 145 if ((hist_name = (char *) malloc(strlen(KADM5_HIST_PRINCIPAL) + 146 strlen(realm) + 2)) == NULL) 147 goto done; 148 149 (void) sprintf(hist_name, "%s@%s", KADM5_HIST_PRINCIPAL, realm); 150 151 if ((ret = krb5_parse_name(handle->context, hist_name, &hist_princ))) 152 goto done; 153 154 if ((ret = kdb_get_entry(handle, hist_princ, &hist_db, NULL))) { 155 kadm5_principal_ent_rec ent; 156 157 if (ret != KADM5_UNK_PRINC) 158 goto done; 159 160 /* try to create the principal */ 161 162 memset(&ent, 0, sizeof(ent)); 163 164 ent.principal = hist_princ; 165 ent.max_life = KRB5_KDB_DISALLOW_ALL_TIX; 166 ent.attributes = 0; 167 168 /* this uses hist_kvno. So we set it to 2, which will be the 169 correct value once the principal is created and randomized. 170 Of course, it doesn't make sense to keep a history for the 171 history principal, anyway. */ 172 173 hist_kvno = 2; 174 ks[0].ks_enctype = handle->params.enctype; 175 ks[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL; 176 ret = kadm5_create_principal_3(handle, &ent, 177 (KADM5_PRINCIPAL | KADM5_MAX_LIFE | 178 KADM5_ATTRIBUTES), 179 1, ks, 180 "to-be-random"); 181 if (ret) 182 goto done; 183 184 /* this won't let us randomize the hist_princ. So we cheat. */ 185 186 hist_princ = NULL; 187 188 ret = kadm5_randkey_principal_3(handle, ent.principal, 0, 1, ks, 189 NULL, NULL); 190 191 hist_princ = ent.principal; 192 193 if (ret) 194 goto done; 195 196 /* now read the newly-created kdb record out of the 197 database. */ 198 199 if ((ret = kdb_get_entry(handle, hist_princ, &hist_db, NULL))) 200 goto done; 201 202 } 203 204 ret = krb5_dbe_find_enctype(handle->context, &hist_db, 205 handle->params.enctype, -1, -1, &key_data); 206 if (ret) 207 goto done; 208 209 /* Solaris Kerberos */ 210 ret = krb5_dbekd_decrypt_key_data(handle->context, 211 &handle->master_keyblock, key_data, &hist_key, NULL); 212 if (ret) 213 goto done; 214 215 hist_kvno = key_data->key_data_kvno; 216 217 done: 218 free(hist_name); 219 if (r == NULL) 220 free(realm); 221 return ret; 222 } 223 224 /* 225 * Function: kdb_get_entry 226 * 227 * Purpose: Gets an entry from the kerberos database and breaks 228 * it out into a krb5_db_entry and an osa_princ_ent_t. 229 * 230 * Arguments: 231 * 232 * handle (r) the server_handle 233 * principal (r) the principal to get 234 * kdb (w) krb5_db_entry to fill in 235 * adb (w) osa_princ_ent_rec to fill in 236 * 237 * when the caller is done with kdb and adb, kdb_free_entry must be 238 * called to release them. The adb record is filled in with the 239 * contents of the KRB5_TL_KADM_DATA record; if that record doesn't 240 * exist, an empty but valid adb record is returned. 241 */ 242 krb5_error_code 243 kdb_get_entry(kadm5_server_handle_t handle, 244 krb5_principal principal, krb5_db_entry *kdb, 245 osa_princ_ent_rec *adb) 246 { 247 krb5_error_code ret; 248 int nprincs; 249 krb5_boolean more; 250 krb5_tl_data tl_data; 251 XDR xdrs; 252 253 ret = krb5_db_get_principal(handle->context, principal, kdb, &nprincs, 254 &more); 255 if (ret) 256 return(ret); 257 258 if (more) { 259 krb5_db_free_principal(handle->context, kdb, nprincs); 260 return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); 261 } else if (nprincs != 1) { 262 krb5_db_free_principal(handle->context, kdb, nprincs); 263 return(KADM5_UNK_PRINC); 264 } 265 266 if (adb) { 267 memset(adb, 0, sizeof(*adb)); 268 269 tl_data.tl_data_type = KRB5_TL_KADM_DATA; 270 /* 271 * XXX Currently, lookup_tl_data always returns zero; it sets 272 * tl_data->tl_data_length to zero if the type isn't found. 273 * This should be fixed... 274 */ 275 if ((ret = krb5_dbe_lookup_tl_data(handle->context, kdb, &tl_data)) 276 || (tl_data.tl_data_length == 0)) { 277 /* there's no admin data. this can happen, if the admin 278 server is put into production after some principals 279 are created. In this case, return valid admin 280 data (which is all zeros with the hist_kvno filled 281 in), and when the entry is written, the admin 282 data will get stored correctly. */ 283 284 adb->admin_history_kvno = hist_kvno; 285 286 return(ret); 287 } 288 289 /* Solaris Kerberos */ 290 xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents, 291 tl_data.tl_data_length, XDR_DECODE); 292 if (! xdr_osa_princ_ent_rec(&xdrs, adb)) { 293 xdr_destroy(&xdrs); 294 krb5_db_free_principal(handle->context, kdb, 1); 295 return(KADM5_XDR_FAILURE); 296 } 297 xdr_destroy(&xdrs); 298 } 299 300 return(0); 301 } 302 303 /* 304 * Function: kdb_free_entry 305 * 306 * Purpose: frees the resources allocated by kdb_get_entry 307 * 308 * Arguments: 309 * 310 * handle (r) the server_handle 311 * kdb (w) krb5_db_entry to fill in 312 * adb (w) osa_princ_ent_rec to fill in 313 * 314 * when the caller is done with kdb and adb, kdb_free_entry must be 315 * called to release them. 316 */ 317 318 krb5_error_code 319 kdb_free_entry(kadm5_server_handle_t handle, 320 krb5_db_entry *kdb, osa_princ_ent_rec *adb) 321 { 322 XDR xdrs; 323 324 325 if (kdb) 326 krb5_db_free_principal(handle->context, kdb, 1); 327 328 if (adb) { 329 xdrmem_create(&xdrs, NULL, 0, XDR_FREE); 330 xdr_osa_princ_ent_rec(&xdrs, adb); 331 xdr_destroy(&xdrs); 332 } 333 334 return(0); 335 } 336 337 /* 338 * Function: kdb_put_entry 339 * 340 * Purpose: Stores the osa_princ_ent_t and krb5_db_entry into to 341 * database. 342 * 343 * Arguments: 344 * 345 * handle (r) the server_handle 346 * kdb (r/w) the krb5_db_entry to store 347 * adb (r) the osa_princ_db_ent to store 348 * 349 * Effects: 350 * 351 * The last modifier field of the kdb is set to the caller at now. 352 * adb is encoded with xdr_osa_princ_ent_ret and stored in kbd as 353 * KRB5_TL_KADM_DATA. kdb is then written to the database. 354 */ 355 krb5_error_code 356 kdb_put_entry(kadm5_server_handle_t handle, 357 krb5_db_entry *kdb, osa_princ_ent_rec *adb) 358 { 359 krb5_error_code ret; 360 krb5_int32 now; 361 XDR xdrs; 362 krb5_tl_data tl_data; 363 int one; 364 365 ret = krb5_timeofday(handle->context, &now); 366 if (ret) 367 return(ret); 368 369 ret = krb5_dbe_update_mod_princ_data(handle->context, kdb, now, 370 handle->current_caller); 371 if (ret) 372 return(ret); 373 374 xdralloc_create(&xdrs, XDR_ENCODE); 375 if(! xdr_osa_princ_ent_rec(&xdrs, adb)) { 376 xdr_destroy(&xdrs); 377 return(KADM5_XDR_FAILURE); 378 } 379 tl_data.tl_data_type = KRB5_TL_KADM_DATA; 380 tl_data.tl_data_length = xdr_getpos(&xdrs); 381 /* Solaris Kerberos */ 382 tl_data.tl_data_contents = (unsigned char *) xdralloc_getdata(&xdrs); 383 384 ret = krb5_dbe_update_tl_data(handle->context, kdb, &tl_data); 385 386 xdr_destroy(&xdrs); 387 388 if (ret) 389 return(ret); 390 391 one = 1; 392 393 ret = krb5_db_put_principal(handle->context, kdb, &one); 394 if (ret) 395 return(ret); 396 397 return(0); 398 } 399 400 krb5_error_code 401 kdb_delete_entry(kadm5_server_handle_t handle, krb5_principal name) 402 { 403 int one = 1; 404 krb5_error_code ret; 405 406 ret = krb5_db_delete_principal(handle->context, name, &one); 407 408 return ret; 409 } 410 411 typedef struct _iter_data { 412 void (*func)(void *, krb5_principal); 413 void *data; 414 } iter_data; 415 416 static krb5_error_code 417 kdb_iter_func(krb5_pointer data, krb5_db_entry *kdb) 418 { 419 iter_data *id = (iter_data *) data; 420 421 (*(id->func))(id->data, kdb->princ); 422 423 return(0); 424 } 425 426 krb5_error_code 427 kdb_iter_entry(kadm5_server_handle_t handle, char *match_entry, 428 void (*iter_fct)(void *, krb5_principal), void *data) 429 { 430 iter_data id; 431 krb5_error_code ret; 432 433 id.func = iter_fct; 434 id.data = data; 435 436 /* Solaris Kerberos: added support for db_args */ 437 ret = krb5_db_iterate(handle->context, match_entry, kdb_iter_func, &id, NULL); 438 if (ret) 439 return(ret); 440 441 return(0); 442 } 443 444