1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 8 /* 9 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved 10 */ 11 12 /* 13 * Copyright (C) 1998 by the FundsXpress, INC. 14 * 15 * All rights reserved. 16 * 17 * Export of this software from the United States of America may require 18 * a specific license from the United States Government. It is the 19 * responsibility of any person or organization contemplating export to 20 * obtain such a license before exporting. 21 * 22 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 23 * distribute this software and its documentation for any purpose and 24 * without fee is hereby granted, provided that the above copyright 25 * notice appear in all copies and that both that copyright notice and 26 * this permission notice appear in supporting documentation, and that 27 * the name of FundsXpress. not be used in advertising or publicity pertaining 28 * to distribution of the software without specific, written prior 29 * permission. FundsXpress makes no representations about the suitability of 30 * this software for any purpose. It is provided "as is" without express 31 * or implied warranty. 32 * 33 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 34 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 35 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 36 */ 37 38 #include <stdio.h> 39 #include <netdb.h> 40 #include "autoconf.h" 41 #ifdef HAVE_MEMORY_H 42 #include <memory.h> 43 #endif 44 #include <string.h> 45 #include <com_err.h> 46 #include <sys/types.h> 47 #include <sys/socket.h> 48 #include <netinet/in.h> 49 #include <k5-int.h> /* for KRB5_ADM_DEFAULT_PORT */ 50 #include <krb5.h> 51 #ifdef __STDC__ 52 #include <stdlib.h> 53 #endif 54 #include <libintl.h> 55 56 #include <kadm5/admin.h> 57 #include <kadm5/kadm_rpc.h> 58 #include "client_internal.h" 59 60 #include <syslog.h> 61 #include <gssapi/gssapi.h> 62 #include <gssapi_krb5.h> 63 #include <gssapiP_krb5.h> 64 #include <rpc/clnt.h> 65 66 #include <iprop_hdr.h> 67 #include "iprop.h" 68 69 #define ADM_CCACHE "/tmp/ovsec_adm.XXXXXX" 70 71 static int old_auth_gssapi = 0; 72 /* connection timeout to kadmind in seconds */ 73 #define KADMIND_CONNECT_TIMEOUT 25 74 75 int _kadm5_check_handle(); 76 77 enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS }; 78 79 static kadm5_ret_t _kadm5_init_any(char *client_name, 80 enum init_type init_type, 81 char *pass, 82 krb5_ccache ccache_in, 83 char *service_name, 84 kadm5_config_params *params, 85 krb5_ui_4 struct_version, 86 krb5_ui_4 api_version, 87 char **db_args, 88 void **server_handle); 89 90 kadm5_ret_t kadm5_init_with_creds(char *client_name, 91 krb5_ccache ccache, 92 char *service_name, 93 kadm5_config_params *params, 94 krb5_ui_4 struct_version, 95 krb5_ui_4 api_version, 96 char **db_args, 97 void **server_handle) 98 { 99 return _kadm5_init_any(client_name, INIT_CREDS, NULL, ccache, 100 service_name, params, 101 struct_version, api_version, db_args, 102 server_handle); 103 } 104 105 106 kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass, 107 char *service_name, 108 kadm5_config_params *params, 109 krb5_ui_4 struct_version, 110 krb5_ui_4 api_version, 111 char **db_args, 112 void **server_handle) 113 { 114 return _kadm5_init_any(client_name, INIT_PASS, pass, NULL, 115 service_name, params, struct_version, 116 api_version, db_args, server_handle); 117 } 118 119 kadm5_ret_t kadm5_init(char *client_name, char *pass, 120 char *service_name, 121 kadm5_config_params *params, 122 krb5_ui_4 struct_version, 123 krb5_ui_4 api_version, 124 char **db_args, 125 void **server_handle) 126 { 127 return _kadm5_init_any(client_name, INIT_PASS, pass, NULL, 128 service_name, params, struct_version, 129 api_version, db_args, server_handle); 130 } 131 132 kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab, 133 char *service_name, 134 kadm5_config_params *params, 135 krb5_ui_4 struct_version, 136 krb5_ui_4 api_version, 137 char **db_args, 138 void **server_handle) 139 { 140 return _kadm5_init_any(client_name, INIT_SKEY, keytab, NULL, 141 service_name, params, struct_version, 142 api_version, db_args, server_handle); 143 } 144 145 krb5_error_code kadm5_free_config_params(); 146 147 static void 148 display_status_1(m, code, type, mech) 149 char *m; 150 OM_uint32 code; 151 int type; 152 const gss_OID mech; 153 { 154 OM_uint32 maj_stat, min_stat; 155 gss_buffer_desc msg = GSS_C_EMPTY_BUFFER; 156 OM_uint32 msg_ctx; 157 158 msg_ctx = 0; 159 ADMIN_LOG(LOG_ERR, "%s\n", m); 160 /* LINTED */ 161 while (1) { 162 maj_stat = gss_display_status(&min_stat, code, 163 type, mech, 164 &msg_ctx, &msg); 165 if (maj_stat != GSS_S_COMPLETE) { 166 syslog(LOG_ERR, 167 dgettext(TEXT_DOMAIN, 168 "error in gss_display_status" 169 " called from <%s>\n"), m); 170 break; 171 } else 172 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 173 "GSS-API error : %s\n"), 174 m); 175 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 176 "GSS-API error : %s\n"), 177 (char *)msg.value); 178 if (msg.length != 0) 179 (void) gss_release_buffer(&min_stat, &msg); 180 181 if (!msg_ctx) 182 break; 183 } 184 } 185 186 /* 187 * Function: display_status 188 * 189 * Purpose: displays GSS-API messages 190 * 191 * Arguments: 192 * 193 * msg a string to be displayed with the message 194 * maj_stat the GSS-API major status code 195 * min_stat the GSS-API minor status code 196 * mech kerberos mech 197 * Effects: 198 * 199 * The GSS-API messages associated with maj_stat and min_stat are 200 * displayed on stderr, each preceeded by "GSS-API error <msg>: " and 201 * followed by a newline. 202 */ 203 void 204 display_status(msg, maj_stat, min_stat, mech) 205 char *msg; 206 OM_uint32 maj_stat; 207 OM_uint32 min_stat; 208 char *mech; 209 { 210 gss_OID mech_oid; 211 212 if (!rpc_gss_mech_to_oid(mech, (rpc_gss_OID *)&mech_oid)) { 213 ADMIN_LOG(LOG_ERR, 214 dgettext(TEXT_DOMAIN, 215 "Invalid mechanism oid <%s>"), mech); 216 return; 217 } 218 219 display_status_1(msg, maj_stat, GSS_C_GSS_CODE, mech_oid); 220 display_status_1(msg, min_stat, GSS_C_MECH_CODE, mech_oid); 221 } 222 223 /* 224 * Open an fd for the given address and connect asynchronously. Wait 225 * KADMIND_CONNECT_TIMEOUT seconds or till it succeeds. If it succeeds 226 * change fd to blocking and return it, else return -1. 227 */ 228 static int 229 get_connection(struct netconfig *nconf, struct netbuf netaddr) 230 { 231 struct t_info tinfo; 232 struct t_call sndcall; 233 struct t_call *rcvcall = NULL; 234 int connect_time; 235 int flags; 236 int fd; 237 238 (void) memset(&tinfo, 0, sizeof (tinfo)); 239 240 /* we'l open with O_NONBLOCK and avoid an fcntl */ 241 fd = t_open(nconf->nc_device, O_RDWR | O_NONBLOCK, &tinfo); 242 if (fd == -1) { 243 return (-1); 244 } 245 246 if (t_bind(fd, (struct t_bind *)NULL, (struct t_bind *)NULL) == -1) { 247 (void) close(fd); 248 return (-1); 249 } 250 251 /* we can't connect unless fd is in IDLE state */ 252 if (t_getstate(fd) != T_IDLE) { 253 (void) close(fd); 254 return (-1); 255 } 256 257 /* setup connect parameters */ 258 netaddr.len = netaddr.maxlen = __rpc_get_a_size(tinfo.addr); 259 sndcall.addr = netaddr; 260 sndcall.opt.len = sndcall.udata.len = 0; 261 262 /* we wait for KADMIND_CONNECT_TIMEOUT seconds from now */ 263 connect_time = time(NULL) + KADMIND_CONNECT_TIMEOUT; 264 if (t_connect(fd, &sndcall, rcvcall) != 0) { 265 if (t_errno != TNODATA) { 266 (void) close(fd); 267 return (-1); 268 } 269 } 270 271 /* loop till success or timeout */ 272 for (;;) { 273 if (t_rcvconnect(fd, rcvcall) == 0) 274 break; 275 276 if (t_errno != TNODATA || time(NULL) > connect_time) { 277 /* we have either timed out or caught an error */ 278 (void) close(fd); 279 if (rcvcall != NULL) 280 t_free((char *)rcvcall, T_CALL); 281 return (-1); 282 } 283 sleep(1); 284 } 285 286 /* make the fd blocking (synchronous) */ 287 flags = fcntl(fd, F_GETFL, 0); 288 (void) fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); 289 if (rcvcall != NULL) 290 t_free((char *)rcvcall, T_CALL); 291 return (fd); 292 } 293 294 /* 295 * Open an RPCSEC_GSS connection and 296 * get a client handle to use for future RPCSEC calls. 297 * 298 * This function is only used when changing passwords and 299 * the kpasswd_protocol is RPCSEC_GSS 300 */ 301 static int 302 _kadm5_initialize_rpcsec_gss_handle(kadm5_server_handle_t handle, 303 char *client_name, 304 char *service_name) 305 { 306 struct netbuf netaddr; 307 struct hostent *hp; 308 int fd; 309 struct sockaddr_in addr; 310 struct sockaddr_in *sin; 311 struct netconfig *nconf; 312 int code = 0; 313 generic_ret *r; 314 char *ccname_orig; 315 char *iprop_svc; 316 boolean_t iprop_enable = B_FALSE; 317 char mech[] = "kerberos_v5"; 318 gss_OID mech_oid; 319 gss_OID_set_desc oid_set; 320 gss_name_t gss_client; 321 gss_buffer_desc input_name; 322 gss_cred_id_t gss_client_creds = GSS_C_NO_CREDENTIAL; 323 rpc_gss_options_req_t options_req; 324 rpc_gss_options_ret_t options_ret; 325 rpc_gss_service_t service = rpc_gss_svc_privacy; 326 OM_uint32 gssstat, minor_stat; 327 void *handlep; 328 enum clnt_stat rpc_err_code; 329 char *server = handle->params.admin_server; 330 331 /* 332 * Try to find the kpasswd_server first if this is for the changepw 333 * service. If defined then it should be resolvable else return error. 334 */ 335 if (strncmp(service_name, KADM5_CHANGEPW_HOST_SERVICE, 336 strlen(KADM5_CHANGEPW_HOST_SERVICE)) == 0) { 337 if (handle->params.kpasswd_server != NULL) 338 server = handle->params.kpasswd_server; 339 } 340 hp = gethostbyname(server); 341 if (hp == (struct hostent *)NULL) { 342 code = KADM5_BAD_SERVER_NAME; 343 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 344 "bad server name\n")); 345 goto cleanup; 346 } 347 348 memset(&addr, 0, sizeof (addr)); 349 addr.sin_family = hp->h_addrtype; 350 (void) memcpy((char *)&addr.sin_addr, (char *)hp->h_addr, 351 sizeof (addr.sin_addr)); 352 addr.sin_port = htons((ushort_t)handle->params.kadmind_port); 353 sin = &addr; 354 #ifdef DEBUG 355 printf("kadmin_port %d\n", handle->params.kadmind_port); 356 printf("addr: sin_port: %d, sin_family: %d, sin_zero %s\n", 357 addr.sin_port, addr.sin_family, addr.sin_zero); 358 printf("sin_addr %d:%d\n", addr.sin_addr.S_un.S_un_w.s_w1, 359 addr.sin_addr.S_un.S_un_w.s_w2); 360 #endif 361 if ((handlep = setnetconfig()) == (void *) NULL) { 362 (void) syslog(LOG_ERR, 363 dgettext(TEXT_DOMAIN, 364 "cannot get any transport information")); 365 goto error; 366 } 367 368 while (nconf = getnetconfig(handlep)) { 369 if ((nconf->nc_semantics == NC_TPI_COTS_ORD) && 370 (strcmp(nconf->nc_protofmly, NC_INET) == 0) && 371 (strcmp(nconf->nc_proto, NC_TCP) == 0)) 372 break; 373 } 374 375 if (nconf == (struct netconfig *)NULL) 376 goto error; 377 378 /* Transform addr to netbuf */ 379 (void) memset(&netaddr, 0, sizeof (netaddr)); 380 netaddr.buf = (char *)sin; 381 382 /* get an fd connected to the given address */ 383 fd = get_connection(nconf, netaddr); 384 if (fd == -1) { 385 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 386 "unable to open connection to ADMIN server " 387 "(t_error %i)"), t_errno); 388 code = KADM5_RPC_ERROR; 389 goto error; 390 } 391 392 #ifdef DEBUG 393 printf("fd: %d, KADM: %d, KADMVERS %d\n", fd, KADM, KADMVERS); 394 printf("nconf: nc_netid: %s, nc_semantics: %d, nc_flag: %d, " 395 "nc_protofmly: %s\n", 396 nconf->nc_netid, nconf->nc_semantics, nconf->nc_flag, 397 nconf->nc_protofmly); 398 printf("nc_proto: %s, nc_device: %s, nc_nlookups: %d, nc_used: %d\n", 399 nconf->nc_proto, nconf->nc_device, nconf->nc_nlookups, 400 nconf->nc_unused); 401 printf("netaddr: maxlen %d, buf: %s, len: %d\n", netaddr.maxlen, 402 netaddr.buf, netaddr.len); 403 #endif 404 /* 405 * Tell clnt_tli_create that given fd is already connected 406 * 407 * If the service_name and client_name are iprop-centric, 408 * we need to clnt_tli_create to the appropriate RPC prog 409 */ 410 iprop_svc = strdup(KIPROP_SVC_NAME); 411 if (iprop_svc == NULL) 412 return (ENOMEM); 413 414 if ((strstr(service_name, iprop_svc) != NULL) && 415 (strstr(client_name, iprop_svc) != NULL)) { 416 iprop_enable = B_TRUE; 417 handle->clnt = clnt_tli_create(fd, nconf, NULL, 418 KRB5_IPROP_PROG, KRB5_IPROP_VERS, 0, 0); 419 } 420 else 421 handle->clnt = clnt_tli_create(fd, nconf, NULL, 422 KADM, KADMVERS, 0, 0); 423 424 if (iprop_svc) 425 free(iprop_svc); 426 427 if (handle->clnt == NULL) { 428 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 429 "clnt_tli_create failed\n")); 430 code = KADM5_RPC_ERROR; 431 (void) close(fd); 432 goto error; 433 } 434 /* 435 * The rpc-handle was created on an fd opened and connected 436 * by us, so we have to explicitly tell rpc to close it. 437 */ 438 if (clnt_control(handle->clnt, CLSET_FD_CLOSE, NULL) != TRUE) { 439 clnt_pcreateerror("ERROR:"); 440 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 441 "clnt_control failed to set CLSET_FD_CLOSE")); 442 code = KADM5_RPC_ERROR; 443 (void) close(fd); 444 goto error; 445 } 446 447 handle->lhandle->clnt = handle->clnt; 448 449 /* now that handle->clnt is set, we can check the handle */ 450 if (code = _kadm5_check_handle((void *) handle)) 451 goto error; 452 453 /* 454 * The RPC connection is open; establish the GSS-API 455 * authentication context. 456 */ 457 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 458 "have an rpc connection open\n")); 459 /* use the kadm5 cache */ 460 ccname_orig = getenv("KRB5CCNAME"); 461 if (ccname_orig) 462 ccname_orig = strdup(ccname_orig); 463 464 (void) krb5_setenv("KRB5CCNAME", handle->cache_name, 1); 465 466 ADMIN_LOG(LOG_ERR, 467 dgettext(TEXT_DOMAIN, 468 "current credential cache: %s"), handle->cache_name); 469 input_name.value = client_name; 470 input_name.length = strlen((char *)input_name.value) + 1; 471 gssstat = gss_import_name(&minor_stat, &input_name, 472 (gss_OID)gss_nt_krb5_name, &gss_client); 473 if (gssstat != GSS_S_COMPLETE) { 474 code = KADM5_GSS_ERROR; 475 ADMIN_LOGO(LOG_ERR, 476 dgettext(TEXT_DOMAIN, 477 "gss_import_name failed for client name\n")); 478 goto error; 479 } 480 481 if (!rpc_gss_mech_to_oid(mech, (rpc_gss_OID *)&mech_oid)) { 482 ADMIN_LOG(LOG_ERR, 483 dgettext(TEXT_DOMAIN, 484 "Invalid mechanism oid <%s>"), mech); 485 goto error; 486 } 487 488 oid_set.count = 1; 489 oid_set.elements = mech_oid; 490 491 gssstat = gss_acquire_cred(&minor_stat, gss_client, 0, 492 &oid_set, GSS_C_INITIATE, 493 &gss_client_creds, NULL, NULL); 494 (void) gss_release_name(&minor_stat, &gss_client); 495 if (gssstat != GSS_S_COMPLETE) { 496 code = KADM5_GSS_ERROR; 497 ADMIN_LOG(LOG_ERR, 498 dgettext(TEXT_DOMAIN, 499 "could not acquire credentials, " 500 "major error code: %d\n"), gssstat); 501 goto error; 502 } 503 handle->my_cred = gss_client_creds; 504 options_req.my_cred = gss_client_creds; 505 options_req.req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG; 506 options_req.time_req = 0; 507 options_req.input_channel_bindings = NULL; 508 #ifndef INIT_TEST 509 handle->clnt->cl_auth = rpc_gss_seccreate(handle->clnt, 510 service_name, 511 mech, 512 service, 513 NULL, 514 &options_req, 515 &options_ret); 516 #endif /* ! INIT_TEST */ 517 518 if (ccname_orig) { 519 (void) krb5_setenv("KRB5CCNAME", ccname_orig, 1); 520 free(ccname_orig); 521 } else 522 (void) krb5_unsetenv("KRB5CCNAME"); 523 524 if (handle->clnt->cl_auth == NULL) { 525 code = KADM5_GSS_ERROR; 526 display_status(dgettext(TEXT_DOMAIN, 527 "rpc_gss_seccreate failed\n"), 528 options_ret.major_status, 529 options_ret.minor_status, 530 mech); 531 goto error; 532 } 533 534 /* 535 * Bypass the remainder of the code and return straightaway 536 * if the gss service requested is kiprop 537 */ 538 if (iprop_enable == B_TRUE) { 539 code = 0; 540 goto cleanup; 541 } 542 543 r = init_2(&handle->api_version, handle->clnt); 544 /* Solaris Kerberos: 163 resync */ 545 if (r == NULL) { 546 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 547 "error during admin api initialization\n")); 548 code = KADM5_RPC_ERROR; 549 goto error; 550 } 551 552 if (r->code) { 553 code = r->code; 554 ADMIN_LOG(LOG_ERR, 555 dgettext(TEXT_DOMAIN, 556 "error during admin api initialization: %d\n"), 557 r->code); 558 goto error; 559 } 560 error: 561 cleanup: 562 563 if (handlep != (void *) NULL) 564 (void) endnetconfig(handlep); 565 /* 566 * gss_client_creds is freed only when there is an error condition, 567 * given that rpc_gss_seccreate() will assign the cred pointer to the 568 * my_cred member in the auth handle's private data structure. 569 */ 570 if (code && (gss_client_creds != GSS_C_NO_CREDENTIAL)) 571 (void) gss_release_cred(&minor_stat, &gss_client_creds); 572 573 return (code); 574 } 575 576 static kadm5_ret_t _kadm5_init_any(char *client_name, 577 enum init_type init_type, 578 char *pass, 579 krb5_ccache ccache_in, 580 char *service_name, 581 kadm5_config_params *params_in, 582 krb5_ui_4 struct_version, 583 krb5_ui_4 api_version, 584 char **db_args, 585 void **server_handle) 586 { 587 int i; 588 krb5_creds creds; 589 krb5_ccache ccache = NULL; 590 krb5_timestamp now; 591 OM_uint32 gssstat, minor_stat; 592 kadm5_server_handle_t handle; 593 kadm5_config_params params_local; 594 int code = 0; 595 krb5_get_init_creds_opt opt; 596 gss_buffer_desc input_name; 597 krb5_error_code kret; 598 krb5_int32 starttime; 599 char *server = NULL; 600 krb5_principal serverp = NULL, clientp = NULL; 601 krb5_principal saved_server = NULL; 602 bool_t cpw = FALSE; 603 604 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 605 "entering kadm5_init_any\n")); 606 if (! server_handle) { 607 return EINVAL; 608 } 609 610 if (! (handle = malloc(sizeof(*handle)))) { 611 return ENOMEM; 612 } 613 if (! (handle->lhandle = malloc(sizeof(*handle)))) { 614 free(handle); 615 return ENOMEM; 616 } 617 618 handle->magic_number = KADM5_SERVER_HANDLE_MAGIC; 619 handle->struct_version = struct_version; 620 handle->api_version = api_version; 621 handle->clnt = 0; 622 handle->cache_name = 0; 623 handle->destroy_cache = 0; 624 *handle->lhandle = *handle; 625 handle->lhandle->api_version = KADM5_API_VERSION_2; 626 handle->lhandle->struct_version = KADM5_STRUCT_VERSION; 627 handle->lhandle->lhandle = handle->lhandle; 628 629 kret = krb5_init_context(&handle->context); 630 if (kret) { 631 free(handle->lhandle); 632 free(handle); 633 return (kret); 634 } 635 636 if(service_name == NULL || client_name == NULL) { 637 krb5_free_context(handle->context); 638 free(handle->lhandle); 639 free(handle); 640 return EINVAL; 641 } 642 memset((char *) &creds, 0, sizeof(creds)); 643 644 /* 645 * Verify the version numbers before proceeding; we can't use 646 * CHECK_HANDLE because not all fields are set yet. 647 */ 648 GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION, 649 KADM5_NEW_LIB_API_VERSION); 650 651 /* 652 * Acquire relevant profile entries. In version 2, merge values 653 * in params_in with values from profile, based on 654 * params_in->mask. 655 * 656 * In version 1, we've given a realm (which may be NULL) instead 657 * of params_in. So use that realm, make params_in contain an 658 * empty mask, and behave like version 2. 659 */ 660 memset((char *) ¶ms_local, 0, sizeof(params_local)); 661 if (api_version == KADM5_API_VERSION_1) { 662 if (params_in) 663 params_local.mask = KADM5_CONFIG_REALM; 664 params_in = ¶ms_local; 665 } 666 667 #define ILLEGAL_PARAMS ( \ 668 KADM5_CONFIG_ACL_FILE | KADM5_CONFIG_ADB_LOCKFILE | \ 669 KADM5_CONFIG_DBNAME | KADM5_CONFIG_ADBNAME | \ 670 KADM5_CONFIG_DICT_FILE | KADM5_CONFIG_ADMIN_KEYTAB | \ 671 KADM5_CONFIG_STASH_FILE | KADM5_CONFIG_MKEY_NAME | \ 672 KADM5_CONFIG_ENCTYPE | KADM5_CONFIG_MAX_LIFE | \ 673 KADM5_CONFIG_MAX_RLIFE | KADM5_CONFIG_EXPIRATION | \ 674 KADM5_CONFIG_FLAGS | KADM5_CONFIG_ENCTYPES | \ 675 KADM5_CONFIG_MKEY_FROM_KBD) 676 677 if (params_in && params_in->mask & ILLEGAL_PARAMS) { 678 krb5_free_context(handle->context); 679 free(handle->lhandle); 680 free(handle); 681 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, 682 "bad client parameters, returning %d"), 683 KADM5_BAD_CLIENT_PARAMS); 684 return KADM5_BAD_CLIENT_PARAMS; 685 } 686 687 if ((code = kadm5_get_config_params(handle->context, 0, 688 params_in, &handle->params))) { 689 krb5_free_context(handle->context); 690 free(handle->lhandle); 691 free(handle); 692 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, 693 "failed to get config_params, return: %d\n"), code); 694 return(code); 695 } 696 697 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \ 698 KADM5_CONFIG_ADMIN_SERVER | \ 699 KADM5_CONFIG_KADMIND_PORT) 700 #define KPW_REQUIRED_PARAMS (KADM5_CONFIG_REALM | \ 701 KADM5_CONFIG_KPASSWD_SERVER | \ 702 KADM5_CONFIG_KPASSWD_PORT) 703 704 if (((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) && 705 ((handle->params.mask & KPW_REQUIRED_PARAMS) != KPW_REQUIRED_PARAMS)) { 706 (void) kadm5_free_config_params(handle->context, 707 &handle->params); 708 krb5_free_context(handle->context); 709 free(handle->lhandle); 710 free(handle); 711 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 712 "missing config parameters\n")); 713 return KADM5_MISSING_KRB5_CONF_PARAMS; 714 } 715 716 /* 717 * Acquire a service ticket for service_name@realm in the name of 718 * client_name, using password pass (which could be NULL), and 719 * create a ccache to store them in. If INIT_CREDS, use the 720 * ccache we were provided instead. 721 */ 722 if ((code = krb5_parse_name(handle->context, client_name, 723 &creds.client))) { 724 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 725 "could not parse client name\n")); 726 goto error; 727 } 728 clientp = creds.client; 729 730 if (strncmp(service_name, KADM5_CHANGEPW_HOST_SERVICE, 731 strlen(KADM5_CHANGEPW_HOST_SERVICE)) == 0) 732 cpw = TRUE; 733 734 if (init_type == INIT_PASS && 735 handle->params.kpasswd_protocol == KRB5_CHGPWD_CHANGEPW_V2 && 736 cpw == TRUE) { 737 /* 738 * The 'service_name' is constructed by the caller 739 * but its done before the parameter which determines 740 * the kpasswd_protocol is found. The servers that 741 * support the SET/CHANGE password protocol expect 742 * a slightly different service principal than 743 * the normal SEAM kadmind so construct the correct 744 * name here and then forget it. 745 */ 746 char *newsvcname = NULL; 747 newsvcname = malloc(strlen(KADM5_CHANGEPW_SERVICE) + 748 strlen(handle->params.realm) + 2); 749 if (newsvcname == NULL) { 750 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 751 "could not malloc\n")); 752 code = ENOMEM; 753 goto error; 754 } 755 sprintf(newsvcname, "%s@%s", KADM5_CHANGEPW_SERVICE, 756 handle->params.realm); 757 758 if ((code = krb5_parse_name(handle->context, newsvcname, 759 &creds.server))) { 760 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 761 "could not parse server " 762 "name\n")); 763 free(newsvcname); 764 goto error; 765 } 766 free(newsvcname); 767 } else { 768 input_name.value = service_name; 769 input_name.length = strlen((char *)input_name.value) + 1; 770 gssstat = krb5_gss_import_name(&minor_stat, 771 &input_name, 772 (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, 773 (gss_name_t *)&creds.server); 774 775 if (gssstat != GSS_S_COMPLETE) { 776 code = KADM5_GSS_ERROR; 777 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 778 "gss_import_name failed for client name\n")); 779 goto error; 780 } 781 } 782 serverp = creds.server; 783 784 /* XXX temporarily fix a bug in krb5_cc_get_type */ 785 #undef krb5_cc_get_type 786 #define krb5_cc_get_type(context, cache) ((cache)->ops->prefix) 787 788 789 if (init_type == INIT_CREDS) { 790 ccache = ccache_in; 791 handle->cache_name = (char *) 792 malloc(strlen(krb5_cc_get_type(handle->context, ccache)) + 793 strlen(krb5_cc_get_name(handle->context, ccache)) + 2); 794 if (handle->cache_name == NULL) { 795 code = ENOMEM; 796 goto error; 797 } 798 sprintf(handle->cache_name, "%s:%s", 799 krb5_cc_get_type(handle->context, ccache), 800 krb5_cc_get_name(handle->context, ccache)); 801 } else { 802 #if 0 803 handle->cache_name = 804 (char *) malloc(strlen(ADM_CCACHE)+strlen("FILE:")+1); 805 if (handle->cache_name == NULL) { 806 code = ENOMEM; 807 goto error; 808 } 809 sprintf(handle->cache_name, "FILE:%s", ADM_CCACHE); 810 mktemp(handle->cache_name + strlen("FILE:")); 811 #endif 812 { 813 static int counter = 0; 814 handle->cache_name = malloc(sizeof("MEMORY:kadm5_") 815 + 3*sizeof(counter)); 816 sprintf(handle->cache_name, "MEMORY:kadm5_%u", counter++); 817 } 818 819 if ((code = krb5_cc_resolve(handle->context, handle->cache_name, 820 &ccache))) 821 goto error; 822 823 if ((code = krb5_cc_initialize (handle->context, ccache, 824 creds.client))) 825 goto error; 826 827 handle->destroy_cache = 1; 828 } 829 handle->lhandle->cache_name = handle->cache_name; 830 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, 831 "cache created: %s\n"), handle->cache_name); 832 833 if ((code = krb5_timeofday(handle->context, &now))) 834 goto error; 835 836 /* 837 * Get a ticket, use the method specified in init_type. 838 */ 839 840 creds.times.starttime = 0; /* start timer at KDC */ 841 creds.times.endtime = 0; /* endtime will be limited by service */ 842 843 memset(&opt, 0, sizeof (opt)); 844 krb5_get_init_creds_opt_init(&opt); 845 846 if (creds.times.endtime) { 847 if (creds.times.starttime) 848 starttime = creds.times.starttime; 849 else 850 starttime = now; 851 852 krb5_get_init_creds_opt_set_tkt_life(&opt, 853 creds.times.endtime - starttime); 854 } 855 code = krb5_unparse_name(handle->context, creds.server, &server); 856 if (code) 857 goto error; 858 859 /* 860 * Solaris Kerberos: 861 * Save the original creds.server as krb5_get_init_creds*() always 862 * sets the realm of the server to the client realm. 863 */ 864 code = krb5_copy_principal(handle->context, creds.server, &saved_server); 865 if (code) 866 goto error; 867 868 if (init_type == INIT_PASS) { 869 code = krb5_get_init_creds_password(handle->context, 870 &creds, creds.client, pass, NULL, 871 NULL, creds.times.starttime, 872 server, &opt); 873 } else if (init_type == INIT_SKEY) { 874 krb5_keytab kt = NULL; 875 876 if (!(pass && (code = krb5_kt_resolve(handle->context, 877 pass, &kt)))) { 878 code = krb5_get_init_creds_keytab( 879 handle->context, 880 &creds, creds.client, kt, 881 creds.times.starttime, 882 server, &opt); 883 884 if (pass) krb5_kt_close(handle->context, kt); 885 } 886 } 887 888 /* Improved error messages */ 889 if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD; 890 if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) 891 code = KADM5_SECURE_PRINC_MISSING; 892 893 if (code != 0) { 894 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 895 "failed to obtain credentials cache\n")); 896 krb5_free_principal(handle->context, saved_server); 897 goto error; 898 } 899 900 /* 901 * Solaris Kerberos: 902 * If the server principal had an empty realm then store that in 903 * the cred cache and not the server realm as returned by 904 * krb5_get_init_creds_{keytab|password}(). This ensures that rpcsec_gss 905 * will find the credential in the cred cache even if a "fallback" 906 * method is being used to determine the realm. 907 */ 908 if (init_type != INIT_CREDS) { 909 krb5_free_principal(handle->context, creds.server); 910 } 911 creds.server = saved_server; 912 913 /* 914 * If we got this far, save the creds in the cache. 915 */ 916 if (ccache) { 917 code = krb5_cc_store_cred(handle->context, ccache, &creds); 918 } 919 920 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "obtained credentials cache\n")); 921 922 #ifdef ZEROPASSWD 923 if (pass != NULL) 924 memset(pass, 0, strlen(pass)); 925 #endif 926 927 if (init_type != INIT_PASS || 928 handle->params.kpasswd_protocol == KRB5_CHGPWD_RPCSEC || 929 cpw == FALSE) { 930 code = _kadm5_initialize_rpcsec_gss_handle(handle, 931 client_name, service_name); 932 933 /* 934 * Solaris Kerberos: 935 * If _kadm5_initialize_rpcsec_gss_handle() fails it will have 936 * called krb5_gss_release_cred(). If the credential cache is a 937 * MEMORY cred cache krb5_gss_release_cred() destroys the 938 * cred cache data. Make sure that the cred-cache is closed 939 * to prevent a double free in the "error" code. 940 */ 941 if (code != 0) { 942 if (init_type != INIT_CREDS) { 943 krb5_cc_close(handle->context, ccache); 944 ccache = NULL; 945 } 946 goto error; 947 } 948 } 949 950 *server_handle = (void *) handle; 951 952 if (init_type != INIT_CREDS) 953 krb5_cc_close(handle->context, ccache); 954 955 goto cleanup; 956 957 error: 958 /* 959 * Note that it is illegal for this code to execute if "handle" 960 * has not been allocated and initialized. I.e., don't use "goto 961 * error" before the block of code at the top of the function 962 * that allocates and initializes "handle". 963 */ 964 if (handle->cache_name) 965 free(handle->cache_name); 966 if (handle->destroy_cache && ccache) 967 krb5_cc_destroy(handle->context, ccache); 968 if(handle->clnt && handle->clnt->cl_auth) 969 AUTH_DESTROY(handle->clnt->cl_auth); 970 if(handle->clnt) 971 clnt_destroy(handle->clnt); 972 (void) kadm5_free_config_params(handle->context, &handle->params); 973 974 cleanup: 975 if (server) 976 free(server); 977 978 /* 979 * cred's server and client pointers could have been overwritten 980 * by the krb5_get_init_* functions. If the addresses are different 981 * before and after the calls then we must free the memory that 982 * was allocated before the call. 983 */ 984 if (clientp && clientp != creds.client) 985 krb5_free_principal(handle->context, clientp); 986 987 if (serverp && serverp != creds.server) 988 krb5_free_principal(handle->context, serverp); 989 990 krb5_free_cred_contents(handle->context, &creds); 991 992 /* 993 * Dont clean up the handle if the code is OK (code==0) 994 * because it is returned to the caller in the 'server_handle' 995 * ptr. 996 */ 997 if (code) { 998 krb5_free_context(handle->context); 999 free(handle->lhandle); 1000 free(handle); 1001 } 1002 1003 return code; 1004 } 1005 1006 kadm5_ret_t 1007 kadm5_destroy(void *server_handle) 1008 { 1009 krb5_ccache ccache = NULL; 1010 int code = KADM5_OK; 1011 kadm5_server_handle_t handle = 1012 (kadm5_server_handle_t) server_handle; 1013 OM_uint32 min_stat; 1014 1015 CHECK_HANDLE(server_handle); 1016 /* SUNW14resync: 1017 * krb5_cc_resolve() will resolve a ccache with the same data that 1018 * handle->my_cred points to. If the ccache is a MEMORY ccache then 1019 * gss_release_cred() will free that data (it doesn't do this when ccache 1020 * is a FILE ccache). 1021 * if'ed out to avoid the double free. 1022 */ 1023 #if 0 1024 if (handle->destroy_cache && handle->cache_name) { 1025 if ((code = krb5_cc_resolve(handle->context, 1026 handle->cache_name, &ccache)) == 0) 1027 code = krb5_cc_destroy (handle->context, ccache); 1028 } 1029 #endif 1030 if (handle->cache_name) 1031 free(handle->cache_name); 1032 if (handle->clnt && handle->clnt->cl_auth) { 1033 /* 1034 * Since kadm5 doesn't use the default credentials we 1035 * must clean this up manually. 1036 */ 1037 if (handle->my_cred != GSS_C_NO_CREDENTIAL) 1038 (void) gss_release_cred(&min_stat, &handle->my_cred); 1039 AUTH_DESTROY(handle->clnt->cl_auth); 1040 } 1041 if (handle->clnt) 1042 clnt_destroy(handle->clnt); 1043 if (handle->lhandle) 1044 free (handle->lhandle); 1045 1046 kadm5_free_config_params(handle->context, &handle->params); 1047 krb5_free_context(handle->context); 1048 1049 handle->magic_number = 0; 1050 free(handle); 1051 1052 return code; 1053 } 1054 /* not supported on client */ 1055 kadm5_ret_t kadm5_lock(void *server_handle) 1056 { 1057 return EINVAL; 1058 } 1059 1060 /* not supported on client */ 1061 kadm5_ret_t kadm5_unlock(void *server_handle) 1062 { 1063 return EINVAL; 1064 } 1065 1066 kadm5_ret_t kadm5_flush(void *server_handle) 1067 { 1068 return KADM5_OK; 1069 } 1070 1071 int _kadm5_check_handle(void *handle) 1072 { 1073 CHECK_HANDLE(handle); 1074 return 0; 1075 } 1076 1077 krb5_error_code kadm5_init_krb5_context (krb5_context *ctx) 1078 { 1079 return krb5_init_context(ctx); 1080 } 1081 1082 /* 1083 * Stub function for kadmin. It was created to eliminate the dependency on 1084 * libkdb's ulog functions. The srv equivalent makes the actual calls. 1085 */ 1086 krb5_error_code 1087 kadm5_init_iprop(void *handle) 1088 { 1089 return (0); 1090 } 1091