1 /* 2 * Copyright (c) 1997 - 2003 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "kadm5_locl.h" 35 #include <sys/types.h> 36 #include <sys/socket.h> 37 #include <netinet/in.h> 38 #include <netdb.h> 39 40 RCSID("$Id: init_c.c,v 1.45.2.1 2003/12/21 22:48:13 lha Exp $"); 41 42 static void 43 set_funcs(kadm5_client_context *c) 44 { 45 #define SET(C, F) (C)->funcs.F = kadm5 ## _c_ ## F 46 SET(c, chpass_principal); 47 SET(c, chpass_principal_with_key); 48 SET(c, create_principal); 49 SET(c, delete_principal); 50 SET(c, destroy); 51 SET(c, flush); 52 SET(c, get_principal); 53 SET(c, get_principals); 54 SET(c, get_privs); 55 SET(c, modify_principal); 56 SET(c, randkey_principal); 57 SET(c, rename_principal); 58 } 59 60 kadm5_ret_t 61 _kadm5_c_init_context(kadm5_client_context **ctx, 62 kadm5_config_params *params, 63 krb5_context context) 64 { 65 krb5_error_code ret; 66 char *colon; 67 68 *ctx = malloc(sizeof(**ctx)); 69 if(*ctx == NULL) 70 return ENOMEM; 71 memset(*ctx, 0, sizeof(**ctx)); 72 krb5_add_et_list (context, initialize_kadm5_error_table_r); 73 set_funcs(*ctx); 74 (*ctx)->context = context; 75 if(params->mask & KADM5_CONFIG_REALM) { 76 ret = 0; 77 (*ctx)->realm = strdup(params->realm); 78 if ((*ctx)->realm == NULL) 79 ret = ENOMEM; 80 } else 81 ret = krb5_get_default_realm((*ctx)->context, &(*ctx)->realm); 82 if (ret) { 83 free(*ctx); 84 return ret; 85 } 86 if(params->mask & KADM5_CONFIG_ADMIN_SERVER) 87 (*ctx)->admin_server = strdup(params->admin_server); 88 else { 89 char **hostlist; 90 91 ret = krb5_get_krb_admin_hst (context, &(*ctx)->realm, &hostlist); 92 if (ret) { 93 free((*ctx)->realm); 94 free(*ctx); 95 return ret; 96 } 97 (*ctx)->admin_server = strdup(*hostlist); 98 krb5_free_krbhst (context, hostlist); 99 } 100 101 if ((*ctx)->admin_server == NULL) { 102 return ENOMEM; 103 free((*ctx)->realm); 104 free(*ctx); 105 } 106 colon = strchr ((*ctx)->admin_server, ':'); 107 if (colon != NULL) 108 *colon++ = '\0'; 109 110 (*ctx)->kadmind_port = 0; 111 112 if(params->mask & KADM5_CONFIG_KADMIND_PORT) 113 (*ctx)->kadmind_port = params->kadmind_port; 114 else if (colon != NULL) { 115 char *end; 116 117 (*ctx)->kadmind_port = htons(strtol (colon, &end, 0)); 118 } 119 if ((*ctx)->kadmind_port == 0) 120 (*ctx)->kadmind_port = krb5_getportbyname (context, "kerberos-adm", 121 "tcp", 749); 122 return 0; 123 } 124 125 static krb5_error_code 126 get_kadm_ticket(krb5_context context, 127 krb5_ccache id, 128 krb5_principal client, 129 const char *server_name) 130 { 131 krb5_error_code ret; 132 krb5_creds in, *out; 133 134 memset(&in, 0, sizeof(in)); 135 in.client = client; 136 ret = krb5_parse_name(context, server_name, &in.server); 137 if(ret) 138 return ret; 139 ret = krb5_get_credentials(context, 0, id, &in, &out); 140 if(ret == 0) 141 krb5_free_creds(context, out); 142 krb5_free_principal(context, in.server); 143 return ret; 144 } 145 146 static krb5_error_code 147 get_new_cache(krb5_context context, 148 krb5_principal client, 149 const char *password, 150 krb5_prompter_fct prompter, 151 const char *keytab, 152 const char *server_name, 153 krb5_ccache *ret_cache) 154 { 155 krb5_error_code ret; 156 krb5_creds cred; 157 krb5_get_init_creds_opt opt; 158 krb5_ccache id; 159 160 krb5_get_init_creds_opt_init (&opt); 161 162 krb5_get_init_creds_opt_set_default_flags(context, "kadmin", 163 krb5_principal_get_realm(context, 164 client), 165 &opt); 166 167 168 krb5_get_init_creds_opt_set_forwardable (&opt, FALSE); 169 krb5_get_init_creds_opt_set_proxiable (&opt, FALSE); 170 171 if(password == NULL && prompter == NULL) { 172 krb5_keytab kt; 173 if(keytab == NULL) 174 ret = krb5_kt_default(context, &kt); 175 else 176 ret = krb5_kt_resolve(context, keytab, &kt); 177 if(ret) 178 return ret; 179 ret = krb5_get_init_creds_keytab (context, 180 &cred, 181 client, 182 kt, 183 0, 184 server_name, 185 &opt); 186 krb5_kt_close(context, kt); 187 } else { 188 ret = krb5_get_init_creds_password (context, 189 &cred, 190 client, 191 password, 192 prompter, 193 NULL, 194 0, 195 server_name, 196 &opt); 197 } 198 switch(ret){ 199 case 0: 200 break; 201 case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */ 202 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 203 case KRB5KRB_AP_ERR_MODIFIED: 204 return KADM5_BAD_PASSWORD; 205 default: 206 return ret; 207 } 208 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &id); 209 if(ret) 210 return ret; 211 ret = krb5_cc_initialize (context, id, cred.client); 212 if (ret) 213 return ret; 214 ret = krb5_cc_store_cred (context, id, &cred); 215 if (ret) 216 return ret; 217 krb5_free_creds_contents (context, &cred); 218 *ret_cache = id; 219 return 0; 220 } 221 222 static krb5_error_code 223 get_cred_cache(krb5_context context, 224 const char *client_name, 225 const char *server_name, 226 const char *password, 227 krb5_prompter_fct prompter, 228 const char *keytab, 229 krb5_ccache ccache, 230 krb5_ccache *ret_cache) 231 { 232 krb5_error_code ret; 233 krb5_ccache id = NULL; 234 krb5_principal default_client = NULL, client = NULL; 235 236 /* treat empty password as NULL */ 237 if(password && *password == '\0') 238 password = NULL; 239 if(server_name == NULL) 240 server_name = KADM5_ADMIN_SERVICE; 241 242 if(client_name != NULL) { 243 ret = krb5_parse_name(context, client_name, &client); 244 if(ret) 245 return ret; 246 } 247 248 if(password != NULL || prompter != NULL) { 249 /* get principal from default cache, ok if this doesn't work */ 250 ret = krb5_cc_default(context, &id); 251 if(ret == 0) { 252 ret = krb5_cc_get_principal(context, id, &default_client); 253 if(ret) { 254 krb5_cc_close(context, id); 255 id = NULL; 256 } else { 257 const char *name, *inst; 258 krb5_principal tmp; 259 name = krb5_principal_get_comp_string(context, 260 default_client, 0); 261 inst = krb5_principal_get_comp_string(context, 262 default_client, 1); 263 if(inst == NULL || strcmp(inst, "admin") != 0) { 264 ret = krb5_make_principal(context, &tmp, NULL, 265 name, "admin", NULL); 266 if(ret != 0) { 267 krb5_free_principal(context, default_client); 268 krb5_cc_close(context, id); 269 return ret; 270 } 271 krb5_free_principal(context, default_client); 272 default_client = tmp; 273 krb5_cc_close(context, id); 274 id = NULL; 275 } 276 } 277 } 278 279 if (client != NULL) { 280 /* A client was specified by the caller. */ 281 if (default_client != NULL) { 282 krb5_free_principal(context, default_client); 283 default_client = NULL; 284 } 285 } 286 else if (default_client != NULL) 287 /* No client was specified by the caller, but we have a 288 * client from the default credentials cache. 289 */ 290 client = default_client; 291 else { 292 /* No client was specified by the caller and we cannot determine 293 * the client from a credentials cache. 294 */ 295 const char *user; 296 297 user = get_default_username (); 298 299 if(user == NULL) 300 return KADM5_FAILURE; 301 ret = krb5_make_principal(context, &client, 302 NULL, user, "admin", NULL); 303 if(ret) 304 return ret; 305 if (id != NULL) { 306 krb5_cc_close(context, id); 307 id = NULL; 308 } 309 } 310 } else if(ccache != NULL) 311 id = ccache; 312 313 if(id && (default_client == NULL || 314 krb5_principal_compare(context, client, default_client))) { 315 ret = get_kadm_ticket(context, id, client, server_name); 316 if(ret == 0) { 317 *ret_cache = id; 318 krb5_free_principal(context, default_client); 319 if (default_client != client) 320 krb5_free_principal(context, client); 321 return 0; 322 } 323 if(ccache != NULL) 324 /* couldn't get ticket from cache */ 325 return -1; 326 } 327 /* get creds via AS request */ 328 if(id) 329 krb5_cc_close(context, id); 330 if (client != default_client) 331 krb5_free_principal(context, default_client); 332 333 ret = get_new_cache(context, client, password, prompter, keytab, 334 server_name, ret_cache); 335 krb5_free_principal(context, client); 336 return ret; 337 } 338 339 static kadm5_ret_t 340 kadm_connect(kadm5_client_context *ctx) 341 { 342 kadm5_ret_t ret; 343 krb5_principal server; 344 krb5_ccache cc; 345 int s; 346 struct addrinfo *ai, *a; 347 struct addrinfo hints; 348 int error; 349 char portstr[NI_MAXSERV]; 350 char *hostname, *slash; 351 char *service_name; 352 krb5_context context = ctx->context; 353 354 memset (&hints, 0, sizeof(hints)); 355 hints.ai_socktype = SOCK_STREAM; 356 hints.ai_protocol = IPPROTO_TCP; 357 358 snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port)); 359 360 hostname = ctx->admin_server; 361 slash = strchr (hostname, '/'); 362 if (slash != NULL) 363 hostname = slash + 1; 364 365 error = getaddrinfo (hostname, portstr, &hints, &ai); 366 if (error) 367 return KADM5_BAD_SERVER_NAME; 368 369 for (a = ai; a != NULL; a = a->ai_next) { 370 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 371 if (s < 0) 372 continue; 373 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 374 krb5_warn (context, errno, "connect(%s)", hostname); 375 close (s); 376 continue; 377 } 378 break; 379 } 380 if (a == NULL) { 381 freeaddrinfo (ai); 382 krb5_warnx (context, "failed to contact %s", hostname); 383 return KADM5_FAILURE; 384 } 385 ret = get_cred_cache(context, ctx->client_name, ctx->service_name, 386 NULL, ctx->prompter, ctx->keytab, 387 ctx->ccache, &cc); 388 389 if(ret) { 390 freeaddrinfo (ai); 391 close(s); 392 return ret; 393 } 394 395 if (ctx->realm) 396 asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE, ctx->realm); 397 else 398 asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE); 399 400 if (service_name == NULL) { 401 freeaddrinfo (ai); 402 close(s); 403 return ENOMEM; 404 } 405 406 ret = krb5_parse_name(context, service_name, &server); 407 free(service_name); 408 if(ret) { 409 freeaddrinfo (ai); 410 if(ctx->ccache == NULL) 411 krb5_cc_close(context, cc); 412 close(s); 413 return ret; 414 } 415 ctx->ac = NULL; 416 417 ret = krb5_sendauth(context, &ctx->ac, &s, 418 KADMIN_APPL_VERSION, NULL, 419 server, AP_OPTS_MUTUAL_REQUIRED, 420 NULL, NULL, cc, NULL, NULL, NULL); 421 if(ret == 0) { 422 krb5_data params; 423 kadm5_config_params p; 424 memset(&p, 0, sizeof(p)); 425 if(ctx->realm) { 426 p.mask |= KADM5_CONFIG_REALM; 427 p.realm = ctx->realm; 428 } 429 ret = _kadm5_marshal_params(context, &p, ¶ms); 430 431 ret = krb5_write_priv_message(context, ctx->ac, &s, ¶ms); 432 krb5_data_free(¶ms); 433 if(ret) { 434 freeaddrinfo (ai); 435 close(s); 436 if(ctx->ccache == NULL) 437 krb5_cc_close(context, cc); 438 return ret; 439 } 440 } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) { 441 close(s); 442 443 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 444 if (s < 0) { 445 freeaddrinfo (ai); 446 return errno; 447 } 448 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 449 close (s); 450 freeaddrinfo (ai); 451 return errno; 452 } 453 ret = krb5_sendauth(context, &ctx->ac, &s, 454 KADMIN_OLD_APPL_VERSION, NULL, 455 server, AP_OPTS_MUTUAL_REQUIRED, 456 NULL, NULL, cc, NULL, NULL, NULL); 457 } 458 freeaddrinfo (ai); 459 if(ret) { 460 close(s); 461 return ret; 462 } 463 464 krb5_free_principal(context, server); 465 if(ctx->ccache == NULL) 466 krb5_cc_close(context, cc); 467 if(ret) { 468 close(s); 469 return ret; 470 } 471 ctx->sock = s; 472 473 return 0; 474 } 475 476 kadm5_ret_t 477 _kadm5_connect(void *handle) 478 { 479 kadm5_client_context *ctx = handle; 480 if(ctx->sock == -1) 481 return kadm_connect(ctx); 482 return 0; 483 } 484 485 static kadm5_ret_t 486 kadm5_c_init_with_context(krb5_context context, 487 const char *client_name, 488 const char *password, 489 krb5_prompter_fct prompter, 490 const char *keytab, 491 krb5_ccache ccache, 492 const char *service_name, 493 kadm5_config_params *realm_params, 494 unsigned long struct_version, 495 unsigned long api_version, 496 void **server_handle) 497 { 498 kadm5_ret_t ret; 499 kadm5_client_context *ctx; 500 krb5_ccache cc; 501 502 ret = _kadm5_c_init_context(&ctx, realm_params, context); 503 if(ret) 504 return ret; 505 506 if(password != NULL && *password != '\0') { 507 ret = get_cred_cache(context, client_name, service_name, 508 password, prompter, keytab, ccache, &cc); 509 if(ret) 510 return ret; /* XXX */ 511 ccache = cc; 512 } 513 514 515 if (client_name != NULL) 516 ctx->client_name = strdup(client_name); 517 else 518 ctx->client_name = NULL; 519 if (service_name != NULL) 520 ctx->service_name = strdup(service_name); 521 else 522 ctx->service_name = NULL; 523 ctx->prompter = prompter; 524 ctx->keytab = keytab; 525 ctx->ccache = ccache; 526 /* maybe we should copy the params here */ 527 ctx->sock = -1; 528 529 *server_handle = ctx; 530 return 0; 531 } 532 533 static kadm5_ret_t 534 init_context(const char *client_name, 535 const char *password, 536 krb5_prompter_fct prompter, 537 const char *keytab, 538 krb5_ccache ccache, 539 const char *service_name, 540 kadm5_config_params *realm_params, 541 unsigned long struct_version, 542 unsigned long api_version, 543 void **server_handle) 544 { 545 krb5_context context; 546 kadm5_ret_t ret; 547 kadm5_server_context *ctx; 548 549 ret = krb5_init_context(&context); 550 if (ret) 551 return ret; 552 ret = kadm5_c_init_with_context(context, 553 client_name, 554 password, 555 prompter, 556 keytab, 557 ccache, 558 service_name, 559 realm_params, 560 struct_version, 561 api_version, 562 server_handle); 563 if(ret){ 564 krb5_free_context(context); 565 return ret; 566 } 567 ctx = *server_handle; 568 ctx->my_context = 1; 569 return 0; 570 } 571 572 kadm5_ret_t 573 kadm5_c_init_with_password_ctx(krb5_context context, 574 const char *client_name, 575 const char *password, 576 const char *service_name, 577 kadm5_config_params *realm_params, 578 unsigned long struct_version, 579 unsigned long api_version, 580 void **server_handle) 581 { 582 return kadm5_c_init_with_context(context, 583 client_name, 584 password, 585 krb5_prompter_posix, 586 NULL, 587 NULL, 588 service_name, 589 realm_params, 590 struct_version, 591 api_version, 592 server_handle); 593 } 594 595 kadm5_ret_t 596 kadm5_c_init_with_password(const char *client_name, 597 const char *password, 598 const char *service_name, 599 kadm5_config_params *realm_params, 600 unsigned long struct_version, 601 unsigned long api_version, 602 void **server_handle) 603 { 604 return init_context(client_name, 605 password, 606 krb5_prompter_posix, 607 NULL, 608 NULL, 609 service_name, 610 realm_params, 611 struct_version, 612 api_version, 613 server_handle); 614 } 615 616 kadm5_ret_t 617 kadm5_c_init_with_skey_ctx(krb5_context context, 618 const char *client_name, 619 const char *keytab, 620 const char *service_name, 621 kadm5_config_params *realm_params, 622 unsigned long struct_version, 623 unsigned long api_version, 624 void **server_handle) 625 { 626 return kadm5_c_init_with_context(context, 627 client_name, 628 NULL, 629 NULL, 630 keytab, 631 NULL, 632 service_name, 633 realm_params, 634 struct_version, 635 api_version, 636 server_handle); 637 } 638 639 640 kadm5_ret_t 641 kadm5_c_init_with_skey(const char *client_name, 642 const char *keytab, 643 const char *service_name, 644 kadm5_config_params *realm_params, 645 unsigned long struct_version, 646 unsigned long api_version, 647 void **server_handle) 648 { 649 return init_context(client_name, 650 NULL, 651 NULL, 652 keytab, 653 NULL, 654 service_name, 655 realm_params, 656 struct_version, 657 api_version, 658 server_handle); 659 } 660 661 kadm5_ret_t 662 kadm5_c_init_with_creds_ctx(krb5_context context, 663 const char *client_name, 664 krb5_ccache ccache, 665 const char *service_name, 666 kadm5_config_params *realm_params, 667 unsigned long struct_version, 668 unsigned long api_version, 669 void **server_handle) 670 { 671 return kadm5_c_init_with_context(context, 672 client_name, 673 NULL, 674 NULL, 675 NULL, 676 ccache, 677 service_name, 678 realm_params, 679 struct_version, 680 api_version, 681 server_handle); 682 } 683 684 kadm5_ret_t 685 kadm5_c_init_with_creds(const char *client_name, 686 krb5_ccache ccache, 687 const char *service_name, 688 kadm5_config_params *realm_params, 689 unsigned long struct_version, 690 unsigned long api_version, 691 void **server_handle) 692 { 693 return init_context(client_name, 694 NULL, 695 NULL, 696 NULL, 697 ccache, 698 service_name, 699 realm_params, 700 struct_version, 701 api_version, 702 server_handle); 703 } 704 705 #if 0 706 kadm5_ret_t 707 kadm5_init(char *client_name, char *pass, 708 char *service_name, 709 kadm5_config_params *realm_params, 710 unsigned long struct_version, 711 unsigned long api_version, 712 void **server_handle) 713 { 714 } 715 #endif 716 717