1 /* 2 * Copyright 2005 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 597 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 598 "entering kadm5_init_any\n")); 599 if (! server_handle) { 600 return (EINVAL); 601 } 602 603 if (! (handle = malloc(sizeof(*handle)))) { 604 return (ENOMEM); 605 } 606 if (! (handle->lhandle = malloc(sizeof(*handle)))) { 607 free(handle); 608 return (ENOMEM); 609 } 610 611 handle->magic_number = KADM5_SERVER_HANDLE_MAGIC; 612 handle->struct_version = struct_version; 613 handle->api_version = api_version; 614 handle->clnt = 0; 615 handle->cache_name = 0; 616 handle->destroy_cache = 0; 617 *handle->lhandle = *handle; 618 handle->lhandle->api_version = KADM5_API_VERSION_2; 619 handle->lhandle->struct_version = KADM5_STRUCT_VERSION; 620 handle->lhandle->lhandle = handle->lhandle; 621 622 kret = krb5_init_context(&handle->context); 623 if (kret) { 624 free(handle->lhandle); 625 free(handle); 626 return (kret); 627 } 628 629 if(service_name == NULL || client_name == NULL) { 630 krb5_free_context(handle->context); 631 free(handle->lhandle); 632 free(handle); 633 return (EINVAL); 634 } 635 memset((char *) &creds, 0, sizeof(creds)); 636 637 /* 638 * Verify the version numbers before proceeding; we can't use 639 * CHECK_HANDLE because not all fields are set yet. 640 */ 641 GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION, 642 KADM5_NEW_LIB_API_VERSION); 643 644 /* 645 * Acquire relevant profile entries. In version 2, merge values 646 * in params_in with values from profile, based on 647 * params_in->mask. 648 * 649 * In version 1, we've given a realm (which may be NULL) instead 650 * of params_in. So use that realm, make params_in contain an 651 * empty mask, and behave like version 2. 652 */ 653 memset((char *) ¶ms_local, 0, sizeof(params_local)); 654 if (api_version == KADM5_API_VERSION_1) { 655 if (params_in) 656 params_local.mask = KADM5_CONFIG_REALM; 657 params_in = ¶ms_local; 658 } 659 660 #define ILLEGAL_PARAMS ( \ 661 KADM5_CONFIG_ACL_FILE | KADM5_CONFIG_ADB_LOCKFILE | \ 662 KADM5_CONFIG_DBNAME | KADM5_CONFIG_ADBNAME | \ 663 KADM5_CONFIG_DICT_FILE | KADM5_CONFIG_ADMIN_KEYTAB | \ 664 KADM5_CONFIG_STASH_FILE | KADM5_CONFIG_MKEY_NAME | \ 665 KADM5_CONFIG_ENCTYPE | KADM5_CONFIG_MAX_LIFE | \ 666 KADM5_CONFIG_MAX_RLIFE | KADM5_CONFIG_EXPIRATION | \ 667 KADM5_CONFIG_FLAGS | KADM5_CONFIG_ENCTYPES | \ 668 KADM5_CONFIG_MKEY_FROM_KBD) 669 670 if (params_in && params_in->mask & ILLEGAL_PARAMS) { 671 krb5_free_context(handle->context); 672 free(handle->lhandle); 673 free(handle); 674 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, 675 "bad client parameters, returning %d"), 676 KADM5_BAD_CLIENT_PARAMS); 677 return (KADM5_BAD_CLIENT_PARAMS); 678 } 679 680 if ((code = kadm5_get_config_params(handle->context, 681 DEFAULT_PROFILE_PATH, 682 "KRB5_CONFIG", 683 params_in, 684 &handle->params))) { 685 krb5_free_context(handle->context); 686 free(handle->lhandle); 687 free(handle); 688 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, 689 "failed to get config_params, return: %d\n"), code); 690 return(code); 691 } 692 693 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \ 694 KADM5_CONFIG_ADMIN_SERVER | \ 695 KADM5_CONFIG_KADMIND_PORT) 696 697 if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { 698 (void) kadm5_free_config_params(handle->context, 699 &handle->params); 700 krb5_free_context(handle->context); 701 free(handle->lhandle); 702 free(handle); 703 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 704 "missing config parameters\n")); 705 return (KADM5_MISSING_CONF_PARAMS); 706 } 707 708 /* 709 * Acquire a service ticket for service_name@realm in the name of 710 * client_name, using password pass (which could be NULL), and 711 * create a ccache to store them in. If INIT_CREDS, use the 712 * ccache we were provided instead. 713 */ 714 if ((code = krb5_parse_name(handle->context, client_name, 715 &creds.client))) { 716 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 717 "could not parse client name\n")); 718 goto error; 719 } 720 clientp = creds.client; 721 722 if (init_type == INIT_PASS && 723 handle->params.kpasswd_protocol == KRB5_CHGPWD_CHANGEPW_V2) { 724 /* 725 * The 'service_name' is constructed by the caller 726 * but its done before the parameter which determines 727 * the kpasswd_protocol is found. The servers that 728 * support the SET/CHANGE password protocol expect 729 * a slightly different service principal than 730 * the normal SEAM kadmind so construct the correct 731 * name here and then forget it. 732 */ 733 char *newsvcname = NULL; 734 newsvcname = malloc(strlen(KADM5_CHANGEPW_SERVICE) + 735 strlen(handle->params.realm) + 2); 736 if (newsvcname == NULL) { 737 return (ENOMEM); 738 } 739 sprintf(newsvcname, "%s@%s", KADM5_CHANGEPW_SERVICE, 740 handle->params.realm); 741 742 if ((code = krb5_parse_name(handle->context, newsvcname, 743 &creds.server))) { 744 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 745 "could not parse server " 746 "name\n")); 747 free(newsvcname); 748 goto error; 749 } 750 free(newsvcname); 751 } else { 752 input_name.value = service_name; 753 input_name.length = strlen((char *)input_name.value) + 1; 754 gssstat = krb5_gss_import_name(handle->context, 755 &minor_stat, 756 &input_name, 757 (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, 758 (gss_name_t *)&creds.server); 759 760 if (gssstat != GSS_S_COMPLETE) { 761 code = KADM5_GSS_ERROR; 762 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 763 "gss_import_name failed for client name\n")); 764 goto error; 765 } 766 } 767 serverp = creds.server; 768 769 /* XXX temporarily fix a bug in krb5_cc_get_type */ 770 #undef krb5_cc_get_type 771 #define krb5_cc_get_type(context, cache) ((cache)->ops->prefix) 772 773 if (init_type == INIT_CREDS) { 774 ccache = ccache_in; 775 handle->cache_name = (char *) 776 malloc(strlen(krb5_cc_get_type(handle->context, ccache)) + 777 strlen(krb5_cc_get_name(handle->context, ccache)) + 2); 778 779 if (handle->cache_name == NULL) { 780 code = ENOMEM; 781 goto error; 782 } 783 sprintf(handle->cache_name, "%s:%s", 784 krb5_cc_get_type(handle->context, ccache), 785 krb5_cc_get_name(handle->context, ccache)); 786 } else { 787 handle->cache_name = 788 (char *) malloc(strlen(ADM_CCACHE)+strlen("FILE:")+1); 789 if (handle->cache_name == NULL) { 790 code = ENOMEM; 791 goto error; 792 } 793 sprintf(handle->cache_name, "FILE:%s", ADM_CCACHE); 794 mktemp(handle->cache_name + strlen("FILE:")); 795 796 if ((code = krb5_cc_resolve(handle->context, 797 handle->cache_name, &ccache))) 798 goto error; 799 800 if ((code = krb5_cc_initialize (handle->context, ccache, 801 creds.client))) 802 goto error; 803 804 handle->destroy_cache = 1; 805 } 806 handle->lhandle->cache_name = handle->cache_name; 807 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, 808 "cache created: %s\n"), handle->cache_name); 809 810 if ((code = krb5_timeofday(handle->context, &now))) 811 goto error; 812 813 /* 814 * Get a ticket, use the method specified in init_type. 815 */ 816 creds.times.starttime = 0; /* start timer at KDC */ 817 creds.times.endtime = 0; /* endtime will be limited by service */ 818 819 memset(&opt, 0, sizeof (opt)); 820 krb5_get_init_creds_opt_init(&opt); 821 822 if (creds.times.endtime) { 823 if (creds.times.starttime) 824 starttime = creds.times.starttime; 825 else 826 starttime = now; 827 828 krb5_get_init_creds_opt_set_tkt_life(&opt, 829 creds.times.endtime - starttime); 830 } 831 code = krb5_unparse_name(handle->context, creds.server, &server); 832 if (code) 833 goto error; 834 835 if (init_type == INIT_PASS) { 836 code = krb5_get_init_creds_password(handle->context, 837 &creds, creds.client, pass, NULL, 838 NULL, creds.times.starttime, 839 server, &opt); 840 } else if (init_type == INIT_SKEY) { 841 krb5_keytab kt = NULL; 842 843 if (!(pass && (code = krb5_kt_resolve(handle->context, 844 pass, &kt)))) { 845 code = krb5_get_init_creds_keytab( 846 handle->context, 847 &creds, creds.client, kt, 848 creds.times.starttime, 849 server, &opt); 850 851 if (pass) 852 krb5_kt_close(handle->context, kt); 853 } 854 } 855 856 /* Improved error messages */ 857 if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) 858 code = KADM5_BAD_PASSWORD; 859 860 if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) 861 code = KADM5_SECURE_PRINC_MISSING; 862 863 if (code != 0) { 864 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 865 "failed to obtain credentials cache\n")); 866 goto error; 867 } 868 869 /* 870 * If we got this far, save the creds in the cache. 871 */ 872 if (ccache) { 873 code = krb5_cc_store_cred(handle->context, ccache, &creds); 874 } 875 876 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "obtained credentials cache\n")); 877 878 #ifdef ZEROPASSWD 879 if (pass != NULL) 880 memset(pass, 0, strlen(pass)); 881 #endif 882 883 if (init_type != INIT_PASS || 884 handle->params.kpasswd_protocol == KRB5_CHGPWD_RPCSEC) { 885 code = _kadm5_initialize_rpcsec_gss_handle(handle, 886 client_name, service_name); 887 if (code != 0) 888 goto error; 889 } 890 891 *server_handle = (void *) handle; 892 893 if (init_type != INIT_CREDS) 894 krb5_cc_close(handle->context, ccache); 895 896 goto cleanup; 897 898 error: 899 /* 900 * Note that it is illegal for this code to execute if "handle" 901 * has not been allocated and initialized. I.e., don't use "goto 902 * error" before the block of code at the top of the function 903 * that allocates and initializes "handle". 904 */ 905 if (handle->cache_name) 906 free(handle->cache_name); 907 if (handle->destroy_cache && ccache) 908 krb5_cc_destroy(handle->context, ccache); 909 if(handle->clnt && handle->clnt->cl_auth) 910 AUTH_DESTROY(handle->clnt->cl_auth); 911 if(handle->clnt) 912 clnt_destroy(handle->clnt); 913 (void) kadm5_free_config_params(handle->context, &handle->params); 914 915 cleanup: 916 if (server) 917 free(server); 918 919 /* 920 * Creds server and client members may actually be different 921 * from when initially passed into the get_init_* functions. 922 * So we must free the members we allocated above since get_init 923 * zeros the members out, without freeing first. 924 */ 925 if (clientp != NULL) { 926 krb5_free_principal(handle->context, clientp); 927 clientp = NULL; 928 } 929 if (serverp != NULL) { 930 krb5_free_principal(handle->context, serverp); 931 serverp = NULL; 932 } 933 934 krb5_free_cred_contents(handle->context, &creds); 935 936 /* 937 * Dont clean up the handle if the code is OK (code==0) 938 * because it is returned to the caller in the 'server_handle' 939 * ptr. 940 */ 941 if (code) { 942 krb5_free_context(handle->context); 943 free(handle->lhandle); 944 free(handle); 945 } 946 947 return (code); 948 } 949 950 kadm5_ret_t 951 kadm5_destroy(void *server_handle) 952 { 953 krb5_ccache ccache = NULL; 954 int code = KADM5_OK; 955 kadm5_server_handle_t handle = 956 (kadm5_server_handle_t) server_handle; 957 OM_uint32 min_stat; 958 959 CHECK_HANDLE(server_handle); 960 961 if (handle->destroy_cache && handle->cache_name) { 962 if ((code = krb5_cc_resolve(handle->context, 963 handle->cache_name, &ccache)) == 0) 964 code = krb5_cc_destroy (handle->context, ccache); 965 } 966 if (handle->cache_name) 967 free(handle->cache_name); 968 969 if (handle->clnt && handle->clnt->cl_auth) { 970 /* 971 * Since kadm5 doesn't use the default credentials we 972 * must clean this up manually. 973 */ 974 if (handle->my_cred != GSS_C_NO_CREDENTIAL) 975 (void) gss_release_cred(&min_stat, &handle->my_cred); 976 AUTH_DESTROY(handle->clnt->cl_auth); 977 } 978 if (handle->clnt) 979 clnt_destroy(handle->clnt); 980 if (handle->lhandle) 981 free (handle->lhandle); 982 983 kadm5_free_config_params(handle->context, &handle->params); 984 krb5_free_context(handle->context); 985 986 handle->magic_number = 0; 987 free(handle); 988 989 return (code); 990 } 991 992 /*ARGSUSED*/ 993 kadm5_ret_t 994 kadm5_flush(void *server_handle) 995 { 996 return (KADM5_OK); 997 } 998 999 int 1000 _kadm5_check_handle(void *handle) 1001 { 1002 CHECK_HANDLE(handle); 1003 return (0); 1004 } 1005 1006 /* 1007 * Stub function for kadmin. It was created to eliminate the dependency on 1008 * libkdb's ulog functions. The srv equivalent makes the actual calls. 1009 */ 1010 krb5_error_code 1011 kadm5_init_iprop(void *handle) 1012 { 1013 return (0); 1014 } 1015