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