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 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, 688 DEFAULT_PROFILE_PATH, 689 "KRB5_CONFIG", 690 params_in, 691 &handle->params))) { 692 krb5_free_context(handle->context); 693 free(handle->lhandle); 694 free(handle); 695 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, 696 "failed to get config_params, return: %d\n"), code); 697 return(code); 698 } 699 700 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \ 701 KADM5_CONFIG_ADMIN_SERVER | \ 702 KADM5_CONFIG_KADMIND_PORT) 703 704 if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { 705 (void) kadm5_free_config_params(handle->context, 706 &handle->params); 707 krb5_free_context(handle->context); 708 free(handle->lhandle); 709 free(handle); 710 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 711 "missing config parameters\n")); 712 return KADM5_MISSING_KRB5_CONF_PARAMS; 713 } 714 715 /* 716 * Acquire a service ticket for service_name@realm in the name of 717 * client_name, using password pass (which could be NULL), and 718 * create a ccache to store them in. If INIT_CREDS, use the 719 * ccache we were provided instead. 720 */ 721 if ((code = krb5_parse_name(handle->context, client_name, 722 &creds.client))) { 723 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 724 "could not parse client name\n")); 725 goto error; 726 } 727 clientp = creds.client; 728 729 if (strncmp(service_name, KADM5_CHANGEPW_HOST_SERVICE, 730 strlen(KADM5_CHANGEPW_HOST_SERVICE)) == 0) 731 cpw = TRUE; 732 733 if (init_type == INIT_PASS && 734 handle->params.kpasswd_protocol == KRB5_CHGPWD_CHANGEPW_V2 && 735 cpw == TRUE) { 736 /* 737 * The 'service_name' is constructed by the caller 738 * but its done before the parameter which determines 739 * the kpasswd_protocol is found. The servers that 740 * support the SET/CHANGE password protocol expect 741 * a slightly different service principal than 742 * the normal SEAM kadmind so construct the correct 743 * name here and then forget it. 744 */ 745 char *newsvcname = NULL; 746 newsvcname = malloc(strlen(KADM5_CHANGEPW_SERVICE) + 747 strlen(handle->params.realm) + 2); 748 if (newsvcname == NULL) { 749 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 750 "could not malloc\n")); 751 code = ENOMEM; 752 goto error; 753 } 754 sprintf(newsvcname, "%s@%s", KADM5_CHANGEPW_SERVICE, 755 handle->params.realm); 756 757 if ((code = krb5_parse_name(handle->context, newsvcname, 758 &creds.server))) { 759 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 760 "could not parse server " 761 "name\n")); 762 free(newsvcname); 763 goto error; 764 } 765 free(newsvcname); 766 } else { 767 input_name.value = service_name; 768 input_name.length = strlen((char *)input_name.value) + 1; 769 gssstat = krb5_gss_import_name(handle->context, 770 &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(). This ensures that rpcsec_gss will find 905 * the credential in the cred cache even if a "fallback" method is 906 * being used to determine the realm. 907 */ 908 krb5_free_principal(handle->context, creds.server); 909 creds.server = saved_server; 910 911 /* 912 * If we got this far, save the creds in the cache. 913 */ 914 if (ccache) { 915 code = krb5_cc_store_cred(handle->context, ccache, &creds); 916 } 917 918 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "obtained credentials cache\n")); 919 920 #ifdef ZEROPASSWD 921 if (pass != NULL) 922 memset(pass, 0, strlen(pass)); 923 #endif 924 925 if (init_type != INIT_PASS || 926 handle->params.kpasswd_protocol == KRB5_CHGPWD_RPCSEC || 927 cpw == FALSE) { 928 code = _kadm5_initialize_rpcsec_gss_handle(handle, 929 client_name, service_name); 930 if (code != 0) 931 goto error; 932 } 933 934 *server_handle = (void *) handle; 935 936 if (init_type != INIT_CREDS) 937 krb5_cc_close(handle->context, ccache); 938 939 goto cleanup; 940 941 error: 942 /* 943 * Note that it is illegal for this code to execute if "handle" 944 * has not been allocated and initialized. I.e., don't use "goto 945 * error" before the block of code at the top of the function 946 * that allocates and initializes "handle". 947 */ 948 if (handle->cache_name) 949 free(handle->cache_name); 950 if (handle->destroy_cache && ccache) 951 krb5_cc_destroy(handle->context, ccache); 952 if(handle->clnt && handle->clnt->cl_auth) 953 AUTH_DESTROY(handle->clnt->cl_auth); 954 if(handle->clnt) 955 clnt_destroy(handle->clnt); 956 (void) kadm5_free_config_params(handle->context, &handle->params); 957 958 cleanup: 959 if (server) 960 free(server); 961 962 /* 963 * cred's server and client pointers could have been overwritten 964 * by the krb5_get_init_* functions. If the addresses are different 965 * before and after the calls then we must free the memory that 966 * was allocated before the call. 967 */ 968 if (clientp && clientp != creds.client) 969 krb5_free_principal(handle->context, clientp); 970 971 if (serverp && serverp != creds.server) 972 krb5_free_principal(handle->context, serverp); 973 974 krb5_free_cred_contents(handle->context, &creds); 975 976 /* 977 * Dont clean up the handle if the code is OK (code==0) 978 * because it is returned to the caller in the 'server_handle' 979 * ptr. 980 */ 981 if (code) { 982 krb5_free_context(handle->context); 983 free(handle->lhandle); 984 free(handle); 985 } 986 987 return code; 988 } 989 990 kadm5_ret_t 991 kadm5_destroy(void *server_handle) 992 { 993 krb5_ccache ccache = NULL; 994 int code = KADM5_OK; 995 kadm5_server_handle_t handle = 996 (kadm5_server_handle_t) server_handle; 997 OM_uint32 min_stat; 998 999 CHECK_HANDLE(server_handle); 1000 /* SUNW14resync: 1001 * krb5_cc_resolve() will resolve a ccache with the same data that 1002 * handle->my_cred points to. If the ccache is a MEMORY ccache then 1003 * gss_release_cred() will free that data (it doesn't do this when ccache 1004 * is a FILE ccache). 1005 * if'ed out to avoid the double free. 1006 */ 1007 #if 0 1008 if (handle->destroy_cache && handle->cache_name) { 1009 if ((code = krb5_cc_resolve(handle->context, 1010 handle->cache_name, &ccache)) == 0) 1011 code = krb5_cc_destroy (handle->context, ccache); 1012 } 1013 #endif 1014 if (handle->cache_name) 1015 free(handle->cache_name); 1016 if (handle->clnt && handle->clnt->cl_auth) { 1017 /* 1018 * Since kadm5 doesn't use the default credentials we 1019 * must clean this up manually. 1020 */ 1021 if (handle->my_cred != GSS_C_NO_CREDENTIAL) 1022 (void) gss_release_cred(&min_stat, &handle->my_cred); 1023 AUTH_DESTROY(handle->clnt->cl_auth); 1024 } 1025 if (handle->clnt) 1026 clnt_destroy(handle->clnt); 1027 if (handle->lhandle) 1028 free (handle->lhandle); 1029 1030 kadm5_free_config_params(handle->context, &handle->params); 1031 krb5_free_context(handle->context); 1032 1033 handle->magic_number = 0; 1034 free(handle); 1035 1036 return code; 1037 } 1038 /* not supported on client */ 1039 kadm5_ret_t kadm5_lock(void *server_handle) 1040 { 1041 return EINVAL; 1042 } 1043 1044 /* not supported on client */ 1045 kadm5_ret_t kadm5_unlock(void *server_handle) 1046 { 1047 return EINVAL; 1048 } 1049 1050 kadm5_ret_t kadm5_flush(void *server_handle) 1051 { 1052 return KADM5_OK; 1053 } 1054 1055 int _kadm5_check_handle(void *handle) 1056 { 1057 CHECK_HANDLE(handle); 1058 return 0; 1059 } 1060 1061 /* 1062 * Stub function for kadmin. It was created to eliminate the dependency on 1063 * libkdb's ulog functions. The srv equivalent makes the actual calls. 1064 */ 1065 krb5_error_code 1066 kadm5_init_iprop(void *handle) 1067 { 1068 return (0); 1069 } 1070