145static size_t svc_rpc_gss_client_count; 146static uint32_t svc_rpc_gss_next_clientid = 1; 147 148#ifdef __GNUC__ 149static void svc_rpc_gss_init(void) __attribute__ ((constructor)); 150#endif 151 152static void 153svc_rpc_gss_init(void) 154{ 155 int i; 156 157 if (!svc_rpc_gss_initialised) { 158 for (i = 0; i < CLIENT_HASH_SIZE; i++) 159 TAILQ_INIT(&svc_rpc_gss_client_hash[i]); 160 TAILQ_INIT(&svc_rpc_gss_clients); 161 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss); 162 svc_rpc_gss_initialised = TRUE; 163 } 164} 165 166bool_t 167rpc_gss_set_callback(rpc_gss_callback_t *cb) 168{ 169 struct svc_rpc_gss_callback *scb; 170 171 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback)); 172 if (!scb) { 173 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 174 return (FALSE); 175 } 176 scb->cb_callback = *cb; 177 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link); 178 179 return (TRUE); 180} 181 182bool_t 183rpc_gss_set_svc_name(const char *principal, const char *mechanism, 184 u_int req_time, u_int program, u_int version) 185{ 186 OM_uint32 maj_stat, min_stat; 187 struct svc_rpc_gss_svc_name *sname; 188 gss_buffer_desc namebuf; 189 gss_name_t name; 190 gss_OID mech_oid; 191 gss_OID_set_desc oid_set; 192 gss_cred_id_t cred; 193 194 svc_rpc_gss_init(); 195 196 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid)) 197 return (FALSE); 198 oid_set.count = 1; 199 oid_set.elements = mech_oid; 200 201 namebuf.value = (void *)(intptr_t) principal; 202 namebuf.length = strlen(principal); 203 204 maj_stat = gss_import_name(&min_stat, &namebuf, 205 GSS_C_NT_HOSTBASED_SERVICE, &name); 206 if (maj_stat != GSS_S_COMPLETE) 207 return (FALSE); 208 209 maj_stat = gss_acquire_cred(&min_stat, name, 210 req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL); 211 if (maj_stat != GSS_S_COMPLETE) 212 return (FALSE); 213 214 gss_release_name(&min_stat, &name); 215 216 sname = malloc(sizeof(struct svc_rpc_gss_svc_name)); 217 if (!sname) 218 return (FALSE); 219 sname->sn_principal = strdup(principal); 220 sname->sn_mech = mech_oid; 221 sname->sn_req_time = req_time; 222 sname->sn_cred = cred; 223 sname->sn_program = program; 224 sname->sn_version = version; 225 SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link); 226 227 return (TRUE); 228} 229 230bool_t 231rpc_gss_get_principal_name(rpc_gss_principal_t *principal, 232 const char *mech, const char *name, const char *node, const char *domain) 233{ 234 OM_uint32 maj_stat, min_stat; 235 gss_OID mech_oid; 236 size_t namelen; 237 gss_buffer_desc buf; 238 gss_name_t gss_name, gss_mech_name; 239 rpc_gss_principal_t result; 240 241 svc_rpc_gss_init(); 242 243 if (!rpc_gss_mech_to_oid(mech, &mech_oid)) 244 return (FALSE); 245 246 /* 247 * Construct a gss_buffer containing the full name formatted 248 * as "name/node@domain" where node and domain are optional. 249 */ 250 namelen = strlen(name); 251 if (node) { 252 namelen += strlen(node) + 1; 253 } 254 if (domain) { 255 namelen += strlen(domain) + 1; 256 } 257 258 buf.value = mem_alloc(namelen); 259 buf.length = namelen; 260 strcpy((char *) buf.value, name); 261 if (node) { 262 strcat((char *) buf.value, "/"); 263 strcat((char *) buf.value, node); 264 } 265 if (domain) { 266 strcat((char *) buf.value, "@"); 267 strcat((char *) buf.value, domain); 268 } 269 270 /* 271 * Convert that to a gss_name_t and then convert that to a 272 * mechanism name in the selected mechanism. 273 */ 274 maj_stat = gss_import_name(&min_stat, &buf, 275 GSS_C_NT_USER_NAME, &gss_name); 276 mem_free(buf.value, buf.length); 277 if (maj_stat != GSS_S_COMPLETE) { 278 log_status("gss_import_name", mech_oid, maj_stat, min_stat); 279 return (FALSE); 280 } 281 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid, 282 &gss_mech_name); 283 if (maj_stat != GSS_S_COMPLETE) { 284 log_status("gss_canonicalize_name", mech_oid, maj_stat, 285 min_stat); 286 gss_release_name(&min_stat, &gss_name); 287 return (FALSE); 288 } 289 gss_release_name(&min_stat, &gss_name); 290 291 /* 292 * Export the mechanism name and use that to construct the 293 * rpc_gss_principal_t result. 294 */ 295 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf); 296 if (maj_stat != GSS_S_COMPLETE) { 297 log_status("gss_export_name", mech_oid, maj_stat, min_stat); 298 gss_release_name(&min_stat, &gss_mech_name); 299 return (FALSE); 300 } 301 gss_release_name(&min_stat, &gss_mech_name); 302 303 result = mem_alloc(sizeof(int) + buf.length); 304 if (!result) { 305 gss_release_buffer(&min_stat, &buf); 306 return (FALSE); 307 } 308 result->len = buf.length; 309 memcpy(result->name, buf.value, buf.length); 310 gss_release_buffer(&min_stat, &buf); 311 312 *principal = result; 313 return (TRUE); 314} 315 316bool_t 317rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred, 318 rpc_gss_ucred_t **ucred, void **cookie) 319{ 320 struct svc_rpc_gss_client *client; 321 322 if (req->rq_cred.oa_flavor != RPCSEC_GSS) 323 return (FALSE); 324 325 client = req->rq_clntcred; 326 if (rcred) 327 *rcred = &client->cl_rawcred; 328 if (ucred) 329 *ucred = &client->cl_ucred; 330 if (cookie) 331 *cookie = client->cl_cookie; 332 return (TRUE); 333} 334 335int 336rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len) 337{ 338 struct svc_rpc_gss_client *client = req->rq_clntcred; 339 int want_conf; 340 OM_uint32 max; 341 OM_uint32 maj_stat, min_stat; 342 int result; 343 344 switch (client->cl_rawcred.service) { 345 case rpc_gss_svc_none: 346 return (max_tp_unit_len); 347 break; 348 349 case rpc_gss_svc_default: 350 case rpc_gss_svc_integrity: 351 want_conf = FALSE; 352 break; 353 354 case rpc_gss_svc_privacy: 355 want_conf = TRUE; 356 break; 357 358 default: 359 return (0); 360 } 361 362 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf, 363 client->cl_qop, max_tp_unit_len, &max); 364 365 if (maj_stat == GSS_S_COMPLETE) { 366 result = (int) max; 367 if (result < 0) 368 result = 0; 369 return (result); 370 } else { 371 log_status("gss_wrap_size_limit", client->cl_mech, 372 maj_stat, min_stat); 373 return (0); 374 } 375} 376 377static struct svc_rpc_gss_client * 378svc_rpc_gss_find_client(uint32_t clientid) 379{ 380 struct svc_rpc_gss_client *client; 381 struct svc_rpc_gss_client_list *list; 382 383 384 log_debug("in svc_rpc_gss_find_client(%d)", clientid); 385 386 list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE]; 387 TAILQ_FOREACH(client, list, cl_link) { 388 if (client->cl_id == clientid) { 389 /* 390 * Move this client to the front of the LRU 391 * list. 392 */ 393 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 394 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, 395 cl_alllink); 396 return client; 397 } 398 } 399 400 return (NULL); 401} 402 403static struct svc_rpc_gss_client * 404svc_rpc_gss_create_client(void) 405{ 406 struct svc_rpc_gss_client *client; 407 struct svc_rpc_gss_client_list *list; 408 409 log_debug("in svc_rpc_gss_create_client()"); 410 411 client = mem_alloc(sizeof(struct svc_rpc_gss_client)); 412 memset(client, 0, sizeof(struct svc_rpc_gss_client)); 413 client->cl_id = svc_rpc_gss_next_clientid++; 414 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE]; 415 TAILQ_INSERT_HEAD(list, client, cl_link); 416 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink); 417 418 /* 419 * Start the client off with a short expiration time. We will 420 * try to get a saner value from the client creds later. 421 */ 422 client->cl_state = CLIENT_NEW; 423 client->cl_locked = FALSE; 424 client->cl_expiration = time(0) + 5*60; 425 svc_rpc_gss_client_count++; 426 427 return (client); 428} 429 430static void 431svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client) 432{ 433 struct svc_rpc_gss_client_list *list; 434 OM_uint32 min_stat; 435 436 log_debug("in svc_rpc_gss_destroy_client()"); 437 438 if (client->cl_ctx) 439 gss_delete_sec_context(&min_stat, 440 &client->cl_ctx, GSS_C_NO_BUFFER); 441 442 if (client->cl_cname) 443 gss_release_name(&min_stat, &client->cl_cname); 444 445 if (client->cl_rawcred.client_principal) 446 mem_free(client->cl_rawcred.client_principal, 447 sizeof(*client->cl_rawcred.client_principal) 448 + client->cl_rawcred.client_principal->len); 449 450 if (client->cl_verf.value) 451 gss_release_buffer(&min_stat, &client->cl_verf); 452 453 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE]; 454 TAILQ_REMOVE(list, client, cl_link); 455 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 456 svc_rpc_gss_client_count--; 457 mem_free(client, sizeof(*client)); 458} 459 460static void 461svc_rpc_gss_timeout_clients(void) 462{ 463 struct svc_rpc_gss_client *client; 464 struct svc_rpc_gss_client *nclient; 465 time_t now = time(0); 466 467 log_debug("in svc_rpc_gss_timeout_clients()"); 468 /* 469 * First enforce the max client limit. We keep 470 * svc_rpc_gss_clients in LRU order. 471 */ 472 while (svc_rpc_gss_client_count > CLIENT_MAX) 473 svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients, 474 svc_rpc_gss_client_list)); 475 TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) { 476 if (client->cl_state == CLIENT_STALE 477 || now > client->cl_expiration) { 478 log_debug("expiring client %p", client); 479 svc_rpc_gss_destroy_client(client); 480 } 481 } 482} 483 484#ifdef DEBUG 485/* 486 * OID<->string routines. These are uuuuugly. 487 */ 488static OM_uint32 489gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) 490{ 491 char numstr[128]; 492 unsigned long number; 493 int numshift; 494 size_t string_length; 495 size_t i; 496 unsigned char *cp; 497 char *bp; 498 499 /* Decoded according to krb5/gssapi_krb5.c */ 500 501 /* First determine the size of the string */ 502 string_length = 0; 503 number = 0; 504 numshift = 0; 505 cp = (unsigned char *) oid->elements; 506 number = (unsigned long) cp[0]; 507 sprintf(numstr, "%ld ", number/40); 508 string_length += strlen(numstr); 509 sprintf(numstr, "%ld ", number%40); 510 string_length += strlen(numstr); 511 for (i=1; i<oid->length; i++) { 512 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { 513 number = (number << 7) | (cp[i] & 0x7f); 514 numshift += 7; 515 } 516 else { 517 *minor_status = 0; 518 return(GSS_S_FAILURE); 519 } 520 if ((cp[i] & 0x80) == 0) { 521 sprintf(numstr, "%ld ", number); 522 string_length += strlen(numstr); 523 number = 0; 524 numshift = 0; 525 } 526 } 527 /* 528 * If we get here, we've calculated the length of "n n n ... n ". Add 4 529 * here for "{ " and "}\0". 530 */ 531 string_length += 4; 532 if ((bp = (char *) mem_alloc(string_length))) { 533 strcpy(bp, "{ "); 534 number = (unsigned long) cp[0]; 535 sprintf(numstr, "%ld ", number/40); 536 strcat(bp, numstr); 537 sprintf(numstr, "%ld ", number%40); 538 strcat(bp, numstr); 539 number = 0; 540 cp = (unsigned char *) oid->elements; 541 for (i=1; i<oid->length; i++) { 542 number = (number << 7) | (cp[i] & 0x7f); 543 if ((cp[i] & 0x80) == 0) { 544 sprintf(numstr, "%ld ", number); 545 strcat(bp, numstr); 546 number = 0; 547 } 548 } 549 strcat(bp, "}"); 550 oid_str->length = strlen(bp)+1; 551 oid_str->value = (void *) bp; 552 *minor_status = 0; 553 return(GSS_S_COMPLETE); 554 } 555 *minor_status = 0; 556 return(GSS_S_FAILURE); 557} 558#endif 559 560static void 561svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, 562 const gss_name_t name) 563{ 564 OM_uint32 maj_stat, min_stat; 565 char buf[128]; 566 uid_t uid; 567 struct passwd pwd, *pw; 568 rpc_gss_ucred_t *uc = &client->cl_ucred; 569 570 uc->uid = 65534; 571 uc->gid = 65534; 572 uc->gidlen = 0; 573 uc->gidlist = client->cl_gid_storage; 574 575 maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid); 576 if (maj_stat != GSS_S_COMPLETE) 577 return; 578 579 getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw); 580 if (pw) { 581 int len = NGRPS; 582 uc->uid = pw->pw_uid; 583 uc->gid = pw->pw_gid; 584 uc->gidlist = client->cl_gid_storage; 585 getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len); 586 uc->gidlen = len; 587 } 588} 589 590static bool_t 591svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, 592 struct svc_req *rqst, 593 struct rpc_gss_init_res *gr, 594 struct rpc_gss_cred *gc) 595{ 596 gss_buffer_desc recv_tok; 597 gss_OID mech; 598 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; 599 OM_uint32 cred_lifetime; 600 struct svc_rpc_gss_svc_name *sname; 601 602 log_debug("in svc_rpc_gss_accept_context()"); 603 604 /* Deserialize arguments. */ 605 memset(&recv_tok, 0, sizeof(recv_tok)); 606 607 if (!svc_getargs(rqst->rq_xprt, 608 (xdrproc_t) xdr_gss_buffer_desc, 609 (caddr_t) &recv_tok)) { 610 client->cl_state = CLIENT_STALE; 611 return (FALSE); 612 } 613 614 /* 615 * First time round, try all the server names we have until 616 * one matches. Afterwards, stick with that one. 617 */ 618 if (!client->cl_sname) { 619 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 620 if (sname->sn_program == rqst->rq_prog 621 && sname->sn_version == rqst->rq_vers) { 622 gr->gr_major = gss_accept_sec_context( 623 &gr->gr_minor, 624 &client->cl_ctx, 625 sname->sn_cred, 626 &recv_tok, 627 GSS_C_NO_CHANNEL_BINDINGS, 628 &client->cl_cname, 629 &mech, 630 &gr->gr_token, 631 &ret_flags, 632 &cred_lifetime, 633 &client->cl_creds); 634 if (gr->gr_major == GSS_S_COMPLETE 635 || gr->gr_major == GSS_S_CONTINUE_NEEDED) { 636 client->cl_sname = sname; 637 break; 638 } 639 client->cl_sname = sname; 640 break; 641 } 642 } 643 if (!sname) { 644 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 645 (char *) &recv_tok); 646 return (FALSE); 647 } 648 } else { 649 gr->gr_major = gss_accept_sec_context( 650 &gr->gr_minor, 651 &client->cl_ctx, 652 client->cl_sname->sn_cred, 653 &recv_tok, 654 GSS_C_NO_CHANNEL_BINDINGS, 655 &client->cl_cname, 656 &mech, 657 &gr->gr_token, 658 &ret_flags, 659 &cred_lifetime, 660 NULL); 661 } 662 663 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); 664 665 /* 666 * If we get an error from gss_accept_sec_context, send the 667 * reply anyway so that the client gets a chance to see what 668 * is wrong. 669 */ 670 if (gr->gr_major != GSS_S_COMPLETE && 671 gr->gr_major != GSS_S_CONTINUE_NEEDED) { 672 log_status("accept_sec_context", client->cl_mech, 673 gr->gr_major, gr->gr_minor); 674 client->cl_state = CLIENT_STALE; 675 return (TRUE); 676 } 677 678 gr->gr_handle.value = &client->cl_id; 679 gr->gr_handle.length = sizeof(client->cl_id); 680 gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 681 682 /* Save client info. */ 683 client->cl_mech = mech; 684 client->cl_qop = GSS_C_QOP_DEFAULT; 685 client->cl_seq = gc->gc_seq; 686 client->cl_win = gr->gr_win; 687 client->cl_done_callback = FALSE; 688 689 if (gr->gr_major == GSS_S_COMPLETE) { 690 gss_buffer_desc export_name; 691 692 /* 693 * Change client expiration time to be near when the 694 * client creds expire (or 24 hours if we can't figure 695 * that out). 696 */ 697 if (cred_lifetime == GSS_C_INDEFINITE) 698 cred_lifetime = time(0) + 24*60*60; 699 700 client->cl_expiration = time(0) + cred_lifetime; 701 702 /* 703 * Fill in cred details in the rawcred structure. 704 */ 705 client->cl_rawcred.version = RPCSEC_GSS_VERSION; 706 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 707 maj_stat = gss_export_name(&min_stat, client->cl_cname, 708 &export_name); 709 if (maj_stat != GSS_S_COMPLETE) { 710 log_status("gss_export_name", client->cl_mech, 711 maj_stat, min_stat); 712 return (FALSE); 713 } 714 client->cl_rawcred.client_principal = 715 mem_alloc(sizeof(*client->cl_rawcred.client_principal) 716 + export_name.length); 717 client->cl_rawcred.client_principal->len = export_name.length; 718 memcpy(client->cl_rawcred.client_principal->name, 719 export_name.value, export_name.length); 720 gss_release_buffer(&min_stat, &export_name); 721 client->cl_rawcred.svc_principal = 722 client->cl_sname->sn_principal; 723 client->cl_rawcred.service = gc->gc_svc; 724 725 /* 726 * Use gss_pname_to_uid to map to unix creds. For 727 * kerberos5, this uses krb5_aname_to_localname. 728 */ 729 svc_rpc_gss_build_ucred(client, client->cl_cname); 730 gss_release_name(&min_stat, &client->cl_cname); 731 732#ifdef DEBUG 733 { 734 gss_buffer_desc mechname; 735 736 gss_oid_to_str(&min_stat, mech, &mechname); 737 738 log_debug("accepted context for %s with " 739 "<mech %.*s, qop %d, svc %d>", 740 client->cl_rawcred.client_principal->name, 741 mechname.length, (char *)mechname.value, 742 client->cl_qop, client->rawcred.service); 743 744 gss_release_buffer(&min_stat, &mechname); 745 } 746#endif /* DEBUG */ 747 } 748 return (TRUE); 749} 750 751static bool_t 752svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 753 gss_qop_t *qop) 754{ 755 struct opaque_auth *oa; 756 gss_buffer_desc rpcbuf, checksum; 757 OM_uint32 maj_stat, min_stat; 758 gss_qop_t qop_state; 759 int32_t rpchdr[128 / sizeof(int32_t)]; 760 int32_t *buf; 761 762 log_debug("in svc_rpc_gss_validate()"); 763 764 memset(rpchdr, 0, sizeof(rpchdr)); 765 766 /* Reconstruct RPC header for signing (from xdr_callmsg). */ 767 buf = rpchdr; 768 IXDR_PUT_LONG(buf, msg->rm_xid); 769 IXDR_PUT_ENUM(buf, msg->rm_direction); 770 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 771 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 772 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 773 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 774 oa = &msg->rm_call.cb_cred; 775 IXDR_PUT_ENUM(buf, oa->oa_flavor); 776 IXDR_PUT_LONG(buf, oa->oa_length); 777 if (oa->oa_length) { 778 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 779 buf += RNDUP(oa->oa_length) / sizeof(int32_t); 780 } 781 rpcbuf.value = rpchdr; 782 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 783 784 checksum.value = msg->rm_call.cb_verf.oa_base; 785 checksum.length = msg->rm_call.cb_verf.oa_length; 786 787 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 788 &qop_state); 789 790 if (maj_stat != GSS_S_COMPLETE) { 791 log_status("gss_verify_mic", client->cl_mech, 792 maj_stat, min_stat); 793 client->cl_state = CLIENT_STALE; 794 return (FALSE); 795 } 796 *qop = qop_state; 797 return (TRUE); 798} 799 800static bool_t 801svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 802 struct svc_req *rqst, u_int seq) 803{ 804 gss_buffer_desc signbuf; 805 OM_uint32 maj_stat, min_stat; 806 uint32_t nseq; 807 808 log_debug("in svc_rpc_gss_nextverf()"); 809 810 nseq = htonl(seq); 811 signbuf.value = &nseq; 812 signbuf.length = sizeof(nseq); 813 814 if (client->cl_verf.value) 815 gss_release_buffer(&min_stat, &client->cl_verf); 816 817 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 818 &signbuf, &client->cl_verf); 819 820 if (maj_stat != GSS_S_COMPLETE) { 821 log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 822 client->cl_state = CLIENT_STALE; 823 return (FALSE); 824 } 825 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; 826 rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value; 827 rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length; 828 829 return (TRUE); 830} 831 832static bool_t 833svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 834{ 835 struct svc_rpc_gss_callback *scb; 836 rpc_gss_lock_t lock; 837 void *cookie; 838 bool_t cb_res; 839 bool_t result; 840 841 /* 842 * See if we have a callback for this guy. 843 */ 844 result = TRUE; 845 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 846 if (scb->cb_callback.program == rqst->rq_prog 847 && scb->cb_callback.version == rqst->rq_vers) { 848 /* 849 * This one matches. Call the callback and see 850 * if it wants to veto or something. 851 */ 852 lock.locked = FALSE; 853 lock.raw_cred = &client->cl_rawcred; 854 cb_res = scb->cb_callback.callback(rqst, 855 client->cl_creds, 856 client->cl_ctx, 857 &lock, 858 &cookie); 859 860 if (!cb_res) { 861 client->cl_state = CLIENT_STALE; 862 result = FALSE; 863 break; 864 } 865 866 /* 867 * The callback accepted the connection - it 868 * is responsible for freeing client->cl_creds 869 * now. 870 */ 871 client->cl_creds = GSS_C_NO_CREDENTIAL; 872 client->cl_locked = lock.locked; 873 client->cl_cookie = cookie; 874 return (TRUE); 875 } 876 } 877 878 /* 879 * Either no callback exists for this program/version or one 880 * of the callbacks rejected the connection. We just need to 881 * clean up the delegated client creds, if any. 882 */ 883 if (client->cl_creds) { 884 OM_uint32 min_ver; 885 gss_release_cred(&min_ver, &client->cl_creds); 886 } 887 return (result); 888} 889 890static bool_t 891svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 892{ 893 u_int32_t offset; 894 int word, bit; 895 896 if (seq <= client->cl_seqlast) { 897 /* 898 * The request sequence number is less than 899 * the largest we have seen so far. If it is 900 * outside the window or if we have seen a 901 * request with this sequence before, silently 902 * discard it. 903 */ 904 offset = client->cl_seqlast - seq; 905 if (offset >= SVC_RPC_GSS_SEQWINDOW) 906 return (FALSE); 907 word = offset / 32; 908 bit = offset % 32; 909 if (client->cl_seqmask[word] & (1 << bit)) 910 return (FALSE); 911 } 912 913 return (TRUE); 914} 915 916static void 917svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) 918{ 919 int offset, i, word, bit; 920 uint32_t carry, newcarry; 921 922 if (seq > client->cl_seqlast) { 923 /* 924 * This request has a sequence number greater 925 * than any we have seen so far. Advance the 926 * seq window and set bit zero of the window 927 * (which corresponds to the new sequence 928 * number) 929 */ 930 offset = seq - client->cl_seqlast; 931 while (offset > 32) { 932 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 933 i > 0; i--) { 934 client->cl_seqmask[i] = client->cl_seqmask[i-1]; 935 } 936 client->cl_seqmask[0] = 0; 937 offset -= 32; 938 } 939 carry = 0; 940 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 941 newcarry = client->cl_seqmask[i] >> (32 - offset); 942 client->cl_seqmask[i] = 943 (client->cl_seqmask[i] << offset) | carry; 944 carry = newcarry; 945 } 946 client->cl_seqmask[0] |= 1; 947 client->cl_seqlast = seq; 948 } else { 949 offset = client->cl_seqlast - seq; 950 word = offset / 32; 951 bit = offset % 32; 952 client->cl_seqmask[word] |= (1 << bit); 953 } 954 955} 956 957enum auth_stat 958svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 959 960{ 961 OM_uint32 min_stat; 962 XDR xdrs; 963 struct svc_rpc_gss_client *client; 964 struct rpc_gss_cred gc; 965 struct rpc_gss_init_res gr; 966 gss_qop_t qop; 967 int call_stat; 968 enum auth_stat result; 969 970 log_debug("in svc_rpc_gss()"); 971 972 /* Garbage collect old clients. */ 973 svc_rpc_gss_timeout_clients(); 974 975 /* Initialize reply. */ 976 rqst->rq_xprt->xp_verf = _null_auth; 977 978 /* Deserialize client credentials. */ 979 if (rqst->rq_cred.oa_length <= 0) 980 return (AUTH_BADCRED); 981 982 memset(&gc, 0, sizeof(gc)); 983 984 xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 985 rqst->rq_cred.oa_length, XDR_DECODE); 986 987 if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 988 XDR_DESTROY(&xdrs); 989 return (AUTH_BADCRED); 990 } 991 XDR_DESTROY(&xdrs); 992 993 /* Check version. */ 994 if (gc.gc_version != RPCSEC_GSS_VERSION) { 995 result = AUTH_BADCRED; 996 goto out; 997 } 998 999 /* Check the proc and find the client (or create it) */ 1000 if (gc.gc_proc == RPCSEC_GSS_INIT) { 1001 if (gc.gc_handle.length != 0) { 1002 result = AUTH_BADCRED; 1003 goto out; 1004 } 1005 client = svc_rpc_gss_create_client(); 1006 } else { 1007 if (gc.gc_handle.length != sizeof(uint32_t)) { 1008 result = AUTH_BADCRED; 1009 goto out; 1010 } 1011 uint32_t *p = gc.gc_handle.value; 1012 client = svc_rpc_gss_find_client(*p); 1013 if (!client) { 1014 /* 1015 * Can't find the client - we may have 1016 * destroyed it - tell the other side to 1017 * re-authenticate. 1018 */ 1019 result = RPCSEC_GSS_CREDPROBLEM; 1020 goto out; 1021 } 1022 } 1023 rqst->rq_clntcred = client; 1024 1025 /* 1026 * The service and sequence number must be ignored for 1027 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 1028 */ 1029 if (gc.gc_proc != RPCSEC_GSS_INIT 1030 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { 1031 /* 1032 * Check for sequence number overflow. 1033 */ 1034 if (gc.gc_seq >= MAXSEQ) { 1035 result = RPCSEC_GSS_CTXPROBLEM; 1036 goto out; 1037 } 1038 client->cl_seq = gc.gc_seq; 1039 1040 /* 1041 * Check for valid service. 1042 */ 1043 if (gc.gc_svc != rpc_gss_svc_none && 1044 gc.gc_svc != rpc_gss_svc_integrity && 1045 gc.gc_svc != rpc_gss_svc_privacy) { 1046 result = AUTH_BADCRED; 1047 goto out; 1048 } 1049 } 1050 1051 /* Handle RPCSEC_GSS control procedure. */ 1052 switch (gc.gc_proc) { 1053 1054 case RPCSEC_GSS_INIT: 1055 case RPCSEC_GSS_CONTINUE_INIT: 1056 if (rqst->rq_proc != NULLPROC) { 1057 result = AUTH_REJECTEDCRED; 1058 break; 1059 } 1060 1061 memset(&gr, 0, sizeof(gr)); 1062 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 1063 result = AUTH_REJECTEDCRED; 1064 break; 1065 } 1066 1067 if (gr.gr_major == GSS_S_COMPLETE) { 1068 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 1069 result = AUTH_REJECTEDCRED; 1070 break; 1071 } 1072 } else { 1073 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL; 1074 rqst->rq_xprt->xp_verf.oa_length = 0; 1075 } 1076 1077 call_stat = svc_sendreply(rqst->rq_xprt, 1078 (xdrproc_t) xdr_rpc_gss_init_res, 1079 (caddr_t) &gr); 1080 1081 gss_release_buffer(&min_stat, &gr.gr_token); 1082 1083 if (!call_stat) { 1084 result = AUTH_FAILED; 1085 break; 1086 } 1087 1088 if (gr.gr_major == GSS_S_COMPLETE) 1089 client->cl_state = CLIENT_ESTABLISHED; 1090 1091 result = RPCSEC_GSS_NODISPATCH; 1092 break; 1093 1094 case RPCSEC_GSS_DATA: 1095 case RPCSEC_GSS_DESTROY: 1096 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 1097 result = RPCSEC_GSS_NODISPATCH; 1098 break; 1099 } 1100 1101 if (!svc_rpc_gss_validate(client, msg, &qop)) { 1102 result = RPCSEC_GSS_CREDPROBLEM; 1103 break; 1104 } 1105 1106 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 1107 result = RPCSEC_GSS_CTXPROBLEM; 1108 break; 1109 } 1110 1111 svc_rpc_gss_update_seq(client, gc.gc_seq); 1112 1113 /* 1114 * Change the SVCAUTH ops on the transport to point at 1115 * our own code so that we can unwrap the arguments 1116 * and wrap the result. The caller will re-set this on 1117 * every request to point to a set of null wrap/unwrap 1118 * methods. 1119 */ 1120 SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops; 1121 SVC_AUTH(rqst->rq_xprt).svc_ah_private = client; 1122 1123 if (gc.gc_proc == RPCSEC_GSS_DATA) { 1124 /* 1125 * We might be ready to do a callback to the server to 1126 * see if it wants to accept/reject the connection. 1127 */ 1128 if (!client->cl_done_callback) { 1129 client->cl_done_callback = TRUE; 1130 client->cl_qop = qop; 1131 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1132 client->cl_rawcred.mechanism, qop); 1133 if (!svc_rpc_gss_callback(client, rqst)) { 1134 result = AUTH_REJECTEDCRED; 1135 break; 1136 } 1137 } 1138 1139 /* 1140 * If the server has locked this client to a 1141 * particular service+qop pair, enforce that 1142 * restriction now. 1143 */ 1144 if (client->cl_locked) { 1145 if (client->cl_rawcred.service != gc.gc_svc) { 1146 result = AUTH_FAILED; 1147 break; 1148 } else if (client->cl_qop != qop) { 1149 result = AUTH_BADVERF; 1150 break; 1151 } 1152 } 1153 1154 /* 1155 * If the qop changed, look up the new qop 1156 * name for rawcred. 1157 */ 1158 if (client->cl_qop != qop) { 1159 client->cl_qop = qop; 1160 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1161 client->cl_rawcred.mechanism, qop); 1162 } 1163 1164 /* 1165 * Make sure we use the right service value 1166 * for unwrap/wrap. 1167 */ 1168 client->cl_rawcred.service = gc.gc_svc; 1169 1170 result = AUTH_OK; 1171 } else { 1172 if (rqst->rq_proc != NULLPROC) { 1173 result = AUTH_REJECTEDCRED; 1174 break; 1175 } 1176 1177 call_stat = svc_sendreply(rqst->rq_xprt, 1178 (xdrproc_t) xdr_void, (caddr_t) NULL); 1179 1180 if (!call_stat) { 1181 result = AUTH_FAILED; 1182 break; 1183 } 1184 1185 svc_rpc_gss_destroy_client(client); 1186 1187 result = RPCSEC_GSS_NODISPATCH; 1188 break; 1189 } 1190 break; 1191 1192 default: 1193 result = AUTH_BADCRED; 1194 break; 1195 } 1196out: 1197 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 1198 return (result); 1199} 1200 1201bool_t 1202svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 1203{ 1204 struct svc_rpc_gss_client *client; 1205 1206 log_debug("in svc_rpc_gss_wrap()"); 1207 1208 client = (struct svc_rpc_gss_client *) auth->svc_ah_private; 1209 if (client->cl_state != CLIENT_ESTABLISHED 1210 || client->cl_rawcred.service == rpc_gss_svc_none) { 1211 return xdr_func(xdrs, xdr_ptr); 1212 } 1213 return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr, 1214 client->cl_ctx, client->cl_qop, 1215 client->cl_rawcred.service, client->cl_seq)); 1216} 1217 1218bool_t 1219svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 1220{ 1221 struct svc_rpc_gss_client *client; 1222 1223 log_debug("in svc_rpc_gss_unwrap()"); 1224 1225 client = (struct svc_rpc_gss_client *) auth->svc_ah_private; 1226 if (client->cl_state != CLIENT_ESTABLISHED 1227 || client->cl_rawcred.service == rpc_gss_svc_none) { 1228 return xdr_func(xdrs, xdr_ptr); 1229 } 1230 return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr, 1231 client->cl_ctx, client->cl_qop, 1232 client->cl_rawcred.service, client->cl_seq)); 1233}
| 145static size_t svc_rpc_gss_client_count; 146static uint32_t svc_rpc_gss_next_clientid = 1; 147 148#ifdef __GNUC__ 149static void svc_rpc_gss_init(void) __attribute__ ((constructor)); 150#endif 151 152static void 153svc_rpc_gss_init(void) 154{ 155 int i; 156 157 if (!svc_rpc_gss_initialised) { 158 for (i = 0; i < CLIENT_HASH_SIZE; i++) 159 TAILQ_INIT(&svc_rpc_gss_client_hash[i]); 160 TAILQ_INIT(&svc_rpc_gss_clients); 161 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss); 162 svc_rpc_gss_initialised = TRUE; 163 } 164} 165 166bool_t 167rpc_gss_set_callback(rpc_gss_callback_t *cb) 168{ 169 struct svc_rpc_gss_callback *scb; 170 171 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback)); 172 if (!scb) { 173 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 174 return (FALSE); 175 } 176 scb->cb_callback = *cb; 177 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link); 178 179 return (TRUE); 180} 181 182bool_t 183rpc_gss_set_svc_name(const char *principal, const char *mechanism, 184 u_int req_time, u_int program, u_int version) 185{ 186 OM_uint32 maj_stat, min_stat; 187 struct svc_rpc_gss_svc_name *sname; 188 gss_buffer_desc namebuf; 189 gss_name_t name; 190 gss_OID mech_oid; 191 gss_OID_set_desc oid_set; 192 gss_cred_id_t cred; 193 194 svc_rpc_gss_init(); 195 196 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid)) 197 return (FALSE); 198 oid_set.count = 1; 199 oid_set.elements = mech_oid; 200 201 namebuf.value = (void *)(intptr_t) principal; 202 namebuf.length = strlen(principal); 203 204 maj_stat = gss_import_name(&min_stat, &namebuf, 205 GSS_C_NT_HOSTBASED_SERVICE, &name); 206 if (maj_stat != GSS_S_COMPLETE) 207 return (FALSE); 208 209 maj_stat = gss_acquire_cred(&min_stat, name, 210 req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL); 211 if (maj_stat != GSS_S_COMPLETE) 212 return (FALSE); 213 214 gss_release_name(&min_stat, &name); 215 216 sname = malloc(sizeof(struct svc_rpc_gss_svc_name)); 217 if (!sname) 218 return (FALSE); 219 sname->sn_principal = strdup(principal); 220 sname->sn_mech = mech_oid; 221 sname->sn_req_time = req_time; 222 sname->sn_cred = cred; 223 sname->sn_program = program; 224 sname->sn_version = version; 225 SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link); 226 227 return (TRUE); 228} 229 230bool_t 231rpc_gss_get_principal_name(rpc_gss_principal_t *principal, 232 const char *mech, const char *name, const char *node, const char *domain) 233{ 234 OM_uint32 maj_stat, min_stat; 235 gss_OID mech_oid; 236 size_t namelen; 237 gss_buffer_desc buf; 238 gss_name_t gss_name, gss_mech_name; 239 rpc_gss_principal_t result; 240 241 svc_rpc_gss_init(); 242 243 if (!rpc_gss_mech_to_oid(mech, &mech_oid)) 244 return (FALSE); 245 246 /* 247 * Construct a gss_buffer containing the full name formatted 248 * as "name/node@domain" where node and domain are optional. 249 */ 250 namelen = strlen(name); 251 if (node) { 252 namelen += strlen(node) + 1; 253 } 254 if (domain) { 255 namelen += strlen(domain) + 1; 256 } 257 258 buf.value = mem_alloc(namelen); 259 buf.length = namelen; 260 strcpy((char *) buf.value, name); 261 if (node) { 262 strcat((char *) buf.value, "/"); 263 strcat((char *) buf.value, node); 264 } 265 if (domain) { 266 strcat((char *) buf.value, "@"); 267 strcat((char *) buf.value, domain); 268 } 269 270 /* 271 * Convert that to a gss_name_t and then convert that to a 272 * mechanism name in the selected mechanism. 273 */ 274 maj_stat = gss_import_name(&min_stat, &buf, 275 GSS_C_NT_USER_NAME, &gss_name); 276 mem_free(buf.value, buf.length); 277 if (maj_stat != GSS_S_COMPLETE) { 278 log_status("gss_import_name", mech_oid, maj_stat, min_stat); 279 return (FALSE); 280 } 281 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid, 282 &gss_mech_name); 283 if (maj_stat != GSS_S_COMPLETE) { 284 log_status("gss_canonicalize_name", mech_oid, maj_stat, 285 min_stat); 286 gss_release_name(&min_stat, &gss_name); 287 return (FALSE); 288 } 289 gss_release_name(&min_stat, &gss_name); 290 291 /* 292 * Export the mechanism name and use that to construct the 293 * rpc_gss_principal_t result. 294 */ 295 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf); 296 if (maj_stat != GSS_S_COMPLETE) { 297 log_status("gss_export_name", mech_oid, maj_stat, min_stat); 298 gss_release_name(&min_stat, &gss_mech_name); 299 return (FALSE); 300 } 301 gss_release_name(&min_stat, &gss_mech_name); 302 303 result = mem_alloc(sizeof(int) + buf.length); 304 if (!result) { 305 gss_release_buffer(&min_stat, &buf); 306 return (FALSE); 307 } 308 result->len = buf.length; 309 memcpy(result->name, buf.value, buf.length); 310 gss_release_buffer(&min_stat, &buf); 311 312 *principal = result; 313 return (TRUE); 314} 315 316bool_t 317rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred, 318 rpc_gss_ucred_t **ucred, void **cookie) 319{ 320 struct svc_rpc_gss_client *client; 321 322 if (req->rq_cred.oa_flavor != RPCSEC_GSS) 323 return (FALSE); 324 325 client = req->rq_clntcred; 326 if (rcred) 327 *rcred = &client->cl_rawcred; 328 if (ucred) 329 *ucred = &client->cl_ucred; 330 if (cookie) 331 *cookie = client->cl_cookie; 332 return (TRUE); 333} 334 335int 336rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len) 337{ 338 struct svc_rpc_gss_client *client = req->rq_clntcred; 339 int want_conf; 340 OM_uint32 max; 341 OM_uint32 maj_stat, min_stat; 342 int result; 343 344 switch (client->cl_rawcred.service) { 345 case rpc_gss_svc_none: 346 return (max_tp_unit_len); 347 break; 348 349 case rpc_gss_svc_default: 350 case rpc_gss_svc_integrity: 351 want_conf = FALSE; 352 break; 353 354 case rpc_gss_svc_privacy: 355 want_conf = TRUE; 356 break; 357 358 default: 359 return (0); 360 } 361 362 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf, 363 client->cl_qop, max_tp_unit_len, &max); 364 365 if (maj_stat == GSS_S_COMPLETE) { 366 result = (int) max; 367 if (result < 0) 368 result = 0; 369 return (result); 370 } else { 371 log_status("gss_wrap_size_limit", client->cl_mech, 372 maj_stat, min_stat); 373 return (0); 374 } 375} 376 377static struct svc_rpc_gss_client * 378svc_rpc_gss_find_client(uint32_t clientid) 379{ 380 struct svc_rpc_gss_client *client; 381 struct svc_rpc_gss_client_list *list; 382 383 384 log_debug("in svc_rpc_gss_find_client(%d)", clientid); 385 386 list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE]; 387 TAILQ_FOREACH(client, list, cl_link) { 388 if (client->cl_id == clientid) { 389 /* 390 * Move this client to the front of the LRU 391 * list. 392 */ 393 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 394 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, 395 cl_alllink); 396 return client; 397 } 398 } 399 400 return (NULL); 401} 402 403static struct svc_rpc_gss_client * 404svc_rpc_gss_create_client(void) 405{ 406 struct svc_rpc_gss_client *client; 407 struct svc_rpc_gss_client_list *list; 408 409 log_debug("in svc_rpc_gss_create_client()"); 410 411 client = mem_alloc(sizeof(struct svc_rpc_gss_client)); 412 memset(client, 0, sizeof(struct svc_rpc_gss_client)); 413 client->cl_id = svc_rpc_gss_next_clientid++; 414 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE]; 415 TAILQ_INSERT_HEAD(list, client, cl_link); 416 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink); 417 418 /* 419 * Start the client off with a short expiration time. We will 420 * try to get a saner value from the client creds later. 421 */ 422 client->cl_state = CLIENT_NEW; 423 client->cl_locked = FALSE; 424 client->cl_expiration = time(0) + 5*60; 425 svc_rpc_gss_client_count++; 426 427 return (client); 428} 429 430static void 431svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client) 432{ 433 struct svc_rpc_gss_client_list *list; 434 OM_uint32 min_stat; 435 436 log_debug("in svc_rpc_gss_destroy_client()"); 437 438 if (client->cl_ctx) 439 gss_delete_sec_context(&min_stat, 440 &client->cl_ctx, GSS_C_NO_BUFFER); 441 442 if (client->cl_cname) 443 gss_release_name(&min_stat, &client->cl_cname); 444 445 if (client->cl_rawcred.client_principal) 446 mem_free(client->cl_rawcred.client_principal, 447 sizeof(*client->cl_rawcred.client_principal) 448 + client->cl_rawcred.client_principal->len); 449 450 if (client->cl_verf.value) 451 gss_release_buffer(&min_stat, &client->cl_verf); 452 453 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE]; 454 TAILQ_REMOVE(list, client, cl_link); 455 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 456 svc_rpc_gss_client_count--; 457 mem_free(client, sizeof(*client)); 458} 459 460static void 461svc_rpc_gss_timeout_clients(void) 462{ 463 struct svc_rpc_gss_client *client; 464 struct svc_rpc_gss_client *nclient; 465 time_t now = time(0); 466 467 log_debug("in svc_rpc_gss_timeout_clients()"); 468 /* 469 * First enforce the max client limit. We keep 470 * svc_rpc_gss_clients in LRU order. 471 */ 472 while (svc_rpc_gss_client_count > CLIENT_MAX) 473 svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients, 474 svc_rpc_gss_client_list)); 475 TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) { 476 if (client->cl_state == CLIENT_STALE 477 || now > client->cl_expiration) { 478 log_debug("expiring client %p", client); 479 svc_rpc_gss_destroy_client(client); 480 } 481 } 482} 483 484#ifdef DEBUG 485/* 486 * OID<->string routines. These are uuuuugly. 487 */ 488static OM_uint32 489gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) 490{ 491 char numstr[128]; 492 unsigned long number; 493 int numshift; 494 size_t string_length; 495 size_t i; 496 unsigned char *cp; 497 char *bp; 498 499 /* Decoded according to krb5/gssapi_krb5.c */ 500 501 /* First determine the size of the string */ 502 string_length = 0; 503 number = 0; 504 numshift = 0; 505 cp = (unsigned char *) oid->elements; 506 number = (unsigned long) cp[0]; 507 sprintf(numstr, "%ld ", number/40); 508 string_length += strlen(numstr); 509 sprintf(numstr, "%ld ", number%40); 510 string_length += strlen(numstr); 511 for (i=1; i<oid->length; i++) { 512 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { 513 number = (number << 7) | (cp[i] & 0x7f); 514 numshift += 7; 515 } 516 else { 517 *minor_status = 0; 518 return(GSS_S_FAILURE); 519 } 520 if ((cp[i] & 0x80) == 0) { 521 sprintf(numstr, "%ld ", number); 522 string_length += strlen(numstr); 523 number = 0; 524 numshift = 0; 525 } 526 } 527 /* 528 * If we get here, we've calculated the length of "n n n ... n ". Add 4 529 * here for "{ " and "}\0". 530 */ 531 string_length += 4; 532 if ((bp = (char *) mem_alloc(string_length))) { 533 strcpy(bp, "{ "); 534 number = (unsigned long) cp[0]; 535 sprintf(numstr, "%ld ", number/40); 536 strcat(bp, numstr); 537 sprintf(numstr, "%ld ", number%40); 538 strcat(bp, numstr); 539 number = 0; 540 cp = (unsigned char *) oid->elements; 541 for (i=1; i<oid->length; i++) { 542 number = (number << 7) | (cp[i] & 0x7f); 543 if ((cp[i] & 0x80) == 0) { 544 sprintf(numstr, "%ld ", number); 545 strcat(bp, numstr); 546 number = 0; 547 } 548 } 549 strcat(bp, "}"); 550 oid_str->length = strlen(bp)+1; 551 oid_str->value = (void *) bp; 552 *minor_status = 0; 553 return(GSS_S_COMPLETE); 554 } 555 *minor_status = 0; 556 return(GSS_S_FAILURE); 557} 558#endif 559 560static void 561svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, 562 const gss_name_t name) 563{ 564 OM_uint32 maj_stat, min_stat; 565 char buf[128]; 566 uid_t uid; 567 struct passwd pwd, *pw; 568 rpc_gss_ucred_t *uc = &client->cl_ucred; 569 570 uc->uid = 65534; 571 uc->gid = 65534; 572 uc->gidlen = 0; 573 uc->gidlist = client->cl_gid_storage; 574 575 maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid); 576 if (maj_stat != GSS_S_COMPLETE) 577 return; 578 579 getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw); 580 if (pw) { 581 int len = NGRPS; 582 uc->uid = pw->pw_uid; 583 uc->gid = pw->pw_gid; 584 uc->gidlist = client->cl_gid_storage; 585 getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len); 586 uc->gidlen = len; 587 } 588} 589 590static bool_t 591svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, 592 struct svc_req *rqst, 593 struct rpc_gss_init_res *gr, 594 struct rpc_gss_cred *gc) 595{ 596 gss_buffer_desc recv_tok; 597 gss_OID mech; 598 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; 599 OM_uint32 cred_lifetime; 600 struct svc_rpc_gss_svc_name *sname; 601 602 log_debug("in svc_rpc_gss_accept_context()"); 603 604 /* Deserialize arguments. */ 605 memset(&recv_tok, 0, sizeof(recv_tok)); 606 607 if (!svc_getargs(rqst->rq_xprt, 608 (xdrproc_t) xdr_gss_buffer_desc, 609 (caddr_t) &recv_tok)) { 610 client->cl_state = CLIENT_STALE; 611 return (FALSE); 612 } 613 614 /* 615 * First time round, try all the server names we have until 616 * one matches. Afterwards, stick with that one. 617 */ 618 if (!client->cl_sname) { 619 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 620 if (sname->sn_program == rqst->rq_prog 621 && sname->sn_version == rqst->rq_vers) { 622 gr->gr_major = gss_accept_sec_context( 623 &gr->gr_minor, 624 &client->cl_ctx, 625 sname->sn_cred, 626 &recv_tok, 627 GSS_C_NO_CHANNEL_BINDINGS, 628 &client->cl_cname, 629 &mech, 630 &gr->gr_token, 631 &ret_flags, 632 &cred_lifetime, 633 &client->cl_creds); 634 if (gr->gr_major == GSS_S_COMPLETE 635 || gr->gr_major == GSS_S_CONTINUE_NEEDED) { 636 client->cl_sname = sname; 637 break; 638 } 639 client->cl_sname = sname; 640 break; 641 } 642 } 643 if (!sname) { 644 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 645 (char *) &recv_tok); 646 return (FALSE); 647 } 648 } else { 649 gr->gr_major = gss_accept_sec_context( 650 &gr->gr_minor, 651 &client->cl_ctx, 652 client->cl_sname->sn_cred, 653 &recv_tok, 654 GSS_C_NO_CHANNEL_BINDINGS, 655 &client->cl_cname, 656 &mech, 657 &gr->gr_token, 658 &ret_flags, 659 &cred_lifetime, 660 NULL); 661 } 662 663 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); 664 665 /* 666 * If we get an error from gss_accept_sec_context, send the 667 * reply anyway so that the client gets a chance to see what 668 * is wrong. 669 */ 670 if (gr->gr_major != GSS_S_COMPLETE && 671 gr->gr_major != GSS_S_CONTINUE_NEEDED) { 672 log_status("accept_sec_context", client->cl_mech, 673 gr->gr_major, gr->gr_minor); 674 client->cl_state = CLIENT_STALE; 675 return (TRUE); 676 } 677 678 gr->gr_handle.value = &client->cl_id; 679 gr->gr_handle.length = sizeof(client->cl_id); 680 gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 681 682 /* Save client info. */ 683 client->cl_mech = mech; 684 client->cl_qop = GSS_C_QOP_DEFAULT; 685 client->cl_seq = gc->gc_seq; 686 client->cl_win = gr->gr_win; 687 client->cl_done_callback = FALSE; 688 689 if (gr->gr_major == GSS_S_COMPLETE) { 690 gss_buffer_desc export_name; 691 692 /* 693 * Change client expiration time to be near when the 694 * client creds expire (or 24 hours if we can't figure 695 * that out). 696 */ 697 if (cred_lifetime == GSS_C_INDEFINITE) 698 cred_lifetime = time(0) + 24*60*60; 699 700 client->cl_expiration = time(0) + cred_lifetime; 701 702 /* 703 * Fill in cred details in the rawcred structure. 704 */ 705 client->cl_rawcred.version = RPCSEC_GSS_VERSION; 706 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 707 maj_stat = gss_export_name(&min_stat, client->cl_cname, 708 &export_name); 709 if (maj_stat != GSS_S_COMPLETE) { 710 log_status("gss_export_name", client->cl_mech, 711 maj_stat, min_stat); 712 return (FALSE); 713 } 714 client->cl_rawcred.client_principal = 715 mem_alloc(sizeof(*client->cl_rawcred.client_principal) 716 + export_name.length); 717 client->cl_rawcred.client_principal->len = export_name.length; 718 memcpy(client->cl_rawcred.client_principal->name, 719 export_name.value, export_name.length); 720 gss_release_buffer(&min_stat, &export_name); 721 client->cl_rawcred.svc_principal = 722 client->cl_sname->sn_principal; 723 client->cl_rawcred.service = gc->gc_svc; 724 725 /* 726 * Use gss_pname_to_uid to map to unix creds. For 727 * kerberos5, this uses krb5_aname_to_localname. 728 */ 729 svc_rpc_gss_build_ucred(client, client->cl_cname); 730 gss_release_name(&min_stat, &client->cl_cname); 731 732#ifdef DEBUG 733 { 734 gss_buffer_desc mechname; 735 736 gss_oid_to_str(&min_stat, mech, &mechname); 737 738 log_debug("accepted context for %s with " 739 "<mech %.*s, qop %d, svc %d>", 740 client->cl_rawcred.client_principal->name, 741 mechname.length, (char *)mechname.value, 742 client->cl_qop, client->rawcred.service); 743 744 gss_release_buffer(&min_stat, &mechname); 745 } 746#endif /* DEBUG */ 747 } 748 return (TRUE); 749} 750 751static bool_t 752svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 753 gss_qop_t *qop) 754{ 755 struct opaque_auth *oa; 756 gss_buffer_desc rpcbuf, checksum; 757 OM_uint32 maj_stat, min_stat; 758 gss_qop_t qop_state; 759 int32_t rpchdr[128 / sizeof(int32_t)]; 760 int32_t *buf; 761 762 log_debug("in svc_rpc_gss_validate()"); 763 764 memset(rpchdr, 0, sizeof(rpchdr)); 765 766 /* Reconstruct RPC header for signing (from xdr_callmsg). */ 767 buf = rpchdr; 768 IXDR_PUT_LONG(buf, msg->rm_xid); 769 IXDR_PUT_ENUM(buf, msg->rm_direction); 770 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 771 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 772 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 773 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 774 oa = &msg->rm_call.cb_cred; 775 IXDR_PUT_ENUM(buf, oa->oa_flavor); 776 IXDR_PUT_LONG(buf, oa->oa_length); 777 if (oa->oa_length) { 778 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 779 buf += RNDUP(oa->oa_length) / sizeof(int32_t); 780 } 781 rpcbuf.value = rpchdr; 782 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 783 784 checksum.value = msg->rm_call.cb_verf.oa_base; 785 checksum.length = msg->rm_call.cb_verf.oa_length; 786 787 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 788 &qop_state); 789 790 if (maj_stat != GSS_S_COMPLETE) { 791 log_status("gss_verify_mic", client->cl_mech, 792 maj_stat, min_stat); 793 client->cl_state = CLIENT_STALE; 794 return (FALSE); 795 } 796 *qop = qop_state; 797 return (TRUE); 798} 799 800static bool_t 801svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 802 struct svc_req *rqst, u_int seq) 803{ 804 gss_buffer_desc signbuf; 805 OM_uint32 maj_stat, min_stat; 806 uint32_t nseq; 807 808 log_debug("in svc_rpc_gss_nextverf()"); 809 810 nseq = htonl(seq); 811 signbuf.value = &nseq; 812 signbuf.length = sizeof(nseq); 813 814 if (client->cl_verf.value) 815 gss_release_buffer(&min_stat, &client->cl_verf); 816 817 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 818 &signbuf, &client->cl_verf); 819 820 if (maj_stat != GSS_S_COMPLETE) { 821 log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 822 client->cl_state = CLIENT_STALE; 823 return (FALSE); 824 } 825 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; 826 rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value; 827 rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length; 828 829 return (TRUE); 830} 831 832static bool_t 833svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 834{ 835 struct svc_rpc_gss_callback *scb; 836 rpc_gss_lock_t lock; 837 void *cookie; 838 bool_t cb_res; 839 bool_t result; 840 841 /* 842 * See if we have a callback for this guy. 843 */ 844 result = TRUE; 845 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 846 if (scb->cb_callback.program == rqst->rq_prog 847 && scb->cb_callback.version == rqst->rq_vers) { 848 /* 849 * This one matches. Call the callback and see 850 * if it wants to veto or something. 851 */ 852 lock.locked = FALSE; 853 lock.raw_cred = &client->cl_rawcred; 854 cb_res = scb->cb_callback.callback(rqst, 855 client->cl_creds, 856 client->cl_ctx, 857 &lock, 858 &cookie); 859 860 if (!cb_res) { 861 client->cl_state = CLIENT_STALE; 862 result = FALSE; 863 break; 864 } 865 866 /* 867 * The callback accepted the connection - it 868 * is responsible for freeing client->cl_creds 869 * now. 870 */ 871 client->cl_creds = GSS_C_NO_CREDENTIAL; 872 client->cl_locked = lock.locked; 873 client->cl_cookie = cookie; 874 return (TRUE); 875 } 876 } 877 878 /* 879 * Either no callback exists for this program/version or one 880 * of the callbacks rejected the connection. We just need to 881 * clean up the delegated client creds, if any. 882 */ 883 if (client->cl_creds) { 884 OM_uint32 min_ver; 885 gss_release_cred(&min_ver, &client->cl_creds); 886 } 887 return (result); 888} 889 890static bool_t 891svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 892{ 893 u_int32_t offset; 894 int word, bit; 895 896 if (seq <= client->cl_seqlast) { 897 /* 898 * The request sequence number is less than 899 * the largest we have seen so far. If it is 900 * outside the window or if we have seen a 901 * request with this sequence before, silently 902 * discard it. 903 */ 904 offset = client->cl_seqlast - seq; 905 if (offset >= SVC_RPC_GSS_SEQWINDOW) 906 return (FALSE); 907 word = offset / 32; 908 bit = offset % 32; 909 if (client->cl_seqmask[word] & (1 << bit)) 910 return (FALSE); 911 } 912 913 return (TRUE); 914} 915 916static void 917svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) 918{ 919 int offset, i, word, bit; 920 uint32_t carry, newcarry; 921 922 if (seq > client->cl_seqlast) { 923 /* 924 * This request has a sequence number greater 925 * than any we have seen so far. Advance the 926 * seq window and set bit zero of the window 927 * (which corresponds to the new sequence 928 * number) 929 */ 930 offset = seq - client->cl_seqlast; 931 while (offset > 32) { 932 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 933 i > 0; i--) { 934 client->cl_seqmask[i] = client->cl_seqmask[i-1]; 935 } 936 client->cl_seqmask[0] = 0; 937 offset -= 32; 938 } 939 carry = 0; 940 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 941 newcarry = client->cl_seqmask[i] >> (32 - offset); 942 client->cl_seqmask[i] = 943 (client->cl_seqmask[i] << offset) | carry; 944 carry = newcarry; 945 } 946 client->cl_seqmask[0] |= 1; 947 client->cl_seqlast = seq; 948 } else { 949 offset = client->cl_seqlast - seq; 950 word = offset / 32; 951 bit = offset % 32; 952 client->cl_seqmask[word] |= (1 << bit); 953 } 954 955} 956 957enum auth_stat 958svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 959 960{ 961 OM_uint32 min_stat; 962 XDR xdrs; 963 struct svc_rpc_gss_client *client; 964 struct rpc_gss_cred gc; 965 struct rpc_gss_init_res gr; 966 gss_qop_t qop; 967 int call_stat; 968 enum auth_stat result; 969 970 log_debug("in svc_rpc_gss()"); 971 972 /* Garbage collect old clients. */ 973 svc_rpc_gss_timeout_clients(); 974 975 /* Initialize reply. */ 976 rqst->rq_xprt->xp_verf = _null_auth; 977 978 /* Deserialize client credentials. */ 979 if (rqst->rq_cred.oa_length <= 0) 980 return (AUTH_BADCRED); 981 982 memset(&gc, 0, sizeof(gc)); 983 984 xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 985 rqst->rq_cred.oa_length, XDR_DECODE); 986 987 if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 988 XDR_DESTROY(&xdrs); 989 return (AUTH_BADCRED); 990 } 991 XDR_DESTROY(&xdrs); 992 993 /* Check version. */ 994 if (gc.gc_version != RPCSEC_GSS_VERSION) { 995 result = AUTH_BADCRED; 996 goto out; 997 } 998 999 /* Check the proc and find the client (or create it) */ 1000 if (gc.gc_proc == RPCSEC_GSS_INIT) { 1001 if (gc.gc_handle.length != 0) { 1002 result = AUTH_BADCRED; 1003 goto out; 1004 } 1005 client = svc_rpc_gss_create_client(); 1006 } else { 1007 if (gc.gc_handle.length != sizeof(uint32_t)) { 1008 result = AUTH_BADCRED; 1009 goto out; 1010 } 1011 uint32_t *p = gc.gc_handle.value; 1012 client = svc_rpc_gss_find_client(*p); 1013 if (!client) { 1014 /* 1015 * Can't find the client - we may have 1016 * destroyed it - tell the other side to 1017 * re-authenticate. 1018 */ 1019 result = RPCSEC_GSS_CREDPROBLEM; 1020 goto out; 1021 } 1022 } 1023 rqst->rq_clntcred = client; 1024 1025 /* 1026 * The service and sequence number must be ignored for 1027 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 1028 */ 1029 if (gc.gc_proc != RPCSEC_GSS_INIT 1030 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { 1031 /* 1032 * Check for sequence number overflow. 1033 */ 1034 if (gc.gc_seq >= MAXSEQ) { 1035 result = RPCSEC_GSS_CTXPROBLEM; 1036 goto out; 1037 } 1038 client->cl_seq = gc.gc_seq; 1039 1040 /* 1041 * Check for valid service. 1042 */ 1043 if (gc.gc_svc != rpc_gss_svc_none && 1044 gc.gc_svc != rpc_gss_svc_integrity && 1045 gc.gc_svc != rpc_gss_svc_privacy) { 1046 result = AUTH_BADCRED; 1047 goto out; 1048 } 1049 } 1050 1051 /* Handle RPCSEC_GSS control procedure. */ 1052 switch (gc.gc_proc) { 1053 1054 case RPCSEC_GSS_INIT: 1055 case RPCSEC_GSS_CONTINUE_INIT: 1056 if (rqst->rq_proc != NULLPROC) { 1057 result = AUTH_REJECTEDCRED; 1058 break; 1059 } 1060 1061 memset(&gr, 0, sizeof(gr)); 1062 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 1063 result = AUTH_REJECTEDCRED; 1064 break; 1065 } 1066 1067 if (gr.gr_major == GSS_S_COMPLETE) { 1068 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 1069 result = AUTH_REJECTEDCRED; 1070 break; 1071 } 1072 } else { 1073 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL; 1074 rqst->rq_xprt->xp_verf.oa_length = 0; 1075 } 1076 1077 call_stat = svc_sendreply(rqst->rq_xprt, 1078 (xdrproc_t) xdr_rpc_gss_init_res, 1079 (caddr_t) &gr); 1080 1081 gss_release_buffer(&min_stat, &gr.gr_token); 1082 1083 if (!call_stat) { 1084 result = AUTH_FAILED; 1085 break; 1086 } 1087 1088 if (gr.gr_major == GSS_S_COMPLETE) 1089 client->cl_state = CLIENT_ESTABLISHED; 1090 1091 result = RPCSEC_GSS_NODISPATCH; 1092 break; 1093 1094 case RPCSEC_GSS_DATA: 1095 case RPCSEC_GSS_DESTROY: 1096 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 1097 result = RPCSEC_GSS_NODISPATCH; 1098 break; 1099 } 1100 1101 if (!svc_rpc_gss_validate(client, msg, &qop)) { 1102 result = RPCSEC_GSS_CREDPROBLEM; 1103 break; 1104 } 1105 1106 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 1107 result = RPCSEC_GSS_CTXPROBLEM; 1108 break; 1109 } 1110 1111 svc_rpc_gss_update_seq(client, gc.gc_seq); 1112 1113 /* 1114 * Change the SVCAUTH ops on the transport to point at 1115 * our own code so that we can unwrap the arguments 1116 * and wrap the result. The caller will re-set this on 1117 * every request to point to a set of null wrap/unwrap 1118 * methods. 1119 */ 1120 SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops; 1121 SVC_AUTH(rqst->rq_xprt).svc_ah_private = client; 1122 1123 if (gc.gc_proc == RPCSEC_GSS_DATA) { 1124 /* 1125 * We might be ready to do a callback to the server to 1126 * see if it wants to accept/reject the connection. 1127 */ 1128 if (!client->cl_done_callback) { 1129 client->cl_done_callback = TRUE; 1130 client->cl_qop = qop; 1131 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1132 client->cl_rawcred.mechanism, qop); 1133 if (!svc_rpc_gss_callback(client, rqst)) { 1134 result = AUTH_REJECTEDCRED; 1135 break; 1136 } 1137 } 1138 1139 /* 1140 * If the server has locked this client to a 1141 * particular service+qop pair, enforce that 1142 * restriction now. 1143 */ 1144 if (client->cl_locked) { 1145 if (client->cl_rawcred.service != gc.gc_svc) { 1146 result = AUTH_FAILED; 1147 break; 1148 } else if (client->cl_qop != qop) { 1149 result = AUTH_BADVERF; 1150 break; 1151 } 1152 } 1153 1154 /* 1155 * If the qop changed, look up the new qop 1156 * name for rawcred. 1157 */ 1158 if (client->cl_qop != qop) { 1159 client->cl_qop = qop; 1160 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1161 client->cl_rawcred.mechanism, qop); 1162 } 1163 1164 /* 1165 * Make sure we use the right service value 1166 * for unwrap/wrap. 1167 */ 1168 client->cl_rawcred.service = gc.gc_svc; 1169 1170 result = AUTH_OK; 1171 } else { 1172 if (rqst->rq_proc != NULLPROC) { 1173 result = AUTH_REJECTEDCRED; 1174 break; 1175 } 1176 1177 call_stat = svc_sendreply(rqst->rq_xprt, 1178 (xdrproc_t) xdr_void, (caddr_t) NULL); 1179 1180 if (!call_stat) { 1181 result = AUTH_FAILED; 1182 break; 1183 } 1184 1185 svc_rpc_gss_destroy_client(client); 1186 1187 result = RPCSEC_GSS_NODISPATCH; 1188 break; 1189 } 1190 break; 1191 1192 default: 1193 result = AUTH_BADCRED; 1194 break; 1195 } 1196out: 1197 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 1198 return (result); 1199} 1200 1201bool_t 1202svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 1203{ 1204 struct svc_rpc_gss_client *client; 1205 1206 log_debug("in svc_rpc_gss_wrap()"); 1207 1208 client = (struct svc_rpc_gss_client *) auth->svc_ah_private; 1209 if (client->cl_state != CLIENT_ESTABLISHED 1210 || client->cl_rawcred.service == rpc_gss_svc_none) { 1211 return xdr_func(xdrs, xdr_ptr); 1212 } 1213 return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr, 1214 client->cl_ctx, client->cl_qop, 1215 client->cl_rawcred.service, client->cl_seq)); 1216} 1217 1218bool_t 1219svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 1220{ 1221 struct svc_rpc_gss_client *client; 1222 1223 log_debug("in svc_rpc_gss_unwrap()"); 1224 1225 client = (struct svc_rpc_gss_client *) auth->svc_ah_private; 1226 if (client->cl_state != CLIENT_ESTABLISHED 1227 || client->cl_rawcred.service == rpc_gss_svc_none) { 1228 return xdr_func(xdrs, xdr_ptr); 1229 } 1230 return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr, 1231 client->cl_ctx, client->cl_qop, 1232 client->cl_rawcred.service, client->cl_seq)); 1233}
|