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