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