1 /* 2 * Copyright 2008 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 char *server = handle->params.admin_server; 335 336 /* 337 * Try to find the kpasswd_server first if this is for the changepw 338 * service. If defined then it should be resolvable else return error. 339 */ 340 if (strncmp(service_name, KADM5_CHANGEPW_HOST_SERVICE, 341 strlen(KADM5_CHANGEPW_HOST_SERVICE)) == 0) { 342 if (handle->params.kpasswd_server != NULL) 343 server = handle->params.kpasswd_server; 344 } 345 hp = gethostbyname(server); 346 if (hp == (struct hostent *)NULL) { 347 code = KADM5_BAD_SERVER_NAME; 348 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 349 "bad server name\n")); 350 goto cleanup; 351 } 352 353 memset(&addr, 0, sizeof (addr)); 354 addr.sin_family = hp->h_addrtype; 355 (void) memcpy((char *)&addr.sin_addr, (char *)hp->h_addr, 356 sizeof (addr.sin_addr)); 357 addr.sin_port = htons((ushort_t)handle->params.kadmind_port); 358 sin = &addr; 359 #ifdef DEBUG 360 printf("kadmin_port %d\n", handle->params.kadmind_port); 361 printf("addr: sin_port: %d, sin_family: %d, sin_zero %s\n", 362 addr.sin_port, addr.sin_family, addr.sin_zero); 363 printf("sin_addr %d:%d\n", addr.sin_addr.S_un.S_un_w.s_w1, 364 addr.sin_addr.S_un.S_un_w.s_w2); 365 #endif 366 if ((handlep = setnetconfig()) == (void *) NULL) { 367 (void) syslog(LOG_ERR, 368 dgettext(TEXT_DOMAIN, 369 "cannot get any transport information")); 370 goto error; 371 } 372 373 while (nconf = getnetconfig(handlep)) { 374 if ((nconf->nc_semantics == NC_TPI_COTS_ORD) && 375 (strcmp(nconf->nc_protofmly, NC_INET) == 0) && 376 (strcmp(nconf->nc_proto, NC_TCP) == 0)) 377 break; 378 } 379 380 if (nconf == (struct netconfig *)NULL) 381 goto error; 382 383 /* Transform addr to netbuf */ 384 (void) memset(&netaddr, 0, sizeof (netaddr)); 385 netaddr.buf = (char *)sin; 386 387 /* get an fd connected to the given address */ 388 fd = get_connection(nconf, netaddr); 389 if (fd == -1) { 390 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 391 "unable to open connection to ADMIN server " 392 "(t_error %i)"), t_errno); 393 code = KADM5_RPC_ERROR; 394 goto error; 395 } 396 397 #ifdef DEBUG 398 printf("fd: %d, KADM: %d, KADMVERS %d\n", fd, KADM, KADMVERS); 399 printf("nconf: nc_netid: %s, nc_semantics: %d, nc_flag: %d, " 400 "nc_protofmly: %s\n", 401 nconf->nc_netid, nconf->nc_semantics, nconf->nc_flag, 402 nconf->nc_protofmly); 403 printf("nc_proto: %s, nc_device: %s, nc_nlookups: %d, nc_used: %d\n", 404 nconf->nc_proto, nconf->nc_device, nconf->nc_nlookups, 405 nconf->nc_unused); 406 printf("netaddr: maxlen %d, buf: %s, len: %d\n", netaddr.maxlen, 407 netaddr.buf, netaddr.len); 408 #endif 409 /* 410 * Tell clnt_tli_create that given fd is already connected 411 * 412 * If the service_name and client_name are iprop-centric, 413 * we need to clnt_tli_create to the appropriate RPC prog 414 */ 415 iprop_svc = strdup(KIPROP_SVC_NAME); 416 if (iprop_svc == NULL) 417 return (ENOMEM); 418 419 if ((strstr(service_name, iprop_svc) != NULL) && 420 (strstr(client_name, iprop_svc) != NULL)) { 421 iprop_enable = B_TRUE; 422 handle->clnt = clnt_tli_create(fd, nconf, NULL, 423 KRB5_IPROP_PROG, KRB5_IPROP_VERS, 0, 0); 424 } 425 else 426 handle->clnt = clnt_tli_create(fd, nconf, NULL, 427 KADM, KADMVERS, 0, 0); 428 429 if (iprop_svc) 430 free(iprop_svc); 431 432 if (handle->clnt == NULL) { 433 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 434 "clnt_tli_create failed\n")); 435 code = KADM5_RPC_ERROR; 436 (void) close(fd); 437 goto error; 438 } 439 /* 440 * The rpc-handle was created on an fd opened and connected 441 * by us, so we have to explicitly tell rpc to close it. 442 */ 443 if (clnt_control(handle->clnt, CLSET_FD_CLOSE, NULL) != TRUE) { 444 clnt_pcreateerror("ERROR:"); 445 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 446 "clnt_control failed to set CLSET_FD_CLOSE")); 447 code = KADM5_RPC_ERROR; 448 (void) close(fd); 449 goto error; 450 } 451 452 handle->lhandle->clnt = handle->clnt; 453 454 /* now that handle->clnt is set, we can check the handle */ 455 if (code = _kadm5_check_handle((void *) handle)) 456 goto error; 457 458 /* 459 * The RPC connection is open; establish the GSS-API 460 * authentication context. 461 */ 462 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 463 "have an rpc connection open\n")); 464 /* use the kadm5 cache */ 465 ccname_orig = getenv("KRB5CCNAME"); 466 if (ccname_orig) 467 ccname_orig = strdup(ccname_orig); 468 469 (void) krb5_setenv("KRB5CCNAME", handle->cache_name, 1); 470 471 ADMIN_LOG(LOG_ERR, 472 dgettext(TEXT_DOMAIN, 473 "current credential cache: %s"), handle->cache_name); 474 input_name.value = client_name; 475 input_name.length = strlen((char *)input_name.value) + 1; 476 gssstat = gss_import_name(&minor_stat, &input_name, 477 (gss_OID)gss_nt_krb5_name, &gss_client); 478 if (gssstat != GSS_S_COMPLETE) { 479 code = KADM5_GSS_ERROR; 480 ADMIN_LOGO(LOG_ERR, 481 dgettext(TEXT_DOMAIN, 482 "gss_import_name failed for client name\n")); 483 goto error; 484 } 485 486 if (!rpc_gss_mech_to_oid(mech, (rpc_gss_OID *)&mech_oid)) { 487 ADMIN_LOG(LOG_ERR, 488 dgettext(TEXT_DOMAIN, 489 "Invalid mechanism oid <%s>"), mech); 490 goto error; 491 } 492 493 oid_set.count = 1; 494 oid_set.elements = mech_oid; 495 496 gssstat = gss_acquire_cred(&minor_stat, gss_client, 0, 497 &oid_set, GSS_C_INITIATE, 498 &gss_client_creds, NULL, NULL); 499 (void) gss_release_name(&minor_stat, &gss_client); 500 if (gssstat != GSS_S_COMPLETE) { 501 code = KADM5_GSS_ERROR; 502 ADMIN_LOG(LOG_ERR, 503 dgettext(TEXT_DOMAIN, 504 "could not acquire credentials, " 505 "major error code: %d\n"), gssstat); 506 goto error; 507 } 508 handle->my_cred = gss_client_creds; 509 options_req.my_cred = gss_client_creds; 510 options_req.req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG; 511 options_req.time_req = 0; 512 options_req.input_channel_bindings = NULL; 513 #ifndef INIT_TEST 514 handle->clnt->cl_auth = rpc_gss_seccreate(handle->clnt, 515 service_name, 516 mech, 517 service, 518 NULL, 519 &options_req, 520 &options_ret); 521 #endif /* ! INIT_TEST */ 522 523 if (ccname_orig) { 524 (void) krb5_setenv("KRB5CCNAME", ccname_orig, 1); 525 free(ccname_orig); 526 } else 527 (void) krb5_unsetenv("KRB5CCNAME"); 528 529 if (handle->clnt->cl_auth == NULL) { 530 code = KADM5_GSS_ERROR; 531 display_status(dgettext(TEXT_DOMAIN, 532 "rpc_gss_seccreate failed\n"), 533 options_ret.major_status, 534 options_ret.minor_status, 535 mech); 536 goto error; 537 } 538 539 /* 540 * Bypass the remainder of the code and return straightaway 541 * if the gss service requested is kiprop 542 */ 543 if (iprop_enable == B_TRUE) { 544 code = 0; 545 goto cleanup; 546 } 547 548 r = init_1(&handle->api_version, handle->clnt, &rpc_err_code); 549 if (r == NULL) { 550 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 551 "error during admin api initialization\n")); 552 553 if (rpc_err_code == RPC_CANTENCODEARGS) { 554 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 555 "encryption needed to encode RPC data may not be " 556 "installed/configured on this system")); 557 code = KADM5_RPC_ERROR_CANTENCODEARGS; 558 } else if (rpc_err_code == RPC_CANTDECODEARGS) { 559 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 560 "encryption needed to decode RPC data may not be " 561 "installed/configured on the server")); 562 code = KADM5_RPC_ERROR_CANTDECODEARGS; 563 } else 564 code = KADM5_RPC_ERROR; 565 566 goto error; 567 568 } 569 if (r->code) { 570 code = r->code; 571 ADMIN_LOG(LOG_ERR, 572 dgettext(TEXT_DOMAIN, 573 "error during admin api initialization: %d\n"), 574 r->code); 575 goto error; 576 } 577 error: 578 cleanup: 579 580 if (handlep != (void *) NULL) 581 (void) endnetconfig(handlep); 582 /* 583 * gss_client_creds is freed only when there is an error condition, 584 * given that rpc_gss_seccreate() will assign the cred pointer to the 585 * my_cred member in the auth handle's private data structure. 586 */ 587 if (code && (gss_client_creds != GSS_C_NO_CREDENTIAL)) 588 (void) gss_release_cred(&minor_stat, &gss_client_creds); 589 590 return (code); 591 } 592 593 static kadm5_ret_t _kadm5_init_any(char *client_name, 594 enum init_type init_type, 595 char *pass, 596 krb5_ccache ccache_in, 597 char *service_name, 598 kadm5_config_params *params_in, 599 krb5_ui_4 struct_version, 600 krb5_ui_4 api_version, 601 char **db_args, 602 void **server_handle) 603 { 604 int i; 605 krb5_creds creds; 606 krb5_ccache ccache = NULL; 607 krb5_timestamp now; 608 OM_uint32 gssstat, minor_stat; 609 kadm5_server_handle_t handle; 610 kadm5_config_params params_local; 611 int code = 0; 612 krb5_get_init_creds_opt opt; 613 gss_buffer_desc input_name; 614 krb5_error_code kret; 615 krb5_int32 starttime; 616 char *server = NULL; 617 krb5_principal serverp = NULL, clientp = NULL; 618 krb5_principal saved_server = NULL; 619 bool_t cpw = FALSE; 620 621 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 622 "entering kadm5_init_any\n")); 623 if (! server_handle) { 624 return EINVAL; 625 } 626 627 if (! (handle = malloc(sizeof(*handle)))) { 628 return ENOMEM; 629 } 630 if (! (handle->lhandle = malloc(sizeof(*handle)))) { 631 free(handle); 632 return ENOMEM; 633 } 634 635 handle->magic_number = KADM5_SERVER_HANDLE_MAGIC; 636 handle->struct_version = struct_version; 637 handle->api_version = api_version; 638 handle->clnt = 0; 639 handle->cache_name = 0; 640 handle->destroy_cache = 0; 641 *handle->lhandle = *handle; 642 handle->lhandle->api_version = KADM5_API_VERSION_2; 643 handle->lhandle->struct_version = KADM5_STRUCT_VERSION; 644 handle->lhandle->lhandle = handle->lhandle; 645 646 kret = krb5_init_context(&handle->context); 647 if (kret) { 648 free(handle->lhandle); 649 free(handle); 650 return (kret); 651 } 652 653 if(service_name == NULL || client_name == NULL) { 654 krb5_free_context(handle->context); 655 free(handle->lhandle); 656 free(handle); 657 return EINVAL; 658 } 659 memset((char *) &creds, 0, sizeof(creds)); 660 661 /* 662 * Verify the version numbers before proceeding; we can't use 663 * CHECK_HANDLE because not all fields are set yet. 664 */ 665 GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION, 666 KADM5_NEW_LIB_API_VERSION); 667 668 /* 669 * Acquire relevant profile entries. In version 2, merge values 670 * in params_in with values from profile, based on 671 * params_in->mask. 672 * 673 * In version 1, we've given a realm (which may be NULL) instead 674 * of params_in. So use that realm, make params_in contain an 675 * empty mask, and behave like version 2. 676 */ 677 memset((char *) ¶ms_local, 0, sizeof(params_local)); 678 if (api_version == KADM5_API_VERSION_1) { 679 if (params_in) 680 params_local.mask = KADM5_CONFIG_REALM; 681 params_in = ¶ms_local; 682 } 683 684 #define ILLEGAL_PARAMS ( \ 685 KADM5_CONFIG_ACL_FILE | KADM5_CONFIG_ADB_LOCKFILE | \ 686 KADM5_CONFIG_DBNAME | KADM5_CONFIG_ADBNAME | \ 687 KADM5_CONFIG_DICT_FILE | KADM5_CONFIG_ADMIN_KEYTAB | \ 688 KADM5_CONFIG_STASH_FILE | KADM5_CONFIG_MKEY_NAME | \ 689 KADM5_CONFIG_ENCTYPE | KADM5_CONFIG_MAX_LIFE | \ 690 KADM5_CONFIG_MAX_RLIFE | KADM5_CONFIG_EXPIRATION | \ 691 KADM5_CONFIG_FLAGS | KADM5_CONFIG_ENCTYPES | \ 692 KADM5_CONFIG_MKEY_FROM_KBD) 693 694 if (params_in && params_in->mask & ILLEGAL_PARAMS) { 695 krb5_free_context(handle->context); 696 free(handle->lhandle); 697 free(handle); 698 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, 699 "bad client parameters, returning %d"), 700 KADM5_BAD_CLIENT_PARAMS); 701 return KADM5_BAD_CLIENT_PARAMS; 702 } 703 704 if ((code = kadm5_get_config_params(handle->context, 705 DEFAULT_PROFILE_PATH, 706 "KRB5_CONFIG", 707 params_in, 708 &handle->params))) { 709 krb5_free_context(handle->context); 710 free(handle->lhandle); 711 free(handle); 712 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, 713 "failed to get config_params, return: %d\n"), code); 714 return(code); 715 } 716 717 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \ 718 KADM5_CONFIG_ADMIN_SERVER | \ 719 KADM5_CONFIG_KADMIND_PORT) 720 #define KPW_REQUIRED_PARAMS (KADM5_CONFIG_REALM | \ 721 KADM5_CONFIG_KPASSWD_SERVER | \ 722 KADM5_CONFIG_KPASSWD_PORT) 723 724 if (((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) && 725 ((handle->params.mask & KPW_REQUIRED_PARAMS) != KPW_REQUIRED_PARAMS)) { 726 (void) kadm5_free_config_params(handle->context, 727 &handle->params); 728 krb5_free_context(handle->context); 729 free(handle->lhandle); 730 free(handle); 731 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 732 "missing config parameters\n")); 733 return KADM5_MISSING_KRB5_CONF_PARAMS; 734 } 735 736 /* 737 * Acquire a service ticket for service_name@realm in the name of 738 * client_name, using password pass (which could be NULL), and 739 * create a ccache to store them in. If INIT_CREDS, use the 740 * ccache we were provided instead. 741 */ 742 if ((code = krb5_parse_name(handle->context, client_name, 743 &creds.client))) { 744 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 745 "could not parse client name\n")); 746 goto error; 747 } 748 clientp = creds.client; 749 750 if (strncmp(service_name, KADM5_CHANGEPW_HOST_SERVICE, 751 strlen(KADM5_CHANGEPW_HOST_SERVICE)) == 0) 752 cpw = TRUE; 753 754 if (init_type == INIT_PASS && 755 handle->params.kpasswd_protocol == KRB5_CHGPWD_CHANGEPW_V2 && 756 cpw == TRUE) { 757 /* 758 * The 'service_name' is constructed by the caller 759 * but its done before the parameter which determines 760 * the kpasswd_protocol is found. The servers that 761 * support the SET/CHANGE password protocol expect 762 * a slightly different service principal than 763 * the normal SEAM kadmind so construct the correct 764 * name here and then forget it. 765 */ 766 char *newsvcname = NULL; 767 newsvcname = malloc(strlen(KADM5_CHANGEPW_SERVICE) + 768 strlen(handle->params.realm) + 2); 769 if (newsvcname == NULL) { 770 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 771 "could not malloc\n")); 772 code = ENOMEM; 773 goto error; 774 } 775 sprintf(newsvcname, "%s@%s", KADM5_CHANGEPW_SERVICE, 776 handle->params.realm); 777 778 if ((code = krb5_parse_name(handle->context, newsvcname, 779 &creds.server))) { 780 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 781 "could not parse server " 782 "name\n")); 783 free(newsvcname); 784 goto error; 785 } 786 free(newsvcname); 787 } else { 788 input_name.value = service_name; 789 input_name.length = strlen((char *)input_name.value) + 1; 790 gssstat = krb5_gss_import_name(&minor_stat, 791 &input_name, 792 (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, 793 (gss_name_t *)&creds.server); 794 795 if (gssstat != GSS_S_COMPLETE) { 796 code = KADM5_GSS_ERROR; 797 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 798 "gss_import_name failed for client name\n")); 799 goto error; 800 } 801 } 802 serverp = creds.server; 803 804 /* XXX temporarily fix a bug in krb5_cc_get_type */ 805 #undef krb5_cc_get_type 806 #define krb5_cc_get_type(context, cache) ((cache)->ops->prefix) 807 808 809 if (init_type == INIT_CREDS) { 810 ccache = ccache_in; 811 handle->cache_name = (char *) 812 malloc(strlen(krb5_cc_get_type(handle->context, ccache)) + 813 strlen(krb5_cc_get_name(handle->context, ccache)) + 2); 814 if (handle->cache_name == NULL) { 815 code = ENOMEM; 816 goto error; 817 } 818 sprintf(handle->cache_name, "%s:%s", 819 krb5_cc_get_type(handle->context, ccache), 820 krb5_cc_get_name(handle->context, ccache)); 821 } else { 822 #if 0 823 handle->cache_name = 824 (char *) malloc(strlen(ADM_CCACHE)+strlen("FILE:")+1); 825 if (handle->cache_name == NULL) { 826 code = ENOMEM; 827 goto error; 828 } 829 sprintf(handle->cache_name, "FILE:%s", ADM_CCACHE); 830 mktemp(handle->cache_name + strlen("FILE:")); 831 #endif 832 { 833 static int counter = 0; 834 handle->cache_name = malloc(sizeof("MEMORY:kadm5_") 835 + 3*sizeof(counter)); 836 sprintf(handle->cache_name, "MEMORY:kadm5_%u", counter++); 837 } 838 839 if ((code = krb5_cc_resolve(handle->context, handle->cache_name, 840 &ccache))) 841 goto error; 842 843 if ((code = krb5_cc_initialize (handle->context, ccache, 844 creds.client))) 845 goto error; 846 847 handle->destroy_cache = 1; 848 } 849 handle->lhandle->cache_name = handle->cache_name; 850 ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, 851 "cache created: %s\n"), handle->cache_name); 852 853 if ((code = krb5_timeofday(handle->context, &now))) 854 goto error; 855 856 /* 857 * Get a ticket, use the method specified in init_type. 858 */ 859 860 creds.times.starttime = 0; /* start timer at KDC */ 861 creds.times.endtime = 0; /* endtime will be limited by service */ 862 863 memset(&opt, 0, sizeof (opt)); 864 krb5_get_init_creds_opt_init(&opt); 865 866 if (creds.times.endtime) { 867 if (creds.times.starttime) 868 starttime = creds.times.starttime; 869 else 870 starttime = now; 871 872 krb5_get_init_creds_opt_set_tkt_life(&opt, 873 creds.times.endtime - starttime); 874 } 875 code = krb5_unparse_name(handle->context, creds.server, &server); 876 if (code) 877 goto error; 878 879 /* 880 * Solaris Kerberos: 881 * Save the original creds.server as krb5_get_init_creds*() always 882 * sets the realm of the server to the client realm. 883 */ 884 code = krb5_copy_principal(handle->context, creds.server, &saved_server); 885 if (code) 886 goto error; 887 888 if (init_type == INIT_PASS) { 889 code = krb5_get_init_creds_password(handle->context, 890 &creds, creds.client, pass, NULL, 891 NULL, creds.times.starttime, 892 server, &opt); 893 } else if (init_type == INIT_SKEY) { 894 krb5_keytab kt = NULL; 895 896 if (!(pass && (code = krb5_kt_resolve(handle->context, 897 pass, &kt)))) { 898 code = krb5_get_init_creds_keytab( 899 handle->context, 900 &creds, creds.client, kt, 901 creds.times.starttime, 902 server, &opt); 903 904 if (pass) krb5_kt_close(handle->context, kt); 905 } 906 } 907 908 /* Improved error messages */ 909 if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD; 910 if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) 911 code = KADM5_SECURE_PRINC_MISSING; 912 913 if (code != 0) { 914 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, 915 "failed to obtain credentials cache\n")); 916 krb5_free_principal(handle->context, saved_server); 917 goto error; 918 } 919 920 /* 921 * Solaris Kerberos: 922 * If the server principal had an empty realm then store that in 923 * the cred cache and not the server realm as returned by 924 * krb5_get_init_creds_{keytab|password}(). This ensures that rpcsec_gss 925 * will find the credential in the cred cache even if a "fallback" 926 * method is being used to determine the realm. 927 */ 928 if (init_type != INIT_CREDS) { 929 krb5_free_principal(handle->context, creds.server); 930 } 931 creds.server = saved_server; 932 933 /* 934 * If we got this far, save the creds in the cache. 935 */ 936 if (ccache) { 937 code = krb5_cc_store_cred(handle->context, ccache, &creds); 938 } 939 940 ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "obtained credentials cache\n")); 941 942 #ifdef ZEROPASSWD 943 if (pass != NULL) 944 memset(pass, 0, strlen(pass)); 945 #endif 946 947 if (init_type != INIT_PASS || 948 handle->params.kpasswd_protocol == KRB5_CHGPWD_RPCSEC || 949 cpw == FALSE) { 950 code = _kadm5_initialize_rpcsec_gss_handle(handle, 951 client_name, service_name); 952 953 /* 954 * Solaris Kerberos: 955 * If _kadm5_initialize_rpcsec_gss_handle() fails it will have 956 * called krb5_gss_release_cred(). If the credential cache is a 957 * MEMORY cred cache krb5_gss_release_cred() destroys the 958 * cred cache data. Make sure that the cred-cache is closed 959 * to prevent a double free in the "error" code. 960 */ 961 if (code != 0) { 962 if (init_type != INIT_CREDS) 963 krb5_cc_close(handle->context, ccache); 964 goto error; 965 } 966 } 967 968 *server_handle = (void *) handle; 969 970 if (init_type != INIT_CREDS) 971 krb5_cc_close(handle->context, ccache); 972 973 goto cleanup; 974 975 error: 976 /* 977 * Note that it is illegal for this code to execute if "handle" 978 * has not been allocated and initialized. I.e., don't use "goto 979 * error" before the block of code at the top of the function 980 * that allocates and initializes "handle". 981 */ 982 if (handle->cache_name) 983 free(handle->cache_name); 984 if (handle->destroy_cache && ccache) 985 krb5_cc_destroy(handle->context, ccache); 986 if(handle->clnt && handle->clnt->cl_auth) 987 AUTH_DESTROY(handle->clnt->cl_auth); 988 if(handle->clnt) 989 clnt_destroy(handle->clnt); 990 (void) kadm5_free_config_params(handle->context, &handle->params); 991 992 cleanup: 993 if (server) 994 free(server); 995 996 /* 997 * cred's server and client pointers could have been overwritten 998 * by the krb5_get_init_* functions. If the addresses are different 999 * before and after the calls then we must free the memory that 1000 * was allocated before the call. 1001 */ 1002 if (clientp && clientp != creds.client) 1003 krb5_free_principal(handle->context, clientp); 1004 1005 if (serverp && serverp != creds.server) 1006 krb5_free_principal(handle->context, serverp); 1007 1008 krb5_free_cred_contents(handle->context, &creds); 1009 1010 /* 1011 * Dont clean up the handle if the code is OK (code==0) 1012 * because it is returned to the caller in the 'server_handle' 1013 * ptr. 1014 */ 1015 if (code) { 1016 krb5_free_context(handle->context); 1017 free(handle->lhandle); 1018 free(handle); 1019 } 1020 1021 return code; 1022 } 1023 1024 kadm5_ret_t 1025 kadm5_destroy(void *server_handle) 1026 { 1027 krb5_ccache ccache = NULL; 1028 int code = KADM5_OK; 1029 kadm5_server_handle_t handle = 1030 (kadm5_server_handle_t) server_handle; 1031 OM_uint32 min_stat; 1032 1033 CHECK_HANDLE(server_handle); 1034 /* SUNW14resync: 1035 * krb5_cc_resolve() will resolve a ccache with the same data that 1036 * handle->my_cred points to. If the ccache is a MEMORY ccache then 1037 * gss_release_cred() will free that data (it doesn't do this when ccache 1038 * is a FILE ccache). 1039 * if'ed out to avoid the double free. 1040 */ 1041 #if 0 1042 if (handle->destroy_cache && handle->cache_name) { 1043 if ((code = krb5_cc_resolve(handle->context, 1044 handle->cache_name, &ccache)) == 0) 1045 code = krb5_cc_destroy (handle->context, ccache); 1046 } 1047 #endif 1048 if (handle->cache_name) 1049 free(handle->cache_name); 1050 if (handle->clnt && handle->clnt->cl_auth) { 1051 /* 1052 * Since kadm5 doesn't use the default credentials we 1053 * must clean this up manually. 1054 */ 1055 if (handle->my_cred != GSS_C_NO_CREDENTIAL) 1056 (void) gss_release_cred(&min_stat, &handle->my_cred); 1057 AUTH_DESTROY(handle->clnt->cl_auth); 1058 } 1059 if (handle->clnt) 1060 clnt_destroy(handle->clnt); 1061 if (handle->lhandle) 1062 free (handle->lhandle); 1063 1064 kadm5_free_config_params(handle->context, &handle->params); 1065 krb5_free_context(handle->context); 1066 1067 handle->magic_number = 0; 1068 free(handle); 1069 1070 return code; 1071 } 1072 /* not supported on client */ 1073 kadm5_ret_t kadm5_lock(void *server_handle) 1074 { 1075 return EINVAL; 1076 } 1077 1078 /* not supported on client */ 1079 kadm5_ret_t kadm5_unlock(void *server_handle) 1080 { 1081 return EINVAL; 1082 } 1083 1084 kadm5_ret_t kadm5_flush(void *server_handle) 1085 { 1086 return KADM5_OK; 1087 } 1088 1089 int _kadm5_check_handle(void *handle) 1090 { 1091 CHECK_HANDLE(handle); 1092 return 0; 1093 } 1094 1095 krb5_error_code kadm5_init_krb5_context (krb5_context *ctx) 1096 { 1097 return krb5_init_context(ctx); 1098 } 1099 1100 /* 1101 * Stub function for kadmin. It was created to eliminate the dependency on 1102 * libkdb's ulog functions. The srv equivalent makes the actual calls. 1103 */ 1104 krb5_error_code 1105 kadm5_init_iprop(void *handle) 1106 { 1107 return (0); 1108 } 1109