1 /* 2 * Copyright (c) 1997 - 2002 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.44 2002/06/16 15:13:25 nectar 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 (*ctx)->realm = strdup(params->realm); 77 else 78 krb5_get_default_realm((*ctx)->context, &(*ctx)->realm); 79 if(params->mask & KADM5_CONFIG_ADMIN_SERVER) 80 (*ctx)->admin_server = strdup(params->admin_server); 81 else { 82 char **hostlist; 83 84 ret = krb5_get_krb_admin_hst (context, &(*ctx)->realm, &hostlist); 85 if (ret) 86 return ret; 87 (*ctx)->admin_server = strdup(*hostlist); 88 krb5_free_krbhst (context, hostlist); 89 } 90 91 if ((*ctx)->admin_server == NULL) 92 return ENOMEM; 93 colon = strchr ((*ctx)->admin_server, ':'); 94 if (colon != NULL) 95 *colon++ = '\0'; 96 97 (*ctx)->kadmind_port = 0; 98 99 if(params->mask & KADM5_CONFIG_KADMIND_PORT) 100 (*ctx)->kadmind_port = params->kadmind_port; 101 else if (colon != NULL) { 102 char *end; 103 104 (*ctx)->kadmind_port = htons(strtol (colon, &end, 0)); 105 } 106 if ((*ctx)->kadmind_port == 0) 107 (*ctx)->kadmind_port = krb5_getportbyname (context, "kerberos-adm", 108 "tcp", 749); 109 return 0; 110 } 111 112 static krb5_error_code 113 get_kadm_ticket(krb5_context context, 114 krb5_ccache id, 115 krb5_principal client, 116 const char *server_name) 117 { 118 krb5_error_code ret; 119 krb5_creds in, *out; 120 121 memset(&in, 0, sizeof(in)); 122 in.client = client; 123 ret = krb5_parse_name(context, server_name, &in.server); 124 if(ret) 125 return ret; 126 ret = krb5_get_credentials(context, 0, id, &in, &out); 127 if(ret == 0) 128 krb5_free_creds(context, out); 129 krb5_free_principal(context, in.server); 130 return ret; 131 } 132 133 static krb5_error_code 134 get_new_cache(krb5_context context, 135 krb5_principal client, 136 const char *password, 137 krb5_prompter_fct prompter, 138 const char *keytab, 139 const char *server_name, 140 krb5_ccache *ret_cache) 141 { 142 krb5_error_code ret; 143 krb5_creds cred; 144 krb5_get_init_creds_opt opt; 145 krb5_ccache id; 146 147 krb5_get_init_creds_opt_init (&opt); 148 149 krb5_get_init_creds_opt_set_default_flags(context, "kadmin", 150 krb5_principal_get_realm(context, 151 client), 152 &opt); 153 154 155 krb5_get_init_creds_opt_set_forwardable (&opt, FALSE); 156 krb5_get_init_creds_opt_set_proxiable (&opt, FALSE); 157 158 if(password == NULL && prompter == NULL) { 159 krb5_keytab kt; 160 if(keytab == NULL) 161 ret = krb5_kt_default(context, &kt); 162 else 163 ret = krb5_kt_resolve(context, keytab, &kt); 164 if(ret) 165 return ret; 166 ret = krb5_get_init_creds_keytab (context, 167 &cred, 168 client, 169 kt, 170 0, 171 server_name, 172 &opt); 173 krb5_kt_close(context, kt); 174 } else { 175 ret = krb5_get_init_creds_password (context, 176 &cred, 177 client, 178 password, 179 prompter, 180 NULL, 181 0, 182 server_name, 183 &opt); 184 } 185 switch(ret){ 186 case 0: 187 break; 188 case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */ 189 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 190 case KRB5KRB_AP_ERR_MODIFIED: 191 return KADM5_BAD_PASSWORD; 192 default: 193 return ret; 194 } 195 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &id); 196 if(ret) 197 return ret; 198 ret = krb5_cc_initialize (context, id, cred.client); 199 if (ret) 200 return ret; 201 ret = krb5_cc_store_cred (context, id, &cred); 202 if (ret) 203 return ret; 204 krb5_free_creds_contents (context, &cred); 205 *ret_cache = id; 206 return 0; 207 } 208 209 static krb5_error_code 210 get_cred_cache(krb5_context context, 211 const char *client_name, 212 const char *server_name, 213 const char *password, 214 krb5_prompter_fct prompter, 215 const char *keytab, 216 krb5_ccache ccache, 217 krb5_ccache *ret_cache) 218 { 219 krb5_error_code ret; 220 krb5_ccache id = NULL; 221 krb5_principal default_client = NULL, client = NULL; 222 223 /* treat empty password as NULL */ 224 if(password && *password == '\0') 225 password = NULL; 226 if(server_name == NULL) 227 server_name = KADM5_ADMIN_SERVICE; 228 229 if(client_name != NULL) { 230 ret = krb5_parse_name(context, client_name, &client); 231 if(ret) 232 return ret; 233 } 234 235 if(password != NULL || prompter != NULL) { 236 /* get principal from default cache, ok if this doesn't work */ 237 ret = krb5_cc_default(context, &id); 238 if(ret == 0) { 239 ret = krb5_cc_get_principal(context, id, &default_client); 240 if(ret) { 241 krb5_cc_close(context, id); 242 id = NULL; 243 } else { 244 const char *name, *inst; 245 krb5_principal tmp; 246 name = krb5_principal_get_comp_string(context, 247 default_client, 0); 248 inst = krb5_principal_get_comp_string(context, 249 default_client, 1); 250 if(inst == NULL || strcmp(inst, "admin") != 0) { 251 ret = krb5_make_principal(context, &tmp, NULL, 252 name, "admin", NULL); 253 if(ret != 0) { 254 krb5_free_principal(context, default_client); 255 krb5_cc_close(context, id); 256 return ret; 257 } 258 krb5_free_principal(context, default_client); 259 default_client = tmp; 260 krb5_cc_close(context, id); 261 id = NULL; 262 } 263 } 264 } 265 266 if (client != NULL) { 267 /* A client was specified by the caller. */ 268 if (default_client != NULL) { 269 krb5_free_principal(context, default_client); 270 default_client = NULL; 271 } 272 } 273 else if (default_client != NULL) 274 /* No client was specified by the caller, but we have a 275 * client from the default credentials cache. 276 */ 277 client = default_client; 278 else { 279 /* No client was specified by the caller and we cannot determine 280 * the client from a credentials cache. 281 */ 282 const char *user; 283 284 user = get_default_username (); 285 286 if(user == NULL) 287 return KADM5_FAILURE; 288 ret = krb5_make_principal(context, &client, 289 NULL, user, "admin", NULL); 290 if(ret) 291 return ret; 292 if (id != NULL) { 293 krb5_cc_close(context, id); 294 id = NULL; 295 } 296 } 297 } else if(ccache != NULL) 298 id = ccache; 299 300 if(id && (default_client == NULL || 301 krb5_principal_compare(context, client, default_client))) { 302 ret = get_kadm_ticket(context, id, client, server_name); 303 if(ret == 0) { 304 *ret_cache = id; 305 krb5_free_principal(context, default_client); 306 if (default_client != client) 307 krb5_free_principal(context, client); 308 return 0; 309 } 310 if(ccache != NULL) 311 /* couldn't get ticket from cache */ 312 return -1; 313 } 314 /* get creds via AS request */ 315 if(id) 316 krb5_cc_close(context, id); 317 if (client != default_client) 318 krb5_free_principal(context, default_client); 319 320 ret = get_new_cache(context, client, password, prompter, keytab, 321 server_name, ret_cache); 322 krb5_free_principal(context, client); 323 return ret; 324 } 325 326 static kadm5_ret_t 327 kadm_connect(kadm5_client_context *ctx) 328 { 329 kadm5_ret_t ret; 330 krb5_principal server; 331 krb5_ccache cc; 332 int s; 333 struct addrinfo *ai, *a; 334 struct addrinfo hints; 335 int error; 336 char portstr[NI_MAXSERV]; 337 char *hostname, *slash; 338 krb5_context context = ctx->context; 339 340 memset (&hints, 0, sizeof(hints)); 341 hints.ai_socktype = SOCK_STREAM; 342 hints.ai_protocol = IPPROTO_TCP; 343 344 snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port)); 345 346 hostname = ctx->admin_server; 347 slash = strchr (hostname, '/'); 348 if (slash != NULL) 349 hostname = slash + 1; 350 351 error = getaddrinfo (hostname, portstr, &hints, &ai); 352 if (error) 353 return KADM5_BAD_SERVER_NAME; 354 355 for (a = ai; a != NULL; a = a->ai_next) { 356 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 357 if (s < 0) 358 continue; 359 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 360 krb5_warn (context, errno, "connect(%s)", hostname); 361 close (s); 362 continue; 363 } 364 break; 365 } 366 if (a == NULL) { 367 freeaddrinfo (ai); 368 krb5_warnx (context, "failed to contact %s", hostname); 369 return KADM5_FAILURE; 370 } 371 ret = get_cred_cache(context, ctx->client_name, ctx->service_name, 372 NULL, ctx->prompter, ctx->keytab, 373 ctx->ccache, &cc); 374 375 if(ret) { 376 freeaddrinfo (ai); 377 close(s); 378 return ret; 379 } 380 ret = krb5_parse_name(context, KADM5_ADMIN_SERVICE, &server); 381 if(ret) { 382 freeaddrinfo (ai); 383 if(ctx->ccache == NULL) 384 krb5_cc_close(context, cc); 385 close(s); 386 return ret; 387 } 388 ctx->ac = NULL; 389 390 ret = krb5_sendauth(context, &ctx->ac, &s, 391 KADMIN_APPL_VERSION, NULL, 392 server, AP_OPTS_MUTUAL_REQUIRED, 393 NULL, NULL, cc, NULL, NULL, NULL); 394 if(ret == 0) { 395 krb5_data params; 396 kadm5_config_params p; 397 memset(&p, 0, sizeof(p)); 398 if(ctx->realm) { 399 p.mask |= KADM5_CONFIG_REALM; 400 p.realm = ctx->realm; 401 } 402 ret = _kadm5_marshal_params(context, &p, ¶ms); 403 404 ret = krb5_write_priv_message(context, ctx->ac, &s, ¶ms); 405 krb5_data_free(¶ms); 406 if(ret) { 407 freeaddrinfo (ai); 408 close(s); 409 if(ctx->ccache == NULL) 410 krb5_cc_close(context, cc); 411 return ret; 412 } 413 } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) { 414 close(s); 415 416 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 417 if (s < 0) { 418 freeaddrinfo (ai); 419 return errno; 420 } 421 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 422 close (s); 423 freeaddrinfo (ai); 424 return errno; 425 } 426 ret = krb5_sendauth(context, &ctx->ac, &s, 427 KADMIN_OLD_APPL_VERSION, NULL, 428 server, AP_OPTS_MUTUAL_REQUIRED, 429 NULL, NULL, cc, NULL, NULL, NULL); 430 } 431 freeaddrinfo (ai); 432 if(ret) { 433 close(s); 434 return ret; 435 } 436 437 krb5_free_principal(context, server); 438 if(ctx->ccache == NULL) 439 krb5_cc_close(context, cc); 440 if(ret) { 441 close(s); 442 return ret; 443 } 444 ctx->sock = s; 445 446 return 0; 447 } 448 449 kadm5_ret_t 450 _kadm5_connect(void *handle) 451 { 452 kadm5_client_context *ctx = handle; 453 if(ctx->sock == -1) 454 return kadm_connect(ctx); 455 return 0; 456 } 457 458 static kadm5_ret_t 459 kadm5_c_init_with_context(krb5_context context, 460 const char *client_name, 461 const char *password, 462 krb5_prompter_fct prompter, 463 const char *keytab, 464 krb5_ccache ccache, 465 const char *service_name, 466 kadm5_config_params *realm_params, 467 unsigned long struct_version, 468 unsigned long api_version, 469 void **server_handle) 470 { 471 kadm5_ret_t ret; 472 kadm5_client_context *ctx; 473 krb5_ccache cc; 474 475 ret = _kadm5_c_init_context(&ctx, realm_params, context); 476 if(ret) 477 return ret; 478 479 if(password != NULL && *password != '\0') { 480 ret = get_cred_cache(context, client_name, service_name, 481 password, prompter, keytab, ccache, &cc); 482 if(ret) 483 return ret; /* XXX */ 484 ccache = cc; 485 } 486 487 488 if (client_name != NULL) 489 ctx->client_name = strdup(client_name); 490 else 491 ctx->client_name = NULL; 492 if (service_name != NULL) 493 ctx->service_name = strdup(service_name); 494 else 495 ctx->service_name = NULL; 496 ctx->prompter = prompter; 497 ctx->keytab = keytab; 498 ctx->ccache = ccache; 499 /* maybe we should copy the params here */ 500 ctx->sock = -1; 501 502 *server_handle = ctx; 503 return 0; 504 } 505 506 static kadm5_ret_t 507 init_context(const char *client_name, 508 const char *password, 509 krb5_prompter_fct prompter, 510 const char *keytab, 511 krb5_ccache ccache, 512 const char *service_name, 513 kadm5_config_params *realm_params, 514 unsigned long struct_version, 515 unsigned long api_version, 516 void **server_handle) 517 { 518 krb5_context context; 519 kadm5_ret_t ret; 520 kadm5_server_context *ctx; 521 522 ret = krb5_init_context(&context); 523 if (ret) 524 return ret; 525 ret = kadm5_c_init_with_context(context, 526 client_name, 527 password, 528 prompter, 529 keytab, 530 ccache, 531 service_name, 532 realm_params, 533 struct_version, 534 api_version, 535 server_handle); 536 if(ret){ 537 krb5_free_context(context); 538 return ret; 539 } 540 ctx = *server_handle; 541 ctx->my_context = 1; 542 return 0; 543 } 544 545 kadm5_ret_t 546 kadm5_c_init_with_password_ctx(krb5_context context, 547 const char *client_name, 548 const char *password, 549 const char *service_name, 550 kadm5_config_params *realm_params, 551 unsigned long struct_version, 552 unsigned long api_version, 553 void **server_handle) 554 { 555 return kadm5_c_init_with_context(context, 556 client_name, 557 password, 558 krb5_prompter_posix, 559 NULL, 560 NULL, 561 service_name, 562 realm_params, 563 struct_version, 564 api_version, 565 server_handle); 566 } 567 568 kadm5_ret_t 569 kadm5_c_init_with_password(const char *client_name, 570 const char *password, 571 const char *service_name, 572 kadm5_config_params *realm_params, 573 unsigned long struct_version, 574 unsigned long api_version, 575 void **server_handle) 576 { 577 return init_context(client_name, 578 password, 579 krb5_prompter_posix, 580 NULL, 581 NULL, 582 service_name, 583 realm_params, 584 struct_version, 585 api_version, 586 server_handle); 587 } 588 589 kadm5_ret_t 590 kadm5_c_init_with_skey_ctx(krb5_context context, 591 const char *client_name, 592 const char *keytab, 593 const char *service_name, 594 kadm5_config_params *realm_params, 595 unsigned long struct_version, 596 unsigned long api_version, 597 void **server_handle) 598 { 599 return kadm5_c_init_with_context(context, 600 client_name, 601 NULL, 602 NULL, 603 keytab, 604 NULL, 605 service_name, 606 realm_params, 607 struct_version, 608 api_version, 609 server_handle); 610 } 611 612 613 kadm5_ret_t 614 kadm5_c_init_with_skey(const char *client_name, 615 const char *keytab, 616 const char *service_name, 617 kadm5_config_params *realm_params, 618 unsigned long struct_version, 619 unsigned long api_version, 620 void **server_handle) 621 { 622 return init_context(client_name, 623 NULL, 624 NULL, 625 keytab, 626 NULL, 627 service_name, 628 realm_params, 629 struct_version, 630 api_version, 631 server_handle); 632 } 633 634 kadm5_ret_t 635 kadm5_c_init_with_creds_ctx(krb5_context context, 636 const char *client_name, 637 krb5_ccache ccache, 638 const char *service_name, 639 kadm5_config_params *realm_params, 640 unsigned long struct_version, 641 unsigned long api_version, 642 void **server_handle) 643 { 644 return kadm5_c_init_with_context(context, 645 client_name, 646 NULL, 647 NULL, 648 NULL, 649 ccache, 650 service_name, 651 realm_params, 652 struct_version, 653 api_version, 654 server_handle); 655 } 656 657 kadm5_ret_t 658 kadm5_c_init_with_creds(const char *client_name, 659 krb5_ccache ccache, 660 const char *service_name, 661 kadm5_config_params *realm_params, 662 unsigned long struct_version, 663 unsigned long api_version, 664 void **server_handle) 665 { 666 return init_context(client_name, 667 NULL, 668 NULL, 669 NULL, 670 ccache, 671 service_name, 672 realm_params, 673 struct_version, 674 api_version, 675 server_handle); 676 } 677 678 #if 0 679 kadm5_ret_t 680 kadm5_init(char *client_name, char *pass, 681 char *service_name, 682 kadm5_config_params *realm_params, 683 unsigned long struct_version, 684 unsigned long api_version, 685 void **server_handle) 686 { 687 } 688 #endif 689 690