1/* 2 * Copyright (c) 2008 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "kadm5_locl.h" 37 38#include <syslog.h> 39#include <gssapi_krb5.h> 40 41#define CHECK(x) \ 42 do { \ 43 int __r; \ 44 if ((__r = (x))) { \ 45 syslog(LOG_ERR, "kadmin: protocol error:%d", __LINE__); \ 46 _exit(1); \ 47 } \ 48 } while(0) 49 50#define INSIST(x) CHECK(!(x)) 51 52#define VERSION2 0x12345702 53 54#define LAST_FRAGMENT 0x80000000 55 56#define RPC_VERSION 2 57#define KADM_SERVER 2112 58#define VVERSION 2 59#define FLAVOR_GSS 6 60#define FLAVOR_GSS_VERSION 1 61 62#define PROC_NULL 0 63#define PROC_CREATE_PRINCIPAL 1 64#define PROC_DELETE_PRINCIPAL 2 65#define PROC_MODIFY_PRINCIPAL 3 66#define PROC_GET_PRINCIPAL 5 67#define PROC_CHRAND_PRINCIPAL 7 68#define PROC_CREATE_PRINCIPAL3 18 69 70 71struct call_header { 72 uint32_t xid; 73 uint32_t rpcvers; 74 uint32_t prog; 75 uint32_t vers; 76 uint32_t proc; 77 struct _kadm5_xdr_opaque_auth cred; 78 struct _kadm5_xdr_opaque_auth verf; 79}; 80 81enum { 82 RPG_DATA = 0, 83 RPG_INIT = 1, 84 RPG_CONTINUE_INIT = 2, 85 RPG_DESTROY = 3 86}; 87 88enum { 89 rpg_privacy = 3 90}; 91 92#if 0 93static void 94gss_error(krb5_context context, 95 gss_OID mech, OM_uint32 type, OM_uint32 error) 96{ 97 OM_uint32 new_stat; 98 OM_uint32 msg_ctx = 0; 99 gss_buffer_desc status_string; 100 OM_uint32 ret; 101 102 do { 103 ret = gss_display_status (&new_stat, 104 error, 105 type, 106 mech, 107 &msg_ctx, 108 &status_string); 109 krb5_warnx(context, "%.*s", 110 (int)status_string.length, 111 (char *)status_string.value); 112 gss_release_buffer (&new_stat, &status_string); 113 } while (!GSS_ERROR(ret) && msg_ctx != 0); 114} 115 116static void 117gss_print_errors (krb5_context context, 118 OM_uint32 maj_stat, OM_uint32 min_stat) 119{ 120 gss_error(context, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat); 121 gss_error(context, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat); 122} 123#endif 124 125static int 126read_data(krb5_storage *sp, krb5_storage *msg, size_t len) 127{ 128 char buf[1024]; 129 130 while (len) { 131 size_t tlen = len; 132 ssize_t slen; 133 134 if (tlen > sizeof(buf)) 135 tlen = sizeof(buf); 136 137 slen = krb5_storage_read(sp, buf, tlen); 138 INSIST(slen >= 0 && (size_t)slen == tlen); 139 140 slen = krb5_storage_write(msg, buf, tlen); 141 INSIST(slen >= 0 || (size_t)slen == tlen); 142 143 len -= tlen; 144 } 145 return 0; 146} 147 148static int 149collect_fragments(krb5_storage *sp, krb5_storage *msg) 150{ 151 krb5_error_code ret; 152 uint32_t len; 153 int last_fragment; 154 size_t total_len = 0; 155 156 do { 157 ret = krb5_ret_uint32(sp, &len); 158 if (ret) 159 return ret; 160 161 last_fragment = (len & LAST_FRAGMENT); 162 len &= ~LAST_FRAGMENT; 163 164 CHECK(read_data(sp, msg, len)); 165 total_len += len; 166 167 } while(!last_fragment || total_len == 0); 168 169 return 0; 170} 171 172struct grpc_client { 173 krb5_data handle; 174 krb5_data last_cred; 175 gss_ctx_id_t ctx; 176 krb5_storage *sp; 177 uint32_t seq_num; 178 uint32_t xid; 179 int done; 180 int inprogress; 181}; 182 183static krb5_error_code 184xdr_close_connection(krb5_context context, 185 struct grpc_client *client) 186{ 187 free(client); 188 return 0; 189} 190 191static krb5_error_code 192xdr_send_request(krb5_context context, 193 struct grpc_client *client, 194 uint32_t xid, 195 uint32_t proc, 196 krb5_data *out) 197{ 198 struct _kadm5_xdr_opaque_auth cred, verf; 199 struct _kadm5_xdr_gcred gcred; 200 OM_uint32 maj_stat, min_stat; 201 gss_buffer_desc gin, gout; 202 krb5_storage *msg; 203 krb5_data data; 204 size_t sret; 205 206 msg = krb5_storage_emem(); 207 208 memset(&gcred, 0, sizeof(gcred)); 209 memset(&cred, 0, sizeof(cred)); 210 memset(&verf, 0, sizeof(verf)); 211 212 cred.flavor = FLAVOR_GSS; 213 cred.data.data = NULL; 214 cred.data.length = 0; 215 216 gcred.version = FLAVOR_GSS_VERSION; 217 if (client->done) 218 gcred.proc = RPG_DATA; 219 else 220 gcred.proc = RPG_INIT; 221 gcred.service = rpg_privacy; 222 gcred.handle = client->handle; 223 gcred.seq_num = client->seq_num; 224 225 CHECK(_kadm5_xdr_store_gcred(&gcred, &cred.data)); 226 227 CHECK(krb5_store_uint32(msg, xid)); 228 CHECK(krb5_store_uint32(msg, 0)); /* mtype ? */ 229 CHECK(krb5_store_uint32(msg, RPC_VERSION)); 230 CHECK(krb5_store_uint32(msg, KADM_SERVER)); 231 CHECK(krb5_store_uint32(msg, VVERSION)); 232 CHECK(krb5_store_uint32(msg, proc)); 233 CHECK(_kadm5_xdr_store_auth_opaque(msg, &cred)); 234 235 /* create header verf */ 236 if (client->done) { 237 krb5_storage_to_data(msg, &data); 238 239 gin.value = data.data; 240 gin.length = data.length; 241 242 maj_stat = gss_get_mic(&min_stat, client->ctx, 0, &gin, &gout); 243 krb5_data_free(&data); 244 INSIST(maj_stat == GSS_S_COMPLETE); 245 246 verf.flavor = FLAVOR_GSS; 247 verf.data.data = gout.value; 248 verf.data.length = gout.length; 249 250 CHECK(_kadm5_xdr_store_auth_opaque(msg, &verf)); 251 gss_release_buffer(&min_stat, &gout); 252 } else { 253 verf.flavor = FLAVOR_GSS; 254 verf.data.data = NULL; 255 verf.data.length = 0; 256 CHECK(_kadm5_xdr_store_auth_opaque(msg, &verf)); 257 } 258 259 if (!client->done) { 260 sret = krb5_storage_write(msg, out->data, out->length); 261 INSIST(sret == out->length); 262 } else if (client->inprogress) { 263 client->inprogress = 0; 264 sret = krb5_storage_write(msg, out->data, out->length); 265 INSIST(sret == out->length); 266 } else { 267 krb5_storage *reply; 268 int conf_state; 269 270 reply = krb5_storage_emem(); 271 272 krb5_store_uint32(reply, client->seq_num); 273 sret = krb5_storage_write(reply, out->data, out->length); 274 INSIST(sret == out->length); 275 276 krb5_storage_to_data(reply, &data); 277 krb5_storage_free(reply); 278 279 gin.value = data.data; 280 gin.length = data.length; 281 282 maj_stat = gss_wrap(&min_stat, client->ctx, 1, 0, 283 &gin, &conf_state, &gout); 284 INSIST(maj_stat == GSS_S_COMPLETE); 285 INSIST(conf_state != 0); 286 krb5_data_free(&data); 287 288 data.data = gout.value; 289 data.length = gout.length; 290 291 CHECK(_kadm5_xdr_store_data_xdr(msg, data)); 292 gss_release_buffer(&min_stat, &gout); 293 } 294 295 /* write packet to output stream */ 296 { 297 CHECK(krb5_storage_to_data(msg, &data)); 298 CHECK(krb5_store_uint32(client->sp, ((uint32_t)data.length) | LAST_FRAGMENT)); 299 sret = krb5_storage_write(client->sp, data.data, data.length); 300 INSIST(sret == data.length); 301 krb5_data_free(&data); 302 } 303 304 return 0; 305} 306 307static krb5_error_code 308xdr_recv_reply(krb5_context context, 309 struct grpc_client *client, 310 uint32_t *xid, 311 krb5_storage **out) 312{ 313 OM_uint32 maj_stat, min_stat; 314 gss_buffer_desc gin, gout; 315 krb5_error_code ret; 316 krb5_storage *reply; 317 krb5_data data; 318 int conf_state; 319 uint32_t tmp; 320 321 reply = krb5_storage_emem(); 322 INSIST(reply != NULL); 323 324 ret = collect_fragments(client->sp, reply); 325 INSIST(ret == 0); 326 327 krb5_storage_seek(reply, 0, SEEK_SET); 328 329 CHECK(krb5_ret_uint32(reply, xid)); 330 CHECK(krb5_ret_uint32(reply, &tmp)); /* REPLY */ 331 INSIST(tmp == 1); 332 CHECK(krb5_ret_uint32(reply, &tmp)); /* MSG_ACCEPTED */ 333 INSIST(tmp == 0); 334 335 336 CHECK(krb5_ret_uint32(reply, &tmp)); /* flavor_gss */ 337 INSIST(tmp == FLAVOR_GSS); 338 339 CHECK(_kadm5_xdr_ret_data_xdr(reply, &data)); 340 341 if (client->done) { 342 uint32_t seqnum = htonl(client->seq_num); 343 344 gin.value = &seqnum; 345 gin.length = sizeof(seqnum); 346 gout.value = data.data; 347 gout.length = data.length; 348 349 maj_stat = gss_verify_mic(&min_stat, client->ctx, &gin, &gout, NULL); 350 krb5_data_free(&data); 351 INSIST(maj_stat == GSS_S_COMPLETE); 352 } else { 353 krb5_data_free(&client->last_cred); 354 client->last_cred = data; 355 } 356 CHECK(krb5_ret_uint32(reply, &tmp)); /* SUCCESS */ 357 INSIST(tmp == 0); 358 359 if (client->done) { 360 /* read body */ 361 CHECK(_kadm5_xdr_ret_data_xdr(reply, &data)); 362 363 krb5_storage_free(reply); 364 365 gin.value = data.data; 366 gin.length = data.length; 367 368 maj_stat = gss_unwrap(&min_stat, client->ctx, &gin, &gout, 369 &conf_state, NULL); 370 krb5_data_free(&data); 371 INSIST(maj_stat == GSS_S_COMPLETE); 372 INSIST(conf_state != 0); 373 374 /* make reply cleartext */ 375 *out = krb5_storage_from_mem_copy(gout.value, gout.length); 376 INSIST(*out != NULL); 377 378 /* check seq num */ 379 CHECK(krb5_ret_uint32(*out, &tmp)); 380 INSIST(tmp == client->seq_num); 381 382 gss_release_buffer(&min_stat, &gout); 383 } else { 384 *out = reply; 385 } 386 387 return 0; 388} 389 390static krb5_error_code 391xdr_process_request(krb5_context context, 392 struct grpc_client *client, 393 uint32_t proc, 394 krb5_data *in, 395 krb5_storage **out) 396{ 397 krb5_error_code ret; 398 uint32_t xid; 399 400 client->xid++; 401 client->seq_num++; 402 403 ret = xdr_send_request(context, client, client->xid, proc, in); 404 if (ret) 405 return ret; 406 ret = xdr_recv_reply(context, client, &xid, out); 407 if (ret == 0) { 408 INSIST(client->xid == xid); 409 } 410 return ret; 411} 412 413static kadm5_ret_t 414kadm5_mit_chpass_principal(void *server_handle, 415 krb5_principal principal, 416 int keepold, 417 const char *password, 418 int n_ks_tuple, 419 krb5_key_salt_tuple *ks_tuple) 420{ 421 kadm5_mit_context *context = server_handle; 422 krb5_set_error_message(context->context, KADM5_RPC_ERROR, 423 "Function not implemented"); 424 return KADM5_RPC_ERROR; 425} 426 427static kadm5_ret_t 428kadm5_mit_chpass_principal_with_key(void *server_handle, 429 krb5_principal princ, 430 int keepold, 431 int n_key_data, 432 krb5_key_data *key_data) 433{ 434 kadm5_mit_context *context = server_handle; 435 krb5_set_error_message(context->context, KADM5_RPC_ERROR, 436 "Function not implemented"); 437 return KADM5_RPC_ERROR; 438} 439 440static kadm5_ret_t 441kadm5_mit_create_principal(void *server_handle, 442 kadm5_principal_ent_t entry, 443 uint32_t mask, 444 const char *password, 445 int n_ks_tuple, 446 krb5_key_salt_tuple *ks_tuple) 447{ 448 kadm5_mit_context *context = server_handle; 449 krb5_storage *sp = NULL; 450 krb5_error_code ret; 451 uint32_t retcode; 452 krb5_data in; 453 454 /* Build request */ 455 456 sp = krb5_storage_emem(); 457 458 CHECK(krb5_store_uint32(sp, VERSION2)); 459 CHECK(_kadm5_xdr_store_principal_ent(context->context, sp, entry)); 460 CHECK(krb5_store_uint32(sp, mask)); 461 CHECK(_kadm5_xdr_store_string_xdr(sp, password)); 462 463 krb5_storage_to_data(sp, &in); 464 krb5_storage_free(sp); 465 466 ret = xdr_process_request(context->context, context->gsscontext, 467 PROC_CREATE_PRINCIPAL, &in, &sp); 468 krb5_data_free(&in); 469 if (ret) 470 return ret; 471 472 /* Read reply */ 473 474 CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */ 475 INSIST(retcode == VERSION2); 476 CHECK(krb5_ret_uint32(sp, &retcode)); /* code */ 477 if (retcode == 0) { 478#if 0 479 CHECK(_kadm5_xdr_ret_principal_ent(context->context, out, ent)); 480#endif 481 } 482 krb5_storage_free(sp); 483 484 return (krb5_error_code)retcode; 485} 486 487static kadm5_ret_t 488kadm5_mit_delete_principal(void *server_handle, krb5_principal principal) 489{ 490 kadm5_mit_context *context = server_handle; 491 krb5_storage *sp = NULL; 492 krb5_error_code ret; 493 uint32_t retcode; 494 krb5_data in; 495 496 /* Build request */ 497 498 sp = krb5_storage_emem(); 499 500 CHECK(krb5_store_uint32(sp, VERSION2)); 501 CHECK(_kadm5_xdr_store_principal_xdr(context->context, sp, principal)); 502 503 krb5_storage_to_data(sp, &in); 504 krb5_storage_free(sp); 505 506 ret = xdr_process_request(context->context, context->gsscontext, 507 PROC_DELETE_PRINCIPAL, &in, &sp); 508 krb5_data_free(&in); 509 if (ret) 510 return ret; 511 512 /* Read reply */ 513 514 CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */ 515 INSIST(retcode == VERSION2); 516 CHECK(krb5_ret_uint32(sp, &retcode)); /* code */ 517 krb5_storage_free(sp); 518 519 return (krb5_error_code)retcode; 520} 521 522static kadm5_ret_t 523kadm5_mit_flush(void *server_handle) 524{ 525 kadm5_mit_context *context = server_handle; 526 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); 527 return KADM5_RPC_ERROR; 528} 529 530static kadm5_ret_t 531kadm5_mit_destroy(void *server_handle) 532{ 533 kadm5_mit_context *context = server_handle; 534 535 krb5_free_principal(context->context, context->caller); 536 xdr_close_connection(context->context, context->gsscontext); 537 if(context->my_context) 538 krb5_free_context(context->context); 539 return 0; 540} 541 542static kadm5_ret_t 543kadm5_mit_get_principal(void *server_handle, 544 krb5_principal principal, 545 kadm5_principal_ent_t entry, 546 uint32_t mask) 547{ 548 kadm5_mit_context *context = server_handle; 549 krb5_storage *sp = NULL; 550 krb5_error_code ret; 551 uint32_t retcode; 552 krb5_data in; 553 554 /* Build request */ 555 556 sp = krb5_storage_emem(); 557 558 CHECK(krb5_store_uint32(sp, VERSION2)); 559 CHECK(_kadm5_xdr_store_principal_xdr(context->context, sp, principal)); 560 CHECK(krb5_store_uint32(sp, mask)); 561 562 krb5_storage_to_data(sp, &in); 563 krb5_storage_free(sp); 564 565 ret = xdr_process_request(context->context, context->gsscontext, 566 PROC_GET_PRINCIPAL, &in, &sp); 567 krb5_data_free(&in); 568 if (ret) 569 return ret; 570 571 /* Read reply */ 572 573 CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */ 574 INSIST(retcode == VERSION2); 575 CHECK(krb5_ret_uint32(sp, &retcode)); /* code */ 576 if (retcode == 0) { 577 CHECK(_kadm5_xdr_ret_principal_ent(context->context, sp, entry)); 578 } 579 krb5_storage_free(sp); 580 581 return (krb5_error_code)retcode; 582} 583 584static kadm5_ret_t 585kadm5_mit_get_principals(void *server_handle, 586 const char *expression, 587 char ***principals, 588 int *count) 589{ 590 kadm5_mit_context *context = server_handle; 591 krb5_set_error_message(context->context, KADM5_RPC_ERROR, 592 "Function not implemented"); 593 return KADM5_RPC_ERROR; 594} 595 596static kadm5_ret_t 597kadm5_mit_get_privs(void *server_handle, uint32_t*privs) 598{ 599 kadm5_mit_context *context = server_handle; 600 krb5_set_error_message(context->context, KADM5_RPC_ERROR, 601 "Function not implemented"); 602 return KADM5_RPC_ERROR; 603} 604 605static kadm5_ret_t 606kadm5_mit_modify_principal(void *server_handle, 607 kadm5_principal_ent_t entry, 608 uint32_t mask) 609{ 610 kadm5_mit_context *context = server_handle; 611 krb5_storage *sp = NULL; 612 krb5_error_code ret; 613 uint32_t retcode; 614 krb5_data in; 615 616 /* Build request */ 617 618 sp = krb5_storage_emem(); 619 620 CHECK(krb5_store_uint32(sp, VERSION2)); 621 CHECK(_kadm5_xdr_store_principal_ent(context->context, sp, entry)); 622 CHECK(krb5_store_uint32(sp, mask)); 623 624 krb5_storage_to_data(sp, &in); 625 krb5_storage_free(sp); 626 627 ret = xdr_process_request(context->context, context->gsscontext, 628 PROC_MODIFY_PRINCIPAL, &in, &sp); 629 krb5_data_free(&in); 630 if (ret) 631 return ret; 632 633 /* Read reply */ 634 635 CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */ 636#if 0 /* modify doesn't set api version */ 637 INSIST(retcode == VERSION2); 638#endif 639 CHECK(krb5_ret_uint32(sp, &retcode)); /* code */ 640 krb5_storage_free(sp); 641 642 return (krb5_error_code)retcode; 643} 644 645static kadm5_ret_t 646kadm5_mit_rename_principal(void *server_handle, 647 krb5_principal from, 648 krb5_principal to) 649{ 650 kadm5_mit_context *context = server_handle; 651 krb5_set_error_message(context->context, KADM5_RPC_ERROR, 652 "Function not implemented"); 653 return KADM5_RPC_ERROR; 654} 655 656static kadm5_ret_t 657kadm5_mit_randkey_principal(void *server_handle, 658 krb5_principal principal, 659 krb5_boolean keepold, 660 int n_ks_tuple, 661 krb5_key_salt_tuple *ks_tuple, 662 krb5_keyblock **keys, 663 int *n_keys) 664{ 665 kadm5_mit_context *context = server_handle; 666 krb5_storage *sp = NULL; 667 krb5_error_code ret; 668 uint32_t retcode; 669 krb5_data in; 670 671 /* Build request */ 672 673 sp = krb5_storage_emem(); 674 675 CHECK(krb5_store_uint32(sp, VERSION2)); 676 CHECK(_kadm5_xdr_store_principal_xdr(context->context, sp, principal)); 677 678 krb5_storage_to_data(sp, &in); 679 krb5_storage_free(sp); 680 681 ret = xdr_process_request(context->context, context->gsscontext, 682 PROC_CHRAND_PRINCIPAL, &in, &sp); 683 krb5_data_free(&in); 684 if (ret) 685 return ret; 686 687 /* Read reply */ 688 689 CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */ 690 INSIST(retcode == VERSION2); 691 CHECK(krb5_ret_uint32(sp, &retcode)); /* code */ 692 if (retcode == 0) { 693 uint32_t enctype, num; 694 int i; 695 696 CHECK(krb5_ret_uint32(sp, &num)); 697 CHECK(num < 2000); 698 699 *n_keys = num; 700 701 *keys = calloc(num, sizeof((*keys)[0])); 702 for (i = 0; i < *n_keys; i++) { 703 CHECK(krb5_ret_uint32(sp, &enctype)); 704 (*keys)[i].keytype = enctype; 705 CHECK(_kadm5_xdr_ret_data_xdr(sp, &(*keys)[i].keyvalue)); 706 } 707 } 708 krb5_storage_free(sp); 709 710 return (krb5_error_code)retcode; 711} 712 713/* 714 * 715 */ 716 717static void 718verify_seq_num(struct grpc_client *c, uint32_t seq, krb5_data *cred) 719{ 720 OM_uint32 maj_stat, min_stat; 721 gss_buffer_desc gin, gout; 722 723 seq = htonl(seq); 724 725 gin.value = &seq; 726 gin.length = sizeof(seq); 727 gout.value = cred->data; 728 gout.length = cred->length; 729 730 INSIST(cred->length != 0); 731 732 maj_stat = gss_verify_mic(&min_stat, c->ctx, &gin, &gout, NULL); 733 krb5_data_free(&c->last_cred); 734 INSIST(maj_stat == GSS_S_COMPLETE); 735} 736 737 738static krb5_error_code 739xdr_setup_connection(krb5_context context, 740 const char *service, 741 const char *hostname, 742 unsigned port, 743 struct grpc_client **client) 744{ 745 struct addrinfo *ai, *a, hints; 746 char portstr[NI_MAXSERV]; 747 struct grpc_client *c; 748 krb5_error_code ret; 749 int error, s = -1; 750 OM_uint32 maj_stat, maj_stat2, min_stat, ret_flags; 751 gss_buffer_desc gin, gout; 752 gss_name_t target; 753 int try_kadmin_admin = 0; 754 uint32_t seq_window = 0; 755 756 c = calloc(1, sizeof(*c)); 757 if (c == NULL) 758 return ENOMEM; 759 760 memset (&hints, 0, sizeof(hints)); 761 hints.ai_socktype = SOCK_STREAM; 762 hints.ai_protocol = IPPROTO_TCP; 763 764 snprintf (portstr, sizeof(portstr), "%u", ntohs(port)); 765 766 error = getaddrinfo (hostname, portstr, &hints, &ai); 767 if (error) 768 return KADM5_BAD_SERVER_NAME; 769 770 for (a = ai; a != NULL; a = a->ai_next) { 771 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 772 if (s < 0) 773 continue; 774 socket_set_nopipe(s, 1); 775 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 776 close (s); 777 continue; 778 } 779 break; 780 } 781 if (a == NULL || s < 0) { 782 freeaddrinfo (ai); 783 return KADM5_FAILURE; 784 } 785 786 c->sp = krb5_storage_from_fd(s); 787 close(s); 788 789 try_again: 790 { 791 char *str; 792 if (!try_kadmin_admin) 793 asprintf(&str, "%s@%s", service, hostname); 794 else 795 asprintf(&str, "kadmin@admin"); 796 gin.value = str; 797 gin.length = strlen(str); 798 maj_stat = gss_import_name(&min_stat, &gin, GSS_C_NT_HOSTBASED_SERVICE, &target); 799 if (maj_stat != GSS_S_COMPLETE) { 800 krb5_set_error_message(context, ENOENT, 801 "failed to import name: %s", str); 802 free(str); 803 return ENOENT; 804 } 805 free(str); 806 } 807 808 809 c->inprogress = 1; 810 gin.value = NULL; 811 gin.length = 0; 812 813 /* do gss-api negotiation dance here */ 814 while (1) { 815 krb5_storage *out, *in = krb5_storage_emem(); 816 krb5_data data; 817 818 maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, 819 &c->ctx, target, GSS_KRB5_MECHANISM, 820 GSS_C_MUTUAL_FLAG|GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG, 821 GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, 822 &gin, NULL, &gout, &ret_flags, NULL); 823 if (gin.value) { 824 free(gin.value); 825 gin.value = NULL; 826 } 827 828 if (GSS_ERROR(maj_stat)) { 829 krb5_storage_free(in); 830 if (!try_kadmin_admin) { 831 try_kadmin_admin = 1; 832 goto try_again; 833 } 834 krb5_set_error_message(context, EINVAL, 835 "init_sec_context failed with %d/%d", 836 maj_stat, min_stat); 837 return EINVAL; 838 } else if (maj_stat & GSS_S_CONTINUE_NEEDED) { 839 INSIST(!c->done); 840 } else { 841 INSIST(maj_stat == GSS_S_COMPLETE); 842 INSIST((ret_flags & GSS_C_MUTUAL_FLAG) != 0); 843 if (c->done) { 844 verify_seq_num(c, seq_window, &c->last_cred); 845 krb5_data_free(&c->last_cred); 846 break; 847 } 848 849 c->done = 1; 850 } 851 852 data.data = gout.value; 853 data.length = gout.length; 854 855 CHECK(_kadm5_xdr_store_data_xdr(in, data)); 856 gss_release_buffer(&min_stat, &gout); 857 krb5_storage_to_data(in, &data); 858 krb5_storage_free(in); 859 860 ret = xdr_process_request(context, c, PROC_NULL, &data, &out); 861 krb5_data_free(&data); 862 if (ret) 863 return ret; 864 865 CHECK(_kadm5_xdr_ret_gss_init_res(out, &c->handle, 866 &maj_stat2, &min_stat, 867 &seq_window, &data)); 868 krb5_storage_free(out); 869 870 gin.length = data.length; 871 gin.value = data.data; 872 873 if (GSS_ERROR(maj_stat2)) { 874 krb5_data_free(&data); 875 krb5_set_error_message(context, EINVAL, 876 "server sent a failure code: %d/%d", 877 maj_stat2, min_stat); 878 return EINVAL; 879 } else if (maj_stat2 & GSS_S_CONTINUE_NEEDED) { 880 INSIST(!c->done); 881 882 } else { 883 INSIST(maj_stat2 == GSS_S_COMPLETE); 884 if (c->done) { 885 verify_seq_num(c, seq_window, &c->last_cred); 886 krb5_data_free(&c->last_cred); 887 break; 888 } 889 c->done = 1; 890 } 891 } 892 893 c->inprogress = 0; 894 895 *client = c; 896 return 0; 897} 898 899 900static void 901set_funcs(kadm5_mit_context *c) 902{ 903#define SET(C, F) (C)->funcs.F = kadm5_mit_ ## F 904 SET(c, chpass_principal); 905 SET(c, chpass_principal_with_key); 906 SET(c, create_principal); 907 SET(c, delete_principal); 908 SET(c, destroy); 909 SET(c, flush); 910 SET(c, get_principal); 911 SET(c, get_principals); 912 SET(c, get_privs); 913 SET(c, modify_principal); 914 SET(c, randkey_principal); 915 SET(c, rename_principal); 916} 917 918/* 919 * 920 */ 921 922kadm5_ret_t 923kadm5_mit_init_with_password_ctx(krb5_context context, 924 const char *client_name, 925 const char *password, 926 kadm5_config_params *params, 927 unsigned long struct_version, 928 unsigned long api_version, 929 void **server_handle) 930{ 931 kadm5_ret_t ret; 932 kadm5_mit_context *ctx; 933 struct grpc_client *c = NULL; 934 char *colon; 935 936 ctx = malloc(sizeof(*ctx)); 937 if(ctx == NULL) 938 return ENOMEM; 939 memset(ctx, 0, sizeof(*ctx)); 940 set_funcs(ctx); 941 942 ctx->context = context; 943 ctx->my_context = 0; 944 krb5_add_et_list (context, initialize_kadm5_error_table_r); 945 946 if (client_name) { 947 ret = krb5_parse_name(ctx->context, client_name, &ctx->caller); 948 } else { 949 krb5_ccache id; 950 951 ret = _kadm5_c_get_cache_principal(context, &id, &ctx->caller); 952 if (ret) { 953 const char *user; 954 955 user = get_default_username (); 956 if(user == NULL) { 957 krb5_set_error_message(context, KADM5_FAILURE, "Unable to find local user name"); 958 ret = KADM5_FAILURE; 959 } else { 960 ret = krb5_make_principal(context, &ctx->caller, 961 NULL, user, "admin", NULL); 962 } 963 } else if (id) { 964 krb5_cc_close(context, id); 965 } 966 } 967 if(ret) { 968 free(ctx); 969 return ret; 970 } 971 972 if(params->mask & KADM5_CONFIG_REALM) { 973 ret = 0; 974 ctx->realm = strdup(params->realm); 975 if (ctx->realm == NULL) 976 ret = ENOMEM; 977 } else 978 ret = krb5_get_default_realm(ctx->context, &ctx->realm); 979 if (ret) { 980 free(ctx); 981 return ret; 982 } 983 984 if(params->mask & KADM5_CONFIG_ADMIN_SERVER) 985 ctx->admin_server = strdup(params->admin_server); 986 else { 987 krb5_krbhst_handle handle = NULL; 988 char host[MAXHOSTNAMELEN]; 989 990 ret = krb5_krbhst_init(context, ctx->realm, KRB5_KRBHST_ADMIN, &handle); 991 if (ret == 0) 992 ret = krb5_krbhst_next_as_string(context, handle, host, sizeof(host)); 993 krb5_krbhst_free(context, handle); 994 if (ret) { 995 free(ctx->realm); 996 free(ctx); 997 return ret; 998 } 999 ctx->admin_server = strdup(host); 1000 } 1001 1002 if (ctx->admin_server == NULL) { 1003 free(ctx->realm); 1004 free(ctx); 1005 return ENOMEM; 1006 } 1007 colon = strchr (ctx->admin_server, ':'); 1008 if (colon != NULL) 1009 *colon++ = '\0'; 1010 1011 ctx->kadmind_port = 0; 1012 1013 if(params->mask & KADM5_CONFIG_KADMIND_PORT) 1014 ctx->kadmind_port = params->kadmind_port; 1015 else if (colon != NULL) { 1016 char *end; 1017 ctx->kadmind_port = htons(strtol (colon, &end, 0)); 1018 } 1019 if (ctx->kadmind_port == 0) 1020 ctx->kadmind_port = krb5_getportbyname (context, "kerberos-adm", 1021 "tcp", 749); 1022 1023 ret = xdr_setup_connection(context, KADM5_KADMIN_SERVICE, 1024 ctx->admin_server, ctx->kadmind_port, &c); 1025 if (ret) { 1026 free(ctx->realm); 1027 free(ctx); 1028 return ret; 1029 } 1030 1031 ctx->gsscontext = c; 1032 1033 *server_handle = ctx; 1034 return 0; 1035} 1036