1/* 2 * Copyright (c) 2008 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 "kadmin_locl.h" 35#include <kadm5/private.h> 36 37#include <gssapi.h> 38#include <gssapi_krb5.h> 39#include <gssapi_spnego.h> 40 41#define CHECK(x) \ 42 do { \ 43 int __r; \ 44 if ((__r = (x))) { \ 45 krb5_errx(dcontext, 1, "Failed (%d) on %s:%d", \ 46 __r, __FILE__, __LINE__); \ 47 } \ 48 } while(0) 49 50#define EXPECT(x,expected) \ 51 do { \ 52 if ((x) != (expected)) { \ 53 krb5_errx(dcontext, 1, \ 54 "Got %d, was not the expected %d at %s:%d", \ 55 x, expected, __FILE__, __LINE__); \ 56 } \ 57 } while(0) 58 59#define EXPECT_EGT(x,expected) \ 60 do { \ 61 if ((x) < (expected)) { \ 62 krb5_errx(dcontext, 1, \ 63 "Got %d that is < %d at %s:%d", \ 64 x, expected, __FILE__, __LINE__); \ 65 } \ 66 } while(0) 67 68static krb5_context dcontext; 69 70#define INSIST(x) CHECK(!(x)) 71 72#define VERSION2 0x12345702 73 74#define LAST_FRAGMENT 0x80000000 75 76#define RPC_VERSION 2 77#define KADM_SERVER 2112 78#define VVERSION 2 79#define FLAVOR_GSS 6 80#define FLAVOR_GSS_VERSION 1 81#define SEQ_WINDOW_SIZE 1 82#define FLAVOR_GSS_OLD 300001 83#define FLAVOR_GSS_OLD_MIN_VERSION 3 84 85enum { 86 RPG_DATA = 0, 87 RPG_INIT = 1, 88 RPG_CONTINUE_INIT = 2, 89 RPG_DESTROY = 3 90}; 91 92enum { 93 rpg_privacy = 3 94}; 95 96/* 97 struct chrand_ret { 98 krb5_ui_4 api_version; 99 kadm5_ret_t ret; 100 int n_keys; 101 krb5_keyblock *keys; 102 }; 103*/ 104 105 106static int 107parse_name(const unsigned char *p, size_t len, 108 const gss_OID oid, char **name) 109{ 110 size_t l; 111 112 if (len < 4) 113 return 1; 114 115 /* TOK_ID */ 116 if (memcmp(p, "\x04\x01", 2) != 0) 117 return 1; 118 len -= 2; 119 p += 2; 120 121 /* MECH_LEN */ 122 l = (p[0] << 8) | p[1]; 123 len -= 2; 124 p += 2; 125 if (l < 2 || len < l) 126 return 1; 127 128 /* oid wrapping */ 129 if (p[0] != 6 || p[1] != l - 2) 130 return 1; 131 p += 2; 132 l -= 2; 133 len -= 2; 134 135 /* MECH */ 136 if (l != oid->length || memcmp(p, oid->elements, oid->length) != 0) 137 return 1; 138 len -= l; 139 p += l; 140 141 /* MECHNAME_LEN */ 142 if (len < 4) 143 return 1; 144 l = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; 145 len -= 4; 146 p += 4; 147 148 /* MECH NAME */ 149 if (len != l) 150 return 1; 151 152 *name = malloc(l + 1); 153 INSIST(*name != NULL); 154 memcpy(*name, p, l); 155 (*name)[l] = '\0'; 156 157 return 0; 158} 159 160 161 162static void 163gss_error(krb5_context lcontext, 164 gss_OID mech, OM_uint32 type, OM_uint32 error) 165{ 166 OM_uint32 new_stat; 167 OM_uint32 msg_ctx = 0; 168 gss_buffer_desc status_string; 169 OM_uint32 ret; 170 171 do { 172 ret = gss_display_status (&new_stat, 173 error, 174 type, 175 mech, 176 &msg_ctx, 177 &status_string); 178 krb5_warnx(lcontext, "%.*s", 179 (int)status_string.length, 180 (char *)status_string.value); 181 gss_release_buffer (&new_stat, &status_string); 182 } while (!GSS_ERROR(ret) && msg_ctx != 0); 183} 184 185static void 186gss_print_errors (krb5_context lcontext, 187 OM_uint32 maj_stat, OM_uint32 min_stat) 188{ 189 gss_error(lcontext, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat); 190 gss_error(lcontext, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat); 191} 192 193static int 194read_data(krb5_storage *sp, krb5_storage *msg, size_t len) 195{ 196 char buf[1024]; 197 198 while (len) { 199 size_t tlen = len; 200 ssize_t slen; 201 202 if (tlen > sizeof(buf)) 203 tlen = sizeof(buf); 204 205 slen = krb5_storage_read(sp, buf, tlen); 206 INSIST(slen == tlen); 207 208 slen = krb5_storage_write(msg, buf, tlen); 209 INSIST(slen == tlen); 210 211 len -= tlen; 212 } 213 return 0; 214} 215 216static int 217collect_fragments(krb5_storage *sp, krb5_storage *msg) 218{ 219 krb5_error_code ret; 220 uint32_t len; 221 int last_fragment; 222 size_t total_len = 0; 223 224 do { 225 ret = krb5_ret_uint32(sp, &len); 226 if (ret) 227 return ret; 228 229 last_fragment = (len & LAST_FRAGMENT); 230 len &= ~LAST_FRAGMENT; 231 232 CHECK(read_data(sp, msg, len)); 233 total_len += len; 234 235 } while(!last_fragment || total_len == 0); 236 237 return 0; 238} 239 240/* 241 * 242 */ 243 244static void 245proc_create_principal(kadm5_server_context *lcontext, 246 krb5_storage *in, 247 krb5_storage *out) 248{ 249 uint32_t version, mask; 250 kadm5_principal_ent_rec ent; 251 krb5_error_code ret; 252 char *password, *princ = NULL; 253 254 memset(&ent, 0, sizeof(ent)); 255 256 CHECK(krb5_ret_uint32(in, &version)); 257 EXPECT_EGT(version, VERSION2); 258 CHECK(_kadm5_xdr_ret_principal_ent(lcontext->context, in, &ent)); 259 INSIST(ent.principal != NULL); 260 CHECK(krb5_unparse_name(lcontext->context, ent.principal, &princ)); 261 CHECK(krb5_ret_uint32(in, &mask)); 262 CHECK(_kadm5_xdr_ret_string_xdr(in, &password)); 263 264 INSIST(ent.principal); 265 266 267 ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_ADD, ent.principal); 268 if (ret) 269 goto fail; 270 271 ret = kadm5_create_principal(lcontext, &ent, mask, password); 272 273 fail: 274 krb5_warn(lcontext->context, ret, "create principal: %s", princ ? princ : "<noprinc>"); 275 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 276 CHECK(krb5_store_uint32(out, ret)); /* code */ 277 278 free(password); 279 free(princ); 280 kadm5_free_principal_ent(context, &ent); 281} 282 283/* get array of salt tuples */ 284 285static krb5_key_salt_tuple * 286parse_ks_tuple(krb5_context lcontext, krb5_storage *in, uint32_t *n_ks_tuple) 287{ 288 krb5_key_salt_tuple *tuples; 289 uint32_t n; 290 291 CHECK(krb5_ret_uint32(in, n_ks_tuple)); 292 INSIST(*n_ks_tuple < 1000); 293 294 if (*n_ks_tuple == 0) 295 return NULL; 296 297 tuples = calloc(*n_ks_tuple, sizeof(tuples[0])); 298 INSIST(tuples != NULL); 299 300 for (n = 0; n < *n_ks_tuple; n++) { 301 int32_t enctype, salttype; 302 CHECK(krb5_ret_int32(in, &enctype)); 303 CHECK(krb5_ret_int32(in, &salttype)); 304 305 tuples[n].ks_enctype = (krb5_enctype)enctype; 306 tuples[n].ks_salttype = (krb5_enctype)salttype; 307 } 308 return tuples; 309} 310 311static void 312proc_create_principal3(kadm5_server_context *lcontext, 313 krb5_storage *in, 314 krb5_storage *out) 315{ 316 uint32_t version, mask, n_ks_tuple; 317 kadm5_principal_ent_rec ent; 318 krb5_error_code ret; 319 char *password, *princ = NULL; 320 krb5_key_salt_tuple *ks_tuple; 321 322 memset(&ent, 0, sizeof(ent)); 323 324 CHECK(krb5_ret_uint32(in, &version)); 325 EXPECT_EGT(version, VERSION2); 326 CHECK(_kadm5_xdr_ret_principal_ent(lcontext->context, in, &ent)); 327 INSIST(ent.principal != NULL); 328 CHECK(krb5_unparse_name(lcontext->context, ent.principal, &princ)); 329 CHECK(krb5_ret_uint32(in, &mask)); 330 ks_tuple = parse_ks_tuple(lcontext->context, in, &n_ks_tuple); 331 CHECK(_kadm5_xdr_ret_string_xdr(in, &password)); 332 333 INSIST(ent.principal); 334 335 ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_ADD, ent.principal); 336 if (ret) 337 goto fail; 338 339 ret = kadm5_create_principal_2(context, &ent, mask, n_ks_tuple, ks_tuple, password); 340 341 fail: 342 krb5_warn(lcontext->context, ret, "create principal: %s", princ ? princ : "<noprinc>"); 343 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 344 CHECK(krb5_store_uint32(out, ret)); /* code */ 345 346 free(password); 347 kadm5_free_principal_ent(lcontext, &ent); 348 349 free(princ); 350 if (ks_tuple) 351 free(ks_tuple); 352 kadm5_free_principal_ent(context, &ent); 353} 354 355static void 356proc_delete_principal(kadm5_server_context *lcontext, 357 krb5_storage *in, 358 krb5_storage *out) 359{ 360 uint32_t version; 361 krb5_principal principal; 362 krb5_error_code ret; 363 char *princ = NULL; 364 365 CHECK(krb5_ret_uint32(in, &version)); 366 EXPECT_EGT(version, VERSION2); 367 CHECK(_kadm5_xdr_ret_principal_xdr(lcontext->context, in, &principal)); 368 CHECK(krb5_unparse_name(lcontext->context, principal, &princ)); 369 370 ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_DELETE, principal); 371 if (ret) 372 goto fail; 373 374 ret = kadm5_delete_principal(lcontext, principal); 375 376 fail: 377 krb5_warn(lcontext->context, ret, "delete principal: %s", princ ? princ : "<noprinc>"); 378 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 379 CHECK(krb5_store_uint32(out, ret)); /* code */ 380 381 free(princ); 382 krb5_free_principal(lcontext->context, principal); 383} 384 385static void 386proc_modify_principal(kadm5_server_context *lcontext, 387 krb5_storage *in, 388 krb5_storage *out) 389{ 390 uint32_t version, mask; 391 kadm5_principal_ent_rec ent; 392 krb5_error_code ret; 393 394 CHECK(krb5_ret_uint32(in, &version)); 395 EXPECT_EGT(version, VERSION2); 396 CHECK(_kadm5_xdr_ret_principal_ent(lcontext->context, in, &ent)); 397 INSIST(ent.principal != NULL); 398 CHECK(krb5_ret_uint32(in, &mask)); 399 400 ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_MODIFY, 401 ent.principal); 402 if (ret) 403 goto fail; 404 405 ret = kadm5_modify_principal(lcontext, &ent, mask); 406 407 fail: 408 krb5_warn(lcontext->context, ret, "modify principal"); 409 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 410 CHECK(krb5_store_uint32(out, ret)); /* code */ 411 412 kadm5_free_principal_ent(lcontext, &ent); 413} 414 415static void 416proc_get_principal(kadm5_server_context *lcontext, 417 krb5_storage *in, 418 krb5_storage *out) 419{ 420 uint32_t version, mask; 421 krb5_principal principal; 422 kadm5_principal_ent_rec ent; 423 krb5_error_code ret; 424 char *princ = NULL; 425 426 memset(&ent, 0, sizeof(ent)); 427 428 CHECK(krb5_ret_uint32(in, &version)); 429 EXPECT_EGT(version, VERSION2); 430 CHECK(_kadm5_xdr_ret_principal_xdr(lcontext->context, in, &principal)); 431 CHECK(krb5_unparse_name(lcontext->context, principal, &princ)); 432 CHECK(krb5_ret_uint32(in, &mask)); 433 434 ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_GET, principal); 435 if(ret) 436 goto fail; 437 438 mask |= KADM5_KVNO | KADM5_PRINCIPAL; 439 440 ret = kadm5_get_principal(lcontext, principal, &ent, mask); 441 442 fail: 443 krb5_warn(lcontext->context, ret, "get principal: %s kvno %d", 444 princ ? princ : "<unknown>", (int)ent.kvno); 445 446 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 447 CHECK(krb5_store_uint32(out, ret)); /* code */ 448 if (ret == 0) { 449 CHECK(_kadm5_xdr_store_principal_ent(lcontext->context, out, &ent)); 450 } 451 krb5_free_principal(lcontext->context, principal); 452 kadm5_free_principal_ent(lcontext, &ent); 453} 454 455static void 456proc_chrand_principal_v2(kadm5_server_context *lcontext, 457 krb5_storage *in, 458 krb5_storage *out) 459{ 460 krb5_error_code ret; 461 krb5_principal principal; 462 uint32_t version; 463 krb5_keyblock *new_keys; 464 int n_keys; 465 char *princ = NULL; 466 467 CHECK(krb5_ret_uint32(in, &version)); 468 EXPECT_EGT(version, VERSION2); 469 CHECK(_kadm5_xdr_ret_principal_xdr(lcontext->context, in, &principal)); 470 CHECK(krb5_unparse_name(lcontext->context, principal, &princ)); 471 472 ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_CPW, principal); 473 if(ret) 474 goto fail; 475 476 ret = kadm5_randkey_principal(lcontext, principal, 477 &new_keys, &n_keys); 478 479 fail: 480 krb5_warn(lcontext->context, ret, "rand key principal v2: %s", 481 princ ? princ : "<unknown>"); 482 483 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 484 CHECK(krb5_store_uint32(out, ret)); 485 if (ret == 0) { 486 size_t i; 487 CHECK(krb5_store_int32(out, n_keys)); 488 489 for(i = 0; i < n_keys; i++){ 490 CHECK(krb5_store_uint32(out, new_keys[i].keytype)); 491 CHECK(_kadm5_xdr_store_data_xdr(out, new_keys[i].keyvalue)); 492 krb5_free_keyblock_contents(lcontext->context, &new_keys[i]); 493 } 494 free(new_keys); 495 } 496 krb5_free_principal(lcontext->context, principal); 497 if (princ) 498 free(princ); 499} 500 501static void 502proc_chrand_principal_v3(kadm5_server_context *lcontext, 503 krb5_storage *in, 504 krb5_storage *out) 505{ 506 krb5_error_code ret; 507 krb5_principal principal; 508 uint32_t version, keepold, n_ks_tuple; 509 krb5_keyblock *new_keys; 510 krb5_key_salt_tuple *ks_tuple; 511 char *princ = NULL; 512 int n_keys; 513 514 CHECK(krb5_ret_uint32(in, &version)); 515 EXPECT_EGT(version, VERSION2); 516 CHECK(_kadm5_xdr_ret_principal_xdr(lcontext->context, in, &principal)); 517 CHECK(krb5_unparse_name(lcontext->context, principal, &princ)); 518 CHECK(krb5_ret_uint32(in, &keepold)); 519 ks_tuple = parse_ks_tuple(lcontext->context, in, &n_ks_tuple); 520 521 ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_CPW, principal); 522 if(ret) 523 goto fail; 524 525 ret = kadm5_randkey_principal_3(context, principal, keepold, n_ks_tuple, ks_tuple, 526 &new_keys, &n_keys); 527 528 fail: 529 krb5_warn(lcontext->context, ret, "rand key principal v3: %s", 530 princ ? princ : "<unknown>"); 531 532 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 533 CHECK(krb5_store_uint32(out, ret)); 534 if (ret == 0) { 535 size_t i; 536 CHECK(krb5_store_int32(out, n_keys)); 537 538 for(i = 0; i < n_keys; i++){ 539 CHECK(krb5_store_uint32(out, new_keys[i].keytype)); 540 CHECK(_kadm5_xdr_store_data_xdr(out, new_keys[i].keyvalue)); 541 krb5_free_keyblock_contents(lcontext->context, &new_keys[i]); 542 } 543 free(new_keys); 544 } 545 if (ks_tuple) 546 free(ks_tuple); 547 krb5_free_principal(lcontext->context, principal); 548 if (princ) 549 free(princ); 550} 551 552 553static void 554proc_init(kadm5_server_context *lcontext, 555 krb5_storage *in, 556 krb5_storage *out) 557{ 558 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 559 CHECK(krb5_store_uint32(out, 0)); /* code */ 560 CHECK(krb5_store_uint32(out, 0)); /* code */ 561} 562 563static void 564proc_get_policy(kadm5_server_context *lcontext, 565 krb5_storage *in, 566 krb5_storage *out) 567{ 568 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */ 569 CHECK(krb5_store_uint32(out, KADM5_AUTH_GET)); /* code */ 570} 571 572 573struct proc { 574 char *name; 575 void (*func)(kadm5_server_context *, krb5_storage *, krb5_storage *); 576} procs[] = { 577 { "NULL", NULL }, 578 { "create principal", proc_create_principal }, 579 { "delete principal", proc_delete_principal }, 580 { "modify principal", proc_modify_principal }, 581 { "rename principal", NULL }, 582 { "get principal", proc_get_principal }, 583 { "chpass principal", NULL }, 584 { "chrand principal v2", proc_chrand_principal_v2 }, 585 { "create policy", NULL }, 586 { "delete policy", NULL }, 587 { "modify policy", NULL }, 588 { "get policy", proc_get_policy }, 589 { "get privs", NULL }, 590 { "init", proc_init }, 591 { "get principals", NULL }, 592 { "get polices", NULL }, 593 { "setkey principal", NULL }, 594 { "setkey principal v4", NULL }, 595 { "create principal v3", proc_create_principal3 }, 596 { "chpass principal v3", NULL }, 597 { "chrand principal v3", proc_chrand_principal_v3 }, 598 { "setkey principal v3", NULL } 599}; 600 601static krb5_error_code 602copyheader(krb5_storage *sp, krb5_data *data) 603{ 604 off_t off; 605 ssize_t sret; 606 607 off = krb5_storage_seek(sp, 0, SEEK_CUR); 608 609 CHECK(krb5_data_alloc(data, off)); 610 INSIST(off == data->length); 611 krb5_storage_seek(sp, 0, SEEK_SET); 612 sret = krb5_storage_read(sp, data->data, data->length); 613 INSIST(sret == off); 614 INSIST(off == krb5_storage_seek(sp, 0, SEEK_CUR)); 615 616 return 0; 617} 618 619struct gctx { 620 krb5_data handle; 621 gss_ctx_id_t ctx; 622 uint32_t seq_num; 623 uint32_t protocol; 624 int done; 625 int inprogress; 626 void *server_handle; 627 void (*verify_header)(struct gctx *, struct _kadm5_xdr_call_header *, krb5_data *); 628 void (*handle_protocol)(struct gctx *, struct _kadm5_xdr_call_header *, 629 krb5_storage *, krb5_storage *); 630 void (*reply)(struct gctx *, krb5_storage *, krb5_storage *); 631 632}; 633 634static void 635setup_context(struct gctx *gctx, gss_name_t src_name) 636{ 637 kadm5_config_params realm_params; 638 gss_buffer_desc buf; 639 OM_uint32 maj_stat, min_stat, junk; 640 krb5_error_code ret; 641 char *client; 642 643 INSIST(gctx->done); 644 645 memset(&realm_params, 0, sizeof(realm_params)); 646 647 maj_stat = gss_export_name(&min_stat, src_name, &buf); 648 EXPECT(maj_stat, GSS_S_COMPLETE); 649 EXPECT(min_stat, 0); 650 651 CHECK(parse_name(buf.value, buf.length, 652 GSS_KRB5_MECHANISM, &client)); 653 654 gss_release_buffer(&junk, &buf); 655 656 krb5_warnx(context, "%s connected", client); 657 658 ret = kadm5_s_init_with_password_ctx(context, 659 client, 660 NULL, 661 KADM5_ADMIN_SERVICE, 662 &realm_params, 663 0, 0, 664 &gctx->server_handle); 665 EXPECT(ret, 0); 666} 667 668/* 669 * GSS flavor 670 */ 671 672static void 673xpcgss_verify_header(struct gctx *gctx, struct _kadm5_xdr_call_header *chdr, krb5_data *header) 674{ 675 OM_uint32 maj_stat, min_stat; 676 gss_buffer_desc gin, gout; 677 678 EXPECT(chdr->verf.flavor, gctx->protocol); 679 680 /* from first byte to last of credential */ 681 gin.value = header->data; 682 gin.length = header->length; 683 gout.value = chdr->verf.data.data; 684 gout.length = chdr->verf.data.length; 685 686 maj_stat = gss_verify_mic(&min_stat, gctx->ctx, &gin, &gout, NULL); 687 EXPECT(maj_stat, GSS_S_COMPLETE); 688} 689 690static void 691rpcgss_handle_protocol(struct gctx *gctx, 692 struct _kadm5_xdr_call_header *chdr, 693 krb5_storage *msg, 694 krb5_storage *dreply) 695{ 696 OM_uint32 maj_stat, min_stat, junk; 697 gss_buffer_desc gin, gout; 698 struct _kadm5_xdr_gcred gcred; 699 700 memset(&gcred, 0, sizeof(gcred)); 701 702 CHECK(_kadm5_xdr_ret_gcred(&chdr->cred.data, &gcred)); 703 EXPECT(gcred.version, FLAVOR_GSS_VERSION); 704 705 switch(gcred.proc) { 706 case RPG_DATA: { 707 krb5_data data; 708 int conf_state; 709 uint32_t seq; 710 krb5_storage *sp; 711 712 EXPECT(gcred.service, rpg_privacy); 713 714 INSIST(gctx->done); 715 716 INSIST(krb5_data_cmp(&gcred.handle, &gctx->handle) == 0); 717 718 CHECK(_kadm5_xdr_ret_data_xdr(msg, &data)); 719 720 gin.value = data.data; 721 gin.length = data.length; 722 723 maj_stat = gss_unwrap(&min_stat, gctx->ctx, &gin, &gout, 724 &conf_state, NULL); 725 krb5_data_free(&data); 726 INSIST(maj_stat == GSS_S_COMPLETE); 727 INSIST(conf_state != 0); 728 729 sp = krb5_storage_from_mem(gout.value, gout.length); 730 INSIST(sp != NULL); 731 732 CHECK(krb5_ret_uint32(sp, &seq)); 733 EXPECT(seq, gcred.seq_num); 734 735 /* 736 * Check sequence number 737 */ 738 INSIST(seq > gctx->seq_num); 739 gctx->seq_num = seq; 740 741 /* 742 * If context is setup, priv data have the seq_num stored 743 * first in the block, so add it here before users data is 744 * added. 745 */ 746 CHECK(krb5_store_uint32(dreply, gctx->seq_num)); 747 748 if (chdr->proc >= sizeof(procs)/sizeof(procs[0])) { 749 krb5_warnx(context, "proc number out of array"); 750 } else if (procs[chdr->proc].func == NULL) { 751 krb5_warnx(context, "proc '%s' never implemented", 752 procs[chdr->proc].name); 753 } else { 754 krb5_warnx(context, "proc %s", procs[chdr->proc].name); 755 INSIST(gctx->server_handle != NULL); 756 (*procs[chdr->proc].func)(gctx->server_handle, sp, dreply); 757 } 758 krb5_storage_free(sp); 759 gss_release_buffer(&min_stat, &gout); 760 761 break; 762 } 763 case RPG_INIT: 764 INSIST(gctx->inprogress == 0); 765 INSIST(gctx->ctx == NULL); 766 767 gctx->inprogress = 1; 768 /* FALL THOUGH */ 769 case RPG_CONTINUE_INIT: { 770 gss_name_t src_name = GSS_C_NO_NAME; 771 krb5_data in; 772 773 INSIST(gctx->inprogress); 774 775 CHECK(_kadm5_xdr_ret_data_xdr(msg, &in)); 776 777 gin.value = in.data; 778 gin.length = in.length; 779 gout.value = NULL; 780 gout.length = 0; 781 782 maj_stat = gss_accept_sec_context(&min_stat, 783 &gctx->ctx, 784 GSS_C_NO_CREDENTIAL, 785 &gin, 786 GSS_C_NO_CHANNEL_BINDINGS, 787 &src_name, 788 NULL, 789 &gout, 790 NULL, 791 NULL, 792 NULL); 793 if (GSS_ERROR(maj_stat)) { 794 gss_print_errors(context, maj_stat, min_stat); 795 krb5_errx(context, 1, "gss error, exit"); 796 } 797 if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) { 798 799 gctx->done = 1; 800 gctx->verify_header = xpcgss_verify_header; 801 802 setup_context(gctx, src_name); 803 } 804 805 INSIST(gctx->ctx != GSS_C_NO_CONTEXT); 806 807 CHECK(_kadm5_xdr_store_gss_init_res(dreply, gctx->handle, 808 maj_stat, min_stat, SEQ_WINDOW_SIZE, &gout)); 809 if (gout.value) 810 gss_release_buffer(&junk, &gout); 811 if (src_name) 812 gss_release_name(&junk, &src_name); 813 814 break; 815 } 816 case RPG_DESTROY: 817 krb5_errx(context, 1, "client destroyed gss context"); 818 default: 819 krb5_errx(context, 1, "client sent unknown gsscode %d", 820 (int)gcred.proc); 821 } 822 823 krb5_data_free(&gcred.handle); 824} 825 826static void 827rpcgss_reply(struct gctx *gctx, krb5_storage *dreply, krb5_storage *reply) 828{ 829 uint32_t seqnum = htonl(gctx->seq_num); 830 gss_buffer_desc gin, gout; 831 OM_uint32 maj_stat, min_stat, junk; 832 krb5_data data; 833 834 /* 835 * The first checksum is really the checksum of the 836 * seq_window in the rpc_gss_init_res packet, lets agree 837 * with that. 838 */ 839 if (gctx->seq_num == 0) 840 seqnum = htonl(SEQ_WINDOW_SIZE); 841 842 gin.value = &seqnum; 843 gin.length = sizeof(seqnum); 844 845 maj_stat = gss_get_mic(&min_stat, gctx->ctx, 0, &gin, &gout); 846 INSIST(maj_stat == GSS_S_COMPLETE); 847 848 data.data = gout.value; 849 data.length = gout.length; 850 851 CHECK(krb5_store_uint32(reply, FLAVOR_GSS)); 852 CHECK(_kadm5_xdr_store_data_xdr(reply, data)); 853 gss_release_buffer(&junk, &gout); 854 855 CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */ 856 857 CHECK(krb5_storage_to_data(dreply, &data)); 858 859 if (gctx->inprogress) { 860 ssize_t sret; 861 gctx->inprogress = 0; 862 sret = krb5_storage_write(reply, data.data, data.length); 863 INSIST(sret == data.length); 864 krb5_data_free(&data); 865 } else { 866 int conf_state; 867 868 gin.value = data.data; 869 gin.length = data.length; 870 871 maj_stat = gss_wrap(&min_stat, gctx->ctx, 1, 0, 872 &gin, &conf_state, &gout); 873 INSIST(maj_stat == GSS_S_COMPLETE); 874 INSIST(conf_state != 0); 875 krb5_data_free(&data); 876 877 data.data = gout.value; 878 data.length = gout.length; 879 880 _kadm5_xdr_store_data_xdr(reply, data); 881 gss_release_buffer(&min_stat, &gout); 882 } 883} 884 885 886/* 887 * GSSAPI flavor 888 */ 889 890enum { 891 RPGA_INIT = 1, 892 RPGA_CONTINUE_INIT = 2, 893 RPGA_MSG = 3, 894 RPGA_DESTORY = 4 895}; 896 897static void 898xpcgssapi_verify_header(struct gctx *gctx, struct _kadm5_xdr_call_header *chdr, krb5_data *header) 899{ 900#if 0 901 OM_uint32 maj_stat, min_stat; 902 gss_buffer_desc gin, gout; 903 904 EXPECT(chdr->verf.flavor, gctx->protocol); 905 906 /* from first byte to last of credential */ 907 gin.value = header->data; 908 gin.length = header->length; 909 gout.value = chdr->verf.data.data; 910 gout.length = chdr->verf.data.length; 911 912 maj_stat = gss_verify_mic(&min_stat, gctx->ctx, &gin, &gout, NULL); 913 EXPECT(maj_stat, GSS_S_COMPLETE); 914#endif 915} 916 917static void 918rpcgssapi_handle_protocol(struct gctx *gctx, 919 struct _kadm5_xdr_call_header *chdr, 920 krb5_storage *msg, 921 krb5_storage *dreply) 922{ 923 struct _kadm5_xdr_gacred gacred; 924 OM_uint32 maj_stat, min_stat, junk; 925 gss_buffer_desc gin, gout, gseq; 926 927 CHECK(_kadm5_xdr_ret_gacred(&chdr->cred.data, &gacred)); 928 gseq.length = 0; 929 gseq.value = NULL; 930 931 if (gctx->done == 0 && chdr->proc == RPGA_INIT) { 932 INSIST(gacred.handle.length == 0); 933 INSIST(gctx->handle.length == 0); 934 935 CHECK(krb5_data_alloc(&gctx->handle, 16)); 936 CCRandomCopyBytes(kCCRandomDefault, gctx->handle.data, gctx->handle.length); 937 938 } else { 939 INSIST(gacred.handle.length != 0); 940 INSIST(krb5_data_cmp(&gacred.handle, &gctx->handle) == 0); 941 } 942 943 944 if (gctx->done == 0) { 945 uint32_t version; 946 krb5_data token, out; 947 948 CHECK(krb5_ret_uint32(msg, &version)); 949 CHECK(_kadm5_xdr_ret_data_xdr(msg, &token)); 950 951 switch (chdr->proc) { 952 case RPGA_INIT: 953 INSIST(gctx->inprogress == 0); 954 INSIST(gctx->ctx == NULL); 955 956 INSIST(version == 3 || version == 4); 957 958 gctx->inprogress = 1; 959 960 /* FALL THOUGH */ 961 case RPGA_CONTINUE_INIT: { 962 gss_name_t src_name = GSS_C_NO_NAME; 963 964 INSIST(gctx->inprogress); 965 966 gin.value = token.data; 967 gin.length = token.length; 968 gout.value = NULL; 969 gout.length = 0; 970 971 maj_stat = gss_accept_sec_context(&min_stat, 972 &gctx->ctx, 973 GSS_C_NO_CREDENTIAL, 974 &gin, 975 GSS_C_NO_CHANNEL_BINDINGS, 976 &src_name, 977 NULL, 978 &gout, 979 NULL, 980 NULL, 981 NULL); 982 if (GSS_ERROR(maj_stat)) { 983 gss_print_errors(context, maj_stat, min_stat); 984 krb5_errx(context, 1, "gss error, exit"); 985 } 986 if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) { 987 uint32_t netseqnum; 988 989 gctx->done = 1; 990 gctx->verify_header = xpcgssapi_verify_header; 991 992 setup_context(gctx, src_name); 993 994 CCRandomCopyBytes(kCCRandomDefault, 995 &gctx->seq_num, sizeof(gctx->seq_num)); 996 997 netseqnum = htonl(gctx->seq_num); 998 gin.value = &netseqnum; 999 gin.length = sizeof(netseqnum); 1000 1001 CHECK(gss_wrap(&junk, gctx->ctx, 0, GSS_C_QOP_DEFAULT, &gin, NULL, &gseq)); 1002 } 1003 1004 INSIST(gctx->ctx != GSS_C_NO_CONTEXT); 1005 1006 /* reply argument */ 1007 1008 CHECK(krb5_store_uint32(dreply, version)); 1009 CHECK(_kadm5_xdr_store_data_xdr(dreply, gctx->handle)); 1010 CHECK(krb5_store_uint32(dreply, maj_stat)); 1011 CHECK(krb5_store_uint32(dreply, min_stat)); 1012 1013 out.data = gout.value; 1014 out.length = gout.length; 1015 CHECK(_kadm5_xdr_store_data_xdr(dreply, out)); 1016 1017 out.data = gseq.value; 1018 out.length = gseq.length; 1019 CHECK(_kadm5_xdr_store_data_xdr(dreply, out)); 1020 1021 if (gout.value) 1022 gss_release_buffer(&junk, &gout); 1023 if (gseq.value) 1024 gss_release_buffer(&junk, &gseq); 1025 if (src_name) 1026 gss_release_name(&junk, &src_name); 1027 1028 break; 1029 } 1030 default: 1031 krb5_errx(context, 1, "unsupported init message %d", (int)chdr->proc); 1032 } 1033 krb5_data_free(&token); 1034 1035 } else if (gacred.auth_msg) { 1036 if (chdr->proc == RPGA_MSG) 1037 krb5_warnx(context, "auth message MSG not supported"); 1038 else if (chdr->proc == RPGA_DESTORY) 1039 krb5_warnx(context, "auth message DESTROY not supported"); 1040 else 1041 krb5_errx(context, 1, "auth message not supported: %d", (int)chdr->proc); 1042 } else { 1043 krb5_storage *sp; 1044 krb5_data data; 1045 int conf_state = 0; 1046 uint32_t seq; 1047 1048 INSIST(gctx->done); 1049 1050 CHECK(_kadm5_xdr_ret_data_xdr(msg, &data)); 1051 1052 gin.value = data.data; 1053 gin.length = data.length; 1054 1055 maj_stat = gss_unwrap(&min_stat, gctx->ctx, &gin, &gout, 1056 &conf_state, NULL); 1057 krb5_data_free(&data); 1058 INSIST(maj_stat == GSS_S_COMPLETE); 1059 INSIST(conf_state != 0); 1060 1061 sp = krb5_storage_from_mem(gout.value, gout.length); 1062 INSIST(sp != NULL); 1063 1064 CHECK(krb5_ret_uint32(sp, &seq)); 1065 1066 /* 1067 * Check sequence number 1068 */ 1069 INSIST(seq == gctx->seq_num + 1); 1070 gctx->seq_num = seq + 1; 1071 1072 /* 1073 * If context is setup, priv data have the seq_num stored 1074 * first in the block, so add it here before users data is 1075 * added. 1076 */ 1077 CHECK(krb5_store_uint32(dreply, gctx->seq_num)); 1078 1079 if (chdr->proc >= sizeof(procs)/sizeof(procs[0])) { 1080 krb5_warnx(context, "proc number out of array"); 1081 } else if (procs[chdr->proc].func == NULL) { 1082 krb5_warnx(context, "proc '%s' never implemented", 1083 procs[chdr->proc].name); 1084 } else { 1085 krb5_warnx(context, "proc %s", procs[chdr->proc].name); 1086 INSIST(gctx->server_handle != NULL); 1087 (*procs[chdr->proc].func)(gctx->server_handle, sp, dreply); 1088 } 1089 krb5_storage_free(sp); 1090 gss_release_buffer(&min_stat, &gout); 1091 } 1092 1093 krb5_data_free(&gacred.handle); 1094} 1095 1096static void 1097rpcgssapi_reply(struct gctx *gctx, krb5_storage *dreply, krb5_storage *reply) 1098{ 1099 uint32_t seqnum = htonl(gctx->seq_num); 1100 gss_buffer_desc gin, gout; 1101 OM_uint32 maj_stat, min_stat, junk; 1102 krb5_data data; 1103 1104 gin.value = &seqnum; 1105 gin.length = sizeof(seqnum); 1106 1107 maj_stat = gss_wrap(&min_stat, gctx->ctx, 0, GSS_C_QOP_DEFAULT, &gin, NULL, &gout); 1108 INSIST(maj_stat == GSS_S_COMPLETE); 1109 1110 data.data = gout.value; 1111 data.length = gout.length; 1112 1113 CHECK(krb5_store_uint32(reply, FLAVOR_GSS_OLD)); 1114 CHECK(_kadm5_xdr_store_data_xdr(reply, data)); 1115 gss_release_buffer(&junk, &gout); 1116 1117 CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */ 1118 1119 CHECK(krb5_storage_to_data(dreply, &data)); 1120 1121 if (gctx->inprogress) { 1122 ssize_t sret; 1123 gctx->inprogress = 0; 1124 sret = krb5_storage_write(reply, data.data, data.length); 1125 INSIST(sret == data.length); 1126 krb5_data_free(&data); 1127 } else { 1128 int conf_state; 1129 1130 gin.value = data.data; 1131 gin.length = data.length; 1132 1133 maj_stat = gss_wrap(&min_stat, gctx->ctx, 1, 0, 1134 &gin, &conf_state, &gout); 1135 INSIST(maj_stat == GSS_S_COMPLETE); 1136 INSIST(conf_state != 0); 1137 krb5_data_free(&data); 1138 1139 data.data = gout.value; 1140 data.length = gout.length; 1141 1142 _kadm5_xdr_store_data_xdr(reply, data); 1143 gss_release_buffer(&min_stat, &gout); 1144 } 1145} 1146 1147 1148/* 1149 * 1150 */ 1151 1152 1153static int 1154process_stream(krb5_context lcontext, 1155 unsigned char *buf, size_t ilen, 1156 krb5_storage *sp) 1157{ 1158 krb5_error_code ret; 1159 krb5_storage *msg, *reply, *dreply; 1160 struct gctx gctx; 1161 1162 memset(&gctx, 0, sizeof(gctx)); 1163 1164 msg = krb5_storage_emem(); 1165 reply = krb5_storage_emem(); 1166 dreply = krb5_storage_emem(); 1167 1168 /* 1169 * First packet comes partly from the caller 1170 */ 1171 1172 INSIST(ilen >= 4); 1173 1174 while (1) { 1175 struct _kadm5_xdr_call_header chdr; 1176 uint32_t mtype; 1177 krb5_data headercopy; 1178 1179 krb5_storage_truncate(dreply, 0); 1180 krb5_storage_truncate(reply, 0); 1181 krb5_storage_truncate(msg, 0); 1182 1183 krb5_data_zero(&headercopy); 1184 memset(&chdr, 0, sizeof(chdr)); 1185 1186 /* 1187 * This is very icky to handle the the auto-detection between 1188 * the Heimdal protocol and the MIT ONC-RPC based protocol. 1189 */ 1190 1191 if (ilen) { 1192 int last_fragment; 1193 unsigned long len; 1194 ssize_t slen; 1195 unsigned char tmp[4]; 1196 1197 if (ilen < 4) { 1198 memcpy(tmp, buf, ilen); 1199 slen = krb5_storage_read(sp, tmp + ilen, sizeof(tmp) - ilen); 1200 INSIST(slen == sizeof(tmp) - ilen); 1201 1202 ilen = sizeof(tmp); 1203 buf = tmp; 1204 } 1205 INSIST(ilen >= 4); 1206 1207 _krb5_get_int(buf, &len, 4); 1208 last_fragment = (len & LAST_FRAGMENT) != 0; 1209 len &= ~LAST_FRAGMENT; 1210 1211 ilen -= 4; 1212 buf += 4; 1213 1214 if (ilen) { 1215 if (len < ilen) { 1216 slen = krb5_storage_write(msg, buf, len); 1217 INSIST(slen == len); 1218 ilen -= len; 1219 len = 0; 1220 } else { 1221 slen = krb5_storage_write(msg, buf, ilen); 1222 INSIST(slen == ilen); 1223 len -= ilen; 1224 } 1225 } 1226 1227 CHECK(read_data(sp, msg, len)); 1228 1229 if (!last_fragment) { 1230 ret = collect_fragments(sp, msg); 1231 if (ret == HEIM_ERR_EOF) 1232 krb5_errx(lcontext, 0, "client disconnected"); 1233 INSIST(ret == 0); 1234 } 1235 } else { 1236 1237 ret = collect_fragments(sp, msg); 1238 if (ret == HEIM_ERR_EOF) 1239 krb5_errx(lcontext, 0, "client disconnected"); 1240 INSIST(ret == 0); 1241 } 1242 krb5_storage_seek(msg, 0, SEEK_SET); 1243 1244 CHECK(krb5_ret_uint32(msg, &chdr.xid)); 1245 CHECK(krb5_ret_uint32(msg, &mtype)); 1246 CHECK(krb5_ret_uint32(msg, &chdr.rpcvers)); 1247 CHECK(krb5_ret_uint32(msg, &chdr.prog)); 1248 CHECK(krb5_ret_uint32(msg, &chdr.vers)); 1249 CHECK(krb5_ret_uint32(msg, &chdr.proc)); 1250 CHECK(_kadm5_xdr_ret_auth_opaque(msg, &chdr.cred)); 1251 CHECK(copyheader(msg, &headercopy)); 1252 CHECK(_kadm5_xdr_ret_auth_opaque(msg, &chdr.verf)); 1253 1254 EXPECT(chdr.rpcvers, RPC_VERSION); 1255 EXPECT(chdr.prog, KADM_SERVER); 1256 EXPECT(chdr.vers, VVERSION); 1257 if (gctx.protocol == 0) { 1258 gctx.protocol = chdr.cred.flavor; 1259 1260 INSIST(gctx.handle_protocol == NULL); 1261 1262 switch(gctx.protocol) { 1263 case FLAVOR_GSS: 1264 gctx.handle_protocol = rpcgss_handle_protocol; 1265 gctx.reply = rpcgss_reply; 1266 break; 1267 case FLAVOR_GSS_OLD: 1268 gctx.handle_protocol = rpcgssapi_handle_protocol; 1269 gctx.reply = rpcgssapi_reply; 1270 break; 1271 default: 1272 krb5_errx(lcontext, 0, "unsupported protocol version: %d", (int)gctx.protocol); 1273 } 1274 1275 } else { 1276 EXPECT(chdr.cred.flavor, gctx.protocol); 1277 } 1278 1279 if (gctx.verify_header) 1280 gctx.verify_header(&gctx, &chdr, &headercopy); 1281 1282 gctx.handle_protocol(&gctx, &chdr, msg, dreply); 1283 1284 krb5_data_free(&chdr.cred.data); 1285 krb5_data_free(&chdr.verf.data); 1286 krb5_data_free(&headercopy); 1287 1288 CHECK(krb5_store_uint32(reply, chdr.xid)); 1289 CHECK(krb5_store_uint32(reply, 1)); /* REPLY */ 1290 CHECK(krb5_store_uint32(reply, 0)); /* MSG_ACCEPTED */ 1291 1292 if (!gctx.done) { 1293 krb5_data data; 1294 1295 CHECK(krb5_store_uint32(reply, 0)); /* flavor_none */ 1296 CHECK(krb5_store_uint32(reply, 0)); /* length */ 1297 1298 CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */ 1299 1300 CHECK(krb5_storage_to_data(dreply, &data)); 1301 INSIST(krb5_storage_write(reply, data.data, data.length) == data.length); 1302 krb5_data_free(&data); 1303 } else { 1304 INSIST(gctx.reply != NULL); 1305 1306 gctx.reply(&gctx, dreply, reply); 1307 } 1308 1309 { 1310 krb5_data data; 1311 ssize_t sret; 1312 CHECK(krb5_storage_to_data(reply, &data)); 1313 CHECK(krb5_store_uint32(sp, ((uint32_t)data.length) | LAST_FRAGMENT)); 1314 sret = krb5_storage_write(sp, data.data, data.length); 1315 INSIST(sret == data.length); 1316 krb5_data_free(&data); 1317 } 1318 1319 } 1320} 1321 1322int 1323handle_mit(krb5_context lcontext, void *buf, size_t len, krb5_socket_t sock) 1324{ 1325 krb5_storage *sp; 1326 1327 dcontext = context; 1328 1329 sp = krb5_storage_from_fd(sock); 1330 INSIST(sp != NULL); 1331 1332 process_stream(lcontext, buf, len, sp); 1333 1334 return 0; 1335} 1336