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 85253049Srmacklem#include <kgssapi/krb5/kcrypto.h> 86253049Srmacklem 87184588Sdfr#include "rpcsec_gss_int.h" 88184588Sdfr 89184588Sdfrstatic void rpc_gss_nextverf(AUTH*); 90184588Sdfrstatic bool_t rpc_gss_marshal(AUTH *, uint32_t, XDR *, struct mbuf *); 91184588Sdfrstatic bool_t rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret); 92184588Sdfrstatic bool_t rpc_gss_refresh(AUTH *, void *); 93184588Sdfrstatic bool_t rpc_gss_validate(AUTH *, uint32_t, struct opaque_auth *, 94184588Sdfr struct mbuf **); 95184588Sdfrstatic void rpc_gss_destroy(AUTH *); 96184588Sdfrstatic void rpc_gss_destroy_context(AUTH *, bool_t); 97184588Sdfr 98184588Sdfrstatic struct auth_ops rpc_gss_ops = { 99184588Sdfr rpc_gss_nextverf, 100184588Sdfr rpc_gss_marshal, 101184588Sdfr rpc_gss_validate, 102184588Sdfr rpc_gss_refresh, 103184588Sdfr rpc_gss_destroy, 104184588Sdfr}; 105184588Sdfr 106184588Sdfrenum rpcsec_gss_state { 107184588Sdfr RPCSEC_GSS_START, 108184588Sdfr RPCSEC_GSS_CONTEXT, 109184588Sdfr RPCSEC_GSS_ESTABLISHED, 110184588Sdfr RPCSEC_GSS_DESTROYING 111184588Sdfr}; 112184588Sdfr 113184588Sdfrstruct rpc_pending_request { 114184588Sdfr uint32_t pr_xid; /* XID of rpc */ 115184588Sdfr uint32_t pr_seq; /* matching GSS seq */ 116184588Sdfr LIST_ENTRY(rpc_pending_request) pr_link; 117184588Sdfr}; 118184588SdfrLIST_HEAD(rpc_pending_request_list, rpc_pending_request); 119184588Sdfr 120184588Sdfrstruct rpc_gss_data { 121184588Sdfr volatile u_int gd_refs; /* number of current users */ 122184588Sdfr struct mtx gd_lock; 123184588Sdfr uint32_t gd_hash; 124184588Sdfr AUTH *gd_auth; /* link back to AUTH */ 125184588Sdfr struct ucred *gd_ucred; /* matching local cred */ 126184588Sdfr char *gd_principal; /* server principal name */ 127253049Srmacklem char *gd_clntprincipal; /* client principal name */ 128184588Sdfr rpc_gss_options_req_t gd_options; /* GSS context options */ 129184588Sdfr enum rpcsec_gss_state gd_state; /* connection state */ 130184588Sdfr gss_buffer_desc gd_verf; /* save GSS_S_COMPLETE 131184588Sdfr * NULL RPC verfier to 132184588Sdfr * process at end of 133184588Sdfr * context negotiation */ 134184588Sdfr CLIENT *gd_clnt; /* client handle */ 135184588Sdfr gss_OID gd_mech; /* mechanism to use */ 136184588Sdfr gss_qop_t gd_qop; /* quality of protection */ 137184588Sdfr gss_ctx_id_t gd_ctx; /* context id */ 138184588Sdfr struct rpc_gss_cred gd_cred; /* client credentials */ 139184588Sdfr uint32_t gd_seq; /* next sequence number */ 140184588Sdfr u_int gd_win; /* sequence window */ 141184588Sdfr struct rpc_pending_request_list gd_reqs; 142184588Sdfr TAILQ_ENTRY(rpc_gss_data) gd_link; 143184588Sdfr TAILQ_ENTRY(rpc_gss_data) gd_alllink; 144184588Sdfr}; 145184588SdfrTAILQ_HEAD(rpc_gss_data_list, rpc_gss_data); 146184588Sdfr 147184588Sdfr#define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private) 148184588Sdfr 149184588Sdfrstatic struct timeval AUTH_TIMEOUT = { 25, 0 }; 150184588Sdfr 151184588Sdfr#define RPC_GSS_HASH_SIZE 11 152184588Sdfr#define RPC_GSS_MAX 256 153184588Sdfrstatic struct rpc_gss_data_list rpc_gss_cache[RPC_GSS_HASH_SIZE]; 154184588Sdfrstatic struct rpc_gss_data_list rpc_gss_all; 155184588Sdfrstatic struct sx rpc_gss_lock; 156184588Sdfrstatic int rpc_gss_count; 157184588Sdfr 158184588Sdfrstatic AUTH *rpc_gss_seccreate_int(CLIENT *, struct ucred *, const char *, 159253049Srmacklem const char *, gss_OID, rpc_gss_service_t, u_int, rpc_gss_options_req_t *, 160184588Sdfr rpc_gss_options_ret_t *); 161184588Sdfr 162184588Sdfrstatic void 163184588Sdfrrpc_gss_hashinit(void *dummy) 164184588Sdfr{ 165184588Sdfr int i; 166184588Sdfr 167184588Sdfr for (i = 0; i < RPC_GSS_HASH_SIZE; i++) 168184588Sdfr TAILQ_INIT(&rpc_gss_cache[i]); 169184588Sdfr TAILQ_INIT(&rpc_gss_all); 170184588Sdfr sx_init(&rpc_gss_lock, "rpc_gss_lock"); 171184588Sdfr} 172184588SdfrSYSINIT(rpc_gss_hashinit, SI_SUB_KMEM, SI_ORDER_ANY, rpc_gss_hashinit, NULL); 173184588Sdfr 174184588Sdfrstatic uint32_t 175184588Sdfrrpc_gss_hash(const char *principal, gss_OID mech, 176184588Sdfr struct ucred *cred, rpc_gss_service_t service) 177184588Sdfr{ 178184588Sdfr uint32_t h; 179184588Sdfr 180184588Sdfr h = HASHSTEP(HASHINIT, cred->cr_uid); 181184588Sdfr h = hash32_str(principal, h); 182184588Sdfr h = hash32_buf(mech->elements, mech->length, h); 183184588Sdfr h = HASHSTEP(h, (int) service); 184184588Sdfr 185184588Sdfr return (h % RPC_GSS_HASH_SIZE); 186184588Sdfr} 187184588Sdfr 188184588Sdfr/* 189184588Sdfr * Simplified interface to create a security association for the 190184588Sdfr * current thread's * ucred. 191184588Sdfr */ 192184588SdfrAUTH * 193184588Sdfrrpc_gss_secfind(CLIENT *clnt, struct ucred *cred, const char *principal, 194184588Sdfr gss_OID mech_oid, rpc_gss_service_t service) 195184588Sdfr{ 196184588Sdfr uint32_t h, th; 197184588Sdfr AUTH *auth; 198184588Sdfr struct rpc_gss_data *gd, *tgd; 199194878Srmacklem rpc_gss_options_ret_t options; 200184588Sdfr 201184588Sdfr if (rpc_gss_count > RPC_GSS_MAX) { 202184588Sdfr while (rpc_gss_count > RPC_GSS_MAX) { 203184588Sdfr sx_xlock(&rpc_gss_lock); 204184588Sdfr tgd = TAILQ_FIRST(&rpc_gss_all); 205184588Sdfr th = tgd->gd_hash; 206184588Sdfr TAILQ_REMOVE(&rpc_gss_cache[th], tgd, gd_link); 207184588Sdfr TAILQ_REMOVE(&rpc_gss_all, tgd, gd_alllink); 208184588Sdfr rpc_gss_count--; 209184588Sdfr sx_xunlock(&rpc_gss_lock); 210184588Sdfr AUTH_DESTROY(tgd->gd_auth); 211184588Sdfr } 212184588Sdfr } 213184588Sdfr 214184588Sdfr /* 215184588Sdfr * See if we already have an AUTH which matches. 216184588Sdfr */ 217184588Sdfr h = rpc_gss_hash(principal, mech_oid, cred, service); 218184588Sdfr 219184588Sdfragain: 220184588Sdfr sx_slock(&rpc_gss_lock); 221184588Sdfr TAILQ_FOREACH(gd, &rpc_gss_cache[h], gd_link) { 222184588Sdfr if (gd->gd_ucred->cr_uid == cred->cr_uid 223184588Sdfr && !strcmp(gd->gd_principal, principal) 224184588Sdfr && gd->gd_mech == mech_oid 225184588Sdfr && gd->gd_cred.gc_svc == service) { 226184588Sdfr refcount_acquire(&gd->gd_refs); 227184588Sdfr if (sx_try_upgrade(&rpc_gss_lock)) { 228184588Sdfr /* 229184588Sdfr * Keep rpc_gss_all LRU sorted. 230184588Sdfr */ 231184588Sdfr TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink); 232184588Sdfr TAILQ_INSERT_TAIL(&rpc_gss_all, gd, 233184588Sdfr gd_alllink); 234184588Sdfr sx_xunlock(&rpc_gss_lock); 235184588Sdfr } else { 236184588Sdfr sx_sunlock(&rpc_gss_lock); 237184588Sdfr } 238194878Srmacklem 239194878Srmacklem /* 240194878Srmacklem * If the state != ESTABLISHED, try and initialize 241194878Srmacklem * the authenticator again. This will happen if the 242194878Srmacklem * user's credentials have expired. It may succeed now, 243194878Srmacklem * if they have done a kinit or similar. 244194878Srmacklem */ 245194878Srmacklem if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) { 246194878Srmacklem memset(&options, 0, sizeof (options)); 247194878Srmacklem (void) rpc_gss_init(gd->gd_auth, &options); 248194878Srmacklem } 249184588Sdfr return (gd->gd_auth); 250184588Sdfr } 251184588Sdfr } 252184588Sdfr sx_sunlock(&rpc_gss_lock); 253184588Sdfr 254184588Sdfr /* 255184588Sdfr * We missed in the cache - create a new association. 256184588Sdfr */ 257253049Srmacklem auth = rpc_gss_seccreate_int(clnt, cred, NULL, principal, mech_oid, 258253049Srmacklem service, GSS_C_QOP_DEFAULT, NULL, NULL); 259184588Sdfr if (!auth) 260184588Sdfr return (NULL); 261184588Sdfr 262184588Sdfr gd = AUTH_PRIVATE(auth); 263184588Sdfr gd->gd_hash = h; 264184588Sdfr 265184588Sdfr sx_xlock(&rpc_gss_lock); 266184588Sdfr TAILQ_FOREACH(tgd, &rpc_gss_cache[h], gd_link) { 267184588Sdfr if (tgd->gd_ucred->cr_uid == cred->cr_uid 268184588Sdfr && !strcmp(tgd->gd_principal, principal) 269184588Sdfr && tgd->gd_mech == mech_oid 270184588Sdfr && tgd->gd_cred.gc_svc == service) { 271184588Sdfr /* 272184588Sdfr * We lost a race to create the AUTH that 273184588Sdfr * matches this cred. 274184588Sdfr */ 275184588Sdfr sx_xunlock(&rpc_gss_lock); 276184588Sdfr AUTH_DESTROY(auth); 277184588Sdfr goto again; 278184588Sdfr } 279184588Sdfr } 280184588Sdfr 281184588Sdfr rpc_gss_count++; 282184588Sdfr TAILQ_INSERT_TAIL(&rpc_gss_cache[h], gd, gd_link); 283184588Sdfr TAILQ_INSERT_TAIL(&rpc_gss_all, gd, gd_alllink); 284184588Sdfr refcount_acquire(&gd->gd_refs); /* one for the cache, one for user */ 285184588Sdfr sx_xunlock(&rpc_gss_lock); 286184588Sdfr 287184588Sdfr return (auth); 288184588Sdfr} 289184588Sdfr 290184588Sdfrvoid 291184588Sdfrrpc_gss_secpurge(CLIENT *clnt) 292184588Sdfr{ 293184588Sdfr uint32_t h; 294184588Sdfr struct rpc_gss_data *gd, *tgd; 295184588Sdfr 296184588Sdfr TAILQ_FOREACH_SAFE(gd, &rpc_gss_all, gd_alllink, tgd) { 297184588Sdfr if (gd->gd_clnt == clnt) { 298184588Sdfr sx_xlock(&rpc_gss_lock); 299184588Sdfr h = gd->gd_hash; 300184588Sdfr TAILQ_REMOVE(&rpc_gss_cache[h], gd, gd_link); 301184588Sdfr TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink); 302184588Sdfr rpc_gss_count--; 303184588Sdfr sx_xunlock(&rpc_gss_lock); 304184588Sdfr AUTH_DESTROY(gd->gd_auth); 305184588Sdfr } 306184588Sdfr } 307184588Sdfr} 308184588Sdfr 309184588SdfrAUTH * 310253049Srmacklemrpc_gss_seccreate(CLIENT *clnt, struct ucred *cred, const char *clnt_principal, 311253049Srmacklem const char *principal, const char *mechanism, rpc_gss_service_t service, 312253049Srmacklem const char *qop, rpc_gss_options_req_t *options_req, 313253049Srmacklem rpc_gss_options_ret_t *options_ret) 314184588Sdfr{ 315184588Sdfr gss_OID oid; 316184588Sdfr u_int qop_num; 317184588Sdfr 318184588Sdfr /* 319184588Sdfr * Bail out now if we don't know this mechanism. 320184588Sdfr */ 321184588Sdfr if (!rpc_gss_mech_to_oid(mechanism, &oid)) 322184588Sdfr return (NULL); 323184588Sdfr 324184588Sdfr if (qop) { 325184588Sdfr if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) 326184588Sdfr return (NULL); 327184588Sdfr } else { 328184588Sdfr qop_num = GSS_C_QOP_DEFAULT; 329184588Sdfr } 330184588Sdfr 331253049Srmacklem return (rpc_gss_seccreate_int(clnt, cred, clnt_principal, principal, 332253049Srmacklem oid, service, qop_num, options_req, options_ret)); 333184588Sdfr} 334184588Sdfr 335253049Srmacklemvoid 336253049Srmacklemrpc_gss_refresh_auth(AUTH *auth) 337253049Srmacklem{ 338253049Srmacklem struct rpc_gss_data *gd; 339253049Srmacklem rpc_gss_options_ret_t options; 340253049Srmacklem 341253049Srmacklem gd = AUTH_PRIVATE(auth); 342253049Srmacklem /* 343253049Srmacklem * If the state != ESTABLISHED, try and initialize 344253049Srmacklem * the authenticator again. This will happen if the 345253049Srmacklem * user's credentials have expired. It may succeed now, 346253049Srmacklem * if they have done a kinit or similar. 347253049Srmacklem */ 348253049Srmacklem if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) { 349253049Srmacklem memset(&options, 0, sizeof (options)); 350253049Srmacklem (void) rpc_gss_init(auth, &options); 351253049Srmacklem } 352253049Srmacklem} 353253049Srmacklem 354184588Sdfrstatic AUTH * 355253049Srmacklemrpc_gss_seccreate_int(CLIENT *clnt, struct ucred *cred, 356253049Srmacklem const char *clnt_principal, const char *principal, gss_OID mech_oid, 357253049Srmacklem rpc_gss_service_t service, u_int qop_num, 358184588Sdfr rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret) 359184588Sdfr{ 360184588Sdfr AUTH *auth; 361184588Sdfr rpc_gss_options_ret_t options; 362184588Sdfr struct rpc_gss_data *gd; 363184588Sdfr 364184588Sdfr /* 365184588Sdfr * If the caller doesn't want the options, point at local 366184588Sdfr * storage to simplify the code below. 367184588Sdfr */ 368184588Sdfr if (!options_ret) 369184588Sdfr options_ret = &options; 370184588Sdfr 371184588Sdfr /* 372184588Sdfr * Default service is integrity. 373184588Sdfr */ 374184588Sdfr if (service == rpc_gss_svc_default) 375184588Sdfr service = rpc_gss_svc_integrity; 376184588Sdfr 377184588Sdfr memset(options_ret, 0, sizeof(*options_ret)); 378184588Sdfr 379184588Sdfr rpc_gss_log_debug("in rpc_gss_seccreate()"); 380184588Sdfr 381184588Sdfr memset(&rpc_createerr, 0, sizeof(rpc_createerr)); 382184588Sdfr 383184588Sdfr auth = mem_alloc(sizeof(*auth)); 384184588Sdfr if (auth == NULL) { 385184588Sdfr rpc_createerr.cf_stat = RPC_SYSTEMERROR; 386184588Sdfr rpc_createerr.cf_error.re_errno = ENOMEM; 387184588Sdfr return (NULL); 388184588Sdfr } 389184588Sdfr gd = mem_alloc(sizeof(*gd)); 390184588Sdfr if (gd == NULL) { 391184588Sdfr rpc_createerr.cf_stat = RPC_SYSTEMERROR; 392184588Sdfr rpc_createerr.cf_error.re_errno = ENOMEM; 393184588Sdfr mem_free(auth, sizeof(*auth)); 394184588Sdfr return (NULL); 395184588Sdfr } 396184588Sdfr 397184588Sdfr auth->ah_ops = &rpc_gss_ops; 398184588Sdfr auth->ah_private = (caddr_t) gd; 399184588Sdfr auth->ah_cred.oa_flavor = RPCSEC_GSS; 400184588Sdfr 401184588Sdfr refcount_init(&gd->gd_refs, 1); 402184588Sdfr mtx_init(&gd->gd_lock, "gd->gd_lock", NULL, MTX_DEF); 403184588Sdfr gd->gd_auth = auth; 404184588Sdfr gd->gd_ucred = crdup(cred); 405184588Sdfr gd->gd_principal = strdup(principal, M_RPC); 406253049Srmacklem if (clnt_principal != NULL) 407253049Srmacklem gd->gd_clntprincipal = strdup(clnt_principal, M_RPC); 408253049Srmacklem else 409253049Srmacklem gd->gd_clntprincipal = NULL; 410184588Sdfr 411184588Sdfr 412184588Sdfr if (options_req) { 413184588Sdfr gd->gd_options = *options_req; 414184588Sdfr } else { 415184588Sdfr gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG; 416184588Sdfr gd->gd_options.time_req = 0; 417184588Sdfr gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL; 418184588Sdfr gd->gd_options.input_channel_bindings = NULL; 419184588Sdfr } 420184588Sdfr CLNT_ACQUIRE(clnt); 421184588Sdfr gd->gd_clnt = clnt; 422184588Sdfr gd->gd_ctx = GSS_C_NO_CONTEXT; 423184588Sdfr gd->gd_mech = mech_oid; 424184588Sdfr gd->gd_qop = qop_num; 425184588Sdfr 426184588Sdfr gd->gd_cred.gc_version = RPCSEC_GSS_VERSION; 427184588Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_INIT; 428184588Sdfr gd->gd_cred.gc_seq = 0; 429184588Sdfr gd->gd_cred.gc_svc = service; 430184588Sdfr LIST_INIT(&gd->gd_reqs); 431184588Sdfr 432184588Sdfr if (!rpc_gss_init(auth, options_ret)) { 433184588Sdfr goto bad; 434184588Sdfr } 435184588Sdfr 436184588Sdfr return (auth); 437184588Sdfr 438184588Sdfr bad: 439184588Sdfr AUTH_DESTROY(auth); 440184588Sdfr return (NULL); 441184588Sdfr} 442184588Sdfr 443184588Sdfrbool_t 444184588Sdfrrpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop) 445184588Sdfr{ 446184588Sdfr struct rpc_gss_data *gd; 447184588Sdfr u_int qop_num; 448184588Sdfr const char *mechanism; 449184588Sdfr 450184588Sdfr gd = AUTH_PRIVATE(auth); 451184588Sdfr if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) { 452184588Sdfr return (FALSE); 453184588Sdfr } 454184588Sdfr 455184588Sdfr if (qop) { 456184588Sdfr if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) { 457184588Sdfr return (FALSE); 458184588Sdfr } 459184588Sdfr } else { 460184588Sdfr qop_num = GSS_C_QOP_DEFAULT; 461184588Sdfr } 462184588Sdfr 463184588Sdfr gd->gd_cred.gc_svc = service; 464184588Sdfr gd->gd_qop = qop_num; 465184588Sdfr return (TRUE); 466184588Sdfr} 467184588Sdfr 468184588Sdfrstatic void 469184588Sdfrrpc_gss_purge_xid(struct rpc_gss_data *gd, uint32_t xid) 470184588Sdfr{ 471184588Sdfr struct rpc_pending_request *pr, *npr; 472184588Sdfr struct rpc_pending_request_list reqs; 473184588Sdfr 474184588Sdfr LIST_INIT(&reqs); 475184588Sdfr mtx_lock(&gd->gd_lock); 476184588Sdfr LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) { 477184588Sdfr if (pr->pr_xid == xid) { 478184588Sdfr LIST_REMOVE(pr, pr_link); 479184588Sdfr LIST_INSERT_HEAD(&reqs, pr, pr_link); 480184588Sdfr } 481184588Sdfr } 482184588Sdfr 483184588Sdfr mtx_unlock(&gd->gd_lock); 484184588Sdfr 485184588Sdfr LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) { 486184588Sdfr mem_free(pr, sizeof(*pr)); 487184588Sdfr } 488184588Sdfr} 489184588Sdfr 490184588Sdfrstatic uint32_t 491184588Sdfrrpc_gss_alloc_seq(struct rpc_gss_data *gd) 492184588Sdfr{ 493184588Sdfr uint32_t seq; 494184588Sdfr 495184588Sdfr mtx_lock(&gd->gd_lock); 496184588Sdfr seq = gd->gd_seq; 497184588Sdfr gd->gd_seq++; 498184588Sdfr mtx_unlock(&gd->gd_lock); 499184588Sdfr 500184588Sdfr return (seq); 501184588Sdfr} 502184588Sdfr 503184588Sdfrstatic void 504184588Sdfrrpc_gss_nextverf(__unused AUTH *auth) 505184588Sdfr{ 506184588Sdfr 507184588Sdfr /* not used */ 508184588Sdfr} 509184588Sdfr 510184588Sdfrstatic bool_t 511184588Sdfrrpc_gss_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args) 512184588Sdfr{ 513184588Sdfr struct rpc_gss_data *gd; 514184588Sdfr struct rpc_pending_request *pr; 515184588Sdfr uint32_t seq; 516184588Sdfr XDR tmpxdrs; 517184588Sdfr struct rpc_gss_cred gsscred; 518184588Sdfr char credbuf[MAX_AUTH_BYTES]; 519184588Sdfr struct opaque_auth creds, verf; 520184588Sdfr gss_buffer_desc rpcbuf, checksum; 521184588Sdfr OM_uint32 maj_stat, min_stat; 522184588Sdfr bool_t xdr_stat; 523184588Sdfr 524184588Sdfr rpc_gss_log_debug("in rpc_gss_marshal()"); 525184588Sdfr 526184588Sdfr gd = AUTH_PRIVATE(auth); 527184588Sdfr 528184588Sdfr gsscred = gd->gd_cred; 529184588Sdfr seq = rpc_gss_alloc_seq(gd); 530184588Sdfr gsscred.gc_seq = seq; 531184588Sdfr 532184588Sdfr xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE); 533184588Sdfr if (!xdr_rpc_gss_cred(&tmpxdrs, &gsscred)) { 534184588Sdfr XDR_DESTROY(&tmpxdrs); 535184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 536184588Sdfr return (FALSE); 537184588Sdfr } 538184588Sdfr creds.oa_flavor = RPCSEC_GSS; 539184588Sdfr creds.oa_base = credbuf; 540184588Sdfr creds.oa_length = XDR_GETPOS(&tmpxdrs); 541184588Sdfr XDR_DESTROY(&tmpxdrs); 542184588Sdfr 543184588Sdfr xdr_opaque_auth(xdrs, &creds); 544184588Sdfr 545184588Sdfr if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT || 546184588Sdfr gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) { 547184588Sdfr if (!xdr_opaque_auth(xdrs, &_null_auth)) { 548184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 549184588Sdfr return (FALSE); 550184588Sdfr } 551184588Sdfr xdrmbuf_append(xdrs, args); 552184588Sdfr return (TRUE); 553184588Sdfr } else { 554184588Sdfr /* 555184588Sdfr * Keep track of this XID + seq pair so that we can do 556184588Sdfr * the matching gss_verify_mic in AUTH_VALIDATE. 557184588Sdfr */ 558184588Sdfr pr = mem_alloc(sizeof(struct rpc_pending_request)); 559184588Sdfr mtx_lock(&gd->gd_lock); 560184588Sdfr pr->pr_xid = xid; 561184588Sdfr pr->pr_seq = seq; 562184588Sdfr LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link); 563184588Sdfr mtx_unlock(&gd->gd_lock); 564184588Sdfr 565184588Sdfr /* 566184588Sdfr * Checksum serialized RPC header, up to and including 567184588Sdfr * credential. For the in-kernel environment, we 568184588Sdfr * assume that our XDR stream is on a contiguous 569184588Sdfr * memory buffer (e.g. an mbuf). 570184588Sdfr */ 571184588Sdfr rpcbuf.length = XDR_GETPOS(xdrs); 572184588Sdfr XDR_SETPOS(xdrs, 0); 573184588Sdfr rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length); 574184588Sdfr 575184588Sdfr maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop, 576184588Sdfr &rpcbuf, &checksum); 577184588Sdfr 578184588Sdfr if (maj_stat != GSS_S_COMPLETE) { 579184588Sdfr rpc_gss_log_status("gss_get_mic", gd->gd_mech, 580184588Sdfr maj_stat, min_stat); 581184588Sdfr if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 582184588Sdfr rpc_gss_destroy_context(auth, TRUE); 583184588Sdfr } 584184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); 585184588Sdfr return (FALSE); 586184588Sdfr } 587184588Sdfr 588184588Sdfr verf.oa_flavor = RPCSEC_GSS; 589184588Sdfr verf.oa_base = checksum.value; 590184588Sdfr verf.oa_length = checksum.length; 591184588Sdfr 592184588Sdfr xdr_stat = xdr_opaque_auth(xdrs, &verf); 593184588Sdfr gss_release_buffer(&min_stat, &checksum); 594184588Sdfr if (!xdr_stat) { 595184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 596184588Sdfr return (FALSE); 597184588Sdfr } 598184588Sdfr if (gd->gd_state != RPCSEC_GSS_ESTABLISHED || 599184588Sdfr gd->gd_cred.gc_svc == rpc_gss_svc_none) { 600184588Sdfr xdrmbuf_append(xdrs, args); 601184588Sdfr return (TRUE); 602184588Sdfr } else { 603184588Sdfr if (!xdr_rpc_gss_wrap_data(&args, 604184588Sdfr gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc, 605184588Sdfr seq)) 606184588Sdfr return (FALSE); 607184588Sdfr xdrmbuf_append(xdrs, args); 608184588Sdfr return (TRUE); 609184588Sdfr } 610184588Sdfr } 611184588Sdfr 612184588Sdfr return (TRUE); 613184588Sdfr} 614184588Sdfr 615184588Sdfrstatic bool_t 616184588Sdfrrpc_gss_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf, 617184588Sdfr struct mbuf **resultsp) 618184588Sdfr{ 619184588Sdfr struct rpc_gss_data *gd; 620184588Sdfr struct rpc_pending_request *pr, *npr; 621184588Sdfr struct rpc_pending_request_list reqs; 622184588Sdfr gss_qop_t qop_state; 623184588Sdfr uint32_t num, seq; 624184588Sdfr gss_buffer_desc signbuf, checksum; 625184588Sdfr OM_uint32 maj_stat, min_stat; 626184588Sdfr 627184588Sdfr rpc_gss_log_debug("in rpc_gss_validate()"); 628184588Sdfr 629184588Sdfr gd = AUTH_PRIVATE(auth); 630184588Sdfr 631184588Sdfr /* 632184588Sdfr * The client will call us with a NULL verf when it gives up 633184588Sdfr * on an XID. 634184588Sdfr */ 635184588Sdfr if (!verf) { 636184588Sdfr rpc_gss_purge_xid(gd, xid); 637184588Sdfr return (TRUE); 638184588Sdfr } 639184588Sdfr 640184588Sdfr if (gd->gd_state == RPCSEC_GSS_CONTEXT) { 641184588Sdfr /* 642184588Sdfr * Save the on the wire verifier to validate last INIT 643184588Sdfr * phase packet after decode if the major status is 644184588Sdfr * GSS_S_COMPLETE. 645184588Sdfr */ 646184588Sdfr if (gd->gd_verf.value) 647184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 648184588Sdfr (char *) &gd->gd_verf); 649184588Sdfr gd->gd_verf.value = mem_alloc(verf->oa_length); 650184588Sdfr if (gd->gd_verf.value == NULL) { 651184588Sdfr printf("gss_validate: out of memory\n"); 652184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 653184588Sdfr m_freem(*resultsp); 654184588Sdfr *resultsp = NULL; 655184588Sdfr return (FALSE); 656184588Sdfr } 657184588Sdfr memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length); 658184588Sdfr gd->gd_verf.length = verf->oa_length; 659184588Sdfr 660184588Sdfr return (TRUE); 661184588Sdfr } 662184588Sdfr 663184588Sdfr /* 664184588Sdfr * We need to check the verifier against all the requests 665184588Sdfr * we've send for this XID - for unreliable protocols, we 666184588Sdfr * retransmit with the same XID but different sequence 667184588Sdfr * number. We temporarily take this set of requests out of the 668184588Sdfr * list so that we can work through the list without having to 669184588Sdfr * hold the lock. 670184588Sdfr */ 671184588Sdfr mtx_lock(&gd->gd_lock); 672184588Sdfr LIST_INIT(&reqs); 673184588Sdfr LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) { 674184588Sdfr if (pr->pr_xid == xid) { 675184588Sdfr LIST_REMOVE(pr, pr_link); 676184588Sdfr LIST_INSERT_HEAD(&reqs, pr, pr_link); 677184588Sdfr } 678184588Sdfr } 679184588Sdfr mtx_unlock(&gd->gd_lock); 680184588Sdfr LIST_FOREACH(pr, &reqs, pr_link) { 681184588Sdfr if (pr->pr_xid == xid) { 682184588Sdfr seq = pr->pr_seq; 683184588Sdfr num = htonl(seq); 684184588Sdfr signbuf.value = # 685184588Sdfr signbuf.length = sizeof(num); 686184588Sdfr 687184588Sdfr checksum.value = verf->oa_base; 688184588Sdfr checksum.length = verf->oa_length; 689184588Sdfr 690184588Sdfr maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, 691184588Sdfr &signbuf, &checksum, &qop_state); 692184588Sdfr if (maj_stat != GSS_S_COMPLETE 693184588Sdfr || qop_state != gd->gd_qop) { 694184588Sdfr continue; 695184588Sdfr } 696184588Sdfr if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 697184588Sdfr rpc_gss_destroy_context(auth, TRUE); 698184588Sdfr break; 699184588Sdfr } 700184588Sdfr //rpc_gss_purge_reqs(gd, seq); 701184588Sdfr LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) 702184588Sdfr mem_free(pr, sizeof(*pr)); 703184588Sdfr 704184588Sdfr if (gd->gd_cred.gc_svc == rpc_gss_svc_none) { 705184588Sdfr return (TRUE); 706184588Sdfr } else { 707184588Sdfr if (!xdr_rpc_gss_unwrap_data(resultsp, 708184588Sdfr gd->gd_ctx, gd->gd_qop, 709184588Sdfr gd->gd_cred.gc_svc, seq)) { 710184588Sdfr return (FALSE); 711184588Sdfr } 712184588Sdfr } 713184588Sdfr return (TRUE); 714184588Sdfr } 715184588Sdfr } 716184588Sdfr 717184588Sdfr /* 718184588Sdfr * We didn't match - put back any entries for this XID so that 719184588Sdfr * a future call to validate can retry. 720184588Sdfr */ 721184588Sdfr mtx_lock(&gd->gd_lock); 722184588Sdfr LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) { 723184588Sdfr LIST_REMOVE(pr, pr_link); 724184588Sdfr LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link); 725184588Sdfr } 726184588Sdfr mtx_unlock(&gd->gd_lock); 727184588Sdfr 728184588Sdfr /* 729184588Sdfr * Nothing matches - give up. 730184588Sdfr */ 731184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); 732184588Sdfr m_freem(*resultsp); 733184588Sdfr *resultsp = NULL; 734184588Sdfr return (FALSE); 735184588Sdfr} 736184588Sdfr 737184588Sdfrstatic bool_t 738184588Sdfrrpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret) 739184588Sdfr{ 740184588Sdfr struct thread *td = curthread; 741184588Sdfr struct ucred *crsave; 742184588Sdfr struct rpc_gss_data *gd; 743184588Sdfr struct rpc_gss_init_res gr; 744184588Sdfr gss_buffer_desc principal_desc; 745184588Sdfr gss_buffer_desc *recv_tokenp, recv_token, send_token; 746184588Sdfr gss_name_t name; 747184588Sdfr OM_uint32 maj_stat, min_stat, call_stat; 748184588Sdfr const char *mech; 749184588Sdfr struct rpc_callextra ext; 750253049Srmacklem gss_OID mech_oid; 751253049Srmacklem gss_OID_set mechlist; 752184588Sdfr 753184588Sdfr rpc_gss_log_debug("in rpc_gss_refresh()"); 754184588Sdfr 755184588Sdfr gd = AUTH_PRIVATE(auth); 756184588Sdfr 757184588Sdfr mtx_lock(&gd->gd_lock); 758184588Sdfr /* 759184588Sdfr * If the context isn't in START state, someone else is 760184588Sdfr * refreshing - we wait till they are done. If they fail, they 761184588Sdfr * will put the state back to START and we can try (most 762184588Sdfr * likely to also fail). 763184588Sdfr */ 764184588Sdfr while (gd->gd_state != RPCSEC_GSS_START 765184588Sdfr && gd->gd_state != RPCSEC_GSS_ESTABLISHED) { 766184588Sdfr msleep(gd, &gd->gd_lock, 0, "gssstate", 0); 767184588Sdfr } 768184588Sdfr if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) { 769184588Sdfr mtx_unlock(&gd->gd_lock); 770184588Sdfr return (TRUE); 771184588Sdfr } 772184588Sdfr gd->gd_state = RPCSEC_GSS_CONTEXT; 773184588Sdfr mtx_unlock(&gd->gd_lock); 774184588Sdfr 775194878Srmacklem gd->gd_cred.gc_proc = RPCSEC_GSS_INIT; 776194878Srmacklem gd->gd_cred.gc_seq = 0; 777194878Srmacklem 778253049Srmacklem /* 779253049Srmacklem * For KerberosV, if there is a client principal name, that implies 780253049Srmacklem * that this is a host based initiator credential in the default 781253049Srmacklem * keytab file. For this case, it is necessary to do a 782253049Srmacklem * gss_acquire_cred(). When this is done, the gssd daemon will 783253049Srmacklem * do the equivalent of "kinit -k" to put a TGT for the name in 784253049Srmacklem * the credential cache file for the gssd daemon. 785253049Srmacklem */ 786253049Srmacklem if (gd->gd_clntprincipal != NULL && 787253049Srmacklem rpc_gss_mech_to_oid("kerberosv5", &mech_oid) && 788253049Srmacklem gd->gd_mech == mech_oid) { 789253049Srmacklem /* Get rid of any old credential. */ 790253049Srmacklem if (gd->gd_options.my_cred != GSS_C_NO_CREDENTIAL) { 791253049Srmacklem gss_release_cred(&min_stat, &gd->gd_options.my_cred); 792253049Srmacklem gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL; 793253049Srmacklem } 794253049Srmacklem 795253049Srmacklem /* 796253049Srmacklem * The mechanism must be set to KerberosV for acquisition 797253049Srmacklem * of credentials to work reliably. 798253049Srmacklem */ 799253049Srmacklem maj_stat = gss_create_empty_oid_set(&min_stat, &mechlist); 800253049Srmacklem if (maj_stat != GSS_S_COMPLETE) { 801253049Srmacklem options_ret->major_status = maj_stat; 802253049Srmacklem options_ret->minor_status = min_stat; 803253049Srmacklem goto out; 804253049Srmacklem } 805253049Srmacklem maj_stat = gss_add_oid_set_member(&min_stat, gd->gd_mech, 806253049Srmacklem &mechlist); 807253049Srmacklem if (maj_stat != GSS_S_COMPLETE) { 808253049Srmacklem options_ret->major_status = maj_stat; 809253049Srmacklem options_ret->minor_status = min_stat; 810253049Srmacklem gss_release_oid_set(&min_stat, &mechlist); 811253049Srmacklem goto out; 812253049Srmacklem } 813253049Srmacklem 814253049Srmacklem principal_desc.value = (void *)gd->gd_clntprincipal; 815253049Srmacklem principal_desc.length = strlen(gd->gd_clntprincipal); 816253049Srmacklem maj_stat = gss_import_name(&min_stat, &principal_desc, 817253049Srmacklem GSS_C_NT_HOSTBASED_SERVICE, &name); 818253049Srmacklem if (maj_stat != GSS_S_COMPLETE) { 819253049Srmacklem options_ret->major_status = maj_stat; 820253049Srmacklem options_ret->minor_status = min_stat; 821253049Srmacklem gss_release_oid_set(&min_stat, &mechlist); 822253049Srmacklem goto out; 823253049Srmacklem } 824253049Srmacklem /* Acquire the credentials. */ 825253049Srmacklem maj_stat = gss_acquire_cred(&min_stat, name, 0, 826253049Srmacklem mechlist, GSS_C_INITIATE, 827253049Srmacklem &gd->gd_options.my_cred, NULL, NULL); 828253049Srmacklem gss_release_name(&min_stat, &name); 829253049Srmacklem gss_release_oid_set(&min_stat, &mechlist); 830253049Srmacklem if (maj_stat != GSS_S_COMPLETE) { 831253049Srmacklem options_ret->major_status = maj_stat; 832253049Srmacklem options_ret->minor_status = min_stat; 833253049Srmacklem goto out; 834253049Srmacklem } 835253049Srmacklem } 836253049Srmacklem 837184588Sdfr principal_desc.value = (void *)gd->gd_principal; 838184588Sdfr principal_desc.length = strlen(gd->gd_principal); 839184588Sdfr maj_stat = gss_import_name(&min_stat, &principal_desc, 840184588Sdfr GSS_C_NT_HOSTBASED_SERVICE, &name); 841184588Sdfr if (maj_stat != GSS_S_COMPLETE) { 842184588Sdfr options_ret->major_status = maj_stat; 843184588Sdfr options_ret->minor_status = min_stat; 844184588Sdfr goto out; 845184588Sdfr } 846184588Sdfr 847184588Sdfr /* GSS context establishment loop. */ 848184588Sdfr memset(&recv_token, 0, sizeof(recv_token)); 849184588Sdfr memset(&gr, 0, sizeof(gr)); 850184588Sdfr memset(options_ret, 0, sizeof(*options_ret)); 851184588Sdfr options_ret->major_status = GSS_S_FAILURE; 852184588Sdfr recv_tokenp = GSS_C_NO_BUFFER; 853184588Sdfr 854184588Sdfr for (;;) { 855184588Sdfr crsave = td->td_ucred; 856184588Sdfr td->td_ucred = gd->gd_ucred; 857184588Sdfr maj_stat = gss_init_sec_context(&min_stat, 858184588Sdfr gd->gd_options.my_cred, 859184588Sdfr &gd->gd_ctx, 860184588Sdfr name, 861184588Sdfr gd->gd_mech, 862184588Sdfr gd->gd_options.req_flags, 863184588Sdfr gd->gd_options.time_req, 864184588Sdfr gd->gd_options.input_channel_bindings, 865184588Sdfr recv_tokenp, 866184588Sdfr &gd->gd_mech, /* used mech */ 867184588Sdfr &send_token, 868184588Sdfr &options_ret->ret_flags, 869184588Sdfr &options_ret->time_req); 870184588Sdfr td->td_ucred = crsave; 871184588Sdfr 872184588Sdfr /* 873184588Sdfr * Free the token which we got from the server (if 874184588Sdfr * any). Remember that this was allocated by XDR, not 875184588Sdfr * GSS-API. 876184588Sdfr */ 877184588Sdfr if (recv_tokenp != GSS_C_NO_BUFFER) { 878184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 879184588Sdfr (char *) &recv_token); 880184588Sdfr recv_tokenp = GSS_C_NO_BUFFER; 881184588Sdfr } 882184588Sdfr if (gd->gd_mech && rpc_gss_oid_to_mech(gd->gd_mech, &mech)) { 883184588Sdfr strlcpy(options_ret->actual_mechanism, 884184588Sdfr mech, 885184588Sdfr sizeof(options_ret->actual_mechanism)); 886184588Sdfr } 887184588Sdfr if (maj_stat != GSS_S_COMPLETE && 888184588Sdfr maj_stat != GSS_S_CONTINUE_NEEDED) { 889184588Sdfr rpc_gss_log_status("gss_init_sec_context", gd->gd_mech, 890184588Sdfr maj_stat, min_stat); 891184588Sdfr options_ret->major_status = maj_stat; 892184588Sdfr options_ret->minor_status = min_stat; 893184588Sdfr break; 894184588Sdfr } 895184588Sdfr if (send_token.length != 0) { 896184588Sdfr memset(&gr, 0, sizeof(gr)); 897184588Sdfr 898184588Sdfr bzero(&ext, sizeof(ext)); 899184588Sdfr ext.rc_auth = auth; 900184588Sdfr call_stat = CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC, 901184588Sdfr (xdrproc_t)xdr_gss_buffer_desc, 902184588Sdfr &send_token, 903184588Sdfr (xdrproc_t)xdr_rpc_gss_init_res, 904184588Sdfr (caddr_t)&gr, AUTH_TIMEOUT); 905184588Sdfr 906184588Sdfr gss_release_buffer(&min_stat, &send_token); 907184588Sdfr 908184588Sdfr if (call_stat != RPC_SUCCESS) 909184588Sdfr break; 910184588Sdfr 911184588Sdfr if (gr.gr_major != GSS_S_COMPLETE && 912184588Sdfr gr.gr_major != GSS_S_CONTINUE_NEEDED) { 913184588Sdfr rpc_gss_log_status("server reply", gd->gd_mech, 914184588Sdfr gr.gr_major, gr.gr_minor); 915184588Sdfr options_ret->major_status = gr.gr_major; 916184588Sdfr options_ret->minor_status = gr.gr_minor; 917184588Sdfr break; 918184588Sdfr } 919184588Sdfr 920184588Sdfr /* 921184588Sdfr * Save the server's gr_handle value, freeing 922184588Sdfr * what we have already (remember that this 923184588Sdfr * was allocated by XDR, not GSS-API). 924184588Sdfr */ 925184588Sdfr if (gr.gr_handle.length != 0) { 926184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 927184588Sdfr (char *) &gd->gd_cred.gc_handle); 928184588Sdfr gd->gd_cred.gc_handle = gr.gr_handle; 929184588Sdfr } 930184588Sdfr 931184588Sdfr /* 932184588Sdfr * Save the server's token as well. 933184588Sdfr */ 934184588Sdfr if (gr.gr_token.length != 0) { 935184588Sdfr recv_token = gr.gr_token; 936184588Sdfr recv_tokenp = &recv_token; 937184588Sdfr } 938184588Sdfr 939184588Sdfr /* 940184588Sdfr * Since we have copied out all the bits of gr 941184588Sdfr * which XDR allocated for us, we don't need 942184588Sdfr * to free it. 943184588Sdfr */ 944184588Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT; 945184588Sdfr } 946184588Sdfr 947184588Sdfr if (maj_stat == GSS_S_COMPLETE) { 948184588Sdfr gss_buffer_desc bufin; 949184588Sdfr u_int seq, qop_state = 0; 950184588Sdfr 951184588Sdfr /* 952184588Sdfr * gss header verifier, 953184588Sdfr * usually checked in gss_validate 954184588Sdfr */ 955184588Sdfr seq = htonl(gr.gr_win); 956184588Sdfr bufin.value = (unsigned char *)&seq; 957184588Sdfr bufin.length = sizeof(seq); 958184588Sdfr 959184588Sdfr maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, 960184588Sdfr &bufin, &gd->gd_verf, &qop_state); 961184588Sdfr 962184588Sdfr if (maj_stat != GSS_S_COMPLETE || 963184588Sdfr qop_state != gd->gd_qop) { 964184588Sdfr rpc_gss_log_status("gss_verify_mic", gd->gd_mech, 965184588Sdfr maj_stat, min_stat); 966184588Sdfr if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 967184588Sdfr rpc_gss_destroy_context(auth, TRUE); 968184588Sdfr } 969184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, 970184588Sdfr EPERM); 971184588Sdfr options_ret->major_status = maj_stat; 972184588Sdfr options_ret->minor_status = min_stat; 973184588Sdfr break; 974184588Sdfr } 975184588Sdfr 976184588Sdfr options_ret->major_status = GSS_S_COMPLETE; 977184588Sdfr options_ret->minor_status = 0; 978184588Sdfr options_ret->rpcsec_version = gd->gd_cred.gc_version; 979184588Sdfr options_ret->gss_context = gd->gd_ctx; 980184588Sdfr 981184588Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_DATA; 982184588Sdfr gd->gd_seq = 1; 983184588Sdfr gd->gd_win = gr.gr_win; 984184588Sdfr break; 985184588Sdfr } 986184588Sdfr } 987184588Sdfr 988184588Sdfr gss_release_name(&min_stat, &name); 989184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 990184588Sdfr (char *) &gd->gd_verf); 991184588Sdfr 992184588Sdfrout: 993184588Sdfr /* End context negotiation loop. */ 994184588Sdfr if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) { 995184588Sdfr rpc_createerr.cf_stat = RPC_AUTHERROR; 996184588Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); 997184588Sdfr if (gd->gd_ctx) { 998184588Sdfr gss_delete_sec_context(&min_stat, &gd->gd_ctx, 999184588Sdfr GSS_C_NO_BUFFER); 1000184588Sdfr } 1001184588Sdfr mtx_lock(&gd->gd_lock); 1002184588Sdfr gd->gd_state = RPCSEC_GSS_START; 1003184588Sdfr wakeup(gd); 1004184588Sdfr mtx_unlock(&gd->gd_lock); 1005184588Sdfr return (FALSE); 1006184588Sdfr } 1007184588Sdfr 1008184588Sdfr mtx_lock(&gd->gd_lock); 1009184588Sdfr gd->gd_state = RPCSEC_GSS_ESTABLISHED; 1010184588Sdfr wakeup(gd); 1011184588Sdfr mtx_unlock(&gd->gd_lock); 1012184588Sdfr 1013184588Sdfr return (TRUE); 1014184588Sdfr} 1015184588Sdfr 1016184588Sdfrstatic bool_t 1017184588Sdfrrpc_gss_refresh(AUTH *auth, void *msg) 1018184588Sdfr{ 1019184588Sdfr struct rpc_msg *reply = (struct rpc_msg *) msg; 1020184588Sdfr rpc_gss_options_ret_t options; 1021195246Srmacklem struct rpc_gss_data *gd; 1022184588Sdfr 1023195246Srmacklem gd = AUTH_PRIVATE(auth); 1024195246Srmacklem 1025184588Sdfr /* 1026195246Srmacklem * If the context is in DESTROYING state, then just return, since 1027195246Srmacklem * there is no point in refreshing the credentials. 1028195246Srmacklem */ 1029195246Srmacklem mtx_lock(&gd->gd_lock); 1030195246Srmacklem if (gd->gd_state == RPCSEC_GSS_DESTROYING) { 1031195246Srmacklem mtx_unlock(&gd->gd_lock); 1032195246Srmacklem return (FALSE); 1033195246Srmacklem } 1034195246Srmacklem mtx_unlock(&gd->gd_lock); 1035195246Srmacklem 1036195246Srmacklem /* 1037184588Sdfr * If the error was RPCSEC_GSS_CREDPROBLEM of 1038184588Sdfr * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All 1039184588Sdfr * other errors are fatal. 1040184588Sdfr */ 1041184588Sdfr if (reply->rm_reply.rp_stat == MSG_DENIED 1042184588Sdfr && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR 1043184588Sdfr && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM 1044184588Sdfr || reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) { 1045184588Sdfr rpc_gss_destroy_context(auth, FALSE); 1046184588Sdfr memset(&options, 0, sizeof(options)); 1047184588Sdfr return (rpc_gss_init(auth, &options)); 1048184588Sdfr } 1049184588Sdfr 1050184588Sdfr return (FALSE); 1051184588Sdfr} 1052184588Sdfr 1053184588Sdfrstatic void 1054184588Sdfrrpc_gss_destroy_context(AUTH *auth, bool_t send_destroy) 1055184588Sdfr{ 1056184588Sdfr struct rpc_gss_data *gd; 1057184588Sdfr struct rpc_pending_request *pr; 1058184588Sdfr OM_uint32 min_stat; 1059184588Sdfr struct rpc_callextra ext; 1060184588Sdfr 1061184588Sdfr rpc_gss_log_debug("in rpc_gss_destroy_context()"); 1062184588Sdfr 1063184588Sdfr gd = AUTH_PRIVATE(auth); 1064184588Sdfr 1065184588Sdfr mtx_lock(&gd->gd_lock); 1066184588Sdfr /* 1067184588Sdfr * If the context isn't in ESTABISHED state, someone else is 1068184588Sdfr * destroying/refreshing - we wait till they are done. 1069184588Sdfr */ 1070184588Sdfr if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) { 1071184588Sdfr while (gd->gd_state != RPCSEC_GSS_START 1072184588Sdfr && gd->gd_state != RPCSEC_GSS_ESTABLISHED) 1073184588Sdfr msleep(gd, &gd->gd_lock, 0, "gssstate", 0); 1074184588Sdfr mtx_unlock(&gd->gd_lock); 1075184588Sdfr return; 1076184588Sdfr } 1077184588Sdfr gd->gd_state = RPCSEC_GSS_DESTROYING; 1078184588Sdfr mtx_unlock(&gd->gd_lock); 1079184588Sdfr 1080184588Sdfr if (send_destroy) { 1081184588Sdfr gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY; 1082184588Sdfr bzero(&ext, sizeof(ext)); 1083184588Sdfr ext.rc_auth = auth; 1084184588Sdfr CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC, 1085184588Sdfr (xdrproc_t)xdr_void, NULL, 1086184588Sdfr (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT); 1087184588Sdfr } 1088184588Sdfr 1089184588Sdfr while ((pr = LIST_FIRST(&gd->gd_reqs)) != NULL) { 1090184588Sdfr LIST_REMOVE(pr, pr_link); 1091184588Sdfr mem_free(pr, sizeof(*pr)); 1092184588Sdfr } 1093184588Sdfr 1094184588Sdfr /* 1095184588Sdfr * Free the context token. Remember that this was 1096184588Sdfr * allocated by XDR, not GSS-API. 1097184588Sdfr */ 1098184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 1099184588Sdfr (char *) &gd->gd_cred.gc_handle); 1100184588Sdfr gd->gd_cred.gc_handle.length = 0; 1101184588Sdfr 1102184588Sdfr if (gd->gd_ctx != GSS_C_NO_CONTEXT) 1103184588Sdfr gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL); 1104184588Sdfr 1105184588Sdfr mtx_lock(&gd->gd_lock); 1106184588Sdfr gd->gd_state = RPCSEC_GSS_START; 1107184588Sdfr wakeup(gd); 1108184588Sdfr mtx_unlock(&gd->gd_lock); 1109184588Sdfr} 1110184588Sdfr 1111184588Sdfrstatic void 1112184588Sdfrrpc_gss_destroy(AUTH *auth) 1113184588Sdfr{ 1114184588Sdfr struct rpc_gss_data *gd; 1115184588Sdfr 1116184588Sdfr rpc_gss_log_debug("in rpc_gss_destroy()"); 1117184588Sdfr 1118184588Sdfr gd = AUTH_PRIVATE(auth); 1119184588Sdfr 1120184588Sdfr if (!refcount_release(&gd->gd_refs)) 1121184588Sdfr return; 1122184588Sdfr 1123184588Sdfr rpc_gss_destroy_context(auth, TRUE); 1124184588Sdfr 1125184588Sdfr CLNT_RELEASE(gd->gd_clnt); 1126184588Sdfr crfree(gd->gd_ucred); 1127184588Sdfr free(gd->gd_principal, M_RPC); 1128253049Srmacklem if (gd->gd_clntprincipal != NULL) 1129253049Srmacklem free(gd->gd_clntprincipal, M_RPC); 1130184588Sdfr if (gd->gd_verf.value) 1131184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 1132184588Sdfr (char *) &gd->gd_verf); 1133184588Sdfr mtx_destroy(&gd->gd_lock); 1134184588Sdfr 1135184588Sdfr mem_free(gd, sizeof(*gd)); 1136184588Sdfr mem_free(auth, sizeof(*auth)); 1137184588Sdfr} 1138184588Sdfr 1139184588Sdfrint 1140184588Sdfrrpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len) 1141184588Sdfr{ 1142184588Sdfr struct rpc_gss_data *gd; 1143184588Sdfr int want_conf; 1144184588Sdfr OM_uint32 max; 1145184588Sdfr OM_uint32 maj_stat, min_stat; 1146184588Sdfr int result; 1147184588Sdfr 1148184588Sdfr gd = AUTH_PRIVATE(auth); 1149184588Sdfr 1150184588Sdfr switch (gd->gd_cred.gc_svc) { 1151184588Sdfr case rpc_gss_svc_none: 1152184588Sdfr return (max_tp_unit_len); 1153184588Sdfr break; 1154184588Sdfr 1155184588Sdfr case rpc_gss_svc_default: 1156184588Sdfr case rpc_gss_svc_integrity: 1157184588Sdfr want_conf = FALSE; 1158184588Sdfr break; 1159184588Sdfr 1160184588Sdfr case rpc_gss_svc_privacy: 1161184588Sdfr want_conf = TRUE; 1162184588Sdfr break; 1163184588Sdfr 1164184588Sdfr default: 1165184588Sdfr return (0); 1166184588Sdfr } 1167184588Sdfr 1168184588Sdfr maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf, 1169184588Sdfr gd->gd_qop, max_tp_unit_len, &max); 1170184588Sdfr 1171184588Sdfr if (maj_stat == GSS_S_COMPLETE) { 1172184588Sdfr result = (int) max; 1173184588Sdfr if (result < 0) 1174184588Sdfr result = 0; 1175184588Sdfr return (result); 1176184588Sdfr } else { 1177184588Sdfr rpc_gss_log_status("gss_wrap_size_limit", gd->gd_mech, 1178184588Sdfr maj_stat, min_stat); 1179184588Sdfr return (0); 1180184588Sdfr } 1181184588Sdfr} 1182