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