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