1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 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 * $Id: server_init.c 18584 2006-09-13 20:30:23Z raeburn $ 29 * $Source$ 30 */ 31 32 #if !defined(lint) && !defined(__CODECENTER__) 33 static char *rcsid = "$Header: /cvs/krbdev/krb5/src/lib/kadm5/srv/server_init.c,v 1.8 2002/10/15 15:40:49 epeisach Exp $"; 34 #endif 35 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <errno.h> 39 #include <com_err.h> 40 #include "k5-int.h" /* needed for gssapiP_krb5.h */ 41 #include <kadm5/admin.h> 42 #include <krb5.h> 43 #include "server_internal.h" 44 #include <kdb/kdb_log.h> 45 46 /* 47 * Function check_handle 48 * 49 * Purpose: Check a server handle and return a com_err code if it is 50 * invalid or 0 if it is valid. 51 * 52 * Arguments: 53 * 54 * handle The server handle. 55 */ 56 57 static int check_handle(void *handle) 58 { 59 CHECK_HANDLE(handle); 60 return 0; 61 } 62 63 static int dup_db_args(kadm5_server_handle_t handle, char **db_args) 64 { 65 int count = 0; 66 int ret = 0; 67 68 for (count=0; db_args && db_args[count]; count++); 69 if (count == 0) { 70 handle->db_args = NULL; 71 goto clean_n_exit; 72 } 73 74 handle->db_args = calloc(sizeof(char*), count+1); 75 if (handle->db_args == NULL) { 76 ret=ENOMEM; 77 goto clean_n_exit; 78 } 79 80 for (count=0; db_args[count]; count++) { 81 handle->db_args[count] = strdup(db_args[count]); 82 if (handle->db_args[count] == NULL) { 83 ret = ENOMEM; 84 goto clean_n_exit; 85 } 86 } 87 88 clean_n_exit: 89 if (ret && handle->db_args) { 90 for (count=0; handle->db_args[count]; count++) 91 free(handle->db_args[count]); 92 93 free(handle->db_args), handle->db_args = NULL; 94 } 95 96 return ret; 97 } 98 99 static void free_db_args(kadm5_server_handle_t handle) 100 { 101 int count; 102 103 if (handle->db_args) { 104 for (count=0; handle->db_args[count]; count++) 105 free(handle->db_args[count]); 106 107 free(handle->db_args), handle->db_args = NULL; 108 } 109 } 110 111 kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass, 112 char *service_name, 113 kadm5_config_params *params, 114 krb5_ui_4 struct_version, 115 krb5_ui_4 api_version, 116 char **db_args, 117 void **server_handle) 118 { 119 return kadm5_init(client_name, pass, service_name, params, 120 struct_version, api_version, db_args, 121 server_handle); 122 } 123 124 kadm5_ret_t kadm5_init_with_creds(char *client_name, 125 krb5_ccache ccache, 126 char *service_name, 127 kadm5_config_params *params, 128 krb5_ui_4 struct_version, 129 krb5_ui_4 api_version, 130 char **db_args, 131 void **server_handle) 132 { 133 /* 134 * A program calling init_with_creds *never* expects to prompt the 135 * user. Therefore, always pass a dummy password in case this is 136 * KADM5_API_VERSION_1. If this is KADM5_API_VERSION_2 and 137 * MKEY_FROM_KBD is non-zero, return an error. 138 */ 139 if (api_version == KADM5_API_VERSION_2 && params && 140 (params->mask & KADM5_CONFIG_MKEY_FROM_KBD) && 141 params->mkey_from_kbd) 142 return KADM5_BAD_SERVER_PARAMS; 143 return kadm5_init(client_name, NULL, service_name, params, 144 struct_version, api_version, db_args, 145 server_handle); 146 } 147 148 149 kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab, 150 char *service_name, 151 kadm5_config_params *params, 152 krb5_ui_4 struct_version, 153 krb5_ui_4 api_version, 154 char **db_args, 155 void **server_handle) 156 { 157 /* 158 * A program calling init_with_skey *never* expects to prompt the 159 * user. Therefore, always pass a dummy password in case this is 160 * KADM5_API_VERSION_1. If this is KADM5_API_VERSION_2 and 161 * MKEY_FROM_KBD is non-zero, return an error. 162 */ 163 if (api_version == KADM5_API_VERSION_2 && params && 164 (params->mask & KADM5_CONFIG_MKEY_FROM_KBD) && 165 params->mkey_from_kbd) 166 return KADM5_BAD_SERVER_PARAMS; 167 return kadm5_init(client_name, NULL, service_name, params, 168 struct_version, api_version, db_args, 169 server_handle); 170 } 171 172 /* 173 * Solaris Kerberos: 174 * A private extended version of kadm5_init which potentially 175 * returns more information in case of an error. 176 */ 177 kadm5_ret_t kadm5_init2(char *client_name, char *pass, 178 char *service_name, 179 kadm5_config_params *params_in, 180 krb5_ui_4 struct_version, 181 krb5_ui_4 api_version, 182 char **db_args, 183 void **server_handle, 184 char **emsg) 185 { 186 int ret; 187 kadm5_server_handle_t handle; 188 kadm5_config_params params_local; /* for v1 compat */ 189 190 if (emsg) 191 *emsg = NULL; 192 193 if (! server_handle) 194 return EINVAL; 195 196 if (! client_name) 197 return EINVAL; 198 199 if (! (handle = (kadm5_server_handle_t) malloc(sizeof *handle))) 200 return ENOMEM; 201 memset(handle, 0, sizeof(*handle)); 202 203 ret = dup_db_args(handle, db_args); 204 if (ret) { 205 free(handle); 206 return ret; 207 } 208 209 ret = (int) krb5int_init_context_kdc(&(handle->context)); 210 if (ret) { 211 free_db_args(handle); 212 free(handle); 213 return(ret); 214 } 215 216 handle->magic_number = KADM5_SERVER_HANDLE_MAGIC; 217 handle->struct_version = struct_version; 218 handle->api_version = api_version; 219 220 /* 221 * Verify the version numbers before proceeding; we can't use 222 * CHECK_HANDLE because not all fields are set yet. 223 */ 224 GENERIC_CHECK_HANDLE(handle, KADM5_OLD_SERVER_API_VERSION, 225 KADM5_NEW_SERVER_API_VERSION); 226 227 /* 228 * Acquire relevant profile entries. In version 2, merge values 229 * in params_in with values from profile, based on 230 * params_in->mask. 231 * 232 * In version 1, we've given a realm (which may be NULL) instead 233 * of params_in. So use that realm, make params_in contain an 234 * empty mask, and behave like version 2. 235 */ 236 memset((char *) ¶ms_local, 0, sizeof(params_local)); 237 if (api_version == KADM5_API_VERSION_1) { 238 params_local.realm = (char *) params_in; 239 if (params_in) 240 params_local.mask = KADM5_CONFIG_REALM; 241 params_in = ¶ms_local; 242 } 243 244 #if 0 /* Now that we look at krb5.conf as well as kdc.conf, we can 245 expect to see admin_server being set sometimes. */ 246 #define ILLEGAL_PARAMS (KADM5_CONFIG_ADMIN_SERVER) 247 if (params_in && (params_in->mask & ILLEGAL_PARAMS)) { 248 krb5_free_context(handle->context); 249 free_db_args(handle); 250 free(handle); 251 return KADM5_BAD_SERVER_PARAMS; 252 } 253 #endif 254 255 ret = kadm5_get_config_params(handle->context, 1, params_in, 256 &handle->params); 257 if (ret) { 258 krb5_free_context(handle->context); 259 free_db_args(handle); 260 free(handle); 261 return(ret); 262 } 263 264 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_DBNAME | \ 265 KADM5_CONFIG_ADBNAME | \ 266 KADM5_CONFIG_ADB_LOCKFILE | \ 267 KADM5_CONFIG_ENCTYPE | \ 268 KADM5_CONFIG_FLAGS | \ 269 KADM5_CONFIG_MAX_LIFE | KADM5_CONFIG_MAX_RLIFE | \ 270 KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_ENCTYPES) 271 272 if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { 273 kadm5_free_config_params(handle->context, &handle->params); 274 krb5_free_context(handle->context); 275 free_db_args(handle); 276 free(handle); 277 return KADM5_MISSING_CONF_PARAMS; 278 } 279 280 ret = krb5_set_default_realm(handle->context, handle->params.realm); 281 if (ret) { 282 kadm5_free_config_params(handle->context, &handle->params); 283 krb5_free_context(handle->context); 284 free_db_args(handle); 285 free(handle); 286 return ret; 287 } 288 289 ret = krb5_db_open(handle->context, db_args, 290 KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN); 291 if (ret) { 292 if (emsg) { 293 const char *m = krb5_get_error_message(handle->context, ret); 294 *emsg = strdup(m); 295 krb5_free_error_message(handle->context, m); 296 } 297 kadm5_free_config_params(handle->context, &handle->params); 298 krb5_free_context(handle->context); 299 free_db_args(handle); 300 free(handle); 301 return(ret); 302 } 303 304 if ((ret = krb5_parse_name(handle->context, client_name, 305 &handle->current_caller))) { 306 krb5_db_fini(handle->context); 307 kadm5_free_config_params(handle->context, &handle->params); 308 krb5_free_context(handle->context); 309 free_db_args(handle); 310 free(handle); 311 return ret; 312 } 313 314 if (! (handle->lhandle = malloc(sizeof(*handle)))) { 315 krb5_db_fini(handle->context); 316 kadm5_free_config_params(handle->context, &handle->params); 317 krb5_free_context(handle->context); 318 free_db_args(handle); 319 free(handle); 320 return ENOMEM; 321 } 322 *handle->lhandle = *handle; 323 handle->lhandle->api_version = KADM5_API_VERSION_2; 324 handle->lhandle->struct_version = KADM5_STRUCT_VERSION; 325 handle->lhandle->lhandle = handle->lhandle; 326 327 /* can't check the handle until current_caller is set */ 328 ret = check_handle((void *) handle); 329 if (ret) { 330 krb5_db_fini(handle->context); 331 kadm5_free_config_params(handle->context, &handle->params); 332 krb5_free_context(handle->context); 333 free_db_args(handle); 334 free(handle); 335 return ret; 336 } 337 338 /* 339 * The KADM5_API_VERSION_1 spec said "If pass (or keytab) is NULL 340 * or an empty string, reads the master password from [the stash 341 * file]. Otherwise, the non-NULL password is ignored and the 342 * user is prompted for it via the tty." However, the code was 343 * implemented the other way: when a non-NULL password was 344 * provided, the stash file was used. This is somewhat more 345 * sensible, as then a local or remote client that provides a 346 * password does not prompt the user. This code maintains the 347 * previous actual behavior, and not the old spec behavior, 348 * because that is how the unit tests are written. 349 * 350 * In KADM5_API_VERSION_2, this decision is controlled by 351 * params. 352 * 353 * kdb_init_master's third argument is "from_keyboard". 354 */ 355 /* 356 * Solaris Kerberos: Setting to an unknown enc type will make the function 357 * read the encryption type in the stash file instead of assumming that it 358 * is the default type. 359 */ 360 if (handle->params.enctype == DEFAULT_KDC_ENCTYPE) 361 handle->params.enctype = ENCTYPE_UNKNOWN; 362 ret = kdb_init_master(handle, handle->params.realm, 363 (handle->api_version == KADM5_API_VERSION_1 ? 364 ((pass == NULL) || !(strlen(pass))) : 365 ((handle->params.mask & KADM5_CONFIG_MKEY_FROM_KBD) 366 && handle->params.mkey_from_kbd) 367 )); 368 if (ret) { 369 krb5_db_fini(handle->context); 370 kadm5_free_config_params(handle->context, &handle->params); 371 krb5_free_context(handle->context); 372 free_db_args(handle); 373 free(handle); 374 return ret; 375 } 376 /* 377 * Solaris Kerberos: We used the enc type that was discovered in the stash 378 * file to associate with the other magic principals in the database. 379 */ 380 handle->params.enctype = handle->master_keyblock.enctype; 381 382 ret = kdb_init_hist(handle, handle->params.realm); 383 if (ret) { 384 krb5_db_fini(handle->context); 385 kadm5_free_config_params(handle->context, &handle->params); 386 krb5_free_context(handle->context); 387 free_db_args(handle); 388 free(handle); 389 return ret; 390 } 391 392 ret = init_dict(&handle->params); 393 if (ret) { 394 krb5_db_fini(handle->context); 395 krb5_free_principal(handle->context, handle->current_caller); 396 kadm5_free_config_params(handle->context, &handle->params); 397 krb5_free_context(handle->context); 398 free_db_args(handle); 399 free(handle); 400 return ret; 401 } 402 403 *server_handle = (void *) handle; 404 405 return KADM5_OK; 406 } 407 408 kadm5_ret_t kadm5_init(char *client_name, char *pass, 409 char *service_name, 410 kadm5_config_params *params_in, 411 krb5_ui_4 struct_version, 412 krb5_ui_4 api_version, 413 char **db_args, 414 void **server_handle) { 415 return (kadm5_init2(client_name, pass, service_name, params_in, 416 struct_version, api_version, db_args, server_handle, NULL)); 417 418 } 419 420 kadm5_ret_t kadm5_destroy(void *server_handle) 421 { 422 kadm5_server_handle_t handle = server_handle; 423 424 CHECK_HANDLE(server_handle); 425 426 destroy_dict(); 427 428 adb_policy_close(handle); 429 krb5_db_fini(handle->context); 430 krb5_free_principal(handle->context, handle->current_caller); 431 kadm5_free_config_params(handle->context, &handle->params); 432 krb5_free_context(handle->context); 433 handle->magic_number = 0; 434 free(handle->lhandle); 435 free_db_args(handle); 436 free(handle); 437 438 return KADM5_OK; 439 } 440 441 kadm5_ret_t kadm5_lock(void *server_handle) 442 { 443 kadm5_server_handle_t handle = server_handle; 444 kadm5_ret_t ret; 445 446 CHECK_HANDLE(server_handle); 447 ret = krb5_db_lock(handle->context, KRB5_DB_LOCKMODE_EXCLUSIVE); 448 if (ret) 449 return ret; 450 451 return KADM5_OK; 452 } 453 454 kadm5_ret_t kadm5_unlock(void *server_handle) 455 { 456 kadm5_server_handle_t handle = server_handle; 457 kadm5_ret_t ret; 458 459 CHECK_HANDLE(server_handle); 460 ret = krb5_db_unlock(handle->context); 461 if (ret) 462 return ret; 463 464 return KADM5_OK; 465 } 466 467 kadm5_ret_t kadm5_flush(void *server_handle) 468 { 469 kadm5_server_handle_t handle = server_handle; 470 kadm5_ret_t ret; 471 472 CHECK_HANDLE(server_handle); 473 474 if ((ret = krb5_db_fini(handle->context)) || 475 (ret = krb5_db_open(handle->context, handle->db_args, 476 KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN)) || 477 (ret = adb_policy_close(handle)) || 478 (ret = adb_policy_init(handle))) { 479 (void) kadm5_destroy(server_handle); 480 return ret; 481 } 482 return KADM5_OK; 483 } 484 485 int _kadm5_check_handle(void *handle) 486 { 487 CHECK_HANDLE(handle); 488 return 0; 489 } 490 491 #include "gssapiP_krb5.h" 492 krb5_error_code kadm5_init_krb5_context (krb5_context *ctx) 493 { 494 /* Solaris Kerberos: not needed */ 495 #if 0 /************** Begin IFDEF'ed OUT *******************************/ 496 static int first_time = 1; 497 if (first_time) { 498 krb5_error_code err; 499 err = krb5_gss_use_kdc_context(); 500 if (err) 501 return err; 502 first_time = 0; 503 } 504 #endif /**************** END IFDEF'ed OUT *******************************/ 505 return krb5int_init_context_kdc(ctx); 506 } 507 508 krb5_error_code 509 kadm5_init_iprop(void *handle) 510 { 511 kadm5_server_handle_t iprop_h; 512 krb5_error_code retval; 513 514 iprop_h = handle; 515 if (iprop_h->params.iprop_enabled) { 516 ulog_set_role(iprop_h->context, IPROP_MASTER); 517 if ((retval = ulog_map(iprop_h->context, &iprop_h->params, 518 FKCOMMAND)) != 0) 519 return (retval); 520 } 521 return (0); 522 } 523