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