init_c.c revision 120945
1178476Sjb/* 2178476Sjb * Copyright (c) 1997 - 2003 Kungliga Tekniska H�gskolan 3178476Sjb * (Royal Institute of Technology, Stockholm, Sweden). 4178476Sjb * All rights reserved. 5178476Sjb * 6178476Sjb * Redistribution and use in source and binary forms, with or without 7178476Sjb * modification, are permitted provided that the following conditions 8178476Sjb * are met: 9178476Sjb * 10178476Sjb * 1. Redistributions of source code must retain the above copyright 11178476Sjb * notice, this list of conditions and the following disclaimer. 12178476Sjb * 13178476Sjb * 2. Redistributions in binary form must reproduce the above copyright 14178476Sjb * notice, this list of conditions and the following disclaimer in the 15178476Sjb * documentation and/or other materials provided with the distribution. 16178476Sjb * 17178476Sjb * 3. Neither the name of the Institute nor the names of its contributors 18178476Sjb * may be used to endorse or promote products derived from this software 19178476Sjb * without specific prior written permission. 20178476Sjb * 21178476Sjb * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22178476Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23178476Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24178476Sjb * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25178476Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26178476Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27178476Sjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28178476Sjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29178476Sjb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30178476Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31178476Sjb * SUCH DAMAGE. 32178476Sjb */ 33178476Sjb 34178476Sjb#include "kadm5_locl.h" 35178476Sjb#include <sys/types.h> 36178476Sjb#include <sys/socket.h> 37178476Sjb#include <netinet/in.h> 38178476Sjb#include <netdb.h> 39178476Sjb 40178476SjbRCSID("$Id: init_c.c,v 1.45 2003/04/01 15:06:41 lha Exp $"); 41178476Sjb 42178476Sjbstatic void 43178476Sjbset_funcs(kadm5_client_context *c) 44178476Sjb{ 45178476Sjb#define SET(C, F) (C)->funcs.F = kadm5 ## _c_ ## F 46178476Sjb SET(c, chpass_principal); 47178476Sjb SET(c, chpass_principal_with_key); 48178476Sjb SET(c, create_principal); 49178476Sjb SET(c, delete_principal); 50178476Sjb SET(c, destroy); 51178476Sjb SET(c, flush); 52178476Sjb SET(c, get_principal); 53178476Sjb SET(c, get_principals); 54178476Sjb SET(c, get_privs); 55178476Sjb SET(c, modify_principal); 56178476Sjb SET(c, randkey_principal); 57178476Sjb SET(c, rename_principal); 58178476Sjb} 59178476Sjb 60178476Sjbkadm5_ret_t 61178476Sjb_kadm5_c_init_context(kadm5_client_context **ctx, 62178476Sjb kadm5_config_params *params, 63178476Sjb krb5_context context) 64178476Sjb{ 65178476Sjb krb5_error_code ret; 66178476Sjb char *colon; 67178476Sjb 68178476Sjb *ctx = malloc(sizeof(**ctx)); 69178476Sjb if(*ctx == NULL) 70178476Sjb return ENOMEM; 71178476Sjb memset(*ctx, 0, sizeof(**ctx)); 72178476Sjb krb5_add_et_list (context, initialize_kadm5_error_table_r); 73178476Sjb set_funcs(*ctx); 74178476Sjb (*ctx)->context = context; 75178476Sjb 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 112static krb5_error_code 113get_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 133static krb5_error_code 134get_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 209static krb5_error_code 210get_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 326static kadm5_ret_t 327kadm_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 char *service_name; 339 krb5_context context = ctx->context; 340 341 memset (&hints, 0, sizeof(hints)); 342 hints.ai_socktype = SOCK_STREAM; 343 hints.ai_protocol = IPPROTO_TCP; 344 345 snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port)); 346 347 hostname = ctx->admin_server; 348 slash = strchr (hostname, '/'); 349 if (slash != NULL) 350 hostname = slash + 1; 351 352 error = getaddrinfo (hostname, portstr, &hints, &ai); 353 if (error) 354 return KADM5_BAD_SERVER_NAME; 355 356 for (a = ai; a != NULL; a = a->ai_next) { 357 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 358 if (s < 0) 359 continue; 360 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 361 krb5_warn (context, errno, "connect(%s)", hostname); 362 close (s); 363 continue; 364 } 365 break; 366 } 367 if (a == NULL) { 368 freeaddrinfo (ai); 369 krb5_warnx (context, "failed to contact %s", hostname); 370 return KADM5_FAILURE; 371 } 372 ret = get_cred_cache(context, ctx->client_name, ctx->service_name, 373 NULL, ctx->prompter, ctx->keytab, 374 ctx->ccache, &cc); 375 376 if(ret) { 377 freeaddrinfo (ai); 378 close(s); 379 return ret; 380 } 381 382 if (ctx->realm) 383 asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE, ctx->realm); 384 else 385 asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE); 386 387 if (service_name == NULL) { 388 freeaddrinfo (ai); 389 close(s); 390 return ENOMEM; 391 } 392 393 ret = krb5_parse_name(context, service_name, &server); 394 free(service_name); 395 if(ret) { 396 freeaddrinfo (ai); 397 if(ctx->ccache == NULL) 398 krb5_cc_close(context, cc); 399 close(s); 400 return ret; 401 } 402 ctx->ac = NULL; 403 404 ret = krb5_sendauth(context, &ctx->ac, &s, 405 KADMIN_APPL_VERSION, NULL, 406 server, AP_OPTS_MUTUAL_REQUIRED, 407 NULL, NULL, cc, NULL, NULL, NULL); 408 if(ret == 0) { 409 krb5_data params; 410 kadm5_config_params p; 411 memset(&p, 0, sizeof(p)); 412 if(ctx->realm) { 413 p.mask |= KADM5_CONFIG_REALM; 414 p.realm = ctx->realm; 415 } 416 ret = _kadm5_marshal_params(context, &p, ¶ms); 417 418 ret = krb5_write_priv_message(context, ctx->ac, &s, ¶ms); 419 krb5_data_free(¶ms); 420 if(ret) { 421 freeaddrinfo (ai); 422 close(s); 423 if(ctx->ccache == NULL) 424 krb5_cc_close(context, cc); 425 return ret; 426 } 427 } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) { 428 close(s); 429 430 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 431 if (s < 0) { 432 freeaddrinfo (ai); 433 return errno; 434 } 435 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 436 close (s); 437 freeaddrinfo (ai); 438 return errno; 439 } 440 ret = krb5_sendauth(context, &ctx->ac, &s, 441 KADMIN_OLD_APPL_VERSION, NULL, 442 server, AP_OPTS_MUTUAL_REQUIRED, 443 NULL, NULL, cc, NULL, NULL, NULL); 444 } 445 freeaddrinfo (ai); 446 if(ret) { 447 close(s); 448 return ret; 449 } 450 451 krb5_free_principal(context, server); 452 if(ctx->ccache == NULL) 453 krb5_cc_close(context, cc); 454 if(ret) { 455 close(s); 456 return ret; 457 } 458 ctx->sock = s; 459 460 return 0; 461} 462 463kadm5_ret_t 464_kadm5_connect(void *handle) 465{ 466 kadm5_client_context *ctx = handle; 467 if(ctx->sock == -1) 468 return kadm_connect(ctx); 469 return 0; 470} 471 472static kadm5_ret_t 473kadm5_c_init_with_context(krb5_context context, 474 const char *client_name, 475 const char *password, 476 krb5_prompter_fct prompter, 477 const char *keytab, 478 krb5_ccache ccache, 479 const char *service_name, 480 kadm5_config_params *realm_params, 481 unsigned long struct_version, 482 unsigned long api_version, 483 void **server_handle) 484{ 485 kadm5_ret_t ret; 486 kadm5_client_context *ctx; 487 krb5_ccache cc; 488 489 ret = _kadm5_c_init_context(&ctx, realm_params, context); 490 if(ret) 491 return ret; 492 493 if(password != NULL && *password != '\0') { 494 ret = get_cred_cache(context, client_name, service_name, 495 password, prompter, keytab, ccache, &cc); 496 if(ret) 497 return ret; /* XXX */ 498 ccache = cc; 499 } 500 501 502 if (client_name != NULL) 503 ctx->client_name = strdup(client_name); 504 else 505 ctx->client_name = NULL; 506 if (service_name != NULL) 507 ctx->service_name = strdup(service_name); 508 else 509 ctx->service_name = NULL; 510 ctx->prompter = prompter; 511 ctx->keytab = keytab; 512 ctx->ccache = ccache; 513 /* maybe we should copy the params here */ 514 ctx->sock = -1; 515 516 *server_handle = ctx; 517 return 0; 518} 519 520static kadm5_ret_t 521init_context(const char *client_name, 522 const char *password, 523 krb5_prompter_fct prompter, 524 const char *keytab, 525 krb5_ccache ccache, 526 const char *service_name, 527 kadm5_config_params *realm_params, 528 unsigned long struct_version, 529 unsigned long api_version, 530 void **server_handle) 531{ 532 krb5_context context; 533 kadm5_ret_t ret; 534 kadm5_server_context *ctx; 535 536 ret = krb5_init_context(&context); 537 if (ret) 538 return ret; 539 ret = kadm5_c_init_with_context(context, 540 client_name, 541 password, 542 prompter, 543 keytab, 544 ccache, 545 service_name, 546 realm_params, 547 struct_version, 548 api_version, 549 server_handle); 550 if(ret){ 551 krb5_free_context(context); 552 return ret; 553 } 554 ctx = *server_handle; 555 ctx->my_context = 1; 556 return 0; 557} 558 559kadm5_ret_t 560kadm5_c_init_with_password_ctx(krb5_context context, 561 const char *client_name, 562 const char *password, 563 const char *service_name, 564 kadm5_config_params *realm_params, 565 unsigned long struct_version, 566 unsigned long api_version, 567 void **server_handle) 568{ 569 return kadm5_c_init_with_context(context, 570 client_name, 571 password, 572 krb5_prompter_posix, 573 NULL, 574 NULL, 575 service_name, 576 realm_params, 577 struct_version, 578 api_version, 579 server_handle); 580} 581 582kadm5_ret_t 583kadm5_c_init_with_password(const char *client_name, 584 const char *password, 585 const char *service_name, 586 kadm5_config_params *realm_params, 587 unsigned long struct_version, 588 unsigned long api_version, 589 void **server_handle) 590{ 591 return init_context(client_name, 592 password, 593 krb5_prompter_posix, 594 NULL, 595 NULL, 596 service_name, 597 realm_params, 598 struct_version, 599 api_version, 600 server_handle); 601} 602 603kadm5_ret_t 604kadm5_c_init_with_skey_ctx(krb5_context context, 605 const char *client_name, 606 const char *keytab, 607 const char *service_name, 608 kadm5_config_params *realm_params, 609 unsigned long struct_version, 610 unsigned long api_version, 611 void **server_handle) 612{ 613 return kadm5_c_init_with_context(context, 614 client_name, 615 NULL, 616 NULL, 617 keytab, 618 NULL, 619 service_name, 620 realm_params, 621 struct_version, 622 api_version, 623 server_handle); 624} 625 626 627kadm5_ret_t 628kadm5_c_init_with_skey(const char *client_name, 629 const char *keytab, 630 const char *service_name, 631 kadm5_config_params *realm_params, 632 unsigned long struct_version, 633 unsigned long api_version, 634 void **server_handle) 635{ 636 return init_context(client_name, 637 NULL, 638 NULL, 639 keytab, 640 NULL, 641 service_name, 642 realm_params, 643 struct_version, 644 api_version, 645 server_handle); 646} 647 648kadm5_ret_t 649kadm5_c_init_with_creds_ctx(krb5_context context, 650 const char *client_name, 651 krb5_ccache ccache, 652 const char *service_name, 653 kadm5_config_params *realm_params, 654 unsigned long struct_version, 655 unsigned long api_version, 656 void **server_handle) 657{ 658 return kadm5_c_init_with_context(context, 659 client_name, 660 NULL, 661 NULL, 662 NULL, 663 ccache, 664 service_name, 665 realm_params, 666 struct_version, 667 api_version, 668 server_handle); 669} 670 671kadm5_ret_t 672kadm5_c_init_with_creds(const char *client_name, 673 krb5_ccache ccache, 674 const char *service_name, 675 kadm5_config_params *realm_params, 676 unsigned long struct_version, 677 unsigned long api_version, 678 void **server_handle) 679{ 680 return init_context(client_name, 681 NULL, 682 NULL, 683 NULL, 684 ccache, 685 service_name, 686 realm_params, 687 struct_version, 688 api_version, 689 server_handle); 690} 691 692#if 0 693kadm5_ret_t 694kadm5_init(char *client_name, char *pass, 695 char *service_name, 696 kadm5_config_params *realm_params, 697 unsigned long struct_version, 698 unsigned long api_version, 699 void **server_handle) 700{ 701} 702#endif 703 704