1 /* 2 * Copyright (c) 1997 - 2000 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.40 2000/12/31 08:00:23 assar 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 krb5_get_init_creds_opt_set_forwardable (&opt, FALSE); 149 krb5_get_init_creds_opt_set_proxiable (&opt, FALSE); 150 151 if(password == NULL && prompter == NULL) { 152 krb5_keytab kt; 153 if(keytab == NULL) 154 ret = krb5_kt_default(context, &kt); 155 else 156 ret = krb5_kt_resolve(context, keytab, &kt); 157 if(ret) 158 return ret; 159 ret = krb5_get_init_creds_keytab (context, 160 &cred, 161 client, 162 kt, 163 0, 164 server_name, 165 &opt); 166 krb5_kt_close(context, kt); 167 } else { 168 ret = krb5_get_init_creds_password (context, 169 &cred, 170 client, 171 password, 172 prompter, 173 NULL, 174 0, 175 server_name, 176 &opt); 177 } 178 switch(ret){ 179 case 0: 180 break; 181 case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */ 182 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 183 case KRB5KRB_AP_ERR_MODIFIED: 184 return KADM5_BAD_PASSWORD; 185 default: 186 return ret; 187 } 188 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &id); 189 if(ret) 190 return ret; 191 ret = krb5_cc_initialize (context, id, cred.client); 192 if (ret) 193 return ret; 194 ret = krb5_cc_store_cred (context, id, &cred); 195 if (ret) 196 return ret; 197 krb5_free_creds_contents (context, &cred); 198 *ret_cache = id; 199 return 0; 200 } 201 202 static krb5_error_code 203 get_cred_cache(krb5_context context, 204 const char *client_name, 205 const char *server_name, 206 const char *password, 207 krb5_prompter_fct prompter, 208 const char *keytab, 209 krb5_ccache ccache, 210 krb5_ccache *ret_cache) 211 { 212 krb5_error_code ret; 213 krb5_ccache id = NULL; 214 krb5_principal default_client = NULL, client = NULL; 215 216 /* treat empty password as NULL */ 217 if(password && *password == '\0') 218 password = NULL; 219 if(server_name == NULL) 220 server_name = KADM5_ADMIN_SERVICE; 221 222 if(client_name != NULL) { 223 ret = krb5_parse_name(context, client_name, &client); 224 if(ret) 225 return ret; 226 } 227 228 if(password != NULL || prompter != NULL) { 229 /* get principal from default cache, ok if this doesn't work */ 230 ret = krb5_cc_default(context, &id); 231 if(ret == 0) { 232 ret = krb5_cc_get_principal(context, id, &default_client); 233 if(ret) { 234 krb5_cc_close(context, id); 235 id = NULL; 236 } 237 } 238 239 if(client == NULL) 240 client = default_client; 241 if(client == NULL) { 242 const char *user; 243 244 user = get_default_username (); 245 246 if(user == NULL) 247 return KADM5_FAILURE; 248 ret = krb5_make_principal(context, &client, 249 NULL, user, "admin", NULL); 250 if(ret) 251 return ret; 252 } 253 if(client != default_client) { 254 krb5_free_principal(context, default_client); 255 default_client = NULL; 256 if (id != NULL) { 257 krb5_cc_close(context, id); 258 id = NULL; 259 } 260 } 261 } else if(ccache != NULL) 262 id = ccache; 263 264 265 if(id && (default_client == NULL || 266 krb5_principal_compare(context, client, default_client))) { 267 ret = get_kadm_ticket(context, id, client, server_name); 268 if(ret == 0) { 269 *ret_cache = id; 270 krb5_free_principal(context, default_client); 271 if (default_client != client) 272 krb5_free_principal(context, client); 273 return 0; 274 } 275 if(ccache != NULL) 276 /* couldn't get ticket from cache */ 277 return -1; 278 } 279 /* get creds via AS request */ 280 if(id) 281 krb5_cc_close(context, id); 282 if (client != default_client) 283 krb5_free_principal(context, default_client); 284 285 ret = get_new_cache(context, client, password, prompter, keytab, 286 server_name, ret_cache); 287 krb5_free_principal(context, client); 288 return ret; 289 } 290 291 static kadm5_ret_t 292 kadm_connect(kadm5_client_context *ctx) 293 { 294 kadm5_ret_t ret; 295 krb5_principal server; 296 krb5_ccache cc; 297 int s; 298 struct addrinfo *ai, *a; 299 struct addrinfo hints; 300 int error; 301 char portstr[NI_MAXSERV]; 302 char *hostname, *slash; 303 krb5_context context = ctx->context; 304 305 memset (&hints, 0, sizeof(hints)); 306 hints.ai_socktype = SOCK_STREAM; 307 hints.ai_protocol = IPPROTO_TCP; 308 309 snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port)); 310 311 hostname = ctx->admin_server; 312 slash = strchr (hostname, '/'); 313 if (slash != NULL) 314 hostname = slash + 1; 315 316 error = getaddrinfo (hostname, portstr, &hints, &ai); 317 if (error) 318 return KADM5_BAD_SERVER_NAME; 319 320 for (a = ai; a != NULL; a = a->ai_next) { 321 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 322 if (s < 0) 323 continue; 324 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 325 krb5_warn (context, errno, "connect(%s)", hostname); 326 close (s); 327 continue; 328 } 329 break; 330 } 331 if (a == NULL) { 332 freeaddrinfo (ai); 333 krb5_warnx (context, "failed to contact %s", hostname); 334 return KADM5_FAILURE; 335 } 336 ret = get_cred_cache(context, ctx->client_name, ctx->service_name, 337 NULL, ctx->prompter, ctx->keytab, 338 ctx->ccache, &cc); 339 340 if(ret) { 341 freeaddrinfo (ai); 342 close(s); 343 return ret; 344 } 345 ret = krb5_parse_name(context, KADM5_ADMIN_SERVICE, &server); 346 if(ret) { 347 freeaddrinfo (ai); 348 if(ctx->ccache == NULL) 349 krb5_cc_close(context, cc); 350 close(s); 351 return ret; 352 } 353 ctx->ac = NULL; 354 355 ret = krb5_sendauth(context, &ctx->ac, &s, 356 KADMIN_APPL_VERSION, NULL, 357 server, AP_OPTS_MUTUAL_REQUIRED, 358 NULL, NULL, cc, NULL, NULL, NULL); 359 if(ret == 0) { 360 krb5_data params; 361 ret = _kadm5_marshal_params(context, ctx->realm_params, ¶ms); 362 363 ret = krb5_write_priv_message(context, ctx->ac, &s, ¶ms); 364 krb5_data_free(¶ms); 365 if(ret) { 366 freeaddrinfo (ai); 367 close(s); 368 if(ctx->ccache == NULL) 369 krb5_cc_close(context, cc); 370 return ret; 371 } 372 } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) { 373 close(s); 374 375 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 376 if (s < 0) { 377 freeaddrinfo (ai); 378 return errno; 379 } 380 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 381 close (s); 382 freeaddrinfo (ai); 383 return errno; 384 } 385 ret = krb5_sendauth(context, &ctx->ac, &s, 386 KADMIN_OLD_APPL_VERSION, NULL, 387 server, AP_OPTS_MUTUAL_REQUIRED, 388 NULL, NULL, cc, NULL, NULL, NULL); 389 } 390 freeaddrinfo (ai); 391 if(ret) { 392 close(s); 393 return ret; 394 } 395 396 krb5_free_principal(context, server); 397 if(ctx->ccache == NULL) 398 krb5_cc_close(context, cc); 399 if(ret) { 400 close(s); 401 return ret; 402 } 403 ctx->sock = s; 404 405 return 0; 406 } 407 408 kadm5_ret_t 409 _kadm5_connect(void *handle) 410 { 411 kadm5_client_context *ctx = handle; 412 if(ctx->sock == -1) 413 return kadm_connect(ctx); 414 return 0; 415 } 416 417 static kadm5_ret_t 418 kadm5_c_init_with_context(krb5_context context, 419 const char *client_name, 420 const char *password, 421 krb5_prompter_fct prompter, 422 const char *keytab, 423 krb5_ccache ccache, 424 const char *service_name, 425 kadm5_config_params *realm_params, 426 unsigned long struct_version, 427 unsigned long api_version, 428 void **server_handle) 429 { 430 kadm5_ret_t ret; 431 kadm5_client_context *ctx; 432 krb5_ccache cc; 433 434 ret = _kadm5_c_init_context(&ctx, realm_params, context); 435 if(ret) 436 return ret; 437 438 if(password != NULL && *password != '\0') { 439 ret = get_cred_cache(context, client_name, service_name, 440 password, prompter, keytab, ccache, &cc); 441 if(ret) 442 return ret; /* XXX */ 443 ccache = cc; 444 } 445 446 447 if (client_name != NULL) 448 ctx->client_name = strdup(client_name); 449 else 450 ctx->client_name = NULL; 451 if (service_name != NULL) 452 ctx->service_name = strdup(service_name); 453 else 454 ctx->service_name = NULL; 455 ctx->prompter = prompter; 456 ctx->keytab = keytab; 457 ctx->ccache = ccache; 458 ctx->realm_params = realm_params; 459 ctx->sock = -1; 460 461 *server_handle = ctx; 462 return 0; 463 } 464 465 static kadm5_ret_t 466 init_context(const char *client_name, 467 const char *password, 468 krb5_prompter_fct prompter, 469 const char *keytab, 470 krb5_ccache ccache, 471 const char *service_name, 472 kadm5_config_params *realm_params, 473 unsigned long struct_version, 474 unsigned long api_version, 475 void **server_handle) 476 { 477 krb5_context context; 478 kadm5_ret_t ret; 479 kadm5_server_context *ctx; 480 481 ret = krb5_init_context(&context); 482 if (ret) 483 return ret; 484 ret = kadm5_c_init_with_context(context, 485 client_name, 486 password, 487 prompter, 488 keytab, 489 ccache, 490 service_name, 491 realm_params, 492 struct_version, 493 api_version, 494 server_handle); 495 if(ret){ 496 krb5_free_context(context); 497 return ret; 498 } 499 ctx = *server_handle; 500 ctx->my_context = 1; 501 return 0; 502 } 503 504 kadm5_ret_t 505 kadm5_c_init_with_password_ctx(krb5_context context, 506 const char *client_name, 507 const char *password, 508 const char *service_name, 509 kadm5_config_params *realm_params, 510 unsigned long struct_version, 511 unsigned long api_version, 512 void **server_handle) 513 { 514 return kadm5_c_init_with_context(context, 515 client_name, 516 password, 517 krb5_prompter_posix, 518 NULL, 519 NULL, 520 service_name, 521 realm_params, 522 struct_version, 523 api_version, 524 server_handle); 525 } 526 527 kadm5_ret_t 528 kadm5_c_init_with_password(const char *client_name, 529 const char *password, 530 const char *service_name, 531 kadm5_config_params *realm_params, 532 unsigned long struct_version, 533 unsigned long api_version, 534 void **server_handle) 535 { 536 return init_context(client_name, 537 password, 538 krb5_prompter_posix, 539 NULL, 540 NULL, 541 service_name, 542 realm_params, 543 struct_version, 544 api_version, 545 server_handle); 546 } 547 548 kadm5_ret_t 549 kadm5_c_init_with_skey_ctx(krb5_context context, 550 const char *client_name, 551 const char *keytab, 552 const char *service_name, 553 kadm5_config_params *realm_params, 554 unsigned long struct_version, 555 unsigned long api_version, 556 void **server_handle) 557 { 558 return kadm5_c_init_with_context(context, 559 client_name, 560 NULL, 561 NULL, 562 keytab, 563 NULL, 564 service_name, 565 realm_params, 566 struct_version, 567 api_version, 568 server_handle); 569 } 570 571 572 kadm5_ret_t 573 kadm5_c_init_with_skey(const char *client_name, 574 const char *keytab, 575 const char *service_name, 576 kadm5_config_params *realm_params, 577 unsigned long struct_version, 578 unsigned long api_version, 579 void **server_handle) 580 { 581 return init_context(client_name, 582 NULL, 583 NULL, 584 keytab, 585 NULL, 586 service_name, 587 realm_params, 588 struct_version, 589 api_version, 590 server_handle); 591 } 592 593 kadm5_ret_t 594 kadm5_c_init_with_creds_ctx(krb5_context context, 595 const char *client_name, 596 krb5_ccache ccache, 597 const char *service_name, 598 kadm5_config_params *realm_params, 599 unsigned long struct_version, 600 unsigned long api_version, 601 void **server_handle) 602 { 603 return kadm5_c_init_with_context(context, 604 client_name, 605 NULL, 606 NULL, 607 NULL, 608 ccache, 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_creds(const char *client_name, 618 krb5_ccache ccache, 619 const char *service_name, 620 kadm5_config_params *realm_params, 621 unsigned long struct_version, 622 unsigned long api_version, 623 void **server_handle) 624 { 625 return init_context(client_name, 626 NULL, 627 NULL, 628 NULL, 629 ccache, 630 service_name, 631 realm_params, 632 struct_version, 633 api_version, 634 server_handle); 635 } 636 637 #if 0 638 kadm5_ret_t 639 kadm5_init(char *client_name, char *pass, 640 char *service_name, 641 kadm5_config_params *realm_params, 642 unsigned long struct_version, 643 unsigned long api_version, 644 void **server_handle) 645 { 646 } 647 #endif 648 649