1 /*- 2 * Copyright (c) 2008 Doug Rabson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 /* 29 SPDX-License-Identifier: BSD-3-Clause 30 31 auth_gss.c 32 33 RPCSEC_GSS client routines. 34 35 Copyright (c) 2000 The Regents of the University of Michigan. 36 All rights reserved. 37 38 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 39 All rights reserved, all wrongs reversed. 40 41 Redistribution and use in source and binary forms, with or without 42 modification, are permitted provided that the following conditions 43 are met: 44 45 1. Redistributions of source code must retain the above copyright 46 notice, this list of conditions and the following disclaimer. 47 2. Redistributions in binary form must reproduce the above copyright 48 notice, this list of conditions and the following disclaimer in the 49 documentation and/or other materials provided with the distribution. 50 3. Neither the name of the University nor the names of its 51 contributors may be used to endorse or promote products derived 52 from this software without specific prior written permission. 53 54 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 55 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 56 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 57 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 58 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 59 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 60 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 61 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 62 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 63 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 64 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 65 66 $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $ 67 */ 68 69 #include <stdio.h> 70 #include <stdlib.h> 71 #include <unistd.h> 72 #include <string.h> 73 #include <errno.h> 74 #include <netinet/in.h> 75 #include <rpc/rpc.h> 76 #include <rpc/rpcsec_gss.h> 77 #include "rpcsec_gss_int.h" 78 79 static void rpc_gss_nextverf(AUTH*); 80 static bool_t rpc_gss_marshal(AUTH *, XDR *); 81 static bool_t rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret); 82 static bool_t rpc_gss_refresh(AUTH *, void *); 83 static bool_t rpc_gss_validate(AUTH *, struct opaque_auth *); 84 static void rpc_gss_destroy(AUTH *); 85 static void rpc_gss_destroy_context(AUTH *, bool_t); 86 87 static struct auth_ops rpc_gss_ops = { 88 rpc_gss_nextverf, 89 rpc_gss_marshal, 90 rpc_gss_validate, 91 rpc_gss_refresh, 92 rpc_gss_destroy 93 }; 94 95 enum rpcsec_gss_state { 96 RPCSEC_GSS_START, 97 RPCSEC_GSS_CONTEXT, 98 RPCSEC_GSS_ESTABLISHED 99 }; 100 101 struct rpc_gss_data { 102 rpc_gss_options_req_t gd_options; /* GSS context options */ 103 enum rpcsec_gss_state gd_state; /* connection state */ 104 gss_buffer_desc gd_verf; /* save GSS_S_COMPLETE 105 * NULL RPC verfier to 106 * process at end of 107 * context negotiation */ 108 CLIENT *gd_clnt; /* client handle */ 109 gss_name_t gd_name; /* service name */ 110 gss_OID gd_mech; /* mechanism to use */ 111 gss_qop_t gd_qop; /* quality of protection */ 112 gss_ctx_id_t gd_ctx; /* context id */ 113 struct rpc_gss_cred gd_cred; /* client credentials */ 114 u_int gd_win; /* sequence window */ 115 }; 116 117 #define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private) 118 119 static struct timeval AUTH_TIMEOUT = { 25, 0 }; 120 121 AUTH * 122 rpc_gss_seccreate(CLIENT *clnt, const char *principal, 123 const char *mechanism, rpc_gss_service_t service, const char *qop, 124 rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret) 125 { 126 AUTH *auth, *save_auth; 127 rpc_gss_options_ret_t options; 128 gss_OID oid; 129 u_int qop_num; 130 struct rpc_gss_data *gd; 131 OM_uint32 maj_stat = 0, min_stat = 0; 132 gss_buffer_desc principal_desc; 133 134 /* 135 * Bail out now if we don't know this mechanism. 136 */ 137 if (!rpc_gss_mech_to_oid(mechanism, &oid)) 138 return (NULL); 139 140 if (qop) { 141 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) 142 return (NULL); 143 } else { 144 qop_num = GSS_C_QOP_DEFAULT; 145 } 146 147 /* 148 * If the caller doesn't want the options, point at local 149 * storage to simplify the code below. 150 */ 151 if (!options_ret) 152 options_ret = &options; 153 154 /* 155 * Default service is integrity. 156 */ 157 if (service == rpc_gss_svc_default) 158 service = rpc_gss_svc_integrity; 159 160 memset(options_ret, 0, sizeof(*options_ret)); 161 162 log_debug("in rpc_gss_seccreate()"); 163 164 memset(&rpc_createerr, 0, sizeof(rpc_createerr)); 165 166 auth = mem_alloc(sizeof(*auth)); 167 if (auth == NULL) { 168 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 169 rpc_createerr.cf_error.re_errno = ENOMEM; 170 return (NULL); 171 } 172 gd = mem_alloc(sizeof(*gd)); 173 if (gd == NULL) { 174 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 175 rpc_createerr.cf_error.re_errno = ENOMEM; 176 free(auth); 177 return (NULL); 178 } 179 180 auth->ah_ops = &rpc_gss_ops; 181 auth->ah_private = (caddr_t) gd; 182 auth->ah_cred.oa_flavor = RPCSEC_GSS; 183 184 principal_desc.value = (void *)(intptr_t) principal; 185 principal_desc.length = strlen(principal); 186 maj_stat = gss_import_name(&min_stat, &principal_desc, 187 GSS_C_NT_HOSTBASED_SERVICE, &gd->gd_name); 188 if (maj_stat != GSS_S_COMPLETE) { 189 options_ret->major_status = maj_stat; 190 options_ret->minor_status = min_stat; 191 goto bad; 192 } 193 194 if (options_req) { 195 gd->gd_options = *options_req; 196 } else { 197 gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG; 198 gd->gd_options.time_req = 0; 199 gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL; 200 gd->gd_options.input_channel_bindings = NULL; 201 } 202 gd->gd_clnt = clnt; 203 gd->gd_ctx = GSS_C_NO_CONTEXT; 204 gd->gd_mech = oid; 205 gd->gd_qop = qop_num; 206 207 gd->gd_cred.gc_version = RPCSEC_GSS_VERSION; 208 gd->gd_cred.gc_proc = RPCSEC_GSS_INIT; 209 gd->gd_cred.gc_seq = 0; 210 gd->gd_cred.gc_svc = service; 211 212 save_auth = clnt->cl_auth; 213 214 clnt->cl_auth = auth; 215 if (!rpc_gss_init(auth, options_ret)) { 216 clnt->cl_auth = save_auth; 217 goto bad; 218 } 219 220 clnt->cl_auth = save_auth; 221 222 return (auth); 223 224 bad: 225 AUTH_DESTROY(auth); 226 return (NULL); 227 } 228 229 bool_t 230 rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop) 231 { 232 struct rpc_gss_data *gd; 233 u_int qop_num; 234 const char *mechanism; 235 236 gd = AUTH_PRIVATE(auth); 237 if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) { 238 return (FALSE); 239 } 240 241 if (qop) { 242 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) { 243 return (FALSE); 244 } 245 } else { 246 qop_num = GSS_C_QOP_DEFAULT; 247 } 248 249 gd->gd_cred.gc_svc = service; 250 gd->gd_qop = qop_num; 251 return (TRUE); 252 } 253 254 static void 255 rpc_gss_nextverf(__unused AUTH *auth) 256 { 257 258 /* not used */ 259 } 260 261 static bool_t 262 rpc_gss_marshal(__unused AUTH *auth, __unused XDR *xdrs) 263 { 264 265 /* not used */ 266 return (FALSE); 267 } 268 269 static bool_t 270 rpc_gss_validate(AUTH *auth, struct opaque_auth *verf) 271 { 272 struct rpc_gss_data *gd; 273 gss_qop_t qop_state; 274 uint32_t num; 275 gss_buffer_desc signbuf, checksum; 276 OM_uint32 maj_stat, min_stat; 277 278 log_debug("in rpc_gss_validate()"); 279 280 gd = AUTH_PRIVATE(auth); 281 282 if (gd->gd_state == RPCSEC_GSS_CONTEXT) { 283 /* 284 * Save the on the wire verifier to validate last INIT 285 * phase packet after decode if the major status is 286 * GSS_S_COMPLETE. 287 */ 288 if (gd->gd_verf.value) 289 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 290 (char *) &gd->gd_verf); 291 gd->gd_verf.value = mem_alloc(verf->oa_length); 292 if (gd->gd_verf.value == NULL) { 293 fprintf(stderr, "gss_validate: out of memory\n"); 294 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 295 return (FALSE); 296 } 297 memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length); 298 gd->gd_verf.length = verf->oa_length; 299 return (TRUE); 300 } 301 302 num = htonl(gd->gd_cred.gc_seq); 303 signbuf.value = # 304 signbuf.length = sizeof(num); 305 306 checksum.value = verf->oa_base; 307 checksum.length = verf->oa_length; 308 309 maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, &signbuf, 310 &checksum, &qop_state); 311 if (maj_stat != GSS_S_COMPLETE || qop_state != gd->gd_qop) { 312 log_status("gss_verify_mic", gd->gd_mech, maj_stat, min_stat); 313 if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 314 rpc_gss_destroy_context(auth, TRUE); 315 } 316 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); 317 return (FALSE); 318 } 319 return (TRUE); 320 } 321 322 static bool_t 323 rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret) 324 { 325 struct rpc_gss_data *gd; 326 struct rpc_gss_init_res gr; 327 gss_buffer_desc *recv_tokenp, recv_token, send_token; 328 OM_uint32 maj_stat, min_stat, call_stat; 329 const char *mech; 330 331 log_debug("in rpc_gss_refresh()"); 332 333 gd = AUTH_PRIVATE(auth); 334 335 if (gd->gd_state != RPCSEC_GSS_START) 336 return (TRUE); 337 338 /* GSS context establishment loop. */ 339 gd->gd_state = RPCSEC_GSS_CONTEXT; 340 gd->gd_cred.gc_proc = RPCSEC_GSS_INIT; 341 gd->gd_cred.gc_seq = 0; 342 343 memset(&recv_token, 0, sizeof(recv_token)); 344 memset(&gr, 0, sizeof(gr)); 345 recv_tokenp = GSS_C_NO_BUFFER; 346 347 for (;;) { 348 maj_stat = gss_init_sec_context(&min_stat, 349 gd->gd_options.my_cred, 350 &gd->gd_ctx, 351 gd->gd_name, 352 gd->gd_mech, 353 gd->gd_options.req_flags, 354 gd->gd_options.time_req, 355 gd->gd_options.input_channel_bindings, 356 recv_tokenp, 357 &gd->gd_mech, /* used mech */ 358 &send_token, 359 &options_ret->ret_flags, 360 &options_ret->time_req); 361 362 /* 363 * Free the token which we got from the server (if 364 * any). Remember that this was allocated by XDR, not 365 * GSS-API. 366 */ 367 if (recv_tokenp != GSS_C_NO_BUFFER) { 368 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 369 (char *) &recv_token); 370 recv_tokenp = GSS_C_NO_BUFFER; 371 } 372 if (maj_stat != GSS_S_COMPLETE && 373 maj_stat != GSS_S_CONTINUE_NEEDED) { 374 log_status("gss_init_sec_context", gd->gd_mech, 375 maj_stat, min_stat); 376 options_ret->major_status = maj_stat; 377 options_ret->minor_status = min_stat; 378 break; 379 } 380 if (send_token.length != 0) { 381 memset(&gr, 0, sizeof(gr)); 382 383 call_stat = clnt_call(gd->gd_clnt, NULLPROC, 384 (xdrproc_t)xdr_gss_buffer_desc, 385 &send_token, 386 (xdrproc_t)xdr_rpc_gss_init_res, 387 (caddr_t)&gr, AUTH_TIMEOUT); 388 389 gss_release_buffer(&min_stat, &send_token); 390 391 if (call_stat != RPC_SUCCESS) 392 break; 393 394 if (gr.gr_major != GSS_S_COMPLETE && 395 gr.gr_major != GSS_S_CONTINUE_NEEDED) { 396 log_status("server reply", gd->gd_mech, 397 gr.gr_major, gr.gr_minor); 398 options_ret->major_status = gr.gr_major; 399 options_ret->minor_status = gr.gr_minor; 400 break; 401 } 402 403 /* 404 * Save the server's gr_handle value, freeing 405 * what we have already (remember that this 406 * was allocated by XDR, not GSS-API). 407 */ 408 if (gr.gr_handle.length != 0) { 409 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 410 (char *) &gd->gd_cred.gc_handle); 411 gd->gd_cred.gc_handle = gr.gr_handle; 412 } 413 414 /* 415 * Save the server's token as well. 416 */ 417 if (gr.gr_token.length != 0) { 418 recv_token = gr.gr_token; 419 recv_tokenp = &recv_token; 420 } 421 422 /* 423 * Since we have copied out all the bits of gr 424 * which XDR allocated for us, we don't need 425 * to free it. 426 */ 427 gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT; 428 } 429 430 if (maj_stat == GSS_S_COMPLETE) { 431 gss_buffer_desc bufin; 432 u_int seq, qop_state = 0; 433 434 /* 435 * gss header verifier, 436 * usually checked in gss_validate 437 */ 438 seq = htonl(gr.gr_win); 439 bufin.value = (unsigned char *)&seq; 440 bufin.length = sizeof(seq); 441 442 maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, 443 &bufin, &gd->gd_verf, &qop_state); 444 445 if (maj_stat != GSS_S_COMPLETE || 446 qop_state != gd->gd_qop) { 447 log_status("gss_verify_mic", gd->gd_mech, 448 maj_stat, min_stat); 449 if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 450 rpc_gss_destroy_context(auth, TRUE); 451 } 452 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, 453 EPERM); 454 options_ret->major_status = maj_stat; 455 options_ret->minor_status = min_stat; 456 return (FALSE); 457 } 458 459 options_ret->major_status = GSS_S_COMPLETE; 460 options_ret->minor_status = 0; 461 options_ret->rpcsec_version = gd->gd_cred.gc_version; 462 options_ret->gss_context = gd->gd_ctx; 463 if (rpc_gss_oid_to_mech(gd->gd_mech, &mech)) { 464 strlcpy(options_ret->actual_mechanism, 465 mech, 466 sizeof(options_ret->actual_mechanism)); 467 } 468 469 gd->gd_state = RPCSEC_GSS_ESTABLISHED; 470 gd->gd_cred.gc_proc = RPCSEC_GSS_DATA; 471 gd->gd_cred.gc_seq = 0; 472 gd->gd_win = gr.gr_win; 473 break; 474 } 475 } 476 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 477 (char *) &gd->gd_verf); 478 479 /* End context negotiation loop. */ 480 if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) { 481 rpc_createerr.cf_stat = RPC_AUTHERROR; 482 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); 483 return (FALSE); 484 } 485 486 return (TRUE); 487 } 488 489 static bool_t 490 rpc_gss_refresh(AUTH *auth, void *msg) 491 { 492 struct rpc_msg *reply = (struct rpc_msg *) msg; 493 rpc_gss_options_ret_t options; 494 495 /* 496 * If the error was RPCSEC_GSS_CREDPROBLEM of 497 * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All 498 * other errors are fatal. 499 */ 500 if (reply->rm_reply.rp_stat == MSG_DENIED 501 && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR 502 && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM 503 || reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) { 504 rpc_gss_destroy_context(auth, FALSE); 505 memset(&options, 0, sizeof(options)); 506 return (rpc_gss_init(auth, &options)); 507 } 508 509 return (FALSE); 510 } 511 512 static void 513 rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy) 514 { 515 struct rpc_gss_data *gd; 516 OM_uint32 min_stat; 517 518 log_debug("in rpc_gss_destroy_context()"); 519 520 gd = AUTH_PRIVATE(auth); 521 522 if (gd->gd_state == RPCSEC_GSS_ESTABLISHED && send_destroy) { 523 gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY; 524 clnt_call(gd->gd_clnt, NULLPROC, 525 (xdrproc_t)xdr_void, NULL, 526 (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT); 527 } 528 529 /* 530 * Free the context token. Remember that this was 531 * allocated by XDR, not GSS-API. 532 */ 533 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 534 (char *) &gd->gd_cred.gc_handle); 535 gd->gd_cred.gc_handle.length = 0; 536 537 if (gd->gd_ctx != GSS_C_NO_CONTEXT) 538 gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL); 539 540 gd->gd_state = RPCSEC_GSS_START; 541 } 542 543 static void 544 rpc_gss_destroy(AUTH *auth) 545 { 546 struct rpc_gss_data *gd; 547 OM_uint32 min_stat; 548 549 log_debug("in rpc_gss_destroy()"); 550 551 gd = AUTH_PRIVATE(auth); 552 553 rpc_gss_destroy_context(auth, TRUE); 554 555 if (gd->gd_name != GSS_C_NO_NAME) 556 gss_release_name(&min_stat, &gd->gd_name); 557 if (gd->gd_verf.value) 558 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 559 (char *) &gd->gd_verf); 560 561 mem_free(gd, sizeof(*gd)); 562 mem_free(auth, sizeof(*auth)); 563 } 564 565 bool_t 566 __rpc_gss_wrap(AUTH *auth, void *header, size_t headerlen, 567 XDR* xdrs, xdrproc_t xdr_args, void *args_ptr) 568 { 569 XDR tmpxdrs; 570 char credbuf[MAX_AUTH_BYTES]; 571 char tmpheader[MAX_AUTH_BYTES]; 572 struct opaque_auth creds, verf; 573 struct rpc_gss_data *gd; 574 gss_buffer_desc rpcbuf, checksum; 575 OM_uint32 maj_stat, min_stat; 576 bool_t xdr_stat; 577 578 log_debug("in rpc_gss_wrap()"); 579 580 gd = AUTH_PRIVATE(auth); 581 582 if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) 583 gd->gd_cred.gc_seq++; 584 585 /* 586 * We need to encode our creds and then put the header and 587 * creds together in a buffer so that we can create a checksum 588 * for the verf. 589 */ 590 xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE); 591 if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gd_cred)) { 592 XDR_DESTROY(&tmpxdrs); 593 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 594 return (FALSE); 595 } 596 creds.oa_flavor = RPCSEC_GSS; 597 creds.oa_base = credbuf; 598 creds.oa_length = XDR_GETPOS(&tmpxdrs); 599 XDR_DESTROY(&tmpxdrs); 600 601 xdrmem_create(&tmpxdrs, tmpheader, sizeof(tmpheader), XDR_ENCODE); 602 if (!XDR_PUTBYTES(&tmpxdrs, header, headerlen) || 603 !xdr_opaque_auth(&tmpxdrs, &creds)) { 604 XDR_DESTROY(&tmpxdrs); 605 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 606 return (FALSE); 607 } 608 headerlen = XDR_GETPOS(&tmpxdrs); 609 XDR_DESTROY(&tmpxdrs); 610 611 if (!XDR_PUTBYTES(xdrs, tmpheader, headerlen)) { 612 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 613 return (FALSE); 614 } 615 616 if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT || 617 gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) { 618 if (!xdr_opaque_auth(xdrs, &_null_auth)) { 619 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 620 return (FALSE); 621 } 622 } else { 623 /* 624 * Checksum serialized RPC header, up to and including 625 * credential. 626 */ 627 rpcbuf.length = headerlen; 628 rpcbuf.value = tmpheader; 629 630 maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop, 631 &rpcbuf, &checksum); 632 633 if (maj_stat != GSS_S_COMPLETE) { 634 log_status("gss_get_mic", gd->gd_mech, 635 maj_stat, min_stat); 636 if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 637 rpc_gss_destroy_context(auth, TRUE); 638 } 639 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); 640 return (FALSE); 641 } 642 643 verf.oa_flavor = RPCSEC_GSS; 644 verf.oa_base = checksum.value; 645 verf.oa_length = checksum.length; 646 647 xdr_stat = xdr_opaque_auth(xdrs, &verf); 648 gss_release_buffer(&min_stat, &checksum); 649 if (!xdr_stat) { 650 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 651 return (FALSE); 652 } 653 } 654 655 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED || 656 gd->gd_cred.gc_svc == rpc_gss_svc_none) { 657 return (xdr_args(xdrs, args_ptr)); 658 } 659 return (xdr_rpc_gss_wrap_data(xdrs, xdr_args, args_ptr, 660 gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc, 661 gd->gd_cred.gc_seq)); 662 } 663 664 bool_t 665 __rpc_gss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, void *xdr_ptr) 666 { 667 struct rpc_gss_data *gd; 668 669 log_debug("in rpc_gss_unwrap()"); 670 671 gd = AUTH_PRIVATE(auth); 672 673 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED || 674 gd->gd_cred.gc_svc == rpc_gss_svc_none) { 675 return (xdr_func(xdrs, xdr_ptr)); 676 } 677 return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr, 678 gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc, 679 gd->gd_cred.gc_seq)); 680 } 681 682 int 683 rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len) 684 { 685 struct rpc_gss_data *gd; 686 int want_conf; 687 OM_uint32 max; 688 OM_uint32 maj_stat, min_stat; 689 int result; 690 691 gd = AUTH_PRIVATE(auth); 692 693 switch (gd->gd_cred.gc_svc) { 694 case rpc_gss_svc_none: 695 return (max_tp_unit_len); 696 break; 697 698 case rpc_gss_svc_default: 699 case rpc_gss_svc_integrity: 700 want_conf = FALSE; 701 break; 702 703 case rpc_gss_svc_privacy: 704 want_conf = TRUE; 705 break; 706 707 default: 708 return (0); 709 } 710 711 maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf, 712 gd->gd_qop, max_tp_unit_len, &max); 713 714 if (maj_stat == GSS_S_COMPLETE) { 715 result = (int) max; 716 if (result < 0) 717 result = 0; 718 return (result); 719 } else { 720 log_status("gss_wrap_size_limit", gd->gd_mech, 721 maj_stat, min_stat); 722 return (0); 723 } 724 } 725