rpc.c
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 68 static 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 85 enum { 86 RPG_DATA = 0, 87 RPG_INIT = 1, 88 RPG_CONTINUE_INIT = 2, 89 RPG_DESTROY = 3 90 }; 91 92 enum { 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 106 static int 107 parse_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 162 static void 163 gss_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 185 static void 186 gss_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 193 static int 194 read_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((size_t)slen == tlen); 207 208 slen = krb5_storage_write(msg, buf, tlen); 209 INSIST((size_t)slen == tlen); 210 211 len -= tlen; 212 } 213 return 0; 214 } 215 216 static int 217 collect_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 244 static void 245 proc_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 285 static krb5_key_salt_tuple * 286 parse_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 311 static void 312 proc_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 355 static void 356 proc_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 385 static void 386 proc_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 415 static void 416 proc_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 455 static void 456 proc_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 = NULL; 464 int n_keys = 0; 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 < (size_t)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 501 static void 502 proc_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 = NULL; 510 krb5_key_salt_tuple *ks_tuple = NULL; 511 char *princ = NULL; 512 int n_keys = 0; 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 int 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 553 static void 554 proc_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 563 static void 564 proc_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 573 struct 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 601 static krb5_error_code 602 copyheader(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 >= 0 && (size_t)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 619 struct 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 634 static void 635 setup_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 672 static void 673 xpcgss_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 690 static void 691 rpcgss_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 826 static void 827 rpcgss_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 >= 0 && (size_t)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 890 enum { 891 RPGA_INIT = 1, 892 RPGA_CONTINUE_INIT = 2, 893 RPGA_MSG = 3, 894 RPGA_DESTORY = 4 895 }; 896 897 static void 898 xpcgssapi_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 917 static void 918 rpcgssapi_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 krb5_generate_random_block(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 krb5_generate_random_block(&gctx->seq_num, sizeof(gctx->seq_num)); 995 996 netseqnum = htonl(gctx->seq_num); 997 gin.value = &netseqnum; 998 gin.length = sizeof(netseqnum); 999 1000 CHECK(gss_wrap(&junk, gctx->ctx, 0, GSS_C_QOP_DEFAULT, &gin, NULL, &gseq)); 1001 } 1002 1003 INSIST(gctx->ctx != GSS_C_NO_CONTEXT); 1004 1005 /* reply argument */ 1006 1007 CHECK(krb5_store_uint32(dreply, version)); 1008 CHECK(_kadm5_xdr_store_data_xdr(dreply, gctx->handle)); 1009 CHECK(krb5_store_uint32(dreply, maj_stat)); 1010 CHECK(krb5_store_uint32(dreply, min_stat)); 1011 1012 out.data = gout.value; 1013 out.length = gout.length; 1014 CHECK(_kadm5_xdr_store_data_xdr(dreply, out)); 1015 1016 out.data = gseq.value; 1017 out.length = gseq.length; 1018 CHECK(_kadm5_xdr_store_data_xdr(dreply, out)); 1019 1020 if (gout.value) 1021 gss_release_buffer(&junk, &gout); 1022 if (gseq.value) 1023 gss_release_buffer(&junk, &gseq); 1024 if (src_name) 1025 gss_release_name(&junk, &src_name); 1026 1027 break; 1028 } 1029 default: 1030 krb5_errx(context, 1, "unsupported init message %d", (int)chdr->proc); 1031 } 1032 krb5_data_free(&token); 1033 1034 } else if (gacred.auth_msg) { 1035 if (chdr->proc == RPGA_MSG) 1036 krb5_warnx(context, "auth message MSG not supported"); 1037 else if (chdr->proc == RPGA_DESTORY) 1038 krb5_warnx(context, "auth message DESTROY not supported"); 1039 else 1040 krb5_errx(context, 1, "auth message not supported: %d", (int)chdr->proc); 1041 } else { 1042 krb5_storage *sp; 1043 krb5_data data; 1044 int conf_state = 0; 1045 uint32_t seq; 1046 1047 INSIST(gctx->done); 1048 1049 CHECK(_kadm5_xdr_ret_data_xdr(msg, &data)); 1050 1051 gin.value = data.data; 1052 gin.length = data.length; 1053 1054 maj_stat = gss_unwrap(&min_stat, gctx->ctx, &gin, &gout, 1055 &conf_state, NULL); 1056 krb5_data_free(&data); 1057 INSIST(maj_stat == GSS_S_COMPLETE); 1058 INSIST(conf_state != 0); 1059 1060 sp = krb5_storage_from_mem(gout.value, gout.length); 1061 INSIST(sp != NULL); 1062 1063 CHECK(krb5_ret_uint32(sp, &seq)); 1064 1065 /* 1066 * Check sequence number 1067 */ 1068 INSIST(seq == gctx->seq_num + 1); 1069 gctx->seq_num = seq + 1; 1070 1071 /* 1072 * If context is setup, priv data have the seq_num stored 1073 * first in the block, so add it here before users data is 1074 * added. 1075 */ 1076 CHECK(krb5_store_uint32(dreply, gctx->seq_num)); 1077 1078 if (chdr->proc >= sizeof(procs)/sizeof(procs[0])) { 1079 krb5_warnx(context, "proc number out of array"); 1080 } else if (procs[chdr->proc].func == NULL) { 1081 krb5_warnx(context, "proc '%s' never implemented", 1082 procs[chdr->proc].name); 1083 } else { 1084 krb5_warnx(context, "proc %s", procs[chdr->proc].name); 1085 INSIST(gctx->server_handle != NULL); 1086 (*procs[chdr->proc].func)(gctx->server_handle, sp, dreply); 1087 } 1088 krb5_storage_free(sp); 1089 gss_release_buffer(&min_stat, &gout); 1090 } 1091 1092 krb5_data_free(&gacred.handle); 1093 } 1094 1095 static void 1096 rpcgssapi_reply(struct gctx *gctx, krb5_storage *dreply, krb5_storage *reply) 1097 { 1098 uint32_t seqnum = htonl(gctx->seq_num); 1099 gss_buffer_desc gin, gout; 1100 OM_uint32 maj_stat, min_stat, junk; 1101 krb5_data data; 1102 1103 gin.value = &seqnum; 1104 gin.length = sizeof(seqnum); 1105 1106 maj_stat = gss_wrap(&min_stat, gctx->ctx, 0, GSS_C_QOP_DEFAULT, &gin, NULL, &gout); 1107 INSIST(maj_stat == GSS_S_COMPLETE); 1108 1109 data.data = gout.value; 1110 data.length = gout.length; 1111 1112 CHECK(krb5_store_uint32(reply, FLAVOR_GSS_OLD)); 1113 CHECK(_kadm5_xdr_store_data_xdr(reply, data)); 1114 gss_release_buffer(&junk, &gout); 1115 1116 CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */ 1117 1118 CHECK(krb5_storage_to_data(dreply, &data)); 1119 1120 if (gctx->inprogress) { 1121 ssize_t sret; 1122 gctx->inprogress = 0; 1123 sret = krb5_storage_write(reply, data.data, data.length); 1124 INSIST(sret >= 0 && (size_t)sret == data.length); 1125 krb5_data_free(&data); 1126 } else { 1127 int conf_state; 1128 1129 gin.value = data.data; 1130 gin.length = data.length; 1131 1132 maj_stat = gss_wrap(&min_stat, gctx->ctx, 1, 0, 1133 &gin, &conf_state, &gout); 1134 INSIST(maj_stat == GSS_S_COMPLETE); 1135 INSIST(conf_state != 0); 1136 krb5_data_free(&data); 1137 1138 data.data = gout.value; 1139 data.length = gout.length; 1140 1141 _kadm5_xdr_store_data_xdr(reply, data); 1142 gss_release_buffer(&min_stat, &gout); 1143 } 1144 } 1145 1146 1147 /* 1148 * 1149 */ 1150 1151 1152 static int 1153 process_stream(krb5_context lcontext, 1154 unsigned char *buf, size_t ilen, 1155 krb5_storage *sp) 1156 { 1157 krb5_error_code ret; 1158 krb5_storage *msg, *reply, *dreply; 1159 struct gctx gctx; 1160 1161 memset(&gctx, 0, sizeof(gctx)); 1162 1163 msg = krb5_storage_emem(); 1164 reply = krb5_storage_emem(); 1165 dreply = krb5_storage_emem(); 1166 1167 /* 1168 * First packet comes partly from the caller 1169 */ 1170 1171 INSIST(ilen >= 4); 1172 1173 while (1) { 1174 struct _kadm5_xdr_call_header chdr; 1175 uint32_t mtype; 1176 krb5_data headercopy; 1177 1178 krb5_storage_truncate(dreply, 0); 1179 krb5_storage_truncate(reply, 0); 1180 krb5_storage_truncate(msg, 0); 1181 1182 krb5_data_zero(&headercopy); 1183 memset(&chdr, 0, sizeof(chdr)); 1184 1185 /* 1186 * This is very icky to handle the the auto-detection between 1187 * the Heimdal protocol and the MIT ONC-RPC based protocol. 1188 */ 1189 1190 if (ilen) { 1191 int last_fragment; 1192 unsigned long len; 1193 ssize_t slen; 1194 unsigned char tmp[4]; 1195 1196 if (ilen < 4) { 1197 memcpy(tmp, buf, ilen); 1198 slen = krb5_storage_read(sp, tmp + ilen, sizeof(tmp) - ilen); 1199 INSIST(slen >= 0 && (size_t)slen == sizeof(tmp) - ilen); 1200 1201 ilen = sizeof(tmp); 1202 buf = tmp; 1203 } 1204 INSIST(ilen >= 4); 1205 1206 _krb5_get_int(buf, &len, 4); 1207 last_fragment = (len & LAST_FRAGMENT) != 0; 1208 len &= ~LAST_FRAGMENT; 1209 1210 ilen -= 4; 1211 buf += 4; 1212 1213 if (ilen) { 1214 if (len < ilen) { 1215 slen = krb5_storage_write(msg, buf, len); 1216 INSIST(slen >= 0 && (size_t)slen == len); 1217 ilen -= len; 1218 len = 0; 1219 } else { 1220 slen = krb5_storage_write(msg, buf, ilen); 1221 INSIST(slen >= 0 && (size_t)slen == ilen); 1222 len -= ilen; 1223 } 1224 } 1225 1226 CHECK(read_data(sp, msg, len)); 1227 1228 if (!last_fragment) { 1229 ret = collect_fragments(sp, msg); 1230 if (ret == HEIM_ERR_EOF) 1231 krb5_errx(lcontext, 0, "client disconnected"); 1232 INSIST(ret == 0); 1233 } 1234 } else { 1235 1236 ret = collect_fragments(sp, msg); 1237 if (ret == HEIM_ERR_EOF) 1238 krb5_errx(lcontext, 0, "client disconnected"); 1239 INSIST(ret == 0); 1240 } 1241 krb5_storage_seek(msg, 0, SEEK_SET); 1242 1243 CHECK(krb5_ret_uint32(msg, &chdr.xid)); 1244 CHECK(krb5_ret_uint32(msg, &mtype)); 1245 CHECK(krb5_ret_uint32(msg, &chdr.rpcvers)); 1246 CHECK(krb5_ret_uint32(msg, &chdr.prog)); 1247 CHECK(krb5_ret_uint32(msg, &chdr.vers)); 1248 CHECK(krb5_ret_uint32(msg, &chdr.proc)); 1249 CHECK(_kadm5_xdr_ret_auth_opaque(msg, &chdr.cred)); 1250 CHECK(copyheader(msg, &headercopy)); 1251 CHECK(_kadm5_xdr_ret_auth_opaque(msg, &chdr.verf)); 1252 1253 EXPECT(chdr.rpcvers, RPC_VERSION); 1254 EXPECT(chdr.prog, KADM_SERVER); 1255 EXPECT(chdr.vers, VVERSION); 1256 if (gctx.protocol == 0) { 1257 gctx.protocol = chdr.cred.flavor; 1258 1259 INSIST(gctx.handle_protocol == NULL); 1260 1261 switch(gctx.protocol) { 1262 case FLAVOR_GSS: 1263 gctx.handle_protocol = rpcgss_handle_protocol; 1264 gctx.reply = rpcgss_reply; 1265 break; 1266 case FLAVOR_GSS_OLD: 1267 gctx.handle_protocol = rpcgssapi_handle_protocol; 1268 gctx.reply = rpcgssapi_reply; 1269 break; 1270 default: 1271 krb5_errx(lcontext, 0, "unsupported protocol version: %d", (int)gctx.protocol); 1272 } 1273 1274 } else { 1275 EXPECT(chdr.cred.flavor, gctx.protocol); 1276 } 1277 1278 if (gctx.verify_header) 1279 gctx.verify_header(&gctx, &chdr, &headercopy); 1280 1281 gctx.handle_protocol(&gctx, &chdr, msg, dreply); 1282 1283 krb5_data_free(&chdr.cred.data); 1284 krb5_data_free(&chdr.verf.data); 1285 krb5_data_free(&headercopy); 1286 1287 CHECK(krb5_store_uint32(reply, chdr.xid)); 1288 CHECK(krb5_store_uint32(reply, 1)); /* REPLY */ 1289 CHECK(krb5_store_uint32(reply, 0)); /* MSG_ACCEPTED */ 1290 1291 if (!gctx.done) { 1292 krb5_data data; 1293 ssize_t sret; 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 sret = krb5_storage_write(reply, data.data, data.length); 1302 INSIST(sret >= 0 && (size_t)sret == data.length); 1303 krb5_data_free(&data); 1304 } else { 1305 INSIST(gctx.reply != NULL); 1306 1307 gctx.reply(&gctx, dreply, reply); 1308 } 1309 1310 { 1311 krb5_data data; 1312 ssize_t sret; 1313 CHECK(krb5_storage_to_data(reply, &data)); 1314 CHECK(krb5_store_uint32(sp, ((uint32_t)data.length) | LAST_FRAGMENT)); 1315 sret = krb5_storage_write(sp, data.data, data.length); 1316 INSIST(sret >= 0 && (size_t)sret == data.length); 1317 krb5_data_free(&data); 1318 } 1319 1320 } 1321 } 1322 1323 int 1324 handle_mit(krb5_context lcontext, void *buf, size_t len, krb5_socket_t sock) 1325 { 1326 krb5_storage *sp; 1327 1328 dcontext = context; 1329 1330 sp = krb5_storage_from_fd(sock); 1331 INSIST(sp != NULL); 1332 1333 process_stream(lcontext, buf, len, sp); 1334 1335 return 0; 1336 }