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