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