1181344Sdfr/*- 2181344Sdfr * Copyright (c) 2008 Doug Rabson 3181344Sdfr * All rights reserved. 4181344Sdfr * 5181344Sdfr * Redistribution and use in source and binary forms, with or without 6181344Sdfr * modification, are permitted provided that the following conditions 7181344Sdfr * are met: 8181344Sdfr * 1. Redistributions of source code must retain the above copyright 9181344Sdfr * notice, this list of conditions and the following disclaimer. 10181344Sdfr * 2. Redistributions in binary form must reproduce the above copyright 11181344Sdfr * notice, this list of conditions and the following disclaimer in the 12181344Sdfr * documentation and/or other materials provided with the distribution. 13181344Sdfr * 14181344Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15181344Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16181344Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17181344Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18181344Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19181344Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20181344Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21181344Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22181344Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23181344Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24181344Sdfr * SUCH DAMAGE. 25181344Sdfr * 26181344Sdfr * $FreeBSD$ 27181344Sdfr */ 28181344Sdfr/* 29181344Sdfr auth_gss.c 30181344Sdfr 31181344Sdfr RPCSEC_GSS client routines. 32181344Sdfr 33181344Sdfr Copyright (c) 2000 The Regents of the University of Michigan. 34181344Sdfr All rights reserved. 35181344Sdfr 36181344Sdfr Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 37181344Sdfr All rights reserved, all wrongs reversed. 38181344Sdfr 39181344Sdfr Redistribution and use in source and binary forms, with or without 40181344Sdfr modification, are permitted provided that the following conditions 41181344Sdfr are met: 42181344Sdfr 43181344Sdfr 1. Redistributions of source code must retain the above copyright 44181344Sdfr notice, this list of conditions and the following disclaimer. 45181344Sdfr 2. Redistributions in binary form must reproduce the above copyright 46181344Sdfr notice, this list of conditions and the following disclaimer in the 47181344Sdfr documentation and/or other materials provided with the distribution. 48181344Sdfr 3. Neither the name of the University nor the names of its 49181344Sdfr contributors may be used to endorse or promote products derived 50181344Sdfr from this software without specific prior written permission. 51181344Sdfr 52181344Sdfr THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 53181344Sdfr WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 54181344Sdfr MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 55181344Sdfr DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 56181344Sdfr FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 57181344Sdfr CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 58181344Sdfr SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 59181344Sdfr BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 60181344Sdfr LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 61181344Sdfr NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 62181344Sdfr SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 63181344Sdfr 64181344Sdfr $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $ 65181344Sdfr*/ 66181344Sdfr 67181344Sdfr#include <stdio.h> 68181344Sdfr#include <stdlib.h> 69181344Sdfr#include <unistd.h> 70181344Sdfr#include <string.h> 71181344Sdfr#include <errno.h> 72181344Sdfr#include <netinet/in.h> 73181344Sdfr#include <rpc/rpc.h> 74181344Sdfr#include <rpc/rpcsec_gss.h> 75181344Sdfr#include "rpcsec_gss_int.h" 76181344Sdfr 77181344Sdfrstatic void rpc_gss_nextverf(AUTH*); 78181344Sdfrstatic bool_t rpc_gss_marshal(AUTH *, XDR *); 79181344Sdfrstatic bool_t rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret); 80181344Sdfrstatic bool_t rpc_gss_refresh(AUTH *, void *); 81181344Sdfrstatic bool_t rpc_gss_validate(AUTH *, struct opaque_auth *); 82181344Sdfrstatic void rpc_gss_destroy(AUTH *); 83181344Sdfrstatic void rpc_gss_destroy_context(AUTH *, bool_t); 84181344Sdfr 85181344Sdfrstatic struct auth_ops rpc_gss_ops = { 86181344Sdfr rpc_gss_nextverf, 87181344Sdfr rpc_gss_marshal, 88181344Sdfr rpc_gss_validate, 89181344Sdfr rpc_gss_refresh, 90181344Sdfr rpc_gss_destroy 91181344Sdfr}; 92181344Sdfr 93181344Sdfrenum rpcsec_gss_state { 94181344Sdfr RPCSEC_GSS_START, 95181344Sdfr RPCSEC_GSS_CONTEXT, 96181344Sdfr RPCSEC_GSS_ESTABLISHED 97181344Sdfr}; 98181344Sdfr 99181344Sdfrstruct rpc_gss_data { 100181344Sdfr rpc_gss_options_req_t gd_options; /* GSS context options */ 101181344Sdfr enum rpcsec_gss_state gd_state; /* connection state */ 102181344Sdfr gss_buffer_desc gd_verf; /* save GSS_S_COMPLETE 103181344Sdfr * NULL RPC verfier to 104181344Sdfr * process at end of 105181344Sdfr * context negotiation */ 106181344Sdfr CLIENT *gd_clnt; /* client handle */ 107181344Sdfr gss_name_t gd_name; /* service name */ 108181344Sdfr gss_OID gd_mech; /* mechanism to use */ 109181344Sdfr gss_qop_t gd_qop; /* quality of protection */ 110181344Sdfr gss_ctx_id_t gd_ctx; /* context id */ 111181344Sdfr struct rpc_gss_cred gd_cred; /* client credentials */ 112181344Sdfr u_int gd_win; /* sequence window */ 113181344Sdfr}; 114181344Sdfr 115181344Sdfr#define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private) 116181344Sdfr 117181344Sdfrstatic struct timeval AUTH_TIMEOUT = { 25, 0 }; 118181344Sdfr 119181344SdfrAUTH * 120181344Sdfrrpc_gss_seccreate(CLIENT *clnt, const char *principal, 121181344Sdfr const char *mechanism, rpc_gss_service_t service, const char *qop, 122181344Sdfr rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret) 123181344Sdfr{ 124181344Sdfr AUTH *auth, *save_auth; 125181344Sdfr rpc_gss_options_ret_t options; 126181344Sdfr gss_OID oid; 127181344Sdfr u_int qop_num; 128181344Sdfr struct rpc_gss_data *gd; 129181344Sdfr OM_uint32 maj_stat = 0, min_stat = 0; 130181344Sdfr gss_buffer_desc principal_desc; 131181344Sdfr 132181344Sdfr /* 133181344Sdfr * Bail out now if we don't know this mechanism. 134181344Sdfr */ 135181344Sdfr if (!rpc_gss_mech_to_oid(mechanism, &oid)) 136181344Sdfr return (NULL); 137181344Sdfr 138181344Sdfr if (qop) { 139181344Sdfr if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) 140181344Sdfr return (NULL); 141181344Sdfr } else { 142181344Sdfr qop_num = GSS_C_QOP_DEFAULT; 143181344Sdfr } 144181344Sdfr 145181344Sdfr /* 146181344Sdfr * If the caller doesn't want the options, point at local 147181344Sdfr * storage to simplify the code below. 148181344Sdfr */ 149181344Sdfr if (!options_ret) 150181344Sdfr options_ret = &options; 151181344Sdfr 152181344Sdfr /* 153181344Sdfr * Default service is integrity. 154181344Sdfr */ 155181344Sdfr if (service == rpc_gss_svc_default) 156181344Sdfr service = rpc_gss_svc_integrity; 157181344Sdfr 158181344Sdfr memset(options_ret, 0, sizeof(*options_ret)); 159181344Sdfr 160181344Sdfr log_debug("in rpc_gss_seccreate()"); 161181344Sdfr 162181344Sdfr memset(&rpc_createerr, 0, sizeof(rpc_createerr)); 163181344Sdfr 164181344Sdfr auth = mem_alloc(sizeof(*auth)); 165181344Sdfr if (auth == NULL) { 166181344Sdfr rpc_createerr.cf_stat = RPC_SYSTEMERROR; 167181344Sdfr rpc_createerr.cf_error.re_errno = ENOMEM; 168181344Sdfr return (NULL); 169181344Sdfr } 170181344Sdfr gd = mem_alloc(sizeof(*gd)); 171181344Sdfr if (gd == NULL) { 172181344Sdfr rpc_createerr.cf_stat = RPC_SYSTEMERROR; 173181344Sdfr rpc_createerr.cf_error.re_errno = ENOMEM; 174181344Sdfr free(auth); 175181344Sdfr return (NULL); 176181344Sdfr } 177181344Sdfr 178181344Sdfr auth->ah_ops = &rpc_gss_ops; 179181344Sdfr auth->ah_private = (caddr_t) gd; 180181344Sdfr auth->ah_cred.oa_flavor = RPCSEC_GSS; 181181344Sdfr 182181344Sdfr principal_desc.value = (void *)(intptr_t) principal; 183181344Sdfr principal_desc.length = strlen(principal); 184181344Sdfr maj_stat = gss_import_name(&min_stat, &principal_desc, 185181344Sdfr GSS_C_NT_HOSTBASED_SERVICE, &gd->gd_name); 186181344Sdfr if (maj_stat != GSS_S_COMPLETE) { 187181344Sdfr options_ret->major_status = maj_stat; 188181344Sdfr options_ret->minor_status = min_stat; 189181344Sdfr goto bad; 190181344Sdfr } 191181344Sdfr 192181344Sdfr if (options_req) { 193181344Sdfr gd->gd_options = *options_req; 194181344Sdfr } else { 195181344Sdfr gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG; 196181344Sdfr gd->gd_options.time_req = 0; 197181344Sdfr gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL; 198181344Sdfr gd->gd_options.input_channel_bindings = NULL; 199181344Sdfr } 200181344Sdfr gd->gd_clnt = clnt; 201181344Sdfr gd->gd_ctx = GSS_C_NO_CONTEXT; 202181344Sdfr gd->gd_mech = oid; 203181344Sdfr gd->gd_qop = qop_num; 204181344Sdfr 205181344Sdfr gd->gd_cred.gc_version = RPCSEC_GSS_VERSION; 206181344Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_INIT; 207181344Sdfr gd->gd_cred.gc_seq = 0; 208181344Sdfr gd->gd_cred.gc_svc = service; 209181344Sdfr 210181344Sdfr save_auth = clnt->cl_auth; 211181344Sdfr 212181344Sdfr clnt->cl_auth = auth; 213181344Sdfr if (!rpc_gss_init(auth, options_ret)) { 214181344Sdfr clnt->cl_auth = save_auth; 215181344Sdfr goto bad; 216181344Sdfr } 217181344Sdfr 218181344Sdfr clnt->cl_auth = save_auth; 219181344Sdfr 220181344Sdfr return (auth); 221181344Sdfr 222181344Sdfr bad: 223181344Sdfr AUTH_DESTROY(auth); 224181344Sdfr return (NULL); 225181344Sdfr} 226181344Sdfr 227181344Sdfrbool_t 228181344Sdfrrpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop) 229181344Sdfr{ 230181344Sdfr struct rpc_gss_data *gd; 231181344Sdfr u_int qop_num; 232181344Sdfr const char *mechanism; 233181344Sdfr 234181344Sdfr gd = AUTH_PRIVATE(auth); 235181344Sdfr if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) { 236181344Sdfr return (FALSE); 237181344Sdfr } 238181344Sdfr 239181344Sdfr if (qop) { 240181344Sdfr if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) { 241181344Sdfr return (FALSE); 242181344Sdfr } 243181344Sdfr } else { 244181344Sdfr qop_num = GSS_C_QOP_DEFAULT; 245181344Sdfr } 246181344Sdfr 247181344Sdfr gd->gd_cred.gc_svc = service; 248181344Sdfr gd->gd_qop = qop_num; 249181344Sdfr return (TRUE); 250181344Sdfr} 251181344Sdfr 252181344Sdfrstatic void 253181344Sdfrrpc_gss_nextverf(__unused AUTH *auth) 254181344Sdfr{ 255181344Sdfr 256181344Sdfr /* not used */ 257181344Sdfr} 258181344Sdfr 259181344Sdfrstatic bool_t 260181344Sdfrrpc_gss_marshal(__unused AUTH *auth, __unused XDR *xdrs) 261181344Sdfr{ 262181344Sdfr 263181344Sdfr /* not used */ 264181344Sdfr return (FALSE); 265181344Sdfr} 266181344Sdfr 267181344Sdfrstatic bool_t 268181344Sdfrrpc_gss_validate(AUTH *auth, struct opaque_auth *verf) 269181344Sdfr{ 270181344Sdfr struct rpc_gss_data *gd; 271181344Sdfr gss_qop_t qop_state; 272181344Sdfr uint32_t num; 273181344Sdfr gss_buffer_desc signbuf, checksum; 274181344Sdfr OM_uint32 maj_stat, min_stat; 275181344Sdfr 276181344Sdfr log_debug("in rpc_gss_validate()"); 277181344Sdfr 278181344Sdfr gd = AUTH_PRIVATE(auth); 279181344Sdfr 280181344Sdfr if (gd->gd_state == RPCSEC_GSS_CONTEXT) { 281181344Sdfr /* 282181344Sdfr * Save the on the wire verifier to validate last INIT 283181344Sdfr * phase packet after decode if the major status is 284181344Sdfr * GSS_S_COMPLETE. 285181344Sdfr */ 286181344Sdfr if (gd->gd_verf.value) 287181344Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 288181344Sdfr (char *) &gd->gd_verf); 289181344Sdfr gd->gd_verf.value = mem_alloc(verf->oa_length); 290181344Sdfr if (gd->gd_verf.value == NULL) { 291181344Sdfr fprintf(stderr, "gss_validate: out of memory\n"); 292181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 293181344Sdfr return (FALSE); 294181344Sdfr } 295181344Sdfr memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length); 296181344Sdfr gd->gd_verf.length = verf->oa_length; 297181344Sdfr return (TRUE); 298181344Sdfr } 299181344Sdfr 300181344Sdfr num = htonl(gd->gd_cred.gc_seq); 301181344Sdfr signbuf.value = # 302181344Sdfr signbuf.length = sizeof(num); 303181344Sdfr 304181344Sdfr checksum.value = verf->oa_base; 305181344Sdfr checksum.length = verf->oa_length; 306181344Sdfr 307181344Sdfr maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, &signbuf, 308181344Sdfr &checksum, &qop_state); 309181344Sdfr if (maj_stat != GSS_S_COMPLETE || qop_state != gd->gd_qop) { 310181344Sdfr log_status("gss_verify_mic", gd->gd_mech, maj_stat, min_stat); 311181344Sdfr if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 312181344Sdfr rpc_gss_destroy_context(auth, TRUE); 313181344Sdfr } 314181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); 315181344Sdfr return (FALSE); 316181344Sdfr } 317181344Sdfr return (TRUE); 318181344Sdfr} 319181344Sdfr 320181344Sdfrstatic bool_t 321181344Sdfrrpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret) 322181344Sdfr{ 323181344Sdfr struct rpc_gss_data *gd; 324181344Sdfr struct rpc_gss_init_res gr; 325181344Sdfr gss_buffer_desc *recv_tokenp, recv_token, send_token; 326181344Sdfr OM_uint32 maj_stat, min_stat, call_stat; 327181344Sdfr const char *mech; 328181344Sdfr 329181344Sdfr log_debug("in rpc_gss_refresh()"); 330181344Sdfr 331181344Sdfr gd = AUTH_PRIVATE(auth); 332181344Sdfr 333181344Sdfr if (gd->gd_state != RPCSEC_GSS_START) 334181344Sdfr return (TRUE); 335181344Sdfr 336181344Sdfr /* GSS context establishment loop. */ 337181344Sdfr gd->gd_state = RPCSEC_GSS_CONTEXT; 338181344Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_INIT; 339181344Sdfr gd->gd_cred.gc_seq = 0; 340181344Sdfr 341181344Sdfr memset(&recv_token, 0, sizeof(recv_token)); 342181344Sdfr memset(&gr, 0, sizeof(gr)); 343181344Sdfr recv_tokenp = GSS_C_NO_BUFFER; 344181344Sdfr 345181344Sdfr for (;;) { 346181344Sdfr maj_stat = gss_init_sec_context(&min_stat, 347181344Sdfr gd->gd_options.my_cred, 348181344Sdfr &gd->gd_ctx, 349181344Sdfr gd->gd_name, 350181344Sdfr gd->gd_mech, 351181344Sdfr gd->gd_options.req_flags, 352181344Sdfr gd->gd_options.time_req, 353181344Sdfr gd->gd_options.input_channel_bindings, 354181344Sdfr recv_tokenp, 355181344Sdfr &gd->gd_mech, /* used mech */ 356181344Sdfr &send_token, 357181344Sdfr &options_ret->ret_flags, 358181344Sdfr &options_ret->time_req); 359181344Sdfr 360181344Sdfr /* 361181344Sdfr * Free the token which we got from the server (if 362181344Sdfr * any). Remember that this was allocated by XDR, not 363181344Sdfr * GSS-API. 364181344Sdfr */ 365181344Sdfr if (recv_tokenp != GSS_C_NO_BUFFER) { 366181344Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 367181344Sdfr (char *) &recv_token); 368181344Sdfr recv_tokenp = GSS_C_NO_BUFFER; 369181344Sdfr } 370181344Sdfr if (maj_stat != GSS_S_COMPLETE && 371181344Sdfr maj_stat != GSS_S_CONTINUE_NEEDED) { 372181344Sdfr log_status("gss_init_sec_context", gd->gd_mech, 373181344Sdfr maj_stat, min_stat); 374181344Sdfr options_ret->major_status = maj_stat; 375181344Sdfr options_ret->minor_status = min_stat; 376181344Sdfr break; 377181344Sdfr } 378181344Sdfr if (send_token.length != 0) { 379181344Sdfr memset(&gr, 0, sizeof(gr)); 380181344Sdfr 381181344Sdfr call_stat = clnt_call(gd->gd_clnt, NULLPROC, 382181344Sdfr (xdrproc_t)xdr_gss_buffer_desc, 383181344Sdfr &send_token, 384181344Sdfr (xdrproc_t)xdr_rpc_gss_init_res, 385181344Sdfr (caddr_t)&gr, AUTH_TIMEOUT); 386181344Sdfr 387181344Sdfr gss_release_buffer(&min_stat, &send_token); 388181344Sdfr 389181344Sdfr if (call_stat != RPC_SUCCESS) 390181344Sdfr break; 391181344Sdfr 392181344Sdfr if (gr.gr_major != GSS_S_COMPLETE && 393181344Sdfr gr.gr_major != GSS_S_CONTINUE_NEEDED) { 394181344Sdfr log_status("server reply", gd->gd_mech, 395181344Sdfr gr.gr_major, gr.gr_minor); 396181344Sdfr options_ret->major_status = gr.gr_major; 397181344Sdfr options_ret->minor_status = gr.gr_minor; 398181344Sdfr break; 399181344Sdfr } 400181344Sdfr 401181344Sdfr /* 402181344Sdfr * Save the server's gr_handle value, freeing 403181344Sdfr * what we have already (remember that this 404181344Sdfr * was allocated by XDR, not GSS-API). 405181344Sdfr */ 406181344Sdfr if (gr.gr_handle.length != 0) { 407181344Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 408181344Sdfr (char *) &gd->gd_cred.gc_handle); 409181344Sdfr gd->gd_cred.gc_handle = gr.gr_handle; 410181344Sdfr } 411181344Sdfr 412181344Sdfr /* 413181344Sdfr * Save the server's token as well. 414181344Sdfr */ 415181344Sdfr if (gr.gr_token.length != 0) { 416181344Sdfr recv_token = gr.gr_token; 417181344Sdfr recv_tokenp = &recv_token; 418181344Sdfr } 419181344Sdfr 420181344Sdfr /* 421181344Sdfr * Since we have copied out all the bits of gr 422181344Sdfr * which XDR allocated for us, we don't need 423181344Sdfr * to free it. 424181344Sdfr */ 425181344Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT; 426181344Sdfr } 427181344Sdfr 428181344Sdfr if (maj_stat == GSS_S_COMPLETE) { 429181344Sdfr gss_buffer_desc bufin; 430181344Sdfr u_int seq, qop_state = 0; 431181344Sdfr 432181344Sdfr /* 433181344Sdfr * gss header verifier, 434181344Sdfr * usually checked in gss_validate 435181344Sdfr */ 436181344Sdfr seq = htonl(gr.gr_win); 437181344Sdfr bufin.value = (unsigned char *)&seq; 438181344Sdfr bufin.length = sizeof(seq); 439181344Sdfr 440181344Sdfr maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, 441181344Sdfr &bufin, &gd->gd_verf, &qop_state); 442181344Sdfr 443181344Sdfr if (maj_stat != GSS_S_COMPLETE || 444181344Sdfr qop_state != gd->gd_qop) { 445181344Sdfr log_status("gss_verify_mic", gd->gd_mech, 446181344Sdfr maj_stat, min_stat); 447181344Sdfr if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 448181344Sdfr rpc_gss_destroy_context(auth, TRUE); 449181344Sdfr } 450181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, 451181344Sdfr EPERM); 452181344Sdfr options_ret->major_status = maj_stat; 453181344Sdfr options_ret->minor_status = min_stat; 454181344Sdfr return (FALSE); 455181344Sdfr } 456181344Sdfr 457181344Sdfr options_ret->major_status = GSS_S_COMPLETE; 458181344Sdfr options_ret->minor_status = 0; 459181344Sdfr options_ret->rpcsec_version = gd->gd_cred.gc_version; 460181344Sdfr options_ret->gss_context = gd->gd_ctx; 461181344Sdfr if (rpc_gss_oid_to_mech(gd->gd_mech, &mech)) { 462181344Sdfr strlcpy(options_ret->actual_mechanism, 463181344Sdfr mech, 464181344Sdfr sizeof(options_ret->actual_mechanism)); 465181344Sdfr } 466181344Sdfr 467181344Sdfr gd->gd_state = RPCSEC_GSS_ESTABLISHED; 468181344Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_DATA; 469181344Sdfr gd->gd_cred.gc_seq = 0; 470181344Sdfr gd->gd_win = gr.gr_win; 471181344Sdfr break; 472181344Sdfr } 473181344Sdfr } 474181344Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 475181344Sdfr (char *) &gd->gd_verf); 476181344Sdfr 477181344Sdfr /* End context negotiation loop. */ 478181344Sdfr if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) { 479181344Sdfr rpc_createerr.cf_stat = RPC_AUTHERROR; 480181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); 481181344Sdfr return (FALSE); 482181344Sdfr } 483181344Sdfr 484181344Sdfr return (TRUE); 485181344Sdfr} 486181344Sdfr 487181344Sdfrstatic bool_t 488181344Sdfrrpc_gss_refresh(AUTH *auth, void *msg) 489181344Sdfr{ 490181344Sdfr struct rpc_msg *reply = (struct rpc_msg *) msg; 491181344Sdfr rpc_gss_options_ret_t options; 492181344Sdfr 493181344Sdfr /* 494181344Sdfr * If the error was RPCSEC_GSS_CREDPROBLEM of 495181344Sdfr * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All 496181344Sdfr * other errors are fatal. 497181344Sdfr */ 498181344Sdfr if (reply->rm_reply.rp_stat == MSG_DENIED 499181344Sdfr && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR 500181344Sdfr && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM 501181344Sdfr || reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) { 502181344Sdfr rpc_gss_destroy_context(auth, FALSE); 503181344Sdfr memset(&options, 0, sizeof(options)); 504181344Sdfr return (rpc_gss_init(auth, &options)); 505181344Sdfr } 506181344Sdfr 507181344Sdfr return (FALSE); 508181344Sdfr} 509181344Sdfr 510181344Sdfrstatic void 511181344Sdfrrpc_gss_destroy_context(AUTH *auth, bool_t send_destroy) 512181344Sdfr{ 513181344Sdfr struct rpc_gss_data *gd; 514181344Sdfr OM_uint32 min_stat; 515181344Sdfr 516181344Sdfr log_debug("in rpc_gss_destroy_context()"); 517181344Sdfr 518181344Sdfr gd = AUTH_PRIVATE(auth); 519181344Sdfr 520181344Sdfr if (gd->gd_state == RPCSEC_GSS_ESTABLISHED && send_destroy) { 521181344Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY; 522181344Sdfr clnt_call(gd->gd_clnt, NULLPROC, 523181344Sdfr (xdrproc_t)xdr_void, NULL, 524181344Sdfr (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT); 525181344Sdfr } 526181344Sdfr 527181344Sdfr /* 528181344Sdfr * Free the context token. Remember that this was 529181344Sdfr * allocated by XDR, not GSS-API. 530181344Sdfr */ 531181344Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 532181344Sdfr (char *) &gd->gd_cred.gc_handle); 533181344Sdfr gd->gd_cred.gc_handle.length = 0; 534181344Sdfr 535181344Sdfr if (gd->gd_ctx != GSS_C_NO_CONTEXT) 536181344Sdfr gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL); 537181344Sdfr 538181344Sdfr gd->gd_state = RPCSEC_GSS_START; 539181344Sdfr} 540181344Sdfr 541181344Sdfrstatic void 542181344Sdfrrpc_gss_destroy(AUTH *auth) 543181344Sdfr{ 544181344Sdfr struct rpc_gss_data *gd; 545181344Sdfr OM_uint32 min_stat; 546181344Sdfr 547181344Sdfr log_debug("in rpc_gss_destroy()"); 548181344Sdfr 549181344Sdfr gd = AUTH_PRIVATE(auth); 550181344Sdfr 551181344Sdfr rpc_gss_destroy_context(auth, TRUE); 552181344Sdfr 553181344Sdfr if (gd->gd_name != GSS_C_NO_NAME) 554181344Sdfr gss_release_name(&min_stat, &gd->gd_name); 555181344Sdfr if (gd->gd_verf.value) 556181344Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 557181344Sdfr (char *) &gd->gd_verf); 558181344Sdfr 559181344Sdfr mem_free(gd, sizeof(*gd)); 560181344Sdfr mem_free(auth, sizeof(*auth)); 561181344Sdfr} 562181344Sdfr 563181344Sdfrbool_t 564181344Sdfr__rpc_gss_wrap(AUTH *auth, void *header, size_t headerlen, 565181344Sdfr XDR* xdrs, xdrproc_t xdr_args, void *args_ptr) 566181344Sdfr{ 567181344Sdfr XDR tmpxdrs; 568181344Sdfr char credbuf[MAX_AUTH_BYTES]; 569181344Sdfr char tmpheader[MAX_AUTH_BYTES]; 570181344Sdfr struct opaque_auth creds, verf; 571181344Sdfr struct rpc_gss_data *gd; 572181344Sdfr gss_buffer_desc rpcbuf, checksum; 573181344Sdfr OM_uint32 maj_stat, min_stat; 574181344Sdfr bool_t xdr_stat; 575181344Sdfr 576181344Sdfr log_debug("in rpc_gss_wrap()"); 577181344Sdfr 578181344Sdfr gd = AUTH_PRIVATE(auth); 579181344Sdfr 580181344Sdfr if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) 581181344Sdfr gd->gd_cred.gc_seq++; 582181344Sdfr 583181344Sdfr /* 584181344Sdfr * We need to encode our creds and then put the header and 585181344Sdfr * creds together in a buffer so that we can create a checksum 586181344Sdfr * for the verf. 587181344Sdfr */ 588181344Sdfr xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE); 589181344Sdfr if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gd_cred)) { 590181344Sdfr XDR_DESTROY(&tmpxdrs); 591181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 592181344Sdfr return (FALSE); 593181344Sdfr } 594181344Sdfr creds.oa_flavor = RPCSEC_GSS; 595181344Sdfr creds.oa_base = credbuf; 596181344Sdfr creds.oa_length = XDR_GETPOS(&tmpxdrs); 597181344Sdfr XDR_DESTROY(&tmpxdrs); 598181344Sdfr 599181344Sdfr xdrmem_create(&tmpxdrs, tmpheader, sizeof(tmpheader), XDR_ENCODE); 600181344Sdfr if (!XDR_PUTBYTES(&tmpxdrs, header, headerlen) || 601181344Sdfr !xdr_opaque_auth(&tmpxdrs, &creds)) { 602181344Sdfr XDR_DESTROY(&tmpxdrs); 603181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 604181344Sdfr return (FALSE); 605181344Sdfr } 606181344Sdfr headerlen = XDR_GETPOS(&tmpxdrs); 607181344Sdfr XDR_DESTROY(&tmpxdrs); 608181344Sdfr 609181344Sdfr if (!XDR_PUTBYTES(xdrs, tmpheader, headerlen)) { 610181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 611181344Sdfr return (FALSE); 612181344Sdfr } 613181344Sdfr 614181344Sdfr if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT || 615181344Sdfr gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) { 616181344Sdfr if (!xdr_opaque_auth(xdrs, &_null_auth)) { 617181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 618181344Sdfr return (FALSE); 619181344Sdfr } 620181344Sdfr } else { 621181344Sdfr /* 622181344Sdfr * Checksum serialized RPC header, up to and including 623181344Sdfr * credential. 624181344Sdfr */ 625181344Sdfr rpcbuf.length = headerlen; 626181344Sdfr rpcbuf.value = tmpheader; 627181344Sdfr 628181344Sdfr maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop, 629181344Sdfr &rpcbuf, &checksum); 630181344Sdfr 631181344Sdfr if (maj_stat != GSS_S_COMPLETE) { 632181344Sdfr log_status("gss_get_mic", gd->gd_mech, 633181344Sdfr maj_stat, min_stat); 634181344Sdfr if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 635181344Sdfr rpc_gss_destroy_context(auth, TRUE); 636181344Sdfr } 637181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); 638181344Sdfr return (FALSE); 639181344Sdfr } 640181344Sdfr 641181344Sdfr verf.oa_flavor = RPCSEC_GSS; 642181344Sdfr verf.oa_base = checksum.value; 643181344Sdfr verf.oa_length = checksum.length; 644181344Sdfr 645181344Sdfr xdr_stat = xdr_opaque_auth(xdrs, &verf); 646181344Sdfr gss_release_buffer(&min_stat, &checksum); 647181344Sdfr if (!xdr_stat) { 648181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 649181344Sdfr return (FALSE); 650181344Sdfr } 651181344Sdfr } 652181344Sdfr 653181344Sdfr if (gd->gd_state != RPCSEC_GSS_ESTABLISHED || 654181344Sdfr gd->gd_cred.gc_svc == rpc_gss_svc_none) { 655181344Sdfr return (xdr_args(xdrs, args_ptr)); 656181344Sdfr } 657181344Sdfr return (xdr_rpc_gss_wrap_data(xdrs, xdr_args, args_ptr, 658181344Sdfr gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc, 659181344Sdfr gd->gd_cred.gc_seq)); 660181344Sdfr} 661181344Sdfr 662181344Sdfrbool_t 663181344Sdfr__rpc_gss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, void *xdr_ptr) 664181344Sdfr{ 665181344Sdfr struct rpc_gss_data *gd; 666181344Sdfr 667181344Sdfr log_debug("in rpc_gss_unwrap()"); 668181344Sdfr 669181344Sdfr gd = AUTH_PRIVATE(auth); 670181344Sdfr 671181344Sdfr if (gd->gd_state != RPCSEC_GSS_ESTABLISHED || 672181344Sdfr gd->gd_cred.gc_svc == rpc_gss_svc_none) { 673181344Sdfr return (xdr_func(xdrs, xdr_ptr)); 674181344Sdfr } 675181344Sdfr return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr, 676181344Sdfr gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc, 677181344Sdfr gd->gd_cred.gc_seq)); 678181344Sdfr} 679181344Sdfr 680181344Sdfrint 681181344Sdfrrpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len) 682181344Sdfr{ 683181344Sdfr struct rpc_gss_data *gd; 684181344Sdfr int want_conf; 685181344Sdfr OM_uint32 max; 686181344Sdfr OM_uint32 maj_stat, min_stat; 687181344Sdfr int result; 688181344Sdfr 689181344Sdfr gd = AUTH_PRIVATE(auth); 690181344Sdfr 691181344Sdfr switch (gd->gd_cred.gc_svc) { 692181344Sdfr case rpc_gss_svc_none: 693181344Sdfr return (max_tp_unit_len); 694181344Sdfr break; 695181344Sdfr 696181344Sdfr case rpc_gss_svc_default: 697181344Sdfr case rpc_gss_svc_integrity: 698181344Sdfr want_conf = FALSE; 699181344Sdfr break; 700181344Sdfr 701181344Sdfr case rpc_gss_svc_privacy: 702181344Sdfr want_conf = TRUE; 703181344Sdfr break; 704181344Sdfr 705181344Sdfr default: 706181344Sdfr return (0); 707181344Sdfr } 708181344Sdfr 709181344Sdfr maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf, 710181344Sdfr gd->gd_qop, max_tp_unit_len, &max); 711181344Sdfr 712181344Sdfr if (maj_stat == GSS_S_COMPLETE) { 713181344Sdfr result = (int) max; 714181344Sdfr if (result < 0) 715181344Sdfr result = 0; 716181344Sdfr return (result); 717181344Sdfr } else { 718181344Sdfr log_status("gss_wrap_size_limit", gd->gd_mech, 719181344Sdfr maj_stat, min_stat); 720181344Sdfr return (0); 721181344Sdfr } 722181344Sdfr} 723