1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. 29 * 30 * $Header: 31 * /afs/gza.com/product/secure/rel-eng/src/1.1/rpc/RCS/auth_gssapi.c,v 32 * 1.14 1995/03/22 22:07:55 jik Exp $ 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <strings.h> 40 #include <errno.h> 41 #include <pthread.h> 42 #include <thread.h> 43 #include <syslog.h> 44 #include <gssapi/gssapi.h> 45 #include <rpc/rpc.h> 46 #include <rpc/rpcsec_defs.h> 47 48 static void rpc_gss_nextverf(); 49 static bool_t rpc_gss_marshall(); 50 static bool_t rpc_gss_validate(); 51 static bool_t rpc_gss_refresh(); 52 static void rpc_gss_destroy(); 53 static void rpc_gss_destroy_pvt(); 54 static bool_t rpc_gss_seccreate_pvt(); 55 static bool_t validate_seqwin(); 56 57 /* 58 * Globals that should have header files but don't. 59 */ 60 extern bool_t xdr_opaque_auth(XDR *, struct opaque_auth *); 61 62 63 static struct auth_ops rpc_gss_ops = { 64 rpc_gss_nextverf, 65 rpc_gss_marshall, 66 rpc_gss_validate, 67 rpc_gss_refresh, 68 rpc_gss_destroy 69 }; 70 71 /* 72 * Private data for RPCSEC_GSS. 73 */ 74 typedef struct _rpc_gss_data { 75 bool_t established; /* TRUE when established */ 76 CLIENT *clnt; /* associated client handle */ 77 uint_t version; /* RPCSEC version */ 78 gss_ctx_id_t context; /* GSS context id */ 79 gss_buffer_desc ctx_handle; /* RPCSEC context handle */ 80 uint_t seq_num; /* last sequence number rcvd */ 81 gss_cred_id_t my_cred; /* GSS credentials */ 82 OM_uint32 qop; /* requested QOP */ 83 rpc_gss_service_t service; /* requested service */ 84 uint_t gss_proc; /* GSS control procedure */ 85 gss_name_t target_name; /* target server */ 86 int req_flags; /* GSS request bits */ 87 gss_OID mech_type; /* GSS mechanism */ 88 OM_uint32 time_req; /* requested cred lifetime */ 89 bool_t invalid; /* can't use this any more */ 90 OM_uint32 seq_window; /* server sequence window */ 91 struct opaque_auth *verifier; /* rpc reply verifier saved for */ 92 /* validating the sequence window */ 93 gss_channel_bindings_t icb; 94 } rpc_gss_data; 95 #define AUTH_PRIVATE(auth) ((rpc_gss_data *)auth->ah_private) 96 97 /* 98 * Create a context. 99 */ 100 AUTH * 101 __rpc_gss_seccreate(clnt, server_name, mech, service, qop, options_req, 102 options_ret) 103 CLIENT *clnt; /* associated client handle */ 104 char *server_name; /* target server */ 105 char *mech; /* security mechanism */ 106 rpc_gss_service_t service; /* security service */ 107 char *qop; /* requested QOP */ 108 rpc_gss_options_req_t *options_req; /* requested options */ 109 rpc_gss_options_ret_t *options_ret; /* returned options */ 110 { 111 OM_uint32 gssstat; 112 OM_uint32 minor_stat; 113 gss_name_t target_name; 114 gss_OID mech_type; 115 OM_uint32 ret_flags; 116 OM_uint32 time_rec; 117 gss_buffer_desc input_name; 118 AUTH *auth = NULL; 119 rpc_gss_data *ap = NULL; 120 OM_uint32 qop_num; 121 122 /* 123 * convert ascii strings to GSS values 124 */ 125 if (!__rpc_gss_qop_to_num(qop, mech, &qop_num)) { 126 return (NULL); 127 } 128 129 if (!__rpc_gss_mech_to_oid(mech, &mech_type)) { 130 return (NULL); 131 } 132 133 /* 134 * convert name to GSS internal type 135 */ 136 input_name.value = server_name; 137 input_name.length = strlen(server_name); 138 gssstat = gss_import_name(&minor_stat, &input_name, 139 (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, 140 &target_name); 141 if (gssstat != GSS_S_COMPLETE) { 142 rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR; 143 rpc_gss_err.system_error = ENOMEM; 144 return (NULL); 145 } 146 147 /* 148 * Create AUTH handle. Save the necessary interface information 149 * so that the client can refresh the handle later if needed. 150 */ 151 if ((auth = (AUTH *) malloc(sizeof (*auth))) != NULL) 152 ap = (rpc_gss_data *) malloc(sizeof (*ap)); 153 if (auth == NULL || ap == NULL) { 154 rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR; 155 rpc_gss_err.system_error = ENOMEM; 156 if (auth != NULL) 157 free((char *)auth); 158 (void) gss_release_name(&minor_stat, &target_name); 159 return (NULL); 160 } 161 162 memset((char *)ap, 0, sizeof (*ap)); 163 ap->clnt = clnt; 164 ap->version = RPCSEC_GSS_VERSION; 165 if (options_req != NULL) { 166 ap->my_cred = options_req->my_cred; 167 ap->req_flags = options_req->req_flags; 168 ap->time_req = options_req->time_req; 169 ap->icb = options_req->input_channel_bindings; 170 } else { 171 ap->my_cred = GSS_C_NO_CREDENTIAL; 172 ap->req_flags = GSS_C_MUTUAL_FLAG; 173 ap->time_req = 0; 174 ap->icb = NULL; 175 } 176 if ((ap->service = service) == rpc_gss_svc_default) 177 ap->service = rpc_gss_svc_integrity; 178 ap->qop = qop_num; 179 ap->target_name = target_name; 180 ap->mech_type = mech_type; 181 182 /* 183 * Now invoke the real interface that sets up the context from 184 * the information stashed away in the private data. 185 */ 186 if (!rpc_gss_seccreate_pvt(&gssstat, &minor_stat, auth, ap, 187 &mech_type, &ret_flags, &time_rec)) { 188 if (ap->target_name) 189 (void) gss_release_name(&minor_stat, &ap->target_name); 190 free((char *)ap); 191 free((char *)auth); 192 return (NULL); 193 } 194 195 /* 196 * Make sure that the requested service is supported. In all 197 * cases, integrity service must be available. 198 */ 199 if ((ap->service == rpc_gss_svc_privacy && 200 !(ret_flags & GSS_C_CONF_FLAG)) || 201 !(ret_flags & GSS_C_INTEG_FLAG)) { 202 rpc_gss_destroy(auth); 203 rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR; 204 rpc_gss_err.system_error = EPROTONOSUPPORT; 205 return (NULL); 206 } 207 208 /* 209 * return option values if requested 210 */ 211 if (options_ret != NULL) { 212 char *s; 213 214 options_ret->major_status = gssstat; 215 options_ret->minor_status = minor_stat; 216 options_ret->rpcsec_version = ap->version; 217 options_ret->ret_flags = ret_flags; 218 options_ret->time_ret = time_rec; 219 options_ret->gss_context = ap->context; 220 if ((s = __rpc_gss_oid_to_mech(mech_type)) != NULL) 221 strcpy(options_ret->actual_mechanism, s); 222 else 223 options_ret->actual_mechanism[0] = '\0'; 224 } 225 return (auth); 226 } 227 228 /* 229 * Private interface to create a context. This is the interface 230 * that's invoked when the context has to be refreshed. 231 */ 232 static bool_t 233 rpc_gss_seccreate_pvt(gssstat, minor_stat, auth, ap, actual_mech_type, 234 ret_flags, time_rec) 235 OM_uint32 *gssstat; 236 OM_uint32 *minor_stat; 237 AUTH *auth; 238 rpc_gss_data *ap; 239 gss_OID *actual_mech_type; 240 OM_uint32 *ret_flags; 241 OM_uint32 *time_rec; 242 { 243 CLIENT *clnt = ap->clnt; 244 AUTH *save_auth; 245 enum clnt_stat callstat; 246 rpc_gss_init_arg call_arg; 247 rpc_gss_init_res call_res; 248 gss_buffer_desc *input_token_p, input_token; 249 bool_t free_results = FALSE; 250 251 /* 252 * initialize error 253 */ 254 memset(&rpc_createerr, 0, sizeof (rpc_createerr)); 255 256 /* 257 * (re)initialize AUTH handle and private data. 258 */ 259 memset((char *)auth, 0, sizeof (*auth)); 260 auth->ah_ops = &rpc_gss_ops; 261 auth->ah_private = (caddr_t)ap; 262 auth->ah_cred.oa_flavor = RPCSEC_GSS; 263 264 ap->established = FALSE; 265 ap->ctx_handle.length = 0; 266 ap->ctx_handle.value = NULL; 267 ap->context = GSS_C_NO_CONTEXT; 268 ap->seq_num = 0; 269 ap->gss_proc = RPCSEC_GSS_INIT; 270 271 /* 272 * should not change clnt->cl_auth at this time, so save 273 * old handle 274 */ 275 save_auth = clnt->cl_auth; 276 clnt->cl_auth = auth; 277 278 /* 279 * set state for starting context setup 280 */ 281 input_token_p = GSS_C_NO_BUFFER; 282 283 next_token: 284 *gssstat = gss_init_sec_context(minor_stat, 285 ap->my_cred, 286 &ap->context, 287 ap->target_name, 288 ap->mech_type, 289 ap->req_flags, 290 ap->time_req, 291 NULL, 292 input_token_p, 293 actual_mech_type, 294 &call_arg, 295 ret_flags, 296 time_rec); 297 298 if (input_token_p != GSS_C_NO_BUFFER) { 299 OM_uint32 minor_stat2; 300 301 (void) gss_release_buffer(&minor_stat2, input_token_p); 302 input_token_p = GSS_C_NO_BUFFER; 303 } 304 305 if (*gssstat != GSS_S_COMPLETE && *gssstat != GSS_S_CONTINUE_NEEDED) { 306 307 goto cleanup; 308 } 309 310 /* 311 * if we got a token, pass it on 312 */ 313 if (call_arg.length != 0) { 314 struct timeval timeout = {30, 0}; 315 316 memset((char *)&call_res, 0, sizeof (call_res)); 317 callstat = clnt_call(clnt, NULLPROC, 318 __xdr_rpc_gss_init_arg, (caddr_t)&call_arg, 319 __xdr_rpc_gss_init_res, (caddr_t)&call_res, 320 timeout); 321 (void) gss_release_buffer(minor_stat, &call_arg); 322 323 if (callstat != RPC_SUCCESS) { 324 goto cleanup; 325 } 326 /* 327 * we have results - note that these need to be freed 328 */ 329 free_results = TRUE; 330 331 if (call_res.gss_major != GSS_S_COMPLETE && 332 call_res.gss_major != GSS_S_CONTINUE_NEEDED) 333 goto cleanup; 334 335 ap->gss_proc = RPCSEC_GSS_CONTINUE_INIT; 336 337 /* 338 * check for ctx_handle 339 */ 340 if (ap->ctx_handle.length == 0) { 341 if (call_res.ctx_handle.length == 0) 342 goto cleanup; 343 GSS_DUP_BUFFER(ap->ctx_handle, 344 call_res.ctx_handle); 345 } else if (!GSS_BUFFERS_EQUAL(ap->ctx_handle, 346 call_res.ctx_handle)) 347 goto cleanup; 348 349 /* 350 * check for token 351 */ 352 if (call_res.token.length != 0) { 353 if (*gssstat == GSS_S_COMPLETE) 354 goto cleanup; 355 GSS_DUP_BUFFER(input_token, call_res.token); 356 input_token_p = &input_token; 357 358 } else if (*gssstat != GSS_S_COMPLETE) 359 goto cleanup; 360 361 /* save the sequence window value; validate later */ 362 ap->seq_window = call_res.seq_window; 363 xdr_free(__xdr_rpc_gss_init_res, (caddr_t)&call_res); 364 free_results = FALSE; 365 } 366 367 /* 368 * results were okay.. continue if necessary 369 */ 370 if (*gssstat == GSS_S_CONTINUE_NEEDED) 371 goto next_token; 372 373 /* 374 * Validate the sequence window - RFC 2203 section 5.2.3.1 375 */ 376 if (!validate_seqwin(ap)) { 377 goto cleanup; 378 } 379 380 /* 381 * Done! Security context creation is successful. 382 * Ready for exchanging data. 383 */ 384 ap->established = TRUE; 385 ap->seq_num = 1; 386 ap->gss_proc = RPCSEC_GSS_DATA; 387 ap->invalid = FALSE; 388 389 clnt->cl_auth = save_auth; /* restore cl_auth */ 390 return (TRUE); 391 392 cleanup: 393 if (ap->context != GSS_C_NO_CONTEXT) 394 rpc_gss_destroy_pvt(auth); 395 if (free_results) 396 xdr_free(__xdr_rpc_gss_init_res, (caddr_t)&call_res); 397 clnt->cl_auth = save_auth; /* restore cl_auth */ 398 399 /* 400 * if (rpc_createerr.cf_stat == 0) 401 * rpc_createerr.cf_stat = RPC_AUTHERROR; 402 */ 403 if (rpc_createerr.cf_stat == 0) { 404 rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR; 405 rpc_gss_err.system_error = RPC_AUTHERROR; 406 } 407 408 return (FALSE); 409 } 410 411 /* 412 * Set service defaults. 413 */ 414 bool_t 415 __rpc_gss_set_defaults(auth, service, qop) 416 AUTH *auth; 417 rpc_gss_service_t service; 418 char *qop; 419 { 420 /*LINTED*/ 421 rpc_gss_data *ap = AUTH_PRIVATE(auth); 422 char *mech; 423 OM_uint32 qop_num; 424 425 switch (service) { 426 case rpc_gss_svc_integrity: 427 case rpc_gss_svc_privacy: 428 case rpc_gss_svc_none: 429 break; 430 case rpc_gss_svc_default: 431 service = rpc_gss_svc_integrity; 432 break; 433 default: 434 return (FALSE); 435 } 436 437 if ((mech = __rpc_gss_oid_to_mech(ap->mech_type)) == NULL) 438 return (FALSE); 439 440 if (!__rpc_gss_qop_to_num(qop, mech, &qop_num)) 441 return (FALSE); 442 443 ap->qop = qop_num; 444 ap->service = service; 445 return (TRUE); 446 } 447 448 /* 449 * Marshall credentials. 450 */ 451 static bool_t 452 marshall_creds(ap, xdrs) 453 rpc_gss_data *ap; 454 XDR *xdrs; 455 { 456 rpc_gss_creds ag_creds; 457 char cred_buf[MAX_AUTH_BYTES]; 458 struct opaque_auth creds; 459 XDR cred_xdrs; 460 461 ag_creds.version = ap->version; 462 ag_creds.gss_proc = ap->gss_proc; 463 ag_creds.seq_num = ap->seq_num; 464 ag_creds.service = ap->service; 465 466 /* 467 * If context has not been set up yet, use NULL handle. 468 */ 469 if (ap->ctx_handle.length > 0) 470 ag_creds.ctx_handle = ap->ctx_handle; 471 else { 472 ag_creds.ctx_handle.length = 0; 473 ag_creds.ctx_handle.value = NULL; 474 } 475 476 xdrmem_create(&cred_xdrs, (caddr_t)cred_buf, MAX_AUTH_BYTES, 477 XDR_ENCODE); 478 if (!__xdr_rpc_gss_creds(&cred_xdrs, &ag_creds)) { 479 XDR_DESTROY(&cred_xdrs); 480 return (FALSE); 481 } 482 483 creds.oa_flavor = RPCSEC_GSS; 484 creds.oa_base = cred_buf; 485 creds.oa_length = xdr_getpos(&cred_xdrs); 486 XDR_DESTROY(&cred_xdrs); 487 488 if (!xdr_opaque_auth(xdrs, &creds)) 489 return (FALSE); 490 491 return (TRUE); 492 } 493 494 /* 495 * Marshall verifier. The verifier is the checksum of the RPC header 496 * up to and including the credential field. The XDR handle that's 497 * passed in has the header up to and including the credential field 498 * encoded. A pointer to the transmit buffer is also passed in. 499 */ 500 static bool_t 501 marshall_verf(ap, xdrs, buf) 502 rpc_gss_data *ap; 503 XDR *xdrs; /* send XDR */ 504 char *buf; /* pointer of send buffer */ 505 { 506 struct opaque_auth verf; 507 OM_uint32 major, minor; 508 gss_buffer_desc in_buf, out_buf; 509 bool_t ret = FALSE; 510 511 /* 512 * If context is not established yet, use NULL verifier. 513 */ 514 if (!ap->established) { 515 verf.oa_flavor = AUTH_NONE; 516 verf.oa_base = NULL; 517 verf.oa_length = 0; 518 return (xdr_opaque_auth(xdrs, &verf)); 519 } 520 521 verf.oa_flavor = RPCSEC_GSS; 522 in_buf.length = xdr_getpos(xdrs); 523 in_buf.value = buf; 524 if ((major = gss_sign(&minor, ap->context, ap->qop, &in_buf, 525 &out_buf)) != GSS_S_COMPLETE) { 526 if (major == GSS_S_CONTEXT_EXPIRED) { 527 ap->invalid = TRUE; 528 } 529 return (FALSE); 530 } 531 verf.oa_base = out_buf.value; 532 verf.oa_length = out_buf.length; 533 ret = xdr_opaque_auth(xdrs, &verf); 534 (void) gss_release_buffer(&minor, &out_buf); 535 536 return (ret); 537 } 538 539 /* 540 * Function: rpc_gss_nextverf. Not used. 541 */ 542 static void 543 rpc_gss_nextverf() 544 { 545 } 546 547 /* 548 * Function: rpc_gss_marshall - not used. 549 */ 550 static bool_t 551 rpc_gss_marshall(auth, xdrs) 552 AUTH *auth; 553 XDR *xdrs; 554 { 555 if (!xdr_opaque_auth(xdrs, &auth->ah_cred) || 556 !xdr_opaque_auth(xdrs, &auth->ah_verf)) 557 return (FALSE); 558 return (TRUE); 559 } 560 561 /* 562 * Validate sequence window upon a successful RPCSEC_GSS INIT session. 563 * The sequence window sent back by the server should be verifiable by 564 * the verifier which is a checksum of the sequence window. 565 */ 566 static bool_t 567 validate_seqwin(rpc_gss_data *ap) 568 { 569 uint_t seq_win_net; 570 OM_uint32 major = 0, minor = 0; 571 gss_buffer_desc msg_buf, tok_buf; 572 int qop_state = 0; 573 574 seq_win_net = (uint_t)htonl(ap->seq_window); 575 msg_buf.length = sizeof (seq_win_net); 576 msg_buf.value = (char *)&seq_win_net; 577 tok_buf.length = ap->verifier->oa_length; 578 tok_buf.value = ap->verifier->oa_base; 579 major = gss_verify(&minor, ap->context, &msg_buf, &tok_buf, &qop_state); 580 if (major != GSS_S_COMPLETE) 581 return (FALSE); 582 return (TRUE); 583 } 584 585 /* 586 * Validate RPC response verifier from server. The response verifier 587 * is the checksum of the request sequence number. 588 */ 589 static bool_t 590 rpc_gss_validate(auth, verf) 591 AUTH *auth; 592 struct opaque_auth *verf; 593 { 594 /*LINTED*/ 595 rpc_gss_data *ap = AUTH_PRIVATE(auth); 596 uint_t seq_num_net; 597 OM_uint32 major, minor; 598 gss_buffer_desc msg_buf, tok_buf; 599 int qop_state; 600 601 /* 602 * If context is not established yet, save the verifier for 603 * validating the sequence window later at the end of context 604 * creation session. 605 */ 606 if (!ap->established) { 607 if (ap->verifier == NULL) { 608 ap->verifier = malloc(sizeof (struct opaque_auth)); 609 memset(ap->verifier, 0, sizeof (struct opaque_auth)); 610 if (verf->oa_length > 0) 611 ap->verifier->oa_base = malloc(verf->oa_length); 612 } else { 613 if (ap->verifier->oa_length > 0) 614 free(ap->verifier->oa_base); 615 if (verf->oa_length > 0) 616 ap->verifier->oa_base = malloc(verf->oa_length); 617 } 618 ap->verifier->oa_length = verf->oa_length; 619 bcopy(verf->oa_base, ap->verifier->oa_base, verf->oa_length); 620 return (TRUE); 621 } 622 623 seq_num_net = (uint_t)htonl(ap->seq_num); 624 msg_buf.length = sizeof (seq_num_net); 625 msg_buf.value = (char *)&seq_num_net; 626 tok_buf.length = verf->oa_length; 627 tok_buf.value = verf->oa_base; 628 major = gss_verify(&minor, ap->context, &msg_buf, &tok_buf, &qop_state); 629 if (major != GSS_S_COMPLETE) 630 return (FALSE); 631 return (TRUE); 632 } 633 634 /* 635 * Refresh client context. This is necessary sometimes because the 636 * server will ocassionally destroy contexts based on LRU method, or 637 * because of expired credentials. 638 */ 639 static bool_t 640 rpc_gss_refresh(auth, msg) 641 AUTH *auth; 642 struct rpc_msg *msg; 643 { 644 /*LINTED*/ 645 rpc_gss_data *ap = AUTH_PRIVATE(auth); 646 OM_uint32 gssstat, minor_stat; 647 648 /* 649 * The context needs to be recreated only when the error status 650 * returned from the server is one of the following: 651 * RPCSEC_GSS_NOCRED and RPCSEC_GSS_FAILED 652 * The existing context should not be destroyed unless the above 653 * error status codes are received or if the context has not 654 * been set up. 655 */ 656 657 if (msg->rjcted_rply.rj_why == RPCSEC_GSS_NOCRED || 658 msg->rjcted_rply.rj_why == RPCSEC_GSS_FAILED || 659 !ap->established) { 660 /* 661 * Destroy the context if necessary. Use the same memory 662 * for the new context since we've already passed a pointer 663 * to it to the user. 664 */ 665 if (ap->context != GSS_C_NO_CONTEXT) { 666 (void) gss_delete_sec_context(&minor_stat, &ap->context, 667 NULL); 668 ap->context = GSS_C_NO_CONTEXT; 669 } 670 if (ap->ctx_handle.length != 0) { 671 (void) gss_release_buffer(&minor_stat, 672 &ap->ctx_handle); 673 ap->ctx_handle.length = 0; 674 ap->ctx_handle.value = NULL; 675 } 676 677 /* 678 * If the context was not already established, don't try to 679 * recreate it. 680 */ 681 if (!ap->established) { 682 ap->invalid = TRUE; 683 return (FALSE); 684 } 685 686 /* 687 * Recreate context. 688 */ 689 if (rpc_gss_seccreate_pvt(&gssstat, &minor_stat, auth, ap, 690 (gss_OID *)0, (OM_uint32 *)0, (OM_uint32 *)0)) 691 return (TRUE); 692 else { 693 ap->invalid = TRUE; 694 return (FALSE); 695 } 696 } 697 return (FALSE); 698 } 699 700 /* 701 * Destroy a context. 702 */ 703 static void 704 rpc_gss_destroy(auth) 705 AUTH *auth; 706 { 707 /*LINTED*/ 708 rpc_gss_data *ap = AUTH_PRIVATE(auth); 709 710 rpc_gss_destroy_pvt(auth); 711 free((char *)ap); 712 free(auth); 713 } 714 715 /* 716 * Private interface to destroy a context without freeing up 717 * the memory used by it. We need to do this when a refresh 718 * fails, for example, so the user will still have a handle. 719 */ 720 static void 721 rpc_gss_destroy_pvt(auth) 722 AUTH *auth; 723 { 724 struct timeval timeout; 725 OM_uint32 minor_stat; 726 /*LINTED*/ 727 rpc_gss_data *ap = AUTH_PRIVATE(auth); 728 729 /* 730 * If we have a server context id, inform server that we are 731 * destroying the context. 732 */ 733 if (ap->ctx_handle.length != 0) { 734 ap->gss_proc = RPCSEC_GSS_DESTROY; 735 timeout.tv_sec = 1; 736 timeout.tv_usec = 0; 737 (void) clnt_call(ap->clnt, NULLPROC, xdr_void, NULL, 738 xdr_void, NULL, timeout); 739 740 (void) gss_release_buffer(&minor_stat, &ap->ctx_handle); 741 ap->ctx_handle.length = 0; 742 ap->ctx_handle.value = NULL; 743 } 744 745 /* 746 * Destroy local GSS context. 747 */ 748 if (ap->context != GSS_C_NO_CONTEXT) { 749 (void) gss_delete_sec_context(&minor_stat, &ap->context, NULL); 750 ap->context = GSS_C_NO_CONTEXT; 751 } 752 753 /* 754 * Looks like we need to release default credentials if we use it. 755 * Non-default creds need to be released by user. 756 */ 757 if (ap->my_cred == GSS_C_NO_CREDENTIAL) 758 (void) gss_release_cred(&minor_stat, &ap->my_cred); 759 760 /* 761 * Release any internal name structures. 762 */ 763 if (ap->target_name != NULL) { 764 (void) gss_release_name(&minor_stat, &ap->target_name); 765 ap->target_name = NULL; 766 } 767 768 /* 769 * Free the verifier saved for sequence window checking. 770 */ 771 if (ap->verifier != NULL) { 772 if (ap->verifier->oa_length > 0) 773 free(ap->verifier->oa_base); 774 free(ap->verifier); 775 ap->verifier = NULL; 776 } 777 } 778 779 /* 780 * Wrap client side data. The encoded header is passed in through 781 * buf and buflen. The header is up to but not including the 782 * credential field. 783 */ 784 bool_t 785 __rpc_gss_wrap(auth, buf, buflen, out_xdrs, xdr_func, xdr_ptr) 786 AUTH *auth; 787 char *buf; /* encoded header */ 788 uint_t buflen; /* encoded header length */ 789 XDR *out_xdrs; 790 bool_t (*xdr_func)(); 791 caddr_t xdr_ptr; 792 { 793 /*LINTED*/ 794 rpc_gss_data *ap = AUTH_PRIVATE(auth); 795 XDR xdrs; 796 char tmp_buf[512]; 797 798 799 /* 800 * Reject an invalid context. 801 */ 802 if (ap->invalid) 803 return (FALSE); 804 805 /* 806 * If context is established, bump up sequence number. 807 */ 808 if (ap->established) 809 ap->seq_num++; 810 811 /* 812 * Create the header in a temporary XDR context and buffer 813 * before putting it out. 814 */ 815 xdrmem_create(&xdrs, tmp_buf, sizeof (tmp_buf), XDR_ENCODE); 816 if (!XDR_PUTBYTES(&xdrs, buf, buflen)) 817 return (FALSE); 818 819 /* 820 * create cred field 821 */ 822 if (!marshall_creds(ap, &xdrs)) 823 return (FALSE); 824 825 /* 826 * create verifier 827 */ 828 if (!marshall_verf(ap, &xdrs, tmp_buf)) 829 return (FALSE); 830 831 /* 832 * write out header and destroy temp structures 833 */ 834 if (!XDR_PUTBYTES(out_xdrs, tmp_buf, XDR_GETPOS(&xdrs))) 835 return (FALSE); 836 XDR_DESTROY(&xdrs); 837 838 /* 839 * If context is not established, or if neither integrity 840 * nor privacy is used, just XDR encode data. 841 */ 842 if (!ap->established || ap->service == rpc_gss_svc_none) 843 return ((*xdr_func)(out_xdrs, xdr_ptr)); 844 845 return (__rpc_gss_wrap_data(ap->service, ap->qop, ap->context, 846 ap->seq_num, out_xdrs, xdr_func, xdr_ptr)); 847 } 848 849 /* 850 * Unwrap received data. 851 */ 852 bool_t 853 __rpc_gss_unwrap(auth, in_xdrs, xdr_func, xdr_ptr) 854 AUTH *auth; 855 XDR *in_xdrs; 856 bool_t (*xdr_func)(); 857 caddr_t xdr_ptr; 858 { 859 /*LINTED*/ 860 rpc_gss_data *ap = AUTH_PRIVATE(auth); 861 862 /* 863 * If context is not established, of if neither integrity 864 * nor privacy is used, just XDR encode data. 865 */ 866 if (!ap->established || ap->service == rpc_gss_svc_none) 867 return ((*xdr_func)(in_xdrs, xdr_ptr)); 868 869 return (__rpc_gss_unwrap_data(ap->service, 870 ap->context, 871 ap->seq_num, 872 ap->qop, 873 in_xdrs, xdr_func, xdr_ptr)); 874 } 875 876 int 877 __rpc_gss_max_data_length(auth, max_tp_unit_len) 878 AUTH *auth; 879 int max_tp_unit_len; 880 { 881 /*LINTED*/ 882 rpc_gss_data *ap = AUTH_PRIVATE(auth); 883 884 if (!ap->established || max_tp_unit_len <= 0) 885 return (0); 886 887 return (__find_max_data_length(ap->service, 888 ap->context, 889 ap->qop, 890 max_tp_unit_len)); 891 } 892 893 void 894 __rpc_gss_get_error(rpc_gss_error_t *error) 895 { 896 *error = rpc_gss_err; 897 } 898 899 #undef rpc_gss_err 900 901 rpc_gss_error_t rpc_gss_err; 902 903 rpc_gss_error_t * 904 __rpc_gss_err() 905 { 906 static thread_key_t rpc_gss_err_key = THR_ONCE_KEY; 907 rpc_gss_error_t *tsd; 908 909 if (thr_main()) 910 return (&rpc_gss_err); 911 if (thr_keycreate_once(&rpc_gss_err_key, free) != 0) 912 return (&rpc_gss_err); 913 tsd = pthread_getspecific(rpc_gss_err_key); 914 if (tsd == NULL) { 915 tsd = (rpc_gss_error_t *)calloc(1, sizeof (rpc_gss_error_t)); 916 if (thr_setspecific(rpc_gss_err_key, tsd) != 0) { 917 if (tsd) 918 free(tsd); 919 return (&rpc_gss_err); 920 } 921 } 922 return (tsd); 923 } 924