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