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