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