1/* $NetBSD: rpc.c,v 1.1.1.1 2011/04/13 18:14:35 elric Exp $ */ 2 3/* 4 * Copyright (c) 2008 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * 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 "kadmin_locl.h" 37 38#include <gssapi/gssapi.h> 39#include <gssapi/gssapi_krb5.h> 40#include <gssapi/gssapi_spnego.h> 41 42#define CHECK(x) \ 43 do { \ 44 int __r; \ 45 if ((__r = (x))) { \ 46 krb5_errx(dcontext, 1, "Failed (%d) on %s:%d", \ 47 __r, __FILE__, __LINE__); \ 48 } \ 49 } while(0) 50 51static krb5_context dcontext; 52 53#define INSIST(x) CHECK(!(x)) 54 55#define VERSION2 0x12345702 56 57#define LAST_FRAGMENT 0x80000000 58 59#define RPC_VERSION 2 60#define KADM_SERVER 2112 61#define VVERSION 2 62#define FLAVOR_GSS 6 63#define FLAVOR_GSS_VERSION 1 64 65struct opaque_auth { 66 uint32_t flavor; 67 krb5_data data; 68}; 69 70struct call_header { 71 uint32_t xid; 72 uint32_t rpcvers; 73 uint32_t prog; 74 uint32_t vers; 75 uint32_t proc; 76 struct opaque_auth cred; 77 struct opaque_auth verf; 78}; 79 80enum { 81 RPG_DATA = 0, 82 RPG_INIT = 1, 83 RPG_CONTINUE_INIT = 2, 84 RPG_DESTROY = 3 85}; 86 87enum { 88 rpg_privacy = 3 89}; 90 91/* 92struct chrand_ret { 93 krb5_ui_4 api_version; 94 kadm5_ret_t ret; 95 int n_keys; 96 krb5_keyblock *keys; 97}; 98*/ 99 100 101struct gcred { 102 uint32_t version; 103 uint32_t proc; 104 uint32_t seq_num; 105 uint32_t service; 106 krb5_data handle; 107}; 108 109static int 110parse_name(const unsigned char *p, size_t len, 111 const gss_OID oid, char **name) 112{ 113 size_t l; 114 115 if (len < 4) 116 return 1; 117 118 /* TOK_ID */ 119 if (memcmp(p, "\x04\x01", 2) != 0) 120 return 1; 121 len -= 2; 122 p += 2; 123 124 /* MECH_LEN */ 125 l = (p[0] << 8) | p[1]; 126 len -= 2; 127 p += 2; 128 if (l < 2 || len < l) 129 return 1; 130 131 /* oid wrapping */ 132 if (p[0] != 6 || p[1] != l - 2) 133 return 1; 134 p += 2; 135 l -= 2; 136 len -= 2; 137 138 /* MECH */ 139 if (l != oid->length || memcmp(p, oid->elements, oid->length) != 0) 140 return 1; 141 len -= l; 142 p += l; 143 144 /* MECHNAME_LEN */ 145 if (len < 4) 146 return 1; 147 l = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; 148 len -= 4; 149 p += 4; 150 151 /* MECH NAME */ 152 if (len != l) 153 return 1; 154 155 *name = malloc(l + 1); 156 INSIST(*name != NULL); 157 memcpy(*name, p, l); 158 (*name)[l] = '\0'; 159 160 return 0; 161} 162 163 164 165static void 166gss_error(krb5_context context, 167 gss_OID mech, OM_uint32 type, OM_uint32 error) 168{ 169 OM_uint32 new_stat; 170 OM_uint32 msg_ctx = 0; 171 gss_buffer_desc status_string; 172 OM_uint32 ret; 173 174 do { 175 ret = gss_display_status (&new_stat, 176 error, 177 type, 178 mech, 179 &msg_ctx, 180 &status_string); 181 krb5_warnx(context, "%.*s", 182 (int)status_string.length, 183 (char *)status_string.value); 184 gss_release_buffer (&new_stat, &status_string); 185 } while (!GSS_ERROR(ret) && msg_ctx != 0); 186} 187 188static void 189gss_print_errors (krb5_context context, 190 OM_uint32 maj_stat, OM_uint32 min_stat) 191{ 192 gss_error(context, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat); 193 gss_error(context, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat); 194} 195 196static int 197read_data(krb5_storage *sp, krb5_storage *msg, size_t len) 198{ 199 char buf[1024]; 200 201 while (len) { 202 size_t tlen = len; 203 ssize_t slen; 204 205 if (tlen > sizeof(buf)) 206 tlen = sizeof(buf); 207 208 slen = krb5_storage_read(sp, buf, tlen); 209 INSIST(slen == tlen); 210 211 slen = krb5_storage_write(msg, buf, tlen); 212 INSIST(slen == tlen); 213 214 len -= tlen; 215 } 216 return 0; 217} 218 219static int 220collect_framents(krb5_storage *sp, krb5_storage *msg) 221{ 222 krb5_error_code ret; 223 uint32_t len; 224 int last_fragment; 225 size_t total_len = 0; 226 227 do { 228 ret = krb5_ret_uint32(sp, &len); 229 if (ret) 230 return ret; 231 232 last_fragment = (len & LAST_FRAGMENT); 233 len &= ~LAST_FRAGMENT; 234 235 CHECK(read_data(sp, msg, len)); 236 total_len += len; 237 238 } while(!last_fragment || total_len == 0); 239 240 return 0; 241} 242 243static krb5_error_code 244store_data_xdr(krb5_storage *sp, krb5_data data) 245{ 246 krb5_error_code ret; 247 size_t res; 248 249 ret = krb5_store_data(sp, data); 250 if (ret) 251 return ret; 252 res = 4 - (data.length % 4); 253 if (res != 4) { 254 static const char zero[4] = { 0, 0, 0, 0 }; 255 256 ret = krb5_storage_write(sp, zero, res); 257 if(ret != res) 258 return (ret < 0)? errno : krb5_storage_get_eof_code(sp); 259 } 260 return 0; 261} 262 263static krb5_error_code 264ret_data_xdr(krb5_storage *sp, krb5_data *data) 265{ 266 krb5_error_code ret; 267 ret = krb5_ret_data(sp, data); 268 if (ret) 269 return ret; 270 271 if ((data->length % 4) != 0) { 272 char buf[4]; 273 size_t res; 274 275 res = 4 - (data->length % 4); 276 if (res != 4) { 277 ret = krb5_storage_read(sp, buf, res); 278 if(ret != res) 279 return (ret < 0)? errno : krb5_storage_get_eof_code(sp); 280 } 281 } 282 return 0; 283} 284 285static krb5_error_code 286ret_auth_opaque(krb5_storage *msg, struct opaque_auth *ao) 287{ 288 krb5_error_code ret; 289 ret = krb5_ret_uint32(msg, &ao->flavor); 290 if (ret) return ret; 291 ret = ret_data_xdr(msg, &ao->data); 292 return ret; 293} 294 295static int 296ret_gcred(krb5_data *data, struct gcred *gcred) 297{ 298 krb5_storage *sp; 299 300 memset(gcred, 0, sizeof(*gcred)); 301 302 sp = krb5_storage_from_data(data); 303 INSIST(sp != NULL); 304 305 CHECK(krb5_ret_uint32(sp, &gcred->version)); 306 CHECK(krb5_ret_uint32(sp, &gcred->proc)); 307 CHECK(krb5_ret_uint32(sp, &gcred->seq_num)); 308 CHECK(krb5_ret_uint32(sp, &gcred->service)); 309 CHECK(ret_data_xdr(sp, &gcred->handle)); 310 311 krb5_storage_free(sp); 312 313 return 0; 314} 315 316static krb5_error_code 317store_gss_init_res(krb5_storage *sp, krb5_data handle, 318 OM_uint32 maj_stat, OM_uint32 min_stat, 319 uint32_t seq_window, gss_buffer_t gout) 320{ 321 krb5_error_code ret; 322 krb5_data out; 323 324 out.data = gout->value; 325 out.length = gout->length; 326 327 ret = store_data_xdr(sp, handle); 328 if (ret) return ret; 329 ret = krb5_store_uint32(sp, maj_stat); 330 if (ret) return ret; 331 ret = krb5_store_uint32(sp, min_stat); 332 if (ret) return ret; 333 ret = store_data_xdr(sp, out); 334 return ret; 335} 336 337static int 338store_string_xdr(krb5_storage *sp, const char *str) 339{ 340 krb5_data c; 341 if (str) { 342 c.data = rk_UNCONST(str); 343 c.length = strlen(str) + 1; 344 } else 345 krb5_data_zero(&c); 346 347 return store_data_xdr(sp, c); 348} 349 350static int 351ret_string_xdr(krb5_storage *sp, char **str) 352{ 353 krb5_data c; 354 *str = NULL; 355 CHECK(ret_data_xdr(sp, &c)); 356 if (c.length) { 357 *str = malloc(c.length + 1); 358 INSIST(*str != NULL); 359 memcpy(*str, c.data, c.length); 360 (*str)[c.length] = '\0'; 361 } 362 krb5_data_free(&c); 363 return 0; 364} 365 366static int 367store_principal_xdr(krb5_context context, 368 krb5_storage *sp, 369 krb5_principal p) 370{ 371 char *str; 372 CHECK(krb5_unparse_name(context, p, &str)); 373 CHECK(store_string_xdr(sp, str)); 374 free(str); 375 return 0; 376} 377 378static int 379ret_principal_xdr(krb5_context context, 380 krb5_storage *sp, 381 krb5_principal *p) 382{ 383 char *str; 384 *p = NULL; 385 CHECK(ret_string_xdr(sp, &str)); 386 if (str) { 387 CHECK(krb5_parse_name(context, str, p)); 388 free(str); 389 } 390 return 0; 391} 392 393static int 394store_principal_ent(krb5_context context, 395 krb5_storage *sp, 396 kadm5_principal_ent_rec *ent) 397{ 398 size_t i; 399 400 CHECK(store_principal_xdr(context, sp, ent->principal)); 401 CHECK(krb5_store_uint32(sp, ent->princ_expire_time)); 402 CHECK(krb5_store_uint32(sp, ent->pw_expiration)); 403 CHECK(krb5_store_uint32(sp, ent->last_pwd_change)); 404 CHECK(krb5_store_uint32(sp, ent->max_life)); 405 CHECK(krb5_store_int32(sp, ent->mod_name == NULL)); 406 if (ent->mod_name) 407 CHECK(store_principal_xdr(context, sp, ent->mod_name)); 408 CHECK(krb5_store_uint32(sp, ent->mod_date)); 409 CHECK(krb5_store_uint32(sp, ent->attributes)); 410 CHECK(krb5_store_uint32(sp, ent->kvno)); 411 CHECK(krb5_store_uint32(sp, ent->mkvno)); 412 CHECK(store_string_xdr(sp, ent->policy)); 413 CHECK(krb5_store_int32(sp, ent->aux_attributes)); 414 CHECK(krb5_store_int32(sp, ent->max_renewable_life)); 415 CHECK(krb5_store_int32(sp, ent->last_success)); 416 CHECK(krb5_store_int32(sp, ent->last_failed)); 417 CHECK(krb5_store_int32(sp, ent->fail_auth_count)); 418 CHECK(krb5_store_int32(sp, ent->n_key_data)); 419 CHECK(krb5_store_int32(sp, ent->n_tl_data)); 420 CHECK(krb5_store_int32(sp, ent->n_tl_data == 0)); 421 if (ent->n_tl_data) { 422 krb5_tl_data *tp; 423 424 for (tp = ent->tl_data; tp; tp = tp->tl_data_next) { 425 krb5_data c; 426 c.length = tp->tl_data_length; 427 c.data = tp->tl_data_contents; 428 429 CHECK(krb5_store_int32(sp, 0)); /* last item */ 430 CHECK(krb5_store_int32(sp, tp->tl_data_type)); 431 CHECK(store_data_xdr(sp, c)); 432 } 433 CHECK(krb5_store_int32(sp, 1)); /* last item */ 434 } 435 436 CHECK(krb5_store_int32(sp, ent->n_key_data)); 437 for (i = 0; i < ent->n_key_data; i++) { 438 CHECK(krb5_store_uint32(sp, 2)); 439 CHECK(krb5_store_uint32(sp, ent->kvno)); 440 CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[0])); 441 CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[1])); 442 } 443 444 return 0; 445} 446 447static int 448ret_principal_ent(krb5_context context, 449 krb5_storage *sp, 450 kadm5_principal_ent_rec *ent) 451{ 452 uint32_t flag, num; 453 size_t i; 454 455 memset(ent, 0, sizeof(*ent)); 456 457 CHECK(ret_principal_xdr(context, sp, &ent->principal)); 458 CHECK(krb5_ret_uint32(sp, &flag)); 459 ent->princ_expire_time = flag; 460 CHECK(krb5_ret_uint32(sp, &flag)); 461 ent->pw_expiration = flag; 462 CHECK(krb5_ret_uint32(sp, &flag)); 463 ent->last_pwd_change = flag; 464 CHECK(krb5_ret_uint32(sp, &flag)); 465 ent->max_life = flag; 466 CHECK(krb5_ret_uint32(sp, &flag)); 467 if (flag == 0) 468 ret_principal_xdr(context, sp, &ent->mod_name); 469 CHECK(krb5_ret_uint32(sp, &flag)); 470 ent->mod_date = flag; 471 CHECK(krb5_ret_uint32(sp, &flag)); 472 ent->attributes = flag; 473 CHECK(krb5_ret_uint32(sp, &flag)); 474 ent->kvno = flag; 475 CHECK(krb5_ret_uint32(sp, &flag)); 476 ent->mkvno = flag; 477 CHECK(ret_string_xdr(sp, &ent->policy)); 478 CHECK(krb5_ret_uint32(sp, &flag)); 479 ent->aux_attributes = flag; 480 CHECK(krb5_ret_uint32(sp, &flag)); 481 ent->max_renewable_life = flag; 482 CHECK(krb5_ret_uint32(sp, &flag)); 483 ent->last_success = flag; 484 CHECK(krb5_ret_uint32(sp, &flag)); 485 ent->last_failed = flag; 486 CHECK(krb5_ret_uint32(sp, &flag)); 487 ent->fail_auth_count = flag; 488 CHECK(krb5_ret_uint32(sp, &flag)); 489 ent->n_key_data = flag; 490 CHECK(krb5_ret_uint32(sp, &flag)); 491 ent->n_tl_data = flag; 492 CHECK(krb5_ret_uint32(sp, &flag)); 493 if (flag == 0) { 494 krb5_tl_data **tp = &ent->tl_data; 495 size_t count = 0; 496 497 while(1) { 498 krb5_data c; 499 CHECK(krb5_ret_uint32(sp, &flag)); /* last item */ 500 if (flag) 501 break; 502 *tp = calloc(1, sizeof(**tp)); 503 INSIST(*tp != NULL); 504 CHECK(krb5_ret_uint32(sp, &flag)); 505 (*tp)->tl_data_type = flag; 506 CHECK(ret_data_xdr(sp, &c)); 507 (*tp)->tl_data_length = c.length; 508 (*tp)->tl_data_contents = c.data; 509 tp = &(*tp)->tl_data_next; 510 511 count++; 512 } 513 INSIST(ent->n_tl_data == count); 514 } else { 515 INSIST(ent->n_tl_data == 0); 516 } 517 518 CHECK(krb5_ret_uint32(sp, &num)); 519 INSIST(num == ent->n_key_data); 520 521 ent->key_data = calloc(num, sizeof(ent->key_data[0])); 522 INSIST(ent->key_data != NULL); 523 524 for (i = 0; i < num; i++) { 525 CHECK(krb5_ret_uint32(sp, &flag)); /* data version */ 526 INSIST(flag > 1); 527 CHECK(krb5_ret_uint32(sp, &flag)); 528 ent->kvno = flag; 529 CHECK(krb5_ret_uint32(sp, &flag)); 530 ent->key_data[i].key_data_type[0] = flag; 531 CHECK(krb5_ret_uint32(sp, &flag)); 532 ent->key_data[i].key_data_type[1] = flag; 533 } 534 535 return 0; 536} 537 538/* 539 * 540 */ 541 542static void 543proc_create_principal(kadm5_server_context *context, 544 krb5_storage *in, 545 krb5_storage *out) 546{ 547 uint32_t version, mask; 548 kadm5_principal_ent_rec ent; 549 krb5_error_code ret; 550 char *password; 551 552 memset(&ent, 0, sizeof(ent)); 553 554 CHECK(krb5_ret_uint32(in, &version)); 555 INSIST(version == VERSION2); 556 CHECK(ret_principal_ent(context->context, in, &ent)); 557 CHECK(krb5_ret_uint32(in, &mask)); 558 CHECK(ret_string_xdr(in, &password)); 559 560 INSIST(ent.principal); 561 562 563 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_ADD, ent.principal); 564 if (ret) 565 goto fail; 566 567 ret = kadm5_create_principal(context, &ent, mask, password); 568 569 fail: 570 krb5_warn(context->context, ret, "create principal"); 571 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 572 CHECK(krb5_store_uint32(out, ret)); /* code */ 573 574 free(password); 575 kadm5_free_principal_ent(context, &ent); 576} 577 578static void 579proc_delete_principal(kadm5_server_context *context, 580 krb5_storage *in, 581 krb5_storage *out) 582{ 583 uint32_t version; 584 krb5_principal princ; 585 krb5_error_code ret; 586 587 CHECK(krb5_ret_uint32(in, &version)); 588 INSIST(version == VERSION2); 589 CHECK(ret_principal_xdr(context->context, in, &princ)); 590 591 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_DELETE, princ); 592 if (ret) 593 goto fail; 594 595 ret = kadm5_delete_principal(context, princ); 596 597 fail: 598 krb5_warn(context->context, ret, "delete principal"); 599 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 600 CHECK(krb5_store_uint32(out, ret)); /* code */ 601 602 krb5_free_principal(context->context, princ); 603} 604 605static void 606proc_get_principal(kadm5_server_context *context, 607 krb5_storage *in, 608 krb5_storage *out) 609{ 610 uint32_t version, mask; 611 krb5_principal princ; 612 kadm5_principal_ent_rec ent; 613 krb5_error_code ret; 614 615 memset(&ent, 0, sizeof(ent)); 616 617 CHECK(krb5_ret_uint32(in, &version)); 618 INSIST(version == VERSION2); 619 CHECK(ret_principal_xdr(context->context, in, &princ)); 620 CHECK(krb5_ret_uint32(in, &mask)); 621 622 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_GET, princ); 623 if(ret) 624 goto fail; 625 626 ret = kadm5_get_principal(context, princ, &ent, mask); 627 628 fail: 629 krb5_warn(context->context, ret, "get principal principal"); 630 631 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 632 CHECK(krb5_store_uint32(out, ret)); /* code */ 633 if (ret == 0) { 634 CHECK(store_principal_ent(context->context, out, &ent)); 635 } 636 krb5_free_principal(context->context, princ); 637 kadm5_free_principal_ent(context, &ent); 638} 639 640static void 641proc_chrand_principal_v2(kadm5_server_context *context, 642 krb5_storage *in, 643 krb5_storage *out) 644{ 645 krb5_error_code ret; 646 krb5_principal princ; 647 uint32_t version; 648 krb5_keyblock *new_keys; 649 int n_keys; 650 651 CHECK(krb5_ret_uint32(in, &version)); 652 INSIST(version == VERSION2); 653 CHECK(ret_principal_xdr(context->context, in, &princ)); 654 655 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW, princ); 656 if(ret) 657 goto fail; 658 659 ret = kadm5_randkey_principal(context, princ, 660 &new_keys, &n_keys); 661 662 fail: 663 krb5_warn(context->context, ret, "rand key principal"); 664 665 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 666 CHECK(krb5_store_uint32(out, ret)); 667 if (ret == 0) { 668 size_t i; 669 CHECK(krb5_store_int32(out, n_keys)); 670 671 for(i = 0; i < n_keys; i++){ 672 CHECK(krb5_store_uint32(out, new_keys[i].keytype)); 673 CHECK(store_data_xdr(out, new_keys[i].keyvalue)); 674 krb5_free_keyblock_contents(context->context, &new_keys[i]); 675 } 676 free(new_keys); 677 } 678 krb5_free_principal(context->context, princ); 679} 680 681static void 682proc_init(kadm5_server_context *context, 683 krb5_storage *in, 684 krb5_storage *out) 685{ 686 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 687 CHECK(krb5_store_uint32(out, 0)); /* code */ 688 CHECK(krb5_store_uint32(out, 0)); /* code */ 689} 690 691struct krb5_proc { 692 char *name; 693 void (*func)(kadm5_server_context *, krb5_storage *, krb5_storage *); 694} procs[] = { 695 { "NULL", NULL }, 696 { "create principal", proc_create_principal }, 697 { "delete principal", proc_delete_principal }, 698 { "modify principal", NULL }, 699 { "rename principal", NULL }, 700 { "get principal", proc_get_principal }, 701 { "chpass principal", NULL }, 702 { "chrand principal", proc_chrand_principal_v2 }, 703 { "create policy", NULL }, 704 { "delete policy", NULL }, 705 { "modify policy", NULL }, 706 { "get policy", NULL }, 707 { "get privs", NULL }, 708 { "init", proc_init }, 709 { "get principals", NULL }, 710 { "get polices", NULL }, 711 { "setkey principal", NULL }, 712 { "setkey principal v4", NULL }, 713 { "create principal v3", NULL }, 714 { "chpass principal v3", NULL }, 715 { "chrand principal v3", NULL }, 716 { "setkey principal v3", NULL } 717}; 718 719static krb5_error_code 720copyheader(krb5_storage *sp, krb5_data *data) 721{ 722 off_t off; 723 ssize_t sret; 724 725 off = krb5_storage_seek(sp, 0, SEEK_CUR); 726 727 CHECK(krb5_data_alloc(data, off)); 728 INSIST(off == data->length); 729 krb5_storage_seek(sp, 0, SEEK_SET); 730 sret = krb5_storage_read(sp, data->data, data->length); 731 INSIST(sret == off); 732 INSIST(off == krb5_storage_seek(sp, 0, SEEK_CUR)); 733 734 return 0; 735} 736 737struct gctx { 738 krb5_data handle; 739 gss_ctx_id_t ctx; 740 uint32_t seq_num; 741 int done; 742 int inprogress; 743}; 744 745static int 746process_stream(krb5_context context, 747 unsigned char *buf, size_t ilen, 748 krb5_storage *sp) 749{ 750 krb5_error_code ret; 751 krb5_storage *msg, *reply, *dreply; 752 OM_uint32 maj_stat, min_stat; 753 gss_buffer_desc gin, gout; 754 struct gctx gctx; 755 void *server_handle = NULL; 756 757 memset(&gctx, 0, sizeof(gctx)); 758 759 msg = krb5_storage_emem(); 760 reply = krb5_storage_emem(); 761 dreply = krb5_storage_emem(); 762 763 /* 764 * First packet comes partly from the caller 765 */ 766 767 INSIST(ilen >= 4); 768 769 while (1) { 770 struct call_header chdr; 771 struct gcred gcred; 772 uint32_t mtype; 773 krb5_data headercopy; 774 775 krb5_storage_truncate(dreply, 0); 776 krb5_storage_truncate(reply, 0); 777 krb5_storage_truncate(msg, 0); 778 779 krb5_data_zero(&headercopy); 780 memset(&chdr, 0, sizeof(chdr)); 781 memset(&gcred, 0, sizeof(gcred)); 782 783 /* 784 * This is very icky to handle the the auto-detection between 785 * the Heimdal protocol and the MIT ONC-RPC based protocol. 786 */ 787 788 if (ilen) { 789 int last_fragment; 790 unsigned long len; 791 ssize_t slen; 792 unsigned char tmp[4]; 793 794 if (ilen < 4) { 795 memcpy(tmp, buf, ilen); 796 slen = krb5_storage_read(sp, tmp + ilen, sizeof(tmp) - ilen); 797 INSIST(slen == sizeof(tmp) - ilen); 798 799 ilen = sizeof(tmp); 800 buf = tmp; 801 } 802 INSIST(ilen >= 4); 803 804 _krb5_get_int(buf, &len, 4); 805 last_fragment = (len & LAST_FRAGMENT) != 0; 806 len &= ~LAST_FRAGMENT; 807 808 ilen -= 4; 809 buf += 4; 810 811 if (ilen) { 812 if (len < ilen) { 813 slen = krb5_storage_write(msg, buf, len); 814 INSIST(slen == len); 815 ilen -= len; 816 len = 0; 817 } else { 818 slen = krb5_storage_write(msg, buf, ilen); 819 INSIST(slen == ilen); 820 len -= ilen; 821 } 822 } 823 824 CHECK(read_data(sp, msg, len)); 825 826 if (!last_fragment) { 827 ret = collect_framents(sp, msg); 828 if (ret == HEIM_ERR_EOF) 829 krb5_errx(context, 0, "client disconnected"); 830 INSIST(ret == 0); 831 } 832 } else { 833 834 ret = collect_framents(sp, msg); 835 if (ret == HEIM_ERR_EOF) 836 krb5_errx(context, 0, "client disconnected"); 837 INSIST(ret == 0); 838 } 839 krb5_storage_seek(msg, 0, SEEK_SET); 840 841 CHECK(krb5_ret_uint32(msg, &chdr.xid)); 842 CHECK(krb5_ret_uint32(msg, &mtype)); 843 CHECK(krb5_ret_uint32(msg, &chdr.rpcvers)); 844 CHECK(krb5_ret_uint32(msg, &chdr.prog)); 845 CHECK(krb5_ret_uint32(msg, &chdr.vers)); 846 CHECK(krb5_ret_uint32(msg, &chdr.proc)); 847 CHECK(ret_auth_opaque(msg, &chdr.cred)); 848 CHECK(copyheader(msg, &headercopy)); 849 CHECK(ret_auth_opaque(msg, &chdr.verf)); 850 851 INSIST(chdr.rpcvers == RPC_VERSION); 852 INSIST(chdr.prog == KADM_SERVER); 853 INSIST(chdr.vers == VVERSION); 854 INSIST(chdr.cred.flavor == FLAVOR_GSS); 855 856 CHECK(ret_gcred(&chdr.cred.data, &gcred)); 857 858 INSIST(gcred.version == FLAVOR_GSS_VERSION); 859 860 if (gctx.done) { 861 INSIST(chdr.verf.flavor == FLAVOR_GSS); 862 863 /* from first byte to last of credential */ 864 gin.value = headercopy.data; 865 gin.length = headercopy.length; 866 gout.value = chdr.verf.data.data; 867 gout.length = chdr.verf.data.length; 868 869 maj_stat = gss_verify_mic(&min_stat, gctx.ctx, &gin, &gout, NULL); 870 INSIST(maj_stat == GSS_S_COMPLETE); 871 } 872 873 switch(gcred.proc) { 874 case RPG_DATA: { 875 krb5_data data; 876 int conf_state; 877 uint32_t seq; 878 krb5_storage *sp; 879 880 INSIST(gcred.service == rpg_privacy); 881 882 INSIST(gctx.done); 883 884 INSIST(krb5_data_cmp(&gcred.handle, &gctx.handle) == 0); 885 886 CHECK(ret_data_xdr(msg, &data)); 887 888 gin.value = data.data; 889 gin.length = data.length; 890 891 maj_stat = gss_unwrap(&min_stat, gctx.ctx, &gin, &gout, 892 &conf_state, NULL); 893 krb5_data_free(&data); 894 INSIST(maj_stat == GSS_S_COMPLETE); 895 INSIST(conf_state != 0); 896 897 sp = krb5_storage_from_mem(gout.value, gout.length); 898 INSIST(sp != NULL); 899 900 CHECK(krb5_ret_uint32(sp, &seq)); 901 INSIST (seq == gcred.seq_num); 902 903 /* 904 * Check sequence number 905 */ 906 INSIST(seq > gctx.seq_num); 907 gctx.seq_num = seq; 908 909 /* 910 * If context is setup, priv data have the seq_num stored 911 * first in the block, so add it here before users data is 912 * added. 913 */ 914 CHECK(krb5_store_uint32(dreply, gctx.seq_num)); 915 916 if (chdr.proc >= sizeof(procs)/sizeof(procs[0])) { 917 krb5_warnx(context, "proc number out of array"); 918 } else if (procs[chdr.proc].func == NULL) { 919 krb5_warnx(context, "proc '%s' never implemented", 920 procs[chdr.proc].name); 921 } else { 922 krb5_warnx(context, "proc %s", procs[chdr.proc].name); 923 INSIST(server_handle != NULL); 924 (*procs[chdr.proc].func)(server_handle, sp, dreply); 925 } 926 krb5_storage_free(sp); 927 gss_release_buffer(&min_stat, &gout); 928 929 break; 930 } 931 case RPG_INIT: 932 INSIST(gctx.inprogress == 0); 933 INSIST(gctx.ctx == NULL); 934 935 gctx.inprogress = 1; 936 /* FALL THOUGH */ 937 case RPG_CONTINUE_INIT: { 938 gss_name_t src_name = GSS_C_NO_NAME; 939 krb5_data in; 940 941 INSIST(gctx.inprogress); 942 943 CHECK(ret_data_xdr(msg, &in)); 944 945 gin.value = in.data; 946 gin.length = in.length; 947 gout.value = NULL; 948 gout.length = 0; 949 950 maj_stat = gss_accept_sec_context(&min_stat, 951 &gctx.ctx, 952 GSS_C_NO_CREDENTIAL, 953 &gin, 954 GSS_C_NO_CHANNEL_BINDINGS, 955 &src_name, 956 NULL, 957 &gout, 958 NULL, 959 NULL, 960 NULL); 961 if (GSS_ERROR(maj_stat)) { 962 gss_print_errors(context, maj_stat, min_stat); 963 krb5_errx(context, 1, "gss error, exit"); 964 } 965 if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) { 966 kadm5_config_params realm_params; 967 gss_buffer_desc buf; 968 char *client; 969 970 gctx.done = 1; 971 972 memset(&realm_params, 0, sizeof(realm_params)); 973 974 maj_stat = gss_export_name(&min_stat, src_name, &buf); 975 INSIST(maj_stat == GSS_S_COMPLETE); 976 977 CHECK(parse_name(buf.value, buf.length, 978 GSS_KRB5_MECHANISM, &client)); 979 980 gss_release_buffer(&min_stat, &buf); 981 982 krb5_warnx(context, "%s connected", client); 983 984 ret = kadm5_s_init_with_password_ctx(context, 985 client, 986 NULL, 987 KADM5_ADMIN_SERVICE, 988 &realm_params, 989 0, 0, 990 &server_handle); 991 INSIST(ret == 0); 992 } 993 994 INSIST(gctx.ctx != GSS_C_NO_CONTEXT); 995 996 CHECK(krb5_store_uint32(dreply, 0)); 997 CHECK(store_gss_init_res(dreply, gctx.handle, 998 maj_stat, min_stat, 1, &gout)); 999 if (gout.value) 1000 gss_release_buffer(&min_stat, &gout); 1001 if (src_name) 1002 gss_release_name(&min_stat, &src_name); 1003 1004 break; 1005 } 1006 case RPG_DESTROY: 1007 krb5_errx(context, 1, "client destroyed gss context"); 1008 default: 1009 krb5_errx(context, 1, "client sent unknown gsscode %d", 1010 (int)gcred.proc); 1011 } 1012 1013 krb5_data_free(&gcred.handle); 1014 krb5_data_free(&chdr.cred.data); 1015 krb5_data_free(&chdr.verf.data); 1016 krb5_data_free(&headercopy); 1017 1018 CHECK(krb5_store_uint32(reply, chdr.xid)); 1019 CHECK(krb5_store_uint32(reply, 1)); /* REPLY */ 1020 CHECK(krb5_store_uint32(reply, 0)); /* MSG_ACCEPTED */ 1021 1022 if (!gctx.done) { 1023 krb5_data data; 1024 1025 CHECK(krb5_store_uint32(reply, 0)); /* flavor_none */ 1026 CHECK(krb5_store_uint32(reply, 0)); /* length */ 1027 1028 CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */ 1029 1030 CHECK(krb5_storage_to_data(dreply, &data)); 1031 INSIST(krb5_storage_write(reply, data.data, data.length) == data.length); 1032 krb5_data_free(&data); 1033 1034 } else { 1035 uint32_t seqnum = htonl(gctx.seq_num); 1036 krb5_data data; 1037 1038 gin.value = &seqnum; 1039 gin.length = sizeof(seqnum); 1040 1041 maj_stat = gss_get_mic(&min_stat, gctx.ctx, 0, &gin, &gout); 1042 INSIST(maj_stat == GSS_S_COMPLETE); 1043 1044 data.data = gout.value; 1045 data.length = gout.length; 1046 1047 CHECK(krb5_store_uint32(reply, FLAVOR_GSS)); 1048 CHECK(store_data_xdr(reply, data)); 1049 gss_release_buffer(&min_stat, &gout); 1050 1051 CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */ 1052 1053 CHECK(krb5_storage_to_data(dreply, &data)); 1054 1055 if (gctx.inprogress) { 1056 ssize_t sret; 1057 gctx.inprogress = 0; 1058 sret = krb5_storage_write(reply, data.data, data.length); 1059 INSIST(sret == data.length); 1060 krb5_data_free(&data); 1061 } else { 1062 int conf_state; 1063 1064 gin.value = data.data; 1065 gin.length = data.length; 1066 1067 maj_stat = gss_wrap(&min_stat, gctx.ctx, 1, 0, 1068 &gin, &conf_state, &gout); 1069 INSIST(maj_stat == GSS_S_COMPLETE); 1070 INSIST(conf_state != 0); 1071 krb5_data_free(&data); 1072 1073 data.data = gout.value; 1074 data.length = gout.length; 1075 1076 store_data_xdr(reply, data); 1077 gss_release_buffer(&min_stat, &gout); 1078 } 1079 } 1080 1081 { 1082 krb5_data data; 1083 ssize_t sret; 1084 CHECK(krb5_storage_to_data(reply, &data)); 1085 CHECK(krb5_store_uint32(sp, data.length | LAST_FRAGMENT)); 1086 sret = krb5_storage_write(sp, data.data, data.length); 1087 INSIST(sret == data.length); 1088 krb5_data_free(&data); 1089 } 1090 1091 } 1092} 1093 1094 1095int 1096handle_mit(krb5_context context, void *buf, size_t len, krb5_socket_t sock) 1097{ 1098 krb5_storage *sp; 1099 1100 dcontext = context; 1101 1102 sp = krb5_storage_from_fd(sock); 1103 INSIST(sp != NULL); 1104 1105 process_stream(context, buf, len, sp); 1106 1107 return 0; 1108} 1109