1184588Sdfr/*- 2184588Sdfr * Copyright (c) 2008 Doug Rabson 3184588Sdfr * All rights reserved. 4184588Sdfr * 5184588Sdfr * Redistribution and use in source and binary forms, with or without 6184588Sdfr * modification, are permitted provided that the following conditions 7184588Sdfr * are met: 8184588Sdfr * 1. Redistributions of source code must retain the above copyright 9184588Sdfr * notice, this list of conditions and the following disclaimer. 10184588Sdfr * 2. Redistributions in binary form must reproduce the above copyright 11184588Sdfr * notice, this list of conditions and the following disclaimer in the 12184588Sdfr * documentation and/or other materials provided with the distribution. 13184588Sdfr * 14184588Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184588Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184588Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184588Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184588Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184588Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184588Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184588Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184588Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184588Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184588Sdfr * SUCH DAMAGE. 25184588Sdfr */ 26184588Sdfr/* 27184588Sdfr auth_gss.c 28184588Sdfr 29184588Sdfr RPCSEC_GSS client routines. 30184588Sdfr 31184588Sdfr Copyright (c) 2000 The Regents of the University of Michigan. 32184588Sdfr All rights reserved. 33184588Sdfr 34184588Sdfr Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 35184588Sdfr All rights reserved, all wrongs reversed. 36184588Sdfr 37184588Sdfr Redistribution and use in source and binary forms, with or without 38184588Sdfr modification, are permitted provided that the following conditions 39184588Sdfr are met: 40184588Sdfr 41184588Sdfr 1. Redistributions of source code must retain the above copyright 42184588Sdfr notice, this list of conditions and the following disclaimer. 43184588Sdfr 2. Redistributions in binary form must reproduce the above copyright 44184588Sdfr notice, this list of conditions and the following disclaimer in the 45184588Sdfr documentation and/or other materials provided with the distribution. 46184588Sdfr 3. Neither the name of the University nor the names of its 47184588Sdfr contributors may be used to endorse or promote products derived 48184588Sdfr from this software without specific prior written permission. 49184588Sdfr 50184588Sdfr THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 51184588Sdfr WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 52184588Sdfr MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 53184588Sdfr DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 54184588Sdfr FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 55184588Sdfr CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 56184588Sdfr SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 57184588Sdfr BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58184588Sdfr LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59184588Sdfr NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60184588Sdfr SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61184588Sdfr 62184588Sdfr $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $ 63184588Sdfr*/ 64184588Sdfr 65184588Sdfr#include <sys/cdefs.h> 66184588Sdfr__FBSDID("$FreeBSD$"); 67184588Sdfr 68184588Sdfr#include <sys/param.h> 69184588Sdfr#include <sys/systm.h> 70184588Sdfr#include <sys/hash.h> 71184588Sdfr#include <sys/kernel.h> 72184588Sdfr#include <sys/kobj.h> 73184588Sdfr#include <sys/lock.h> 74184588Sdfr#include <sys/malloc.h> 75184588Sdfr#include <sys/mbuf.h> 76184588Sdfr#include <sys/mutex.h> 77184588Sdfr#include <sys/proc.h> 78184588Sdfr#include <sys/refcount.h> 79184588Sdfr#include <sys/sx.h> 80184588Sdfr#include <sys/ucred.h> 81184588Sdfr 82184588Sdfr#include <rpc/rpc.h> 83184588Sdfr#include <rpc/rpcsec_gss.h> 84184588Sdfr 85184588Sdfr#include "rpcsec_gss_int.h" 86184588Sdfr 87184588Sdfrstatic void rpc_gss_nextverf(AUTH*); 88184588Sdfrstatic bool_t rpc_gss_marshal(AUTH *, uint32_t, XDR *, struct mbuf *); 89184588Sdfrstatic bool_t rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret); 90184588Sdfrstatic bool_t rpc_gss_refresh(AUTH *, void *); 91184588Sdfrstatic bool_t rpc_gss_validate(AUTH *, uint32_t, struct opaque_auth *, 92184588Sdfr struct mbuf **); 93184588Sdfrstatic void rpc_gss_destroy(AUTH *); 94184588Sdfrstatic void rpc_gss_destroy_context(AUTH *, bool_t); 95184588Sdfr 96184588Sdfrstatic struct auth_ops rpc_gss_ops = { 97184588Sdfr rpc_gss_nextverf, 98184588Sdfr rpc_gss_marshal, 99184588Sdfr rpc_gss_validate, 100184588Sdfr rpc_gss_refresh, 101184588Sdfr rpc_gss_destroy, 102184588Sdfr}; 103184588Sdfr 104184588Sdfrenum rpcsec_gss_state { 105184588Sdfr RPCSEC_GSS_START, 106184588Sdfr RPCSEC_GSS_CONTEXT, 107184588Sdfr RPCSEC_GSS_ESTABLISHED, 108184588Sdfr RPCSEC_GSS_DESTROYING 109184588Sdfr}; 110184588Sdfr 111184588Sdfrstruct rpc_pending_request { 112184588Sdfr uint32_t pr_xid; /* XID of rpc */ 113184588Sdfr uint32_t pr_seq; /* matching GSS seq */ 114184588Sdfr LIST_ENTRY(rpc_pending_request) pr_link; 115184588Sdfr}; 116184588SdfrLIST_HEAD(rpc_pending_request_list, rpc_pending_request); 117184588Sdfr 118184588Sdfrstruct rpc_gss_data { 119184588Sdfr volatile u_int gd_refs; /* number of current users */ 120184588Sdfr struct mtx gd_lock; 121184588Sdfr uint32_t gd_hash; 122184588Sdfr AUTH *gd_auth; /* link back to AUTH */ 123184588Sdfr struct ucred *gd_ucred; /* matching local cred */ 124184588Sdfr char *gd_principal; /* server principal name */ 125184588Sdfr rpc_gss_options_req_t gd_options; /* GSS context options */ 126184588Sdfr enum rpcsec_gss_state gd_state; /* connection state */ 127184588Sdfr gss_buffer_desc gd_verf; /* save GSS_S_COMPLETE 128184588Sdfr * NULL RPC verfier to 129184588Sdfr * process at end of 130184588Sdfr * context negotiation */ 131184588Sdfr CLIENT *gd_clnt; /* client handle */ 132184588Sdfr gss_OID gd_mech; /* mechanism to use */ 133184588Sdfr gss_qop_t gd_qop; /* quality of protection */ 134184588Sdfr gss_ctx_id_t gd_ctx; /* context id */ 135184588Sdfr struct rpc_gss_cred gd_cred; /* client credentials */ 136184588Sdfr uint32_t gd_seq; /* next sequence number */ 137184588Sdfr u_int gd_win; /* sequence window */ 138184588Sdfr struct rpc_pending_request_list gd_reqs; 139184588Sdfr TAILQ_ENTRY(rpc_gss_data) gd_link; 140184588Sdfr TAILQ_ENTRY(rpc_gss_data) gd_alllink; 141184588Sdfr}; 142184588SdfrTAILQ_HEAD(rpc_gss_data_list, rpc_gss_data); 143184588Sdfr 144184588Sdfr#define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private) 145184588Sdfr 146184588Sdfrstatic struct timeval AUTH_TIMEOUT = { 25, 0 }; 147184588Sdfr 148184588Sdfr#define RPC_GSS_HASH_SIZE 11 149184588Sdfr#define RPC_GSS_MAX 256 150184588Sdfrstatic struct rpc_gss_data_list rpc_gss_cache[RPC_GSS_HASH_SIZE]; 151184588Sdfrstatic struct rpc_gss_data_list rpc_gss_all; 152184588Sdfrstatic struct sx rpc_gss_lock; 153184588Sdfrstatic int rpc_gss_count; 154184588Sdfr 155184588Sdfrstatic AUTH *rpc_gss_seccreate_int(CLIENT *, struct ucred *, const char *, 156184588Sdfr gss_OID, rpc_gss_service_t, u_int, rpc_gss_options_req_t *, 157184588Sdfr rpc_gss_options_ret_t *); 158184588Sdfr 159184588Sdfrstatic void 160184588Sdfrrpc_gss_hashinit(void *dummy) 161184588Sdfr{ 162184588Sdfr int i; 163184588Sdfr 164184588Sdfr for (i = 0; i < RPC_GSS_HASH_SIZE; i++) 165184588Sdfr TAILQ_INIT(&rpc_gss_cache[i]); 166184588Sdfr TAILQ_INIT(&rpc_gss_all); 167184588Sdfr sx_init(&rpc_gss_lock, "rpc_gss_lock"); 168184588Sdfr} 169184588SdfrSYSINIT(rpc_gss_hashinit, SI_SUB_KMEM, SI_ORDER_ANY, rpc_gss_hashinit, NULL); 170184588Sdfr 171184588Sdfrstatic uint32_t 172184588Sdfrrpc_gss_hash(const char *principal, gss_OID mech, 173184588Sdfr struct ucred *cred, rpc_gss_service_t service) 174184588Sdfr{ 175184588Sdfr uint32_t h; 176184588Sdfr 177184588Sdfr h = HASHSTEP(HASHINIT, cred->cr_uid); 178184588Sdfr h = hash32_str(principal, h); 179184588Sdfr h = hash32_buf(mech->elements, mech->length, h); 180184588Sdfr h = HASHSTEP(h, (int) service); 181184588Sdfr 182184588Sdfr return (h % RPC_GSS_HASH_SIZE); 183184588Sdfr} 184184588Sdfr 185184588Sdfr/* 186184588Sdfr * Simplified interface to create a security association for the 187184588Sdfr * current thread's * ucred. 188184588Sdfr */ 189184588SdfrAUTH * 190184588Sdfrrpc_gss_secfind(CLIENT *clnt, struct ucred *cred, const char *principal, 191184588Sdfr gss_OID mech_oid, rpc_gss_service_t service) 192184588Sdfr{ 193184588Sdfr uint32_t h, th; 194184588Sdfr AUTH *auth; 195184588Sdfr struct rpc_gss_data *gd, *tgd; 196194878Srmacklem rpc_gss_options_ret_t options; 197184588Sdfr 198184588Sdfr if (rpc_gss_count > RPC_GSS_MAX) { 199184588Sdfr while (rpc_gss_count > RPC_GSS_MAX) { 200184588Sdfr sx_xlock(&rpc_gss_lock); 201184588Sdfr tgd = TAILQ_FIRST(&rpc_gss_all); 202184588Sdfr th = tgd->gd_hash; 203184588Sdfr TAILQ_REMOVE(&rpc_gss_cache[th], tgd, gd_link); 204184588Sdfr TAILQ_REMOVE(&rpc_gss_all, tgd, gd_alllink); 205184588Sdfr rpc_gss_count--; 206184588Sdfr sx_xunlock(&rpc_gss_lock); 207184588Sdfr AUTH_DESTROY(tgd->gd_auth); 208184588Sdfr } 209184588Sdfr } 210184588Sdfr 211184588Sdfr /* 212184588Sdfr * See if we already have an AUTH which matches. 213184588Sdfr */ 214184588Sdfr h = rpc_gss_hash(principal, mech_oid, cred, service); 215184588Sdfr 216184588Sdfragain: 217184588Sdfr sx_slock(&rpc_gss_lock); 218184588Sdfr TAILQ_FOREACH(gd, &rpc_gss_cache[h], gd_link) { 219184588Sdfr if (gd->gd_ucred->cr_uid == cred->cr_uid 220184588Sdfr && !strcmp(gd->gd_principal, principal) 221184588Sdfr && gd->gd_mech == mech_oid 222184588Sdfr && gd->gd_cred.gc_svc == service) { 223184588Sdfr refcount_acquire(&gd->gd_refs); 224184588Sdfr if (sx_try_upgrade(&rpc_gss_lock)) { 225184588Sdfr /* 226184588Sdfr * Keep rpc_gss_all LRU sorted. 227184588Sdfr */ 228184588Sdfr TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink); 229184588Sdfr TAILQ_INSERT_TAIL(&rpc_gss_all, gd, 230184588Sdfr gd_alllink); 231184588Sdfr sx_xunlock(&rpc_gss_lock); 232184588Sdfr } else { 233184588Sdfr sx_sunlock(&rpc_gss_lock); 234184588Sdfr } 235194878Srmacklem 236194878Srmacklem /* 237194878Srmacklem * If the state != ESTABLISHED, try and initialize 238194878Srmacklem * the authenticator again. This will happen if the 239194878Srmacklem * user's credentials have expired. It may succeed now, 240194878Srmacklem * if they have done a kinit or similar. 241194878Srmacklem */ 242194878Srmacklem if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) { 243194878Srmacklem memset(&options, 0, sizeof (options)); 244194878Srmacklem (void) rpc_gss_init(gd->gd_auth, &options); 245194878Srmacklem } 246184588Sdfr return (gd->gd_auth); 247184588Sdfr } 248184588Sdfr } 249184588Sdfr sx_sunlock(&rpc_gss_lock); 250184588Sdfr 251184588Sdfr /* 252184588Sdfr * We missed in the cache - create a new association. 253184588Sdfr */ 254184588Sdfr auth = rpc_gss_seccreate_int(clnt, cred, principal, mech_oid, service, 255184588Sdfr GSS_C_QOP_DEFAULT, NULL, NULL); 256184588Sdfr if (!auth) 257184588Sdfr return (NULL); 258184588Sdfr 259184588Sdfr gd = AUTH_PRIVATE(auth); 260184588Sdfr gd->gd_hash = h; 261184588Sdfr 262184588Sdfr sx_xlock(&rpc_gss_lock); 263184588Sdfr TAILQ_FOREACH(tgd, &rpc_gss_cache[h], gd_link) { 264184588Sdfr if (tgd->gd_ucred->cr_uid == cred->cr_uid 265184588Sdfr && !strcmp(tgd->gd_principal, principal) 266184588Sdfr && tgd->gd_mech == mech_oid 267184588Sdfr && tgd->gd_cred.gc_svc == service) { 268184588Sdfr /* 269184588Sdfr * We lost a race to create the AUTH that 270184588Sdfr * matches this cred. 271184588Sdfr */ 272184588Sdfr sx_xunlock(&rpc_gss_lock); 273184588Sdfr AUTH_DESTROY(auth); 274184588Sdfr goto again; 275184588Sdfr } 276184588Sdfr } 277184588Sdfr 278184588Sdfr rpc_gss_count++; 279184588Sdfr TAILQ_INSERT_TAIL(&rpc_gss_cache[h], gd, gd_link); 280184588Sdfr TAILQ_INSERT_TAIL(&rpc_gss_all, gd, gd_alllink); 281184588Sdfr refcount_acquire(&gd->gd_refs); /* one for the cache, one for user */ 282184588Sdfr sx_xunlock(&rpc_gss_lock); 283184588Sdfr 284184588Sdfr return (auth); 285184588Sdfr} 286184588Sdfr 287184588Sdfrvoid 288184588Sdfrrpc_gss_secpurge(CLIENT *clnt) 289184588Sdfr{ 290184588Sdfr uint32_t h; 291184588Sdfr struct rpc_gss_data *gd, *tgd; 292184588Sdfr 293184588Sdfr TAILQ_FOREACH_SAFE(gd, &rpc_gss_all, gd_alllink, tgd) { 294184588Sdfr if (gd->gd_clnt == clnt) { 295184588Sdfr sx_xlock(&rpc_gss_lock); 296184588Sdfr h = gd->gd_hash; 297184588Sdfr TAILQ_REMOVE(&rpc_gss_cache[h], gd, gd_link); 298184588Sdfr TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink); 299184588Sdfr rpc_gss_count--; 300184588Sdfr sx_xunlock(&rpc_gss_lock); 301184588Sdfr AUTH_DESTROY(gd->gd_auth); 302184588Sdfr } 303184588Sdfr } 304184588Sdfr} 305184588Sdfr 306184588SdfrAUTH * 307184588Sdfrrpc_gss_seccreate(CLIENT *clnt, struct ucred *cred, const char *principal, 308184588Sdfr const char *mechanism, rpc_gss_service_t service, const char *qop, 309184588Sdfr rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret) 310184588Sdfr{ 311184588Sdfr gss_OID oid; 312184588Sdfr u_int qop_num; 313184588Sdfr 314184588Sdfr /* 315184588Sdfr * Bail out now if we don't know this mechanism. 316184588Sdfr */ 317184588Sdfr if (!rpc_gss_mech_to_oid(mechanism, &oid)) 318184588Sdfr return (NULL); 319184588Sdfr 320184588Sdfr if (qop) { 321184588Sdfr if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) 322184588Sdfr return (NULL); 323184588Sdfr } else { 324184588Sdfr qop_num = GSS_C_QOP_DEFAULT; 325184588Sdfr } 326184588Sdfr 327184588Sdfr return (rpc_gss_seccreate_int(clnt, cred, principal, oid, service, 328184588Sdfr qop_num, options_req, options_ret)); 329184588Sdfr} 330184588Sdfr 331184588Sdfrstatic AUTH * 332184588Sdfrrpc_gss_seccreate_int(CLIENT *clnt, struct ucred *cred, const char *principal, 333184588Sdfr gss_OID mech_oid, rpc_gss_service_t service, u_int qop_num, 334184588Sdfr rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret) 335184588Sdfr{ 336184588Sdfr AUTH *auth; 337184588Sdfr rpc_gss_options_ret_t options; 338184588Sdfr struct rpc_gss_data *gd; 339184588Sdfr 340184588Sdfr /* 341184588Sdfr * If the caller doesn't want the options, point at local 342184588Sdfr * storage to simplify the code below. 343184588Sdfr */ 344184588Sdfr if (!options_ret) 345184588Sdfr options_ret = &options; 346184588Sdfr 347184588Sdfr /* 348184588Sdfr * Default service is integrity. 349184588Sdfr */ 350184588Sdfr if (service == rpc_gss_svc_default) 351184588Sdfr service = rpc_gss_svc_integrity; 352184588Sdfr 353184588Sdfr memset(options_ret, 0, sizeof(*options_ret)); 354184588Sdfr 355184588Sdfr rpc_gss_log_debug("in rpc_gss_seccreate()"); 356184588Sdfr 357184588Sdfr memset(&rpc_createerr, 0, sizeof(rpc_createerr)); 358184588Sdfr 359184588Sdfr auth = mem_alloc(sizeof(*auth)); 360184588Sdfr if (auth == NULL) { 361184588Sdfr rpc_createerr.cf_stat = RPC_SYSTEMERROR; 362184588Sdfr rpc_createerr.cf_error.re_errno = ENOMEM; 363184588Sdfr return (NULL); 364184588Sdfr } 365184588Sdfr gd = mem_alloc(sizeof(*gd)); 366184588Sdfr if (gd == NULL) { 367184588Sdfr rpc_createerr.cf_stat = RPC_SYSTEMERROR; 368184588Sdfr rpc_createerr.cf_error.re_errno = ENOMEM; 369184588Sdfr mem_free(auth, sizeof(*auth)); 370184588Sdfr return (NULL); 371184588Sdfr } 372184588Sdfr 373184588Sdfr auth->ah_ops = &rpc_gss_ops; 374184588Sdfr auth->ah_private = (caddr_t) gd; 375184588Sdfr auth->ah_cred.oa_flavor = RPCSEC_GSS; 376184588Sdfr 377184588Sdfr refcount_init(&gd->gd_refs, 1); 378184588Sdfr mtx_init(&gd->gd_lock, "gd->gd_lock", NULL, MTX_DEF); 379184588Sdfr gd->gd_auth = auth; 380184588Sdfr gd->gd_ucred = crdup(cred); 381184588Sdfr gd->gd_principal = strdup(principal, M_RPC); 382184588Sdfr 383184588Sdfr 384184588Sdfr if (options_req) { 385184588Sdfr gd->gd_options = *options_req; 386184588Sdfr } else { 387184588Sdfr gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG; 388184588Sdfr gd->gd_options.time_req = 0; 389184588Sdfr gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL; 390184588Sdfr gd->gd_options.input_channel_bindings = NULL; 391184588Sdfr } 392184588Sdfr CLNT_ACQUIRE(clnt); 393184588Sdfr gd->gd_clnt = clnt; 394184588Sdfr gd->gd_ctx = GSS_C_NO_CONTEXT; 395184588Sdfr gd->gd_mech = mech_oid; 396184588Sdfr gd->gd_qop = qop_num; 397184588Sdfr 398184588Sdfr gd->gd_cred.gc_version = RPCSEC_GSS_VERSION; 399184588Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_INIT; 400184588Sdfr gd->gd_cred.gc_seq = 0; 401184588Sdfr gd->gd_cred.gc_svc = service; 402184588Sdfr LIST_INIT(&gd->gd_reqs); 403184588Sdfr 404184588Sdfr if (!rpc_gss_init(auth, options_ret)) { 405184588Sdfr goto bad; 406184588Sdfr } 407184588Sdfr 408184588Sdfr return (auth); 409184588Sdfr 410184588Sdfr bad: 411184588Sdfr AUTH_DESTROY(auth); 412184588Sdfr return (NULL); 413184588Sdfr} 414184588Sdfr 415184588Sdfrbool_t 416184588Sdfrrpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop) 417184588Sdfr{ 418184588Sdfr struct rpc_gss_data *gd; 419184588Sdfr u_int qop_num; 420184588Sdfr const char *mechanism; 421184588Sdfr 422184588Sdfr gd = AUTH_PRIVATE(auth); 423184588Sdfr if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) { 424184588Sdfr return (FALSE); 425184588Sdfr } 426184588Sdfr 427184588Sdfr if (qop) { 428184588Sdfr if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) { 429184588Sdfr return (FALSE); 430184588Sdfr } 431184588Sdfr } else { 432184588Sdfr qop_num = GSS_C_QOP_DEFAULT; 433184588Sdfr } 434184588Sdfr 435184588Sdfr gd->gd_cred.gc_svc = service; 436184588Sdfr gd->gd_qop = qop_num; 437184588Sdfr return (TRUE); 438184588Sdfr} 439184588Sdfr 440184588Sdfrstatic void 441184588Sdfrrpc_gss_purge_xid(struct rpc_gss_data *gd, uint32_t xid) 442184588Sdfr{ 443184588Sdfr struct rpc_pending_request *pr, *npr; 444184588Sdfr struct rpc_pending_request_list reqs; 445184588Sdfr 446184588Sdfr LIST_INIT(&reqs); 447184588Sdfr mtx_lock(&gd->gd_lock); 448184588Sdfr LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) { 449184588Sdfr if (pr->pr_xid == xid) { 450184588Sdfr LIST_REMOVE(pr, pr_link); 451184588Sdfr LIST_INSERT_HEAD(&reqs, pr, pr_link); 452184588Sdfr } 453184588Sdfr } 454184588Sdfr 455184588Sdfr mtx_unlock(&gd->gd_lock); 456184588Sdfr 457184588Sdfr LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) { 458184588Sdfr mem_free(pr, sizeof(*pr)); 459184588Sdfr } 460184588Sdfr} 461184588Sdfr 462184588Sdfrstatic uint32_t 463184588Sdfrrpc_gss_alloc_seq(struct rpc_gss_data *gd) 464184588Sdfr{ 465184588Sdfr uint32_t seq; 466184588Sdfr 467184588Sdfr mtx_lock(&gd->gd_lock); 468184588Sdfr seq = gd->gd_seq; 469184588Sdfr gd->gd_seq++; 470184588Sdfr mtx_unlock(&gd->gd_lock); 471184588Sdfr 472184588Sdfr return (seq); 473184588Sdfr} 474184588Sdfr 475184588Sdfrstatic void 476184588Sdfrrpc_gss_nextverf(__unused AUTH *auth) 477184588Sdfr{ 478184588Sdfr 479184588Sdfr /* not used */ 480184588Sdfr} 481184588Sdfr 482184588Sdfrstatic bool_t 483184588Sdfrrpc_gss_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args) 484184588Sdfr{ 485184588Sdfr struct rpc_gss_data *gd; 486184588Sdfr struct rpc_pending_request *pr; 487184588Sdfr uint32_t seq; 488184588Sdfr XDR tmpxdrs; 489184588Sdfr struct rpc_gss_cred gsscred; 490184588Sdfr char credbuf[MAX_AUTH_BYTES]; 491184588Sdfr struct opaque_auth creds, verf; 492184588Sdfr gss_buffer_desc rpcbuf, checksum; 493184588Sdfr OM_uint32 maj_stat, min_stat; 494184588Sdfr bool_t xdr_stat; 495184588Sdfr 496184588Sdfr rpc_gss_log_debug("in rpc_gss_marshal()"); 497184588Sdfr 498184588Sdfr gd = AUTH_PRIVATE(auth); 499184588Sdfr 500184588Sdfr gsscred = gd->gd_cred; 501184588Sdfr seq = rpc_gss_alloc_seq(gd); 502184588Sdfr gsscred.gc_seq = seq; 503184588Sdfr 504184588Sdfr xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE); 505184588Sdfr if (!xdr_rpc_gss_cred(&tmpxdrs, &gsscred)) { 506184588Sdfr XDR_DESTROY(&tmpxdrs); 507184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 508184588Sdfr return (FALSE); 509184588Sdfr } 510184588Sdfr creds.oa_flavor = RPCSEC_GSS; 511184588Sdfr creds.oa_base = credbuf; 512184588Sdfr creds.oa_length = XDR_GETPOS(&tmpxdrs); 513184588Sdfr XDR_DESTROY(&tmpxdrs); 514184588Sdfr 515184588Sdfr xdr_opaque_auth(xdrs, &creds); 516184588Sdfr 517184588Sdfr if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT || 518184588Sdfr gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) { 519184588Sdfr if (!xdr_opaque_auth(xdrs, &_null_auth)) { 520184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 521184588Sdfr return (FALSE); 522184588Sdfr } 523184588Sdfr xdrmbuf_append(xdrs, args); 524184588Sdfr return (TRUE); 525184588Sdfr } else { 526184588Sdfr /* 527184588Sdfr * Keep track of this XID + seq pair so that we can do 528184588Sdfr * the matching gss_verify_mic in AUTH_VALIDATE. 529184588Sdfr */ 530184588Sdfr pr = mem_alloc(sizeof(struct rpc_pending_request)); 531184588Sdfr mtx_lock(&gd->gd_lock); 532184588Sdfr pr->pr_xid = xid; 533184588Sdfr pr->pr_seq = seq; 534184588Sdfr LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link); 535184588Sdfr mtx_unlock(&gd->gd_lock); 536184588Sdfr 537184588Sdfr /* 538184588Sdfr * Checksum serialized RPC header, up to and including 539184588Sdfr * credential. For the in-kernel environment, we 540184588Sdfr * assume that our XDR stream is on a contiguous 541184588Sdfr * memory buffer (e.g. an mbuf). 542184588Sdfr */ 543184588Sdfr rpcbuf.length = XDR_GETPOS(xdrs); 544184588Sdfr XDR_SETPOS(xdrs, 0); 545184588Sdfr rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length); 546184588Sdfr 547184588Sdfr maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop, 548184588Sdfr &rpcbuf, &checksum); 549184588Sdfr 550184588Sdfr if (maj_stat != GSS_S_COMPLETE) { 551184588Sdfr rpc_gss_log_status("gss_get_mic", gd->gd_mech, 552184588Sdfr maj_stat, min_stat); 553184588Sdfr if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 554184588Sdfr rpc_gss_destroy_context(auth, TRUE); 555184588Sdfr } 556184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); 557184588Sdfr return (FALSE); 558184588Sdfr } 559184588Sdfr 560184588Sdfr verf.oa_flavor = RPCSEC_GSS; 561184588Sdfr verf.oa_base = checksum.value; 562184588Sdfr verf.oa_length = checksum.length; 563184588Sdfr 564184588Sdfr xdr_stat = xdr_opaque_auth(xdrs, &verf); 565184588Sdfr gss_release_buffer(&min_stat, &checksum); 566184588Sdfr if (!xdr_stat) { 567184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 568184588Sdfr return (FALSE); 569184588Sdfr } 570184588Sdfr if (gd->gd_state != RPCSEC_GSS_ESTABLISHED || 571184588Sdfr gd->gd_cred.gc_svc == rpc_gss_svc_none) { 572184588Sdfr xdrmbuf_append(xdrs, args); 573184588Sdfr return (TRUE); 574184588Sdfr } else { 575184588Sdfr if (!xdr_rpc_gss_wrap_data(&args, 576184588Sdfr gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc, 577184588Sdfr seq)) 578184588Sdfr return (FALSE); 579184588Sdfr xdrmbuf_append(xdrs, args); 580184588Sdfr return (TRUE); 581184588Sdfr } 582184588Sdfr } 583184588Sdfr 584184588Sdfr return (TRUE); 585184588Sdfr} 586184588Sdfr 587184588Sdfrstatic bool_t 588184588Sdfrrpc_gss_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf, 589184588Sdfr struct mbuf **resultsp) 590184588Sdfr{ 591184588Sdfr struct rpc_gss_data *gd; 592184588Sdfr struct rpc_pending_request *pr, *npr; 593184588Sdfr struct rpc_pending_request_list reqs; 594184588Sdfr gss_qop_t qop_state; 595184588Sdfr uint32_t num, seq; 596184588Sdfr gss_buffer_desc signbuf, checksum; 597184588Sdfr OM_uint32 maj_stat, min_stat; 598184588Sdfr 599184588Sdfr rpc_gss_log_debug("in rpc_gss_validate()"); 600184588Sdfr 601184588Sdfr gd = AUTH_PRIVATE(auth); 602184588Sdfr 603184588Sdfr /* 604184588Sdfr * The client will call us with a NULL verf when it gives up 605184588Sdfr * on an XID. 606184588Sdfr */ 607184588Sdfr if (!verf) { 608184588Sdfr rpc_gss_purge_xid(gd, xid); 609184588Sdfr return (TRUE); 610184588Sdfr } 611184588Sdfr 612184588Sdfr if (gd->gd_state == RPCSEC_GSS_CONTEXT) { 613184588Sdfr /* 614184588Sdfr * Save the on the wire verifier to validate last INIT 615184588Sdfr * phase packet after decode if the major status is 616184588Sdfr * GSS_S_COMPLETE. 617184588Sdfr */ 618184588Sdfr if (gd->gd_verf.value) 619184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 620184588Sdfr (char *) &gd->gd_verf); 621184588Sdfr gd->gd_verf.value = mem_alloc(verf->oa_length); 622184588Sdfr if (gd->gd_verf.value == NULL) { 623184588Sdfr printf("gss_validate: out of memory\n"); 624184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 625184588Sdfr m_freem(*resultsp); 626184588Sdfr *resultsp = NULL; 627184588Sdfr return (FALSE); 628184588Sdfr } 629184588Sdfr memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length); 630184588Sdfr gd->gd_verf.length = verf->oa_length; 631184588Sdfr 632184588Sdfr return (TRUE); 633184588Sdfr } 634184588Sdfr 635184588Sdfr /* 636184588Sdfr * We need to check the verifier against all the requests 637184588Sdfr * we've send for this XID - for unreliable protocols, we 638184588Sdfr * retransmit with the same XID but different sequence 639184588Sdfr * number. We temporarily take this set of requests out of the 640184588Sdfr * list so that we can work through the list without having to 641184588Sdfr * hold the lock. 642184588Sdfr */ 643184588Sdfr mtx_lock(&gd->gd_lock); 644184588Sdfr LIST_INIT(&reqs); 645184588Sdfr LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) { 646184588Sdfr if (pr->pr_xid == xid) { 647184588Sdfr LIST_REMOVE(pr, pr_link); 648184588Sdfr LIST_INSERT_HEAD(&reqs, pr, pr_link); 649184588Sdfr } 650184588Sdfr } 651184588Sdfr mtx_unlock(&gd->gd_lock); 652184588Sdfr LIST_FOREACH(pr, &reqs, pr_link) { 653184588Sdfr if (pr->pr_xid == xid) { 654184588Sdfr seq = pr->pr_seq; 655184588Sdfr num = htonl(seq); 656184588Sdfr signbuf.value = # 657184588Sdfr signbuf.length = sizeof(num); 658184588Sdfr 659184588Sdfr checksum.value = verf->oa_base; 660184588Sdfr checksum.length = verf->oa_length; 661184588Sdfr 662184588Sdfr maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, 663184588Sdfr &signbuf, &checksum, &qop_state); 664184588Sdfr if (maj_stat != GSS_S_COMPLETE 665184588Sdfr || qop_state != gd->gd_qop) { 666184588Sdfr continue; 667184588Sdfr } 668184588Sdfr if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 669184588Sdfr rpc_gss_destroy_context(auth, TRUE); 670184588Sdfr break; 671184588Sdfr } 672184588Sdfr //rpc_gss_purge_reqs(gd, seq); 673184588Sdfr LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) 674184588Sdfr mem_free(pr, sizeof(*pr)); 675184588Sdfr 676184588Sdfr if (gd->gd_cred.gc_svc == rpc_gss_svc_none) { 677184588Sdfr return (TRUE); 678184588Sdfr } else { 679184588Sdfr if (!xdr_rpc_gss_unwrap_data(resultsp, 680184588Sdfr gd->gd_ctx, gd->gd_qop, 681184588Sdfr gd->gd_cred.gc_svc, seq)) { 682184588Sdfr return (FALSE); 683184588Sdfr } 684184588Sdfr } 685184588Sdfr return (TRUE); 686184588Sdfr } 687184588Sdfr } 688184588Sdfr 689184588Sdfr /* 690184588Sdfr * We didn't match - put back any entries for this XID so that 691184588Sdfr * a future call to validate can retry. 692184588Sdfr */ 693184588Sdfr mtx_lock(&gd->gd_lock); 694184588Sdfr LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) { 695184588Sdfr LIST_REMOVE(pr, pr_link); 696184588Sdfr LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link); 697184588Sdfr } 698184588Sdfr mtx_unlock(&gd->gd_lock); 699184588Sdfr 700184588Sdfr /* 701184588Sdfr * Nothing matches - give up. 702184588Sdfr */ 703184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); 704184588Sdfr m_freem(*resultsp); 705184588Sdfr *resultsp = NULL; 706184588Sdfr return (FALSE); 707184588Sdfr} 708184588Sdfr 709184588Sdfrstatic bool_t 710184588Sdfrrpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret) 711184588Sdfr{ 712184588Sdfr struct thread *td = curthread; 713184588Sdfr struct ucred *crsave; 714184588Sdfr struct rpc_gss_data *gd; 715184588Sdfr struct rpc_gss_init_res gr; 716184588Sdfr gss_buffer_desc principal_desc; 717184588Sdfr gss_buffer_desc *recv_tokenp, recv_token, send_token; 718184588Sdfr gss_name_t name; 719184588Sdfr OM_uint32 maj_stat, min_stat, call_stat; 720184588Sdfr const char *mech; 721184588Sdfr struct rpc_callextra ext; 722184588Sdfr 723184588Sdfr rpc_gss_log_debug("in rpc_gss_refresh()"); 724184588Sdfr 725184588Sdfr gd = AUTH_PRIVATE(auth); 726184588Sdfr 727184588Sdfr mtx_lock(&gd->gd_lock); 728184588Sdfr /* 729184588Sdfr * If the context isn't in START state, someone else is 730184588Sdfr * refreshing - we wait till they are done. If they fail, they 731184588Sdfr * will put the state back to START and we can try (most 732184588Sdfr * likely to also fail). 733184588Sdfr */ 734184588Sdfr while (gd->gd_state != RPCSEC_GSS_START 735184588Sdfr && gd->gd_state != RPCSEC_GSS_ESTABLISHED) { 736184588Sdfr msleep(gd, &gd->gd_lock, 0, "gssstate", 0); 737184588Sdfr } 738184588Sdfr if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) { 739184588Sdfr mtx_unlock(&gd->gd_lock); 740184588Sdfr return (TRUE); 741184588Sdfr } 742184588Sdfr gd->gd_state = RPCSEC_GSS_CONTEXT; 743184588Sdfr mtx_unlock(&gd->gd_lock); 744184588Sdfr 745194878Srmacklem gd->gd_cred.gc_proc = RPCSEC_GSS_INIT; 746194878Srmacklem gd->gd_cred.gc_seq = 0; 747194878Srmacklem 748184588Sdfr principal_desc.value = (void *)gd->gd_principal; 749184588Sdfr principal_desc.length = strlen(gd->gd_principal); 750184588Sdfr maj_stat = gss_import_name(&min_stat, &principal_desc, 751184588Sdfr GSS_C_NT_HOSTBASED_SERVICE, &name); 752184588Sdfr if (maj_stat != GSS_S_COMPLETE) { 753184588Sdfr options_ret->major_status = maj_stat; 754184588Sdfr options_ret->minor_status = min_stat; 755184588Sdfr goto out; 756184588Sdfr } 757184588Sdfr 758184588Sdfr /* GSS context establishment loop. */ 759184588Sdfr memset(&recv_token, 0, sizeof(recv_token)); 760184588Sdfr memset(&gr, 0, sizeof(gr)); 761184588Sdfr memset(options_ret, 0, sizeof(*options_ret)); 762184588Sdfr options_ret->major_status = GSS_S_FAILURE; 763184588Sdfr recv_tokenp = GSS_C_NO_BUFFER; 764184588Sdfr 765184588Sdfr for (;;) { 766184588Sdfr crsave = td->td_ucred; 767184588Sdfr td->td_ucred = gd->gd_ucred; 768184588Sdfr maj_stat = gss_init_sec_context(&min_stat, 769184588Sdfr gd->gd_options.my_cred, 770184588Sdfr &gd->gd_ctx, 771184588Sdfr name, 772184588Sdfr gd->gd_mech, 773184588Sdfr gd->gd_options.req_flags, 774184588Sdfr gd->gd_options.time_req, 775184588Sdfr gd->gd_options.input_channel_bindings, 776184588Sdfr recv_tokenp, 777184588Sdfr &gd->gd_mech, /* used mech */ 778184588Sdfr &send_token, 779184588Sdfr &options_ret->ret_flags, 780184588Sdfr &options_ret->time_req); 781184588Sdfr td->td_ucred = crsave; 782184588Sdfr 783184588Sdfr /* 784184588Sdfr * Free the token which we got from the server (if 785184588Sdfr * any). Remember that this was allocated by XDR, not 786184588Sdfr * GSS-API. 787184588Sdfr */ 788184588Sdfr if (recv_tokenp != GSS_C_NO_BUFFER) { 789184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 790184588Sdfr (char *) &recv_token); 791184588Sdfr recv_tokenp = GSS_C_NO_BUFFER; 792184588Sdfr } 793184588Sdfr if (gd->gd_mech && rpc_gss_oid_to_mech(gd->gd_mech, &mech)) { 794184588Sdfr strlcpy(options_ret->actual_mechanism, 795184588Sdfr mech, 796184588Sdfr sizeof(options_ret->actual_mechanism)); 797184588Sdfr } 798184588Sdfr if (maj_stat != GSS_S_COMPLETE && 799184588Sdfr maj_stat != GSS_S_CONTINUE_NEEDED) { 800184588Sdfr rpc_gss_log_status("gss_init_sec_context", gd->gd_mech, 801184588Sdfr maj_stat, min_stat); 802184588Sdfr options_ret->major_status = maj_stat; 803184588Sdfr options_ret->minor_status = min_stat; 804184588Sdfr break; 805184588Sdfr } 806184588Sdfr if (send_token.length != 0) { 807184588Sdfr memset(&gr, 0, sizeof(gr)); 808184588Sdfr 809184588Sdfr bzero(&ext, sizeof(ext)); 810184588Sdfr ext.rc_auth = auth; 811184588Sdfr call_stat = CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC, 812184588Sdfr (xdrproc_t)xdr_gss_buffer_desc, 813184588Sdfr &send_token, 814184588Sdfr (xdrproc_t)xdr_rpc_gss_init_res, 815184588Sdfr (caddr_t)&gr, AUTH_TIMEOUT); 816184588Sdfr 817184588Sdfr gss_release_buffer(&min_stat, &send_token); 818184588Sdfr 819184588Sdfr if (call_stat != RPC_SUCCESS) 820184588Sdfr break; 821184588Sdfr 822184588Sdfr if (gr.gr_major != GSS_S_COMPLETE && 823184588Sdfr gr.gr_major != GSS_S_CONTINUE_NEEDED) { 824184588Sdfr rpc_gss_log_status("server reply", gd->gd_mech, 825184588Sdfr gr.gr_major, gr.gr_minor); 826184588Sdfr options_ret->major_status = gr.gr_major; 827184588Sdfr options_ret->minor_status = gr.gr_minor; 828184588Sdfr break; 829184588Sdfr } 830184588Sdfr 831184588Sdfr /* 832184588Sdfr * Save the server's gr_handle value, freeing 833184588Sdfr * what we have already (remember that this 834184588Sdfr * was allocated by XDR, not GSS-API). 835184588Sdfr */ 836184588Sdfr if (gr.gr_handle.length != 0) { 837184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 838184588Sdfr (char *) &gd->gd_cred.gc_handle); 839184588Sdfr gd->gd_cred.gc_handle = gr.gr_handle; 840184588Sdfr } 841184588Sdfr 842184588Sdfr /* 843184588Sdfr * Save the server's token as well. 844184588Sdfr */ 845184588Sdfr if (gr.gr_token.length != 0) { 846184588Sdfr recv_token = gr.gr_token; 847184588Sdfr recv_tokenp = &recv_token; 848184588Sdfr } 849184588Sdfr 850184588Sdfr /* 851184588Sdfr * Since we have copied out all the bits of gr 852184588Sdfr * which XDR allocated for us, we don't need 853184588Sdfr * to free it. 854184588Sdfr */ 855184588Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT; 856184588Sdfr } 857184588Sdfr 858184588Sdfr if (maj_stat == GSS_S_COMPLETE) { 859184588Sdfr gss_buffer_desc bufin; 860184588Sdfr u_int seq, qop_state = 0; 861184588Sdfr 862184588Sdfr /* 863184588Sdfr * gss header verifier, 864184588Sdfr * usually checked in gss_validate 865184588Sdfr */ 866184588Sdfr seq = htonl(gr.gr_win); 867184588Sdfr bufin.value = (unsigned char *)&seq; 868184588Sdfr bufin.length = sizeof(seq); 869184588Sdfr 870184588Sdfr maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, 871184588Sdfr &bufin, &gd->gd_verf, &qop_state); 872184588Sdfr 873184588Sdfr if (maj_stat != GSS_S_COMPLETE || 874184588Sdfr qop_state != gd->gd_qop) { 875184588Sdfr rpc_gss_log_status("gss_verify_mic", gd->gd_mech, 876184588Sdfr maj_stat, min_stat); 877184588Sdfr if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 878184588Sdfr rpc_gss_destroy_context(auth, TRUE); 879184588Sdfr } 880184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, 881184588Sdfr EPERM); 882184588Sdfr options_ret->major_status = maj_stat; 883184588Sdfr options_ret->minor_status = min_stat; 884184588Sdfr break; 885184588Sdfr } 886184588Sdfr 887184588Sdfr options_ret->major_status = GSS_S_COMPLETE; 888184588Sdfr options_ret->minor_status = 0; 889184588Sdfr options_ret->rpcsec_version = gd->gd_cred.gc_version; 890184588Sdfr options_ret->gss_context = gd->gd_ctx; 891184588Sdfr 892184588Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_DATA; 893184588Sdfr gd->gd_seq = 1; 894184588Sdfr gd->gd_win = gr.gr_win; 895184588Sdfr break; 896184588Sdfr } 897184588Sdfr } 898184588Sdfr 899184588Sdfr gss_release_name(&min_stat, &name); 900184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 901184588Sdfr (char *) &gd->gd_verf); 902184588Sdfr 903184588Sdfrout: 904184588Sdfr /* End context negotiation loop. */ 905184588Sdfr if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) { 906184588Sdfr rpc_createerr.cf_stat = RPC_AUTHERROR; 907184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); 908184588Sdfr if (gd->gd_ctx) { 909184588Sdfr gss_delete_sec_context(&min_stat, &gd->gd_ctx, 910184588Sdfr GSS_C_NO_BUFFER); 911184588Sdfr } 912184588Sdfr mtx_lock(&gd->gd_lock); 913184588Sdfr gd->gd_state = RPCSEC_GSS_START; 914184588Sdfr wakeup(gd); 915184588Sdfr mtx_unlock(&gd->gd_lock); 916184588Sdfr return (FALSE); 917184588Sdfr } 918184588Sdfr 919184588Sdfr mtx_lock(&gd->gd_lock); 920184588Sdfr gd->gd_state = RPCSEC_GSS_ESTABLISHED; 921184588Sdfr wakeup(gd); 922184588Sdfr mtx_unlock(&gd->gd_lock); 923184588Sdfr 924184588Sdfr return (TRUE); 925184588Sdfr} 926184588Sdfr 927184588Sdfrstatic bool_t 928184588Sdfrrpc_gss_refresh(AUTH *auth, void *msg) 929184588Sdfr{ 930184588Sdfr struct rpc_msg *reply = (struct rpc_msg *) msg; 931184588Sdfr rpc_gss_options_ret_t options; 932195246Srmacklem struct rpc_gss_data *gd; 933184588Sdfr 934195246Srmacklem gd = AUTH_PRIVATE(auth); 935195246Srmacklem 936184588Sdfr /* 937195246Srmacklem * If the context is in DESTROYING state, then just return, since 938195246Srmacklem * there is no point in refreshing the credentials. 939195246Srmacklem */ 940195246Srmacklem mtx_lock(&gd->gd_lock); 941195246Srmacklem if (gd->gd_state == RPCSEC_GSS_DESTROYING) { 942195246Srmacklem mtx_unlock(&gd->gd_lock); 943195246Srmacklem return (FALSE); 944195246Srmacklem } 945195246Srmacklem mtx_unlock(&gd->gd_lock); 946195246Srmacklem 947195246Srmacklem /* 948184588Sdfr * If the error was RPCSEC_GSS_CREDPROBLEM of 949184588Sdfr * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All 950184588Sdfr * other errors are fatal. 951184588Sdfr */ 952184588Sdfr if (reply->rm_reply.rp_stat == MSG_DENIED 953184588Sdfr && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR 954184588Sdfr && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM 955184588Sdfr || reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) { 956184588Sdfr rpc_gss_destroy_context(auth, FALSE); 957184588Sdfr memset(&options, 0, sizeof(options)); 958184588Sdfr return (rpc_gss_init(auth, &options)); 959184588Sdfr } 960184588Sdfr 961184588Sdfr return (FALSE); 962184588Sdfr} 963184588Sdfr 964184588Sdfrstatic void 965184588Sdfrrpc_gss_destroy_context(AUTH *auth, bool_t send_destroy) 966184588Sdfr{ 967184588Sdfr struct rpc_gss_data *gd; 968184588Sdfr struct rpc_pending_request *pr; 969184588Sdfr OM_uint32 min_stat; 970184588Sdfr struct rpc_callextra ext; 971184588Sdfr 972184588Sdfr rpc_gss_log_debug("in rpc_gss_destroy_context()"); 973184588Sdfr 974184588Sdfr gd = AUTH_PRIVATE(auth); 975184588Sdfr 976184588Sdfr mtx_lock(&gd->gd_lock); 977184588Sdfr /* 978184588Sdfr * If the context isn't in ESTABISHED state, someone else is 979184588Sdfr * destroying/refreshing - we wait till they are done. 980184588Sdfr */ 981184588Sdfr if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) { 982184588Sdfr while (gd->gd_state != RPCSEC_GSS_START 983184588Sdfr && gd->gd_state != RPCSEC_GSS_ESTABLISHED) 984184588Sdfr msleep(gd, &gd->gd_lock, 0, "gssstate", 0); 985184588Sdfr mtx_unlock(&gd->gd_lock); 986184588Sdfr return; 987184588Sdfr } 988184588Sdfr gd->gd_state = RPCSEC_GSS_DESTROYING; 989184588Sdfr mtx_unlock(&gd->gd_lock); 990184588Sdfr 991184588Sdfr if (send_destroy) { 992184588Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY; 993184588Sdfr bzero(&ext, sizeof(ext)); 994184588Sdfr ext.rc_auth = auth; 995184588Sdfr CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC, 996184588Sdfr (xdrproc_t)xdr_void, NULL, 997184588Sdfr (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT); 998184588Sdfr } 999184588Sdfr 1000184588Sdfr while ((pr = LIST_FIRST(&gd->gd_reqs)) != NULL) { 1001184588Sdfr LIST_REMOVE(pr, pr_link); 1002184588Sdfr mem_free(pr, sizeof(*pr)); 1003184588Sdfr } 1004184588Sdfr 1005184588Sdfr /* 1006184588Sdfr * Free the context token. Remember that this was 1007184588Sdfr * allocated by XDR, not GSS-API. 1008184588Sdfr */ 1009184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 1010184588Sdfr (char *) &gd->gd_cred.gc_handle); 1011184588Sdfr gd->gd_cred.gc_handle.length = 0; 1012184588Sdfr 1013184588Sdfr if (gd->gd_ctx != GSS_C_NO_CONTEXT) 1014184588Sdfr gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL); 1015184588Sdfr 1016184588Sdfr mtx_lock(&gd->gd_lock); 1017184588Sdfr gd->gd_state = RPCSEC_GSS_START; 1018184588Sdfr wakeup(gd); 1019184588Sdfr mtx_unlock(&gd->gd_lock); 1020184588Sdfr} 1021184588Sdfr 1022184588Sdfrstatic void 1023184588Sdfrrpc_gss_destroy(AUTH *auth) 1024184588Sdfr{ 1025184588Sdfr struct rpc_gss_data *gd; 1026184588Sdfr 1027184588Sdfr rpc_gss_log_debug("in rpc_gss_destroy()"); 1028184588Sdfr 1029184588Sdfr gd = AUTH_PRIVATE(auth); 1030184588Sdfr 1031184588Sdfr if (!refcount_release(&gd->gd_refs)) 1032184588Sdfr return; 1033184588Sdfr 1034184588Sdfr rpc_gss_destroy_context(auth, TRUE); 1035184588Sdfr 1036184588Sdfr CLNT_RELEASE(gd->gd_clnt); 1037184588Sdfr crfree(gd->gd_ucred); 1038184588Sdfr free(gd->gd_principal, M_RPC); 1039184588Sdfr if (gd->gd_verf.value) 1040184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 1041184588Sdfr (char *) &gd->gd_verf); 1042184588Sdfr mtx_destroy(&gd->gd_lock); 1043184588Sdfr 1044184588Sdfr mem_free(gd, sizeof(*gd)); 1045184588Sdfr mem_free(auth, sizeof(*auth)); 1046184588Sdfr} 1047184588Sdfr 1048184588Sdfrint 1049184588Sdfrrpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len) 1050184588Sdfr{ 1051184588Sdfr struct rpc_gss_data *gd; 1052184588Sdfr int want_conf; 1053184588Sdfr OM_uint32 max; 1054184588Sdfr OM_uint32 maj_stat, min_stat; 1055184588Sdfr int result; 1056184588Sdfr 1057184588Sdfr gd = AUTH_PRIVATE(auth); 1058184588Sdfr 1059184588Sdfr switch (gd->gd_cred.gc_svc) { 1060184588Sdfr case rpc_gss_svc_none: 1061184588Sdfr return (max_tp_unit_len); 1062184588Sdfr break; 1063184588Sdfr 1064184588Sdfr case rpc_gss_svc_default: 1065184588Sdfr case rpc_gss_svc_integrity: 1066184588Sdfr want_conf = FALSE; 1067184588Sdfr break; 1068184588Sdfr 1069184588Sdfr case rpc_gss_svc_privacy: 1070184588Sdfr want_conf = TRUE; 1071184588Sdfr break; 1072184588Sdfr 1073184588Sdfr default: 1074184588Sdfr return (0); 1075184588Sdfr } 1076184588Sdfr 1077184588Sdfr maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf, 1078184588Sdfr gd->gd_qop, max_tp_unit_len, &max); 1079184588Sdfr 1080184588Sdfr if (maj_stat == GSS_S_COMPLETE) { 1081184588Sdfr result = (int) max; 1082184588Sdfr if (result < 0) 1083184588Sdfr result = 0; 1084184588Sdfr return (result); 1085184588Sdfr } else { 1086184588Sdfr rpc_gss_log_status("gss_wrap_size_limit", gd->gd_mech, 1087184588Sdfr maj_stat, min_stat); 1088184588Sdfr return (0); 1089184588Sdfr } 1090184588Sdfr} 1091