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.42 2002/02/08 18:31:49 joda 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 } 244 } 245 246 if(client == NULL) 247 client = default_client; 248 if(client == NULL) { 249 const char *user; 250 251 user = get_default_username (); 252 253 if(user == NULL) 254 return KADM5_FAILURE; 255 ret = krb5_make_principal(context, &client, 256 NULL, user, "admin", NULL); 257 if(ret) 258 return ret; 259 } 260 if(client != default_client) { 261 krb5_free_principal(context, default_client); 262 default_client = NULL; 263 if (id != NULL) { 264 krb5_cc_close(context, id); 265 id = NULL; 266 } 267 } 268 } else if(ccache != NULL) 269 id = ccache; 270 271 272 if(id && (default_client == NULL || 273 krb5_principal_compare(context, client, default_client))) { 274 ret = get_kadm_ticket(context, id, client, server_name); 275 if(ret == 0) { 276 *ret_cache = id; 277 krb5_free_principal(context, default_client); 278 if (default_client != client) 279 krb5_free_principal(context, client); 280 return 0; 281 } 282 if(ccache != NULL) 283 /* couldn't get ticket from cache */ 284 return -1; 285 } 286 /* get creds via AS request */ 287 if(id) 288 krb5_cc_close(context, id); 289 if (client != default_client) 290 krb5_free_principal(context, default_client); 291 292 ret = get_new_cache(context, client, password, prompter, keytab, 293 server_name, ret_cache); 294 krb5_free_principal(context, client); 295 return ret; 296 } 297 298 static kadm5_ret_t 299 kadm_connect(kadm5_client_context *ctx) 300 { 301 kadm5_ret_t ret; 302 krb5_principal server; 303 krb5_ccache cc; 304 int s; 305 struct addrinfo *ai, *a; 306 struct addrinfo hints; 307 int error; 308 char portstr[NI_MAXSERV]; 309 char *hostname, *slash; 310 krb5_context context = ctx->context; 311 312 memset (&hints, 0, sizeof(hints)); 313 hints.ai_socktype = SOCK_STREAM; 314 hints.ai_protocol = IPPROTO_TCP; 315 316 snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port)); 317 318 hostname = ctx->admin_server; 319 slash = strchr (hostname, '/'); 320 if (slash != NULL) 321 hostname = slash + 1; 322 323 error = getaddrinfo (hostname, portstr, &hints, &ai); 324 if (error) 325 return KADM5_BAD_SERVER_NAME; 326 327 for (a = ai; a != NULL; a = a->ai_next) { 328 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 329 if (s < 0) 330 continue; 331 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 332 krb5_warn (context, errno, "connect(%s)", hostname); 333 close (s); 334 continue; 335 } 336 break; 337 } 338 if (a == NULL) { 339 freeaddrinfo (ai); 340 krb5_warnx (context, "failed to contact %s", hostname); 341 return KADM5_FAILURE; 342 } 343 ret = get_cred_cache(context, ctx->client_name, ctx->service_name, 344 NULL, ctx->prompter, ctx->keytab, 345 ctx->ccache, &cc); 346 347 if(ret) { 348 freeaddrinfo (ai); 349 close(s); 350 return ret; 351 } 352 ret = krb5_parse_name(context, KADM5_ADMIN_SERVICE, &server); 353 if(ret) { 354 freeaddrinfo (ai); 355 if(ctx->ccache == NULL) 356 krb5_cc_close(context, cc); 357 close(s); 358 return ret; 359 } 360 ctx->ac = NULL; 361 362 ret = krb5_sendauth(context, &ctx->ac, &s, 363 KADMIN_APPL_VERSION, NULL, 364 server, AP_OPTS_MUTUAL_REQUIRED, 365 NULL, NULL, cc, NULL, NULL, NULL); 366 if(ret == 0) { 367 krb5_data params; 368 kadm5_config_params p; 369 memset(&p, 0, sizeof(p)); 370 if(ctx->realm) { 371 p.mask |= KADM5_CONFIG_REALM; 372 p.realm = ctx->realm; 373 } 374 ret = _kadm5_marshal_params(context, &p, ¶ms); 375 376 ret = krb5_write_priv_message(context, ctx->ac, &s, ¶ms); 377 krb5_data_free(¶ms); 378 if(ret) { 379 freeaddrinfo (ai); 380 close(s); 381 if(ctx->ccache == NULL) 382 krb5_cc_close(context, cc); 383 return ret; 384 } 385 } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) { 386 close(s); 387 388 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 389 if (s < 0) { 390 freeaddrinfo (ai); 391 return errno; 392 } 393 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 394 close (s); 395 freeaddrinfo (ai); 396 return errno; 397 } 398 ret = krb5_sendauth(context, &ctx->ac, &s, 399 KADMIN_OLD_APPL_VERSION, NULL, 400 server, AP_OPTS_MUTUAL_REQUIRED, 401 NULL, NULL, cc, NULL, NULL, NULL); 402 } 403 freeaddrinfo (ai); 404 if(ret) { 405 close(s); 406 return ret; 407 } 408 409 krb5_free_principal(context, server); 410 if(ctx->ccache == NULL) 411 krb5_cc_close(context, cc); 412 if(ret) { 413 close(s); 414 return ret; 415 } 416 ctx->sock = s; 417 418 return 0; 419 } 420 421 kadm5_ret_t 422 _kadm5_connect(void *handle) 423 { 424 kadm5_client_context *ctx = handle; 425 if(ctx->sock == -1) 426 return kadm_connect(ctx); 427 return 0; 428 } 429 430 static kadm5_ret_t 431 kadm5_c_init_with_context(krb5_context context, 432 const char *client_name, 433 const char *password, 434 krb5_prompter_fct prompter, 435 const char *keytab, 436 krb5_ccache ccache, 437 const char *service_name, 438 kadm5_config_params *realm_params, 439 unsigned long struct_version, 440 unsigned long api_version, 441 void **server_handle) 442 { 443 kadm5_ret_t ret; 444 kadm5_client_context *ctx; 445 krb5_ccache cc; 446 447 ret = _kadm5_c_init_context(&ctx, realm_params, context); 448 if(ret) 449 return ret; 450 451 if(password != NULL && *password != '\0') { 452 ret = get_cred_cache(context, client_name, service_name, 453 password, prompter, keytab, ccache, &cc); 454 if(ret) 455 return ret; /* XXX */ 456 ccache = cc; 457 } 458 459 460 if (client_name != NULL) 461 ctx->client_name = strdup(client_name); 462 else 463 ctx->client_name = NULL; 464 if (service_name != NULL) 465 ctx->service_name = strdup(service_name); 466 else 467 ctx->service_name = NULL; 468 ctx->prompter = prompter; 469 ctx->keytab = keytab; 470 ctx->ccache = ccache; 471 /* maybe we should copy the params here */ 472 ctx->sock = -1; 473 474 *server_handle = ctx; 475 return 0; 476 } 477 478 static kadm5_ret_t 479 init_context(const char *client_name, 480 const char *password, 481 krb5_prompter_fct prompter, 482 const char *keytab, 483 krb5_ccache ccache, 484 const char *service_name, 485 kadm5_config_params *realm_params, 486 unsigned long struct_version, 487 unsigned long api_version, 488 void **server_handle) 489 { 490 krb5_context context; 491 kadm5_ret_t ret; 492 kadm5_server_context *ctx; 493 494 ret = krb5_init_context(&context); 495 if (ret) 496 return ret; 497 ret = kadm5_c_init_with_context(context, 498 client_name, 499 password, 500 prompter, 501 keytab, 502 ccache, 503 service_name, 504 realm_params, 505 struct_version, 506 api_version, 507 server_handle); 508 if(ret){ 509 krb5_free_context(context); 510 return ret; 511 } 512 ctx = *server_handle; 513 ctx->my_context = 1; 514 return 0; 515 } 516 517 kadm5_ret_t 518 kadm5_c_init_with_password_ctx(krb5_context context, 519 const char *client_name, 520 const char *password, 521 const char *service_name, 522 kadm5_config_params *realm_params, 523 unsigned long struct_version, 524 unsigned long api_version, 525 void **server_handle) 526 { 527 return kadm5_c_init_with_context(context, 528 client_name, 529 password, 530 krb5_prompter_posix, 531 NULL, 532 NULL, 533 service_name, 534 realm_params, 535 struct_version, 536 api_version, 537 server_handle); 538 } 539 540 kadm5_ret_t 541 kadm5_c_init_with_password(const char *client_name, 542 const char *password, 543 const char *service_name, 544 kadm5_config_params *realm_params, 545 unsigned long struct_version, 546 unsigned long api_version, 547 void **server_handle) 548 { 549 return init_context(client_name, 550 password, 551 krb5_prompter_posix, 552 NULL, 553 NULL, 554 service_name, 555 realm_params, 556 struct_version, 557 api_version, 558 server_handle); 559 } 560 561 kadm5_ret_t 562 kadm5_c_init_with_skey_ctx(krb5_context context, 563 const char *client_name, 564 const char *keytab, 565 const char *service_name, 566 kadm5_config_params *realm_params, 567 unsigned long struct_version, 568 unsigned long api_version, 569 void **server_handle) 570 { 571 return kadm5_c_init_with_context(context, 572 client_name, 573 NULL, 574 NULL, 575 keytab, 576 NULL, 577 service_name, 578 realm_params, 579 struct_version, 580 api_version, 581 server_handle); 582 } 583 584 585 kadm5_ret_t 586 kadm5_c_init_with_skey(const char *client_name, 587 const char *keytab, 588 const char *service_name, 589 kadm5_config_params *realm_params, 590 unsigned long struct_version, 591 unsigned long api_version, 592 void **server_handle) 593 { 594 return init_context(client_name, 595 NULL, 596 NULL, 597 keytab, 598 NULL, 599 service_name, 600 realm_params, 601 struct_version, 602 api_version, 603 server_handle); 604 } 605 606 kadm5_ret_t 607 kadm5_c_init_with_creds_ctx(krb5_context context, 608 const char *client_name, 609 krb5_ccache ccache, 610 const char *service_name, 611 kadm5_config_params *realm_params, 612 unsigned long struct_version, 613 unsigned long api_version, 614 void **server_handle) 615 { 616 return kadm5_c_init_with_context(context, 617 client_name, 618 NULL, 619 NULL, 620 NULL, 621 ccache, 622 service_name, 623 realm_params, 624 struct_version, 625 api_version, 626 server_handle); 627 } 628 629 kadm5_ret_t 630 kadm5_c_init_with_creds(const char *client_name, 631 krb5_ccache ccache, 632 const char *service_name, 633 kadm5_config_params *realm_params, 634 unsigned long struct_version, 635 unsigned long api_version, 636 void **server_handle) 637 { 638 return init_context(client_name, 639 NULL, 640 NULL, 641 NULL, 642 ccache, 643 service_name, 644 realm_params, 645 struct_version, 646 api_version, 647 server_handle); 648 } 649 650 #if 0 651 kadm5_ret_t 652 kadm5_init(char *client_name, char *pass, 653 char *service_name, 654 kadm5_config_params *realm_params, 655 unsigned long struct_version, 656 unsigned long api_version, 657 void **server_handle) 658 { 659 } 660 #endif 661 662