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.35 2000/01/28 03:20:18 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); 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 kadm5_c_init_with_context(krb5_context context, 293 const char *client_name, 294 const char *password, 295 krb5_prompter_fct prompter, 296 const char *keytab, 297 krb5_ccache ccache, 298 const char *service_name, 299 kadm5_config_params *realm_params, 300 unsigned long struct_version, 301 unsigned long api_version, 302 void **server_handle) 303 { 304 kadm5_ret_t ret; 305 kadm5_client_context *ctx; 306 krb5_principal server; 307 krb5_ccache cc; 308 int s; 309 struct addrinfo *ai, *a; 310 struct addrinfo hints; 311 int error; 312 char portstr[NI_MAXSERV]; 313 char *hostname, *slash; 314 315 memset (&hints, 0, sizeof(hints)); 316 hints.ai_socktype = SOCK_STREAM; 317 hints.ai_protocol = IPPROTO_TCP; 318 319 ret = _kadm5_c_init_context(&ctx, realm_params, context); 320 if(ret) 321 return ret; 322 323 snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port)); 324 325 hostname = ctx->admin_server; 326 slash = strchr (hostname, '/'); 327 if (slash != NULL) 328 hostname = slash + 1; 329 330 error = getaddrinfo (hostname, portstr, &hints, &ai); 331 if (error) 332 return KADM5_BAD_SERVER_NAME; 333 334 for (a = ai; a != NULL; a = a->ai_next) { 335 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 336 if (s < 0) 337 continue; 338 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 339 krb5_warn (context, errno, "connect(%s)", hostname); 340 close (s); 341 continue; 342 } 343 break; 344 } 345 if (a == NULL) { 346 freeaddrinfo (ai); 347 krb5_warnx (context, "failed to contact %s", hostname); 348 return KADM5_FAILURE; 349 } 350 ret = get_cred_cache(context, client_name, service_name, 351 password, prompter, keytab, ccache, &cc); 352 353 if(ret) { 354 freeaddrinfo (ai); 355 close(s); 356 return ret; 357 } 358 ret = krb5_parse_name(context, KADM5_ADMIN_SERVICE, &server); 359 if(ret) { 360 freeaddrinfo (ai); 361 if(ccache == NULL) 362 krb5_cc_close(context, cc); 363 close(s); 364 return ret; 365 } 366 ctx->ac = NULL; 367 368 ret = krb5_sendauth(context, &ctx->ac, &s, 369 KADMIN_APPL_VERSION, NULL, 370 server, AP_OPTS_MUTUAL_REQUIRED, 371 NULL, NULL, cc, NULL, NULL, NULL); 372 if(ret == 0) { 373 krb5_data params, enc_data; 374 ret = _kadm5_marshal_params(context, realm_params, ¶ms); 375 376 ret = krb5_mk_priv(context, 377 ctx->ac, 378 ¶ms, 379 &enc_data, 380 NULL); 381 382 ret = krb5_write_message(context, &s, &enc_data); 383 384 krb5_data_free(¶ms); 385 krb5_data_free(&enc_data); 386 } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) { 387 close(s); 388 389 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 390 if (s < 0) { 391 freeaddrinfo (ai); 392 return errno; 393 } 394 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 395 close (s); 396 freeaddrinfo (ai); 397 return errno; 398 } 399 freeaddrinfo (ai); 400 401 ret = krb5_sendauth(context, &ctx->ac, &s, 402 KADMIN_OLD_APPL_VERSION, NULL, 403 server, AP_OPTS_MUTUAL_REQUIRED, 404 NULL, NULL, cc, NULL, NULL, NULL); 405 } 406 freeaddrinfo (ai); 407 if(ret) { 408 close(s); 409 return ret; 410 } 411 412 krb5_free_principal(context, server); 413 if(ccache == NULL) 414 krb5_cc_close(context, cc); 415 if(ret) { 416 close(s); 417 return ret; 418 } 419 ctx->sock = s; 420 *server_handle = ctx; 421 return 0; 422 } 423 424 static kadm5_ret_t 425 init_context(const char *client_name, 426 const char *password, 427 krb5_prompter_fct prompter, 428 const char *keytab, 429 krb5_ccache ccache, 430 const char *service_name, 431 kadm5_config_params *realm_params, 432 unsigned long struct_version, 433 unsigned long api_version, 434 void **server_handle) 435 { 436 krb5_context context; 437 kadm5_ret_t ret; 438 kadm5_server_context *ctx; 439 440 krb5_init_context(&context); 441 ret = kadm5_c_init_with_context(context, 442 client_name, 443 password, 444 prompter, 445 keytab, 446 ccache, 447 service_name, 448 realm_params, 449 struct_version, 450 api_version, 451 server_handle); 452 if(ret){ 453 krb5_free_context(context); 454 return ret; 455 } 456 ctx = *server_handle; 457 ctx->my_context = 1; 458 return 0; 459 } 460 461 kadm5_ret_t 462 kadm5_c_init_with_password_ctx(krb5_context context, 463 const char *client_name, 464 const char *password, 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 return kadm5_c_init_with_context(context, 472 client_name, 473 password, 474 krb5_prompter_posix, 475 NULL, 476 NULL, 477 service_name, 478 realm_params, 479 struct_version, 480 api_version, 481 server_handle); 482 } 483 484 kadm5_ret_t 485 kadm5_c_init_with_password(const char *client_name, 486 const char *password, 487 const char *service_name, 488 kadm5_config_params *realm_params, 489 unsigned long struct_version, 490 unsigned long api_version, 491 void **server_handle) 492 { 493 return init_context(client_name, 494 password, 495 krb5_prompter_posix, 496 NULL, 497 NULL, 498 service_name, 499 realm_params, 500 struct_version, 501 api_version, 502 server_handle); 503 } 504 505 kadm5_ret_t 506 kadm5_c_init_with_skey_ctx(krb5_context context, 507 const char *client_name, 508 const char *keytab, 509 const char *service_name, 510 kadm5_config_params *realm_params, 511 unsigned long struct_version, 512 unsigned long api_version, 513 void **server_handle) 514 { 515 return kadm5_c_init_with_context(context, 516 client_name, 517 NULL, 518 NULL, 519 keytab, 520 NULL, 521 service_name, 522 realm_params, 523 struct_version, 524 api_version, 525 server_handle); 526 } 527 528 529 kadm5_ret_t 530 kadm5_c_init_with_skey(const char *client_name, 531 const char *keytab, 532 const char *service_name, 533 kadm5_config_params *realm_params, 534 unsigned long struct_version, 535 unsigned long api_version, 536 void **server_handle) 537 { 538 return init_context(client_name, 539 NULL, 540 NULL, 541 keytab, 542 NULL, 543 service_name, 544 realm_params, 545 struct_version, 546 api_version, 547 server_handle); 548 } 549 550 kadm5_ret_t 551 kadm5_c_init_with_creds_ctx(krb5_context context, 552 const char *client_name, 553 krb5_ccache ccache, 554 const char *service_name, 555 kadm5_config_params *realm_params, 556 unsigned long struct_version, 557 unsigned long api_version, 558 void **server_handle) 559 { 560 return kadm5_c_init_with_context(context, 561 client_name, 562 NULL, 563 NULL, 564 NULL, 565 ccache, 566 service_name, 567 realm_params, 568 struct_version, 569 api_version, 570 server_handle); 571 } 572 573 kadm5_ret_t 574 kadm5_c_init_with_creds(const char *client_name, 575 krb5_ccache ccache, 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 init_context(client_name, 583 NULL, 584 NULL, 585 NULL, 586 ccache, 587 service_name, 588 realm_params, 589 struct_version, 590 api_version, 591 server_handle); 592 } 593 594 #if 0 595 kadm5_ret_t 596 kadm5_init(char *client_name, char *pass, 597 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 } 604 #endif 605 606