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