1184588Sdfr/*- 2197583Sjamie * Copyright (c) 2008 Doug Rabson 3197583Sjamie * 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 * 14197583Sjamie * 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 17197583Sjamie * 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 */ 26197583Sjamie/* 27197583Sjamie svc_rpcsec_gss.c 28197583Sjamie 29197583Sjamie Copyright (c) 2000 The Regents of the University of Michigan. 30197583Sjamie All rights reserved. 31184588Sdfr 32197583Sjamie Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 33197583Sjamie All rights reserved, all wrongs reversed. 34197583Sjamie 35197583Sjamie Redistribution and use in source and binary forms, with or without 36197583Sjamie modification, are permitted provided that the following conditions 37197583Sjamie are met: 38197583Sjamie 39197583Sjamie 1. Redistributions of source code must retain the above copyright 40197583Sjamie notice, this list of conditions and the following disclaimer. 41197583Sjamie 2. Redistributions in binary form must reproduce the above copyright 42197583Sjamie notice, this list of conditions and the following disclaimer in the 43197583Sjamie documentation and/or other materials provided with the distribution. 44197583Sjamie 3. Neither the name of the University nor the names of its 45197583Sjamie contributors may be used to endorse or promote products derived 46197583Sjamie from this software without specific prior written permission. 47197583Sjamie 48197583Sjamie THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 49197583Sjamie WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 50197583Sjamie MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 51197583Sjamie DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52197583Sjamie FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 53197583Sjamie CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 54197583Sjamie SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 55197583Sjamie BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 56197583Sjamie LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 57197583Sjamie NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 58197583Sjamie SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59197583Sjamie 60197583Sjamie $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $ 61197583Sjamie */ 62197583Sjamie 63184588Sdfr#include <sys/cdefs.h> 64184588Sdfr__FBSDID("$FreeBSD$"); 65184588Sdfr 66184588Sdfr#include <sys/param.h> 67197583Sjamie#include <sys/systm.h> 68194239Srmacklem#include <sys/jail.h> 69184588Sdfr#include <sys/kernel.h> 70197583Sjamie#include <sys/kobj.h> 71184588Sdfr#include <sys/lock.h> 72184588Sdfr#include <sys/malloc.h> 73184588Sdfr#include <sys/mbuf.h> 74184588Sdfr#include <sys/mutex.h> 75197583Sjamie#include <sys/proc.h> 76197583Sjamie#include <sys/sx.h> 77197583Sjamie#include <sys/ucred.h> 78184588Sdfr 79197583Sjamie#include <rpc/rpc.h> 80197583Sjamie#include <rpc/rpcsec_gss.h> 81184588Sdfr 82197583Sjamie#include "rpcsec_gss_int.h" 83184588Sdfr 84197583Sjamiestatic bool_t svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **); 85197583Sjamiestatic bool_t svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **); 86197583Sjamiestatic void svc_rpc_gss_release(SVCAUTH *); 87197583Sjamiestatic enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *); 88197583Sjamiestatic int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *); 89184588Sdfr 90197583Sjamiestatic struct svc_auth_ops svc_auth_gss_ops = { 91197583Sjamie svc_rpc_gss_wrap, 92197583Sjamie svc_rpc_gss_unwrap, 93197583Sjamie svc_rpc_gss_release, 94184588Sdfr}; 95184588Sdfr 96197583Sjamiestruct sx svc_rpc_gss_lock; 97197583Sjamie 98197583Sjamiestruct svc_rpc_gss_callback { 99197583Sjamie SLIST_ENTRY(svc_rpc_gss_callback) cb_link; 100197583Sjamie rpc_gss_callback_t cb_callback; 101197583Sjamie}; 102197583Sjamiestatic SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback) 103201145Santoine svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks); 104197583Sjamie 105197583Sjamiestruct svc_rpc_gss_svc_name { 106197583Sjamie SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link; 107197583Sjamie char *sn_principal; 108197583Sjamie gss_OID sn_mech; 109197583Sjamie u_int sn_req_time; 110197583Sjamie gss_cred_id_t sn_cred; 111197583Sjamie u_int sn_program; 112197583Sjamie u_int sn_version; 113197583Sjamie}; 114197583Sjamiestatic SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name) 115201145Santoine svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names); 116197583Sjamie 117197583Sjamieenum svc_rpc_gss_client_state { 118197583Sjamie CLIENT_NEW, /* still authenticating */ 119197583Sjamie CLIENT_ESTABLISHED, /* context established */ 120197583Sjamie CLIENT_STALE /* garbage to collect */ 121197583Sjamie}; 122197583Sjamie 123197583Sjamie#define SVC_RPC_GSS_SEQWINDOW 128 124201853Sbrooks#ifndef RPCAUTH_UNIXGIDS 125201853Sbrooks#define RPCAUTH_UNIXGIDS 16 126201853Sbrooks#endif 127197583Sjamie 128197583Sjamiestruct svc_rpc_gss_clientid { 129197583Sjamie unsigned long ci_hostid; 130197583Sjamie uint32_t ci_boottime; 131197583Sjamie uint32_t ci_id; 132197583Sjamie}; 133197583Sjamie 134197583Sjamiestruct svc_rpc_gss_client { 135197583Sjamie TAILQ_ENTRY(svc_rpc_gss_client) cl_link; 136197583Sjamie TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink; 137197583Sjamie volatile u_int cl_refs; 138197583Sjamie struct sx cl_lock; 139197583Sjamie struct svc_rpc_gss_clientid cl_id; 140197583Sjamie time_t cl_expiration; /* when to gc */ 141197583Sjamie enum svc_rpc_gss_client_state cl_state; /* client state */ 142197583Sjamie bool_t cl_locked; /* fixed service+qop */ 143197583Sjamie gss_ctx_id_t cl_ctx; /* context id */ 144197583Sjamie gss_cred_id_t cl_creds; /* delegated creds */ 145197583Sjamie gss_name_t cl_cname; /* client name */ 146197583Sjamie struct svc_rpc_gss_svc_name *cl_sname; /* server name used */ 147197583Sjamie rpc_gss_rawcred_t cl_rawcred; /* raw credentials */ 148197583Sjamie rpc_gss_ucred_t cl_ucred; /* unix-style credentials */ 149197583Sjamie struct ucred *cl_cred; /* kernel-style credentials */ 150197583Sjamie int cl_rpcflavor; /* RPC pseudo sec flavor */ 151197583Sjamie bool_t cl_done_callback; /* TRUE after call */ 152197583Sjamie void *cl_cookie; /* user cookie from callback */ 153201853Sbrooks gid_t cl_gid_storage[RPCAUTH_UNIXGIDS]; 154197583Sjamie gss_OID cl_mech; /* mechanism */ 155197583Sjamie gss_qop_t cl_qop; /* quality of protection */ 156197583Sjamie uint32_t cl_seqlast; /* sequence window origin */ 157197583Sjamie uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */ 158197583Sjamie}; 159197583SjamieTAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client); 160197583Sjamie 161197581Sjamie/* 162197583Sjamie * This structure holds enough information to unwrap arguments or wrap 163197583Sjamie * results for a given request. We use the rq_clntcred area for this 164197583Sjamie * (which is a per-request buffer). 165197581Sjamie */ 166197583Sjamiestruct svc_rpc_gss_cookedcred { 167197583Sjamie struct svc_rpc_gss_client *cc_client; 168197583Sjamie rpc_gss_service_t cc_service; 169197583Sjamie uint32_t cc_seq; 170184588Sdfr}; 171184588Sdfr 172197583Sjamie#define CLIENT_HASH_SIZE 256 173197583Sjamie#define CLIENT_MAX 128 174197583Sjamiestruct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE]; 175197583Sjamiestruct svc_rpc_gss_client_list svc_rpc_gss_clients; 176197583Sjamiestatic size_t svc_rpc_gss_client_count; 177197583Sjamiestatic uint32_t svc_rpc_gss_next_clientid = 1; 178197583Sjamie 179197583Sjamiestatic void 180197583Sjamiesvc_rpc_gss_init(void *arg) 181184588Sdfr{ 182197583Sjamie int i; 183184588Sdfr 184197583Sjamie for (i = 0; i < CLIENT_HASH_SIZE; i++) 185197583Sjamie TAILQ_INIT(&svc_rpc_gss_client_hash[i]); 186197583Sjamie TAILQ_INIT(&svc_rpc_gss_clients); 187197583Sjamie svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred); 188197583Sjamie sx_init(&svc_rpc_gss_lock, "gsslock"); 189197583Sjamie} 190197583SjamieSYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL); 191197583Sjamie 192197583Sjamiebool_t 193197583Sjamierpc_gss_set_callback(rpc_gss_callback_t *cb) 194197583Sjamie{ 195197583Sjamie struct svc_rpc_gss_callback *scb; 196197583Sjamie 197197583Sjamie scb = mem_alloc(sizeof(struct svc_rpc_gss_callback)); 198197583Sjamie if (!scb) { 199197583Sjamie _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 200197583Sjamie return (FALSE); 201184588Sdfr } 202197583Sjamie scb->cb_callback = *cb; 203197583Sjamie sx_xlock(&svc_rpc_gss_lock); 204197583Sjamie SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link); 205197583Sjamie sx_xunlock(&svc_rpc_gss_lock); 206184588Sdfr 207197583Sjamie return (TRUE); 208197583Sjamie} 209197583Sjamie 210197583Sjamievoid 211197583Sjamierpc_gss_clear_callback(rpc_gss_callback_t *cb) 212197583Sjamie{ 213197583Sjamie struct svc_rpc_gss_callback *scb; 214197583Sjamie 215197583Sjamie sx_xlock(&svc_rpc_gss_lock); 216197583Sjamie SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 217197583Sjamie if (scb->cb_callback.program == cb->program 218197583Sjamie && scb->cb_callback.version == cb->version 219197583Sjamie && scb->cb_callback.callback == cb->callback) { 220197583Sjamie SLIST_REMOVE(&svc_rpc_gss_callbacks, scb, 221197583Sjamie svc_rpc_gss_callback, cb_link); 222197583Sjamie sx_xunlock(&svc_rpc_gss_lock); 223197583Sjamie mem_free(scb, sizeof(*scb)); 224197583Sjamie return; 225184588Sdfr } 226184588Sdfr } 227197583Sjamie sx_xunlock(&svc_rpc_gss_lock); 228197583Sjamie} 229184588Sdfr 230197583Sjamiestatic bool_t 231197583Sjamierpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname) 232197583Sjamie{ 233197583Sjamie OM_uint32 maj_stat, min_stat; 234197583Sjamie gss_buffer_desc namebuf; 235197583Sjamie gss_name_t name; 236197583Sjamie gss_OID_set_desc oid_set; 237197583Sjamie 238197583Sjamie oid_set.count = 1; 239197583Sjamie oid_set.elements = sname->sn_mech; 240197583Sjamie 241197583Sjamie namebuf.value = (void *) sname->sn_principal; 242197583Sjamie namebuf.length = strlen(sname->sn_principal); 243197583Sjamie 244197583Sjamie maj_stat = gss_import_name(&min_stat, &namebuf, 245197583Sjamie GSS_C_NT_HOSTBASED_SERVICE, &name); 246197583Sjamie if (maj_stat != GSS_S_COMPLETE) 247197583Sjamie return (FALSE); 248197583Sjamie 249197583Sjamie if (sname->sn_cred != GSS_C_NO_CREDENTIAL) 250197583Sjamie gss_release_cred(&min_stat, &sname->sn_cred); 251197583Sjamie 252197583Sjamie maj_stat = gss_acquire_cred(&min_stat, name, 253197583Sjamie sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred, 254197583Sjamie NULL, NULL); 255197583Sjamie if (maj_stat != GSS_S_COMPLETE) { 256197583Sjamie gss_release_name(&min_stat, &name); 257197583Sjamie return (FALSE); 258184588Sdfr } 259197583Sjamie gss_release_name(&min_stat, &name); 260184588Sdfr 261197583Sjamie return (TRUE); 262197583Sjamie} 263197583Sjamie 264197583Sjamiebool_t 265197583Sjamierpc_gss_set_svc_name(const char *principal, const char *mechanism, 266197583Sjamie u_int req_time, u_int program, u_int version) 267197583Sjamie{ 268197583Sjamie struct svc_rpc_gss_svc_name *sname; 269197583Sjamie gss_OID mech_oid; 270197583Sjamie 271197583Sjamie if (!rpc_gss_mech_to_oid(mechanism, &mech_oid)) 272197583Sjamie return (FALSE); 273197583Sjamie 274197583Sjamie sname = mem_alloc(sizeof(*sname)); 275197583Sjamie if (!sname) 276197583Sjamie return (FALSE); 277197583Sjamie sname->sn_principal = strdup(principal, M_RPC); 278197583Sjamie sname->sn_mech = mech_oid; 279197583Sjamie sname->sn_req_time = req_time; 280197583Sjamie sname->sn_cred = GSS_C_NO_CREDENTIAL; 281197583Sjamie sname->sn_program = program; 282197583Sjamie sname->sn_version = version; 283197583Sjamie 284197583Sjamie if (!rpc_gss_acquire_svc_cred(sname)) { 285197583Sjamie free(sname->sn_principal, M_RPC); 286197583Sjamie mem_free(sname, sizeof(*sname)); 287197583Sjamie return (FALSE); 288184588Sdfr } 289197583Sjamie 290197583Sjamie sx_xlock(&svc_rpc_gss_lock); 291197583Sjamie SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link); 292197583Sjamie sx_xunlock(&svc_rpc_gss_lock); 293197583Sjamie 294197583Sjamie return (TRUE); 295197583Sjamie} 296197583Sjamie 297197583Sjamievoid 298197583Sjamierpc_gss_clear_svc_name(u_int program, u_int version) 299197583Sjamie{ 300197583Sjamie OM_uint32 min_stat; 301197583Sjamie struct svc_rpc_gss_svc_name *sname; 302197583Sjamie 303197583Sjamie sx_xlock(&svc_rpc_gss_lock); 304197583Sjamie SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 305197583Sjamie if (sname->sn_program == program 306197583Sjamie && sname->sn_version == version) { 307197583Sjamie SLIST_REMOVE(&svc_rpc_gss_svc_names, sname, 308197583Sjamie svc_rpc_gss_svc_name, sn_link); 309197583Sjamie sx_xunlock(&svc_rpc_gss_lock); 310197583Sjamie gss_release_cred(&min_stat, &sname->sn_cred); 311197583Sjamie free(sname->sn_principal, M_RPC); 312197583Sjamie mem_free(sname, sizeof(*sname)); 313197583Sjamie return; 314184588Sdfr } 315184588Sdfr } 316197583Sjamie sx_xunlock(&svc_rpc_gss_lock); 317197583Sjamie} 318197583Sjamie 319197583Sjamiebool_t 320197583Sjamierpc_gss_get_principal_name(rpc_gss_principal_t *principal, 321197583Sjamie const char *mech, const char *name, const char *node, const char *domain) 322197583Sjamie{ 323197583Sjamie OM_uint32 maj_stat, min_stat; 324197583Sjamie gss_OID mech_oid; 325197583Sjamie size_t namelen; 326197583Sjamie gss_buffer_desc buf; 327197583Sjamie gss_name_t gss_name, gss_mech_name; 328197583Sjamie rpc_gss_principal_t result; 329197583Sjamie 330197583Sjamie if (!rpc_gss_mech_to_oid(mech, &mech_oid)) 331197583Sjamie return (FALSE); 332197583Sjamie 333197583Sjamie /* 334197583Sjamie * Construct a gss_buffer containing the full name formatted 335197583Sjamie * as "name/node@domain" where node and domain are optional. 336197583Sjamie */ 337197583Sjamie namelen = strlen(name); 338197583Sjamie if (node) { 339197583Sjamie namelen += strlen(node) + 1; 340184588Sdfr } 341197583Sjamie if (domain) { 342197583Sjamie namelen += strlen(domain) + 1; 343197583Sjamie } 344197583Sjamie 345197583Sjamie buf.value = mem_alloc(namelen); 346197583Sjamie buf.length = namelen; 347197583Sjamie strcpy((char *) buf.value, name); 348197583Sjamie if (node) { 349197583Sjamie strcat((char *) buf.value, "/"); 350197583Sjamie strcat((char *) buf.value, node); 351197583Sjamie } 352197583Sjamie if (domain) { 353197583Sjamie strcat((char *) buf.value, "@"); 354197583Sjamie strcat((char *) buf.value, domain); 355197583Sjamie } 356197583Sjamie 357197583Sjamie /* 358197583Sjamie * Convert that to a gss_name_t and then convert that to a 359197583Sjamie * mechanism name in the selected mechanism. 360197583Sjamie */ 361197583Sjamie maj_stat = gss_import_name(&min_stat, &buf, 362197583Sjamie GSS_C_NT_USER_NAME, &gss_name); 363197583Sjamie mem_free(buf.value, buf.length); 364197583Sjamie if (maj_stat != GSS_S_COMPLETE) { 365197583Sjamie rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat); 366197583Sjamie return (FALSE); 367197583Sjamie } 368197583Sjamie maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid, 369197583Sjamie &gss_mech_name); 370197583Sjamie if (maj_stat != GSS_S_COMPLETE) { 371197583Sjamie rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat, 372197583Sjamie min_stat); 373197583Sjamie gss_release_name(&min_stat, &gss_name); 374197583Sjamie return (FALSE); 375197583Sjamie } 376197583Sjamie gss_release_name(&min_stat, &gss_name); 377197583Sjamie 378197583Sjamie /* 379197583Sjamie * Export the mechanism name and use that to construct the 380197583Sjamie * rpc_gss_principal_t result. 381197583Sjamie */ 382197583Sjamie maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf); 383197583Sjamie if (maj_stat != GSS_S_COMPLETE) { 384197583Sjamie rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat); 385197583Sjamie gss_release_name(&min_stat, &gss_mech_name); 386197583Sjamie return (FALSE); 387197583Sjamie } 388197583Sjamie gss_release_name(&min_stat, &gss_mech_name); 389197583Sjamie 390197583Sjamie result = mem_alloc(sizeof(int) + buf.length); 391197583Sjamie if (!result) { 392197583Sjamie gss_release_buffer(&min_stat, &buf); 393197583Sjamie return (FALSE); 394197583Sjamie } 395197583Sjamie result->len = buf.length; 396197583Sjamie memcpy(result->name, buf.value, buf.length); 397197583Sjamie gss_release_buffer(&min_stat, &buf); 398197583Sjamie 399197583Sjamie *principal = result; 400197583Sjamie return (TRUE); 401184588Sdfr} 402184588Sdfr 403197583Sjamiebool_t 404197583Sjamierpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred, 405197583Sjamie rpc_gss_ucred_t **ucred, void **cookie) 406197583Sjamie{ 407197583Sjamie struct svc_rpc_gss_cookedcred *cc; 408197583Sjamie struct svc_rpc_gss_client *client; 409197583Sjamie 410197583Sjamie if (req->rq_cred.oa_flavor != RPCSEC_GSS) 411197583Sjamie return (FALSE); 412197583Sjamie 413197583Sjamie cc = req->rq_clntcred; 414197583Sjamie client = cc->cc_client; 415197583Sjamie if (rcred) 416197583Sjamie *rcred = &client->cl_rawcred; 417197583Sjamie if (ucred) 418197583Sjamie *ucred = &client->cl_ucred; 419197583Sjamie if (cookie) 420197583Sjamie *cookie = client->cl_cookie; 421197583Sjamie return (TRUE); 422197583Sjamie} 423197583Sjamie 424197583Sjamie/* 425197583Sjamie * This simpler interface is used by svc_getcred to copy the cred data 426197583Sjamie * into a kernel cred structure. 427197583Sjamie */ 428184588Sdfrstatic int 429197583Sjamierpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp) 430184588Sdfr{ 431197583Sjamie struct ucred *cr; 432197583Sjamie struct svc_rpc_gss_cookedcred *cc; 433197583Sjamie struct svc_rpc_gss_client *client; 434197583Sjamie rpc_gss_ucred_t *uc; 435184588Sdfr 436197583Sjamie if (req->rq_cred.oa_flavor != RPCSEC_GSS) 437197583Sjamie return (FALSE); 438197583Sjamie 439197583Sjamie cc = req->rq_clntcred; 440197583Sjamie client = cc->cc_client; 441197583Sjamie 442197583Sjamie if (flavorp) 443197583Sjamie *flavorp = client->cl_rpcflavor; 444197583Sjamie 445197583Sjamie if (client->cl_cred) { 446197583Sjamie *crp = crhold(client->cl_cred); 447197583Sjamie return (TRUE); 448197583Sjamie } 449197583Sjamie 450197583Sjamie uc = &client->cl_ucred; 451197583Sjamie cr = client->cl_cred = crget(); 452197583Sjamie cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid; 453197583Sjamie cr->cr_rgid = cr->cr_svgid = uc->gid; 454197583Sjamie crsetgroups(cr, uc->gidlen, uc->gidlist); 455197584Sjamie cr->cr_prison = &prison0; 456197584Sjamie prison_hold(cr->cr_prison); 457197583Sjamie *crp = crhold(cr); 458197583Sjamie 459197583Sjamie return (TRUE); 460184588Sdfr} 461184588Sdfr 462197583Sjamieint 463197583Sjamierpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len) 464197583Sjamie{ 465197583Sjamie struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred; 466197583Sjamie struct svc_rpc_gss_client *client = cc->cc_client; 467197583Sjamie int want_conf; 468197583Sjamie OM_uint32 max; 469197583Sjamie OM_uint32 maj_stat, min_stat; 470197583Sjamie int result; 471197583Sjamie 472197583Sjamie switch (client->cl_rawcred.service) { 473197583Sjamie case rpc_gss_svc_none: 474197583Sjamie return (max_tp_unit_len); 475197583Sjamie break; 476197583Sjamie 477197583Sjamie case rpc_gss_svc_default: 478197583Sjamie case rpc_gss_svc_integrity: 479197583Sjamie want_conf = FALSE; 480197583Sjamie break; 481197583Sjamie 482197583Sjamie case rpc_gss_svc_privacy: 483197583Sjamie want_conf = TRUE; 484197583Sjamie break; 485197583Sjamie 486197583Sjamie default: 487197583Sjamie return (0); 488197583Sjamie } 489197583Sjamie 490197583Sjamie maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf, 491197583Sjamie client->cl_qop, max_tp_unit_len, &max); 492197583Sjamie 493197583Sjamie if (maj_stat == GSS_S_COMPLETE) { 494197583Sjamie result = (int) max; 495197583Sjamie if (result < 0) 496197583Sjamie result = 0; 497197583Sjamie return (result); 498197583Sjamie } else { 499197583Sjamie rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech, 500197583Sjamie maj_stat, min_stat); 501197583Sjamie return (0); 502197583Sjamie } 503197583Sjamie} 504197583Sjamie 505197583Sjamiestatic struct svc_rpc_gss_client * 506197583Sjamiesvc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id) 507197583Sjamie{ 508197583Sjamie struct svc_rpc_gss_client *client; 509197583Sjamie struct svc_rpc_gss_client_list *list; 510197583Sjamie unsigned long hostid; 511197583Sjamie 512197583Sjamie rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id); 513197583Sjamie 514197583Sjamie getcredhostid(curthread->td_ucred, &hostid); 515197583Sjamie if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec) 516197583Sjamie return (NULL); 517197583Sjamie 518197583Sjamie list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE]; 519197583Sjamie sx_xlock(&svc_rpc_gss_lock); 520197583Sjamie TAILQ_FOREACH(client, list, cl_link) { 521197583Sjamie if (client->cl_id.ci_id == id->ci_id) { 522197583Sjamie /* 523197583Sjamie * Move this client to the front of the LRU 524197583Sjamie * list. 525197583Sjamie */ 526197583Sjamie TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 527197583Sjamie TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, 528197583Sjamie cl_alllink); 529197583Sjamie refcount_acquire(&client->cl_refs); 530197583Sjamie break; 531197583Sjamie } 532197583Sjamie } 533197583Sjamie sx_xunlock(&svc_rpc_gss_lock); 534197583Sjamie 535197583Sjamie return (client); 536197583Sjamie} 537197583Sjamie 538197583Sjamiestatic struct svc_rpc_gss_client * 539197583Sjamiesvc_rpc_gss_create_client(void) 540197583Sjamie{ 541197583Sjamie struct svc_rpc_gss_client *client; 542197583Sjamie struct svc_rpc_gss_client_list *list; 543197583Sjamie unsigned long hostid; 544197583Sjamie 545197583Sjamie rpc_gss_log_debug("in svc_rpc_gss_create_client()"); 546197583Sjamie 547197583Sjamie client = mem_alloc(sizeof(struct svc_rpc_gss_client)); 548197583Sjamie memset(client, 0, sizeof(struct svc_rpc_gss_client)); 549197583Sjamie refcount_init(&client->cl_refs, 1); 550197583Sjamie sx_init(&client->cl_lock, "GSS-client"); 551197583Sjamie getcredhostid(curthread->td_ucred, &hostid); 552197583Sjamie client->cl_id.ci_hostid = hostid; 553197583Sjamie client->cl_id.ci_boottime = boottime.tv_sec; 554197583Sjamie client->cl_id.ci_id = svc_rpc_gss_next_clientid++; 555197583Sjamie list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; 556197583Sjamie sx_xlock(&svc_rpc_gss_lock); 557197583Sjamie TAILQ_INSERT_HEAD(list, client, cl_link); 558197583Sjamie TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink); 559197583Sjamie svc_rpc_gss_client_count++; 560197583Sjamie sx_xunlock(&svc_rpc_gss_lock); 561197583Sjamie 562197583Sjamie /* 563197583Sjamie * Start the client off with a short expiration time. We will 564197583Sjamie * try to get a saner value from the client creds later. 565197583Sjamie */ 566197583Sjamie client->cl_state = CLIENT_NEW; 567197583Sjamie client->cl_locked = FALSE; 568197583Sjamie client->cl_expiration = time_uptime + 5*60; 569197583Sjamie 570197583Sjamie return (client); 571197583Sjamie} 572197583Sjamie 573197583Sjamiestatic void 574197583Sjamiesvc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client) 575197583Sjamie{ 576197583Sjamie OM_uint32 min_stat; 577197583Sjamie 578197583Sjamie rpc_gss_log_debug("in svc_rpc_gss_destroy_client()"); 579197583Sjamie 580197583Sjamie if (client->cl_ctx) 581197583Sjamie gss_delete_sec_context(&min_stat, 582197583Sjamie &client->cl_ctx, GSS_C_NO_BUFFER); 583197583Sjamie 584197583Sjamie if (client->cl_cname) 585197583Sjamie gss_release_name(&min_stat, &client->cl_cname); 586197583Sjamie 587197583Sjamie if (client->cl_rawcred.client_principal) 588197583Sjamie mem_free(client->cl_rawcred.client_principal, 589197583Sjamie sizeof(*client->cl_rawcred.client_principal) 590197583Sjamie + client->cl_rawcred.client_principal->len); 591197583Sjamie 592197583Sjamie if (client->cl_cred) 593197583Sjamie crfree(client->cl_cred); 594197583Sjamie 595197583Sjamie sx_destroy(&client->cl_lock); 596197583Sjamie mem_free(client, sizeof(*client)); 597197583Sjamie} 598197583Sjamie 599184588Sdfr/* 600197583Sjamie * Drop a reference to a client and free it if that was the last reference. 601184588Sdfr */ 602184588Sdfrstatic void 603197583Sjamiesvc_rpc_gss_release_client(struct svc_rpc_gss_client *client) 604184588Sdfr{ 605184588Sdfr 606197583Sjamie if (!refcount_release(&client->cl_refs)) 607197583Sjamie return; 608197583Sjamie svc_rpc_gss_destroy_client(client); 609197583Sjamie} 610197583Sjamie 611197583Sjamie/* 612226209Srmacklem * Remove a client from our global lists. 613226209Srmacklem * Must be called with svc_rpc_gss_lock held. 614226209Srmacklem */ 615226209Srmacklemstatic void 616226209Srmacklemsvc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client) 617226209Srmacklem{ 618226209Srmacklem struct svc_rpc_gss_client_list *list; 619226209Srmacklem 620226209Srmacklem sx_assert(&svc_rpc_gss_lock, SX_XLOCKED); 621226209Srmacklem list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; 622226209Srmacklem TAILQ_REMOVE(list, client, cl_link); 623226209Srmacklem TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 624226209Srmacklem svc_rpc_gss_client_count--; 625226209Srmacklem} 626226209Srmacklem 627226209Srmacklem/* 628197583Sjamie * Remove a client from our global lists and free it if we can. 629197583Sjamie */ 630197583Sjamiestatic void 631197583Sjamiesvc_rpc_gss_forget_client(struct svc_rpc_gss_client *client) 632197583Sjamie{ 633197583Sjamie struct svc_rpc_gss_client_list *list; 634226209Srmacklem struct svc_rpc_gss_client *tclient; 635197583Sjamie 636197583Sjamie list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; 637197583Sjamie sx_xlock(&svc_rpc_gss_lock); 638226209Srmacklem TAILQ_FOREACH(tclient, list, cl_link) { 639226209Srmacklem /* 640226209Srmacklem * Make sure this client has not already been removed 641226209Srmacklem * from the lists by svc_rpc_gss_forget_client() or 642226209Srmacklem * svc_rpc_gss_forget_client_locked(). 643226209Srmacklem */ 644226209Srmacklem if (client == tclient) { 645226209Srmacklem svc_rpc_gss_forget_client_locked(client); 646226209Srmacklem sx_xunlock(&svc_rpc_gss_lock); 647226209Srmacklem svc_rpc_gss_release_client(client); 648226209Srmacklem return; 649226209Srmacklem } 650226209Srmacklem } 651197583Sjamie sx_xunlock(&svc_rpc_gss_lock); 652197583Sjamie} 653197583Sjamie 654197583Sjamiestatic void 655197583Sjamiesvc_rpc_gss_timeout_clients(void) 656197583Sjamie{ 657197583Sjamie struct svc_rpc_gss_client *client; 658197583Sjamie time_t now = time_uptime; 659197583Sjamie 660197583Sjamie rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()"); 661197583Sjamie 662197583Sjamie /* 663197583Sjamie * First enforce the max client limit. We keep 664197583Sjamie * svc_rpc_gss_clients in LRU order. 665197583Sjamie */ 666226209Srmacklem sx_xlock(&svc_rpc_gss_lock); 667226209Srmacklem client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list); 668226209Srmacklem while (svc_rpc_gss_client_count > CLIENT_MAX && client != NULL) { 669226209Srmacklem svc_rpc_gss_forget_client_locked(client); 670226209Srmacklem sx_xunlock(&svc_rpc_gss_lock); 671226209Srmacklem svc_rpc_gss_release_client(client); 672226209Srmacklem sx_xlock(&svc_rpc_gss_lock); 673226209Srmacklem client = TAILQ_LAST(&svc_rpc_gss_clients, 674226209Srmacklem svc_rpc_gss_client_list); 675226209Srmacklem } 676226209Srmacklemagain: 677226209Srmacklem TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) { 678197583Sjamie if (client->cl_state == CLIENT_STALE 679197583Sjamie || now > client->cl_expiration) { 680226209Srmacklem svc_rpc_gss_forget_client_locked(client); 681226209Srmacklem sx_xunlock(&svc_rpc_gss_lock); 682197583Sjamie rpc_gss_log_debug("expiring client %p", client); 683226209Srmacklem svc_rpc_gss_release_client(client); 684226209Srmacklem sx_xlock(&svc_rpc_gss_lock); 685226209Srmacklem goto again; 686184588Sdfr } 687184588Sdfr } 688226209Srmacklem sx_xunlock(&svc_rpc_gss_lock); 689184588Sdfr} 690184588Sdfr 691197583Sjamie#ifdef DEBUG 692184588Sdfr/* 693197583Sjamie * OID<->string routines. These are uuuuugly. 694184588Sdfr */ 695197583Sjamiestatic OM_uint32 696197583Sjamiegss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) 697184588Sdfr{ 698197583Sjamie char numstr[128]; 699197583Sjamie unsigned long number; 700197583Sjamie int numshift; 701197583Sjamie size_t string_length; 702197583Sjamie size_t i; 703197583Sjamie unsigned char *cp; 704197583Sjamie char *bp; 705184588Sdfr 706197583Sjamie /* Decoded according to krb5/gssapi_krb5.c */ 707184588Sdfr 708197583Sjamie /* First determine the size of the string */ 709197583Sjamie string_length = 0; 710197583Sjamie number = 0; 711197583Sjamie numshift = 0; 712197583Sjamie cp = (unsigned char *) oid->elements; 713197583Sjamie number = (unsigned long) cp[0]; 714197583Sjamie sprintf(numstr, "%ld ", number/40); 715197583Sjamie string_length += strlen(numstr); 716197583Sjamie sprintf(numstr, "%ld ", number%40); 717197583Sjamie string_length += strlen(numstr); 718197583Sjamie for (i=1; i<oid->length; i++) { 719197583Sjamie if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { 720197583Sjamie number = (number << 7) | (cp[i] & 0x7f); 721197583Sjamie numshift += 7; 722184588Sdfr } 723197583Sjamie else { 724197583Sjamie *minor_status = 0; 725197583Sjamie return(GSS_S_FAILURE); 726184588Sdfr } 727197583Sjamie if ((cp[i] & 0x80) == 0) { 728197583Sjamie sprintf(numstr, "%ld ", number); 729197583Sjamie string_length += strlen(numstr); 730197583Sjamie number = 0; 731197583Sjamie numshift = 0; 732197583Sjamie } 733184588Sdfr } 734197583Sjamie /* 735197583Sjamie * If we get here, we've calculated the length of "n n n ... n ". Add 4 736197583Sjamie * here for "{ " and "}\0". 737197583Sjamie */ 738197583Sjamie string_length += 4; 739197583Sjamie if ((bp = (char *) mem_alloc(string_length))) { 740197583Sjamie strcpy(bp, "{ "); 741197583Sjamie number = (unsigned long) cp[0]; 742197583Sjamie sprintf(numstr, "%ld ", number/40); 743197583Sjamie strcat(bp, numstr); 744197583Sjamie sprintf(numstr, "%ld ", number%40); 745197583Sjamie strcat(bp, numstr); 746197583Sjamie number = 0; 747197583Sjamie cp = (unsigned char *) oid->elements; 748197583Sjamie for (i=1; i<oid->length; i++) { 749197583Sjamie number = (number << 7) | (cp[i] & 0x7f); 750197583Sjamie if ((cp[i] & 0x80) == 0) { 751197583Sjamie sprintf(numstr, "%ld ", number); 752197583Sjamie strcat(bp, numstr); 753197583Sjamie number = 0; 754197583Sjamie } 755184588Sdfr } 756197583Sjamie strcat(bp, "}"); 757197583Sjamie oid_str->length = strlen(bp)+1; 758197583Sjamie oid_str->value = (void *) bp; 759197583Sjamie *minor_status = 0; 760197583Sjamie return(GSS_S_COMPLETE); 761184588Sdfr } 762197583Sjamie *minor_status = 0; 763197583Sjamie return(GSS_S_FAILURE); 764197583Sjamie} 765197583Sjamie#endif 766184588Sdfr 767197583Sjamiestatic void 768197583Sjamiesvc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, 769197583Sjamie const gss_name_t name) 770197583Sjamie{ 771197583Sjamie OM_uint32 maj_stat, min_stat; 772197583Sjamie rpc_gss_ucred_t *uc = &client->cl_ucred; 773197583Sjamie int numgroups; 774197583Sjamie 775197583Sjamie uc->uid = 65534; 776197583Sjamie uc->gid = 65534; 777197583Sjamie uc->gidlist = client->cl_gid_storage; 778197583Sjamie 779201853Sbrooks numgroups = RPCAUTH_UNIXGIDS; 780197583Sjamie maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech, 781197583Sjamie &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]); 782197583Sjamie if (GSS_ERROR(maj_stat)) 783197583Sjamie uc->gidlen = 0; 784197583Sjamie else 785197583Sjamie uc->gidlen = numgroups; 786197583Sjamie} 787197583Sjamie 788197583Sjamiestatic void 789197583Sjamiesvc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client) 790197583Sjamie{ 791197583Sjamie static gss_OID_desc krb5_mech_oid = 792197583Sjamie {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; 793197583Sjamie 794184588Sdfr /* 795197583Sjamie * Attempt to translate mech type and service into a 796197583Sjamie * 'pseudo flavor'. Hardwire in krb5 support for now. 797184588Sdfr */ 798197583Sjamie if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) { 799197583Sjamie switch (client->cl_rawcred.service) { 800197583Sjamie case rpc_gss_svc_default: 801197583Sjamie case rpc_gss_svc_none: 802197583Sjamie client->cl_rpcflavor = RPCSEC_GSS_KRB5; 803197583Sjamie break; 804197583Sjamie case rpc_gss_svc_integrity: 805197583Sjamie client->cl_rpcflavor = RPCSEC_GSS_KRB5I; 806197583Sjamie break; 807197583Sjamie case rpc_gss_svc_privacy: 808197583Sjamie client->cl_rpcflavor = RPCSEC_GSS_KRB5P; 809197583Sjamie break; 810197583Sjamie } 811197583Sjamie } else { 812197583Sjamie client->cl_rpcflavor = RPCSEC_GSS; 813197583Sjamie } 814184588Sdfr} 815184588Sdfr 816197583Sjamiestatic bool_t 817197583Sjamiesvc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, 818197583Sjamie struct svc_req *rqst, 819197583Sjamie struct rpc_gss_init_res *gr, 820197583Sjamie struct rpc_gss_cred *gc) 821184588Sdfr{ 822197583Sjamie gss_buffer_desc recv_tok; 823197583Sjamie gss_OID mech; 824197583Sjamie OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; 825197583Sjamie OM_uint32 cred_lifetime; 826197583Sjamie struct svc_rpc_gss_svc_name *sname; 827184588Sdfr 828197583Sjamie rpc_gss_log_debug("in svc_rpc_gss_accept_context()"); 829197583Sjamie 830197583Sjamie /* Deserialize arguments. */ 831197583Sjamie memset(&recv_tok, 0, sizeof(recv_tok)); 832197583Sjamie 833197583Sjamie if (!svc_getargs(rqst, 834197583Sjamie (xdrproc_t) xdr_gss_buffer_desc, 835197583Sjamie (caddr_t) &recv_tok)) { 836197583Sjamie client->cl_state = CLIENT_STALE; 837197583Sjamie return (FALSE); 838197583Sjamie } 839197583Sjamie 840184588Sdfr /* 841197583Sjamie * First time round, try all the server names we have until 842197583Sjamie * one matches. Afterwards, stick with that one. 843184588Sdfr */ 844197583Sjamie sx_xlock(&svc_rpc_gss_lock); 845197583Sjamie if (!client->cl_sname) { 846197583Sjamie SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 847197583Sjamie if (sname->sn_program == rqst->rq_prog 848197583Sjamie && sname->sn_version == rqst->rq_vers) { 849197583Sjamie retry: 850197583Sjamie gr->gr_major = gss_accept_sec_context( 851197583Sjamie &gr->gr_minor, 852197583Sjamie &client->cl_ctx, 853197583Sjamie sname->sn_cred, 854197583Sjamie &recv_tok, 855197583Sjamie GSS_C_NO_CHANNEL_BINDINGS, 856197583Sjamie &client->cl_cname, 857197583Sjamie &mech, 858197583Sjamie &gr->gr_token, 859197583Sjamie &ret_flags, 860197583Sjamie &cred_lifetime, 861197583Sjamie &client->cl_creds); 862197583Sjamie if (gr->gr_major == 863197583Sjamie GSS_S_CREDENTIALS_EXPIRED) { 864197583Sjamie /* 865197583Sjamie * Either our creds really did 866197583Sjamie * expire or gssd was 867197583Sjamie * restarted. 868197583Sjamie */ 869197583Sjamie if (rpc_gss_acquire_svc_cred(sname)) 870197583Sjamie goto retry; 871197583Sjamie } 872197583Sjamie client->cl_sname = sname; 873197583Sjamie break; 874184588Sdfr } 875184588Sdfr } 876197583Sjamie if (!sname) { 877197583Sjamie xdr_free((xdrproc_t) xdr_gss_buffer_desc, 878197583Sjamie (char *) &recv_tok); 879197583Sjamie sx_xunlock(&svc_rpc_gss_lock); 880197583Sjamie return (FALSE); 881197583Sjamie } 882197583Sjamie } else { 883197583Sjamie gr->gr_major = gss_accept_sec_context( 884197583Sjamie &gr->gr_minor, 885197583Sjamie &client->cl_ctx, 886197583Sjamie client->cl_sname->sn_cred, 887197583Sjamie &recv_tok, 888197583Sjamie GSS_C_NO_CHANNEL_BINDINGS, 889197583Sjamie &client->cl_cname, 890197583Sjamie &mech, 891197583Sjamie &gr->gr_token, 892197583Sjamie &ret_flags, 893197583Sjamie &cred_lifetime, 894197583Sjamie NULL); 895184588Sdfr } 896197583Sjamie sx_xunlock(&svc_rpc_gss_lock); 897197583Sjamie 898197583Sjamie xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); 899184588Sdfr 900184588Sdfr /* 901197583Sjamie * If we get an error from gss_accept_sec_context, send the 902197583Sjamie * reply anyway so that the client gets a chance to see what 903197583Sjamie * is wrong. 904184588Sdfr */ 905197583Sjamie if (gr->gr_major != GSS_S_COMPLETE && 906197583Sjamie gr->gr_major != GSS_S_CONTINUE_NEEDED) { 907197583Sjamie rpc_gss_log_status("accept_sec_context", client->cl_mech, 908197583Sjamie gr->gr_major, gr->gr_minor); 909197583Sjamie client->cl_state = CLIENT_STALE; 910197583Sjamie return (TRUE); 911197583Sjamie } 912184588Sdfr 913197583Sjamie gr->gr_handle.value = &client->cl_id; 914197583Sjamie gr->gr_handle.length = sizeof(client->cl_id); 915197583Sjamie gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 916197583Sjamie 917197583Sjamie /* Save client info. */ 918197583Sjamie client->cl_mech = mech; 919197583Sjamie client->cl_qop = GSS_C_QOP_DEFAULT; 920197583Sjamie client->cl_done_callback = FALSE; 921184588Sdfr 922197583Sjamie if (gr->gr_major == GSS_S_COMPLETE) { 923197583Sjamie gss_buffer_desc export_name; 924184588Sdfr 925197583Sjamie /* 926197583Sjamie * Change client expiration time to be near when the 927197583Sjamie * client creds expire (or 24 hours if we can't figure 928197583Sjamie * that out). 929197583Sjamie */ 930197583Sjamie if (cred_lifetime == GSS_C_INDEFINITE) 931197583Sjamie cred_lifetime = time_uptime + 24*60*60; 932184588Sdfr 933197583Sjamie client->cl_expiration = time_uptime + cred_lifetime; 934184588Sdfr 935197583Sjamie /* 936197583Sjamie * Fill in cred details in the rawcred structure. 937197583Sjamie */ 938197583Sjamie client->cl_rawcred.version = RPCSEC_GSS_VERSION; 939197583Sjamie rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 940197583Sjamie maj_stat = gss_export_name(&min_stat, client->cl_cname, 941197583Sjamie &export_name); 942197583Sjamie if (maj_stat != GSS_S_COMPLETE) { 943197583Sjamie rpc_gss_log_status("gss_export_name", client->cl_mech, 944197583Sjamie maj_stat, min_stat); 945197583Sjamie return (FALSE); 946197583Sjamie } 947197583Sjamie client->cl_rawcred.client_principal = 948197583Sjamie mem_alloc(sizeof(*client->cl_rawcred.client_principal) 949197583Sjamie + export_name.length); 950197583Sjamie client->cl_rawcred.client_principal->len = export_name.length; 951197583Sjamie memcpy(client->cl_rawcred.client_principal->name, 952197583Sjamie export_name.value, export_name.length); 953197583Sjamie gss_release_buffer(&min_stat, &export_name); 954197583Sjamie client->cl_rawcred.svc_principal = 955197583Sjamie client->cl_sname->sn_principal; 956197583Sjamie client->cl_rawcred.service = gc->gc_svc; 957197583Sjamie 958197583Sjamie /* 959197583Sjamie * Use gss_pname_to_uid to map to unix creds. For 960197583Sjamie * kerberos5, this uses krb5_aname_to_localname. 961197583Sjamie */ 962197583Sjamie svc_rpc_gss_build_ucred(client, client->cl_cname); 963197583Sjamie svc_rpc_gss_set_flavor(client); 964197583Sjamie gss_release_name(&min_stat, &client->cl_cname); 965197583Sjamie 966197583Sjamie#ifdef DEBUG 967197583Sjamie { 968197583Sjamie gss_buffer_desc mechname; 969197583Sjamie 970197583Sjamie gss_oid_to_str(&min_stat, mech, &mechname); 971197583Sjamie 972197583Sjamie rpc_gss_log_debug("accepted context for %s with " 973197583Sjamie "<mech %.*s, qop %d, svc %d>", 974197583Sjamie client->cl_rawcred.client_principal->name, 975197583Sjamie mechname.length, (char *)mechname.value, 976201853Sbrooks client->cl_qop, client->cl_rawcred.service); 977197583Sjamie 978197583Sjamie gss_release_buffer(&min_stat, &mechname); 979197583Sjamie } 980197583Sjamie#endif /* DEBUG */ 981197583Sjamie } 982197583Sjamie return (TRUE); 983197583Sjamie} 984197583Sjamie 985197583Sjamiestatic bool_t 986197583Sjamiesvc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 987241570Srmacklem gss_qop_t *qop, rpc_gss_proc_t gcproc) 988197583Sjamie{ 989197583Sjamie struct opaque_auth *oa; 990197583Sjamie gss_buffer_desc rpcbuf, checksum; 991197583Sjamie OM_uint32 maj_stat, min_stat; 992197583Sjamie gss_qop_t qop_state; 993197583Sjamie int32_t rpchdr[128 / sizeof(int32_t)]; 994197583Sjamie int32_t *buf; 995197583Sjamie 996197583Sjamie rpc_gss_log_debug("in svc_rpc_gss_validate()"); 997197583Sjamie 998197583Sjamie memset(rpchdr, 0, sizeof(rpchdr)); 999197583Sjamie 1000197583Sjamie /* Reconstruct RPC header for signing (from xdr_callmsg). */ 1001197583Sjamie buf = rpchdr; 1002197583Sjamie IXDR_PUT_LONG(buf, msg->rm_xid); 1003197583Sjamie IXDR_PUT_ENUM(buf, msg->rm_direction); 1004197583Sjamie IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 1005197583Sjamie IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 1006197583Sjamie IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 1007197583Sjamie IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 1008197583Sjamie oa = &msg->rm_call.cb_cred; 1009197583Sjamie IXDR_PUT_ENUM(buf, oa->oa_flavor); 1010197583Sjamie IXDR_PUT_LONG(buf, oa->oa_length); 1011197583Sjamie if (oa->oa_length) { 1012197583Sjamie memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 1013197583Sjamie buf += RNDUP(oa->oa_length) / sizeof(int32_t); 1014197583Sjamie } 1015197583Sjamie rpcbuf.value = rpchdr; 1016197583Sjamie rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 1017197583Sjamie 1018197583Sjamie checksum.value = msg->rm_call.cb_verf.oa_base; 1019197583Sjamie checksum.length = msg->rm_call.cb_verf.oa_length; 1020197583Sjamie 1021197583Sjamie maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 1022197583Sjamie &qop_state); 1023197583Sjamie 1024197583Sjamie if (maj_stat != GSS_S_COMPLETE) { 1025197583Sjamie rpc_gss_log_status("gss_verify_mic", client->cl_mech, 1026197583Sjamie maj_stat, min_stat); 1027242017Srmacklem /* 1028242220Srmacklem * A bug in some versions of the Linux client generates a 1029242220Srmacklem * Destroy operation with a bogus encrypted checksum. Deleting 1030242220Srmacklem * the credential handle for that case causes the mount to fail. 1031242017Srmacklem * Since the checksum is bogus (gss_verify_mic() failed), it 1032242017Srmacklem * doesn't make sense to destroy the handle and not doing so 1033242017Srmacklem * fixes the Linux mount. 1034242017Srmacklem */ 1035241570Srmacklem if (gcproc != RPCSEC_GSS_DESTROY) 1036241570Srmacklem client->cl_state = CLIENT_STALE; 1037197583Sjamie return (FALSE); 1038197583Sjamie } 1039197583Sjamie 1040197583Sjamie *qop = qop_state; 1041197583Sjamie return (TRUE); 1042197583Sjamie} 1043197583Sjamie 1044197583Sjamiestatic bool_t 1045197583Sjamiesvc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 1046197583Sjamie struct svc_req *rqst, u_int seq) 1047197583Sjamie{ 1048197583Sjamie gss_buffer_desc signbuf; 1049197583Sjamie gss_buffer_desc mic; 1050197583Sjamie OM_uint32 maj_stat, min_stat; 1051197583Sjamie uint32_t nseq; 1052197583Sjamie 1053197583Sjamie rpc_gss_log_debug("in svc_rpc_gss_nextverf()"); 1054197583Sjamie 1055197583Sjamie nseq = htonl(seq); 1056197583Sjamie signbuf.value = &nseq; 1057197583Sjamie signbuf.length = sizeof(nseq); 1058197583Sjamie 1059197583Sjamie maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 1060197583Sjamie &signbuf, &mic); 1061197583Sjamie 1062197583Sjamie if (maj_stat != GSS_S_COMPLETE) { 1063197583Sjamie rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 1064197583Sjamie client->cl_state = CLIENT_STALE; 1065197583Sjamie return (FALSE); 1066197583Sjamie } 1067197583Sjamie 1068197583Sjamie KASSERT(mic.length <= MAX_AUTH_BYTES, 1069197583Sjamie ("MIC too large for RPCSEC_GSS")); 1070197583Sjamie 1071197583Sjamie rqst->rq_verf.oa_flavor = RPCSEC_GSS; 1072197583Sjamie rqst->rq_verf.oa_length = mic.length; 1073197583Sjamie bcopy(mic.value, rqst->rq_verf.oa_base, mic.length); 1074197583Sjamie 1075197583Sjamie gss_release_buffer(&min_stat, &mic); 1076197583Sjamie 1077197583Sjamie return (TRUE); 1078197583Sjamie} 1079197583Sjamie 1080197583Sjamiestatic bool_t 1081197583Sjamiesvc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 1082197583Sjamie{ 1083197583Sjamie struct svc_rpc_gss_callback *scb; 1084197583Sjamie rpc_gss_lock_t lock; 1085197583Sjamie void *cookie; 1086197583Sjamie bool_t cb_res; 1087197583Sjamie bool_t result; 1088197583Sjamie 1089184588Sdfr /* 1090197583Sjamie * See if we have a callback for this guy. 1091184588Sdfr */ 1092197583Sjamie result = TRUE; 1093197583Sjamie SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 1094197583Sjamie if (scb->cb_callback.program == rqst->rq_prog 1095197583Sjamie && scb->cb_callback.version == rqst->rq_vers) { 1096184588Sdfr /* 1097197583Sjamie * This one matches. Call the callback and see 1098197583Sjamie * if it wants to veto or something. 1099184588Sdfr */ 1100197583Sjamie lock.locked = FALSE; 1101197583Sjamie lock.raw_cred = &client->cl_rawcred; 1102197583Sjamie cb_res = scb->cb_callback.callback(rqst, 1103197583Sjamie client->cl_creds, 1104197583Sjamie client->cl_ctx, 1105197583Sjamie &lock, 1106197583Sjamie &cookie); 1107197583Sjamie 1108197583Sjamie if (!cb_res) { 1109197583Sjamie client->cl_state = CLIENT_STALE; 1110197583Sjamie result = FALSE; 1111197583Sjamie break; 1112184588Sdfr } 1113197583Sjamie 1114197583Sjamie /* 1115197583Sjamie * The callback accepted the connection - it 1116197583Sjamie * is responsible for freeing client->cl_creds 1117197583Sjamie * now. 1118197583Sjamie */ 1119197583Sjamie client->cl_creds = GSS_C_NO_CREDENTIAL; 1120197583Sjamie client->cl_locked = lock.locked; 1121197583Sjamie client->cl_cookie = cookie; 1122197583Sjamie return (TRUE); 1123184588Sdfr } 1124197583Sjamie } 1125197583Sjamie 1126197583Sjamie /* 1127197583Sjamie * Either no callback exists for this program/version or one 1128197583Sjamie * of the callbacks rejected the connection. We just need to 1129197583Sjamie * clean up the delegated client creds, if any. 1130197583Sjamie */ 1131197583Sjamie if (client->cl_creds) { 1132197583Sjamie OM_uint32 min_ver; 1133197583Sjamie gss_release_cred(&min_ver, &client->cl_creds); 1134197583Sjamie } 1135197583Sjamie return (result); 1136197583Sjamie} 1137197583Sjamie 1138197583Sjamiestatic bool_t 1139197583Sjamiesvc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 1140197583Sjamie{ 1141197583Sjamie u_int32_t offset; 1142197583Sjamie int word, bit; 1143197583Sjamie bool_t result; 1144197583Sjamie 1145197583Sjamie sx_xlock(&client->cl_lock); 1146197583Sjamie if (seq <= client->cl_seqlast) { 1147197583Sjamie /* 1148197583Sjamie * The request sequence number is less than 1149197583Sjamie * the largest we have seen so far. If it is 1150197583Sjamie * outside the window or if we have seen a 1151197583Sjamie * request with this sequence before, silently 1152197583Sjamie * discard it. 1153197583Sjamie */ 1154197583Sjamie offset = client->cl_seqlast - seq; 1155197583Sjamie if (offset >= SVC_RPC_GSS_SEQWINDOW) { 1156197583Sjamie result = FALSE; 1157197583Sjamie goto out; 1158184588Sdfr } 1159197583Sjamie word = offset / 32; 1160197583Sjamie bit = offset % 32; 1161197583Sjamie if (client->cl_seqmask[word] & (1 << bit)) { 1162197583Sjamie result = FALSE; 1163197583Sjamie goto out; 1164197583Sjamie } 1165184588Sdfr } 1166184588Sdfr 1167197583Sjamie result = TRUE; 1168197583Sjamieout: 1169197583Sjamie sx_xunlock(&client->cl_lock); 1170197583Sjamie return (result); 1171184588Sdfr} 1172184588Sdfr 1173197583Sjamiestatic void 1174197583Sjamiesvc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) 1175184588Sdfr{ 1176197583Sjamie int offset, i, word, bit; 1177197583Sjamie uint32_t carry, newcarry; 1178184588Sdfr 1179197583Sjamie sx_xlock(&client->cl_lock); 1180197583Sjamie if (seq > client->cl_seqlast) { 1181184588Sdfr /* 1182197583Sjamie * This request has a sequence number greater 1183197583Sjamie * than any we have seen so far. Advance the 1184197583Sjamie * seq window and set bit zero of the window 1185197583Sjamie * (which corresponds to the new sequence 1186197583Sjamie * number) 1187184588Sdfr */ 1188197583Sjamie offset = seq - client->cl_seqlast; 1189197583Sjamie while (offset > 32) { 1190197583Sjamie for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 1191197583Sjamie i > 0; i--) { 1192197583Sjamie client->cl_seqmask[i] = client->cl_seqmask[i-1]; 1193184588Sdfr } 1194197583Sjamie client->cl_seqmask[0] = 0; 1195197583Sjamie offset -= 32; 1196184588Sdfr } 1197197583Sjamie carry = 0; 1198197583Sjamie for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 1199197583Sjamie newcarry = client->cl_seqmask[i] >> (32 - offset); 1200197583Sjamie client->cl_seqmask[i] = 1201197583Sjamie (client->cl_seqmask[i] << offset) | carry; 1202197583Sjamie carry = newcarry; 1203197583Sjamie } 1204197583Sjamie client->cl_seqmask[0] |= 1; 1205197583Sjamie client->cl_seqlast = seq; 1206197583Sjamie } else { 1207197583Sjamie offset = client->cl_seqlast - seq; 1208197583Sjamie word = offset / 32; 1209197583Sjamie bit = offset % 32; 1210197583Sjamie client->cl_seqmask[word] |= (1 << bit); 1211197583Sjamie } 1212197583Sjamie sx_xunlock(&client->cl_lock); 1213197583Sjamie} 1214197583Sjamie 1215197583Sjamieenum auth_stat 1216197583Sjamiesvc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 1217197583Sjamie 1218197583Sjamie{ 1219197583Sjamie OM_uint32 min_stat; 1220197583Sjamie XDR xdrs; 1221197583Sjamie struct svc_rpc_gss_cookedcred *cc; 1222197583Sjamie struct svc_rpc_gss_client *client; 1223197583Sjamie struct rpc_gss_cred gc; 1224197583Sjamie struct rpc_gss_init_res gr; 1225197583Sjamie gss_qop_t qop; 1226197583Sjamie int call_stat; 1227197583Sjamie enum auth_stat result; 1228197583Sjamie 1229197583Sjamie rpc_gss_log_debug("in svc_rpc_gss()"); 1230197583Sjamie 1231197583Sjamie /* Garbage collect old clients. */ 1232197583Sjamie svc_rpc_gss_timeout_clients(); 1233197583Sjamie 1234197583Sjamie /* Initialize reply. */ 1235197583Sjamie rqst->rq_verf = _null_auth; 1236197583Sjamie 1237197583Sjamie /* Deserialize client credentials. */ 1238197583Sjamie if (rqst->rq_cred.oa_length <= 0) 1239197583Sjamie return (AUTH_BADCRED); 1240197583Sjamie 1241197583Sjamie memset(&gc, 0, sizeof(gc)); 1242197583Sjamie 1243197583Sjamie xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 1244197583Sjamie rqst->rq_cred.oa_length, XDR_DECODE); 1245197583Sjamie 1246197583Sjamie if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 1247197583Sjamie XDR_DESTROY(&xdrs); 1248197583Sjamie return (AUTH_BADCRED); 1249197583Sjamie } 1250197583Sjamie XDR_DESTROY(&xdrs); 1251197583Sjamie 1252197583Sjamie client = NULL; 1253197583Sjamie 1254197583Sjamie /* Check version. */ 1255197583Sjamie if (gc.gc_version != RPCSEC_GSS_VERSION) { 1256197583Sjamie result = AUTH_BADCRED; 1257197583Sjamie goto out; 1258197583Sjamie } 1259197583Sjamie 1260197583Sjamie /* Check the proc and find the client (or create it) */ 1261197583Sjamie if (gc.gc_proc == RPCSEC_GSS_INIT) { 1262197583Sjamie if (gc.gc_handle.length != 0) { 1263197583Sjamie result = AUTH_BADCRED; 1264197583Sjamie goto out; 1265197583Sjamie } 1266197583Sjamie client = svc_rpc_gss_create_client(); 1267197583Sjamie refcount_acquire(&client->cl_refs); 1268197583Sjamie } else { 1269197583Sjamie struct svc_rpc_gss_clientid *p; 1270197583Sjamie if (gc.gc_handle.length != sizeof(*p)) { 1271197583Sjamie result = AUTH_BADCRED; 1272197583Sjamie goto out; 1273197583Sjamie } 1274197583Sjamie p = gc.gc_handle.value; 1275197583Sjamie client = svc_rpc_gss_find_client(p); 1276197583Sjamie if (!client) { 1277197583Sjamie /* 1278197583Sjamie * Can't find the client - we may have 1279197583Sjamie * destroyed it - tell the other side to 1280197583Sjamie * re-authenticate. 1281197583Sjamie */ 1282197583Sjamie result = RPCSEC_GSS_CREDPROBLEM; 1283197583Sjamie goto out; 1284197583Sjamie } 1285197583Sjamie } 1286197583Sjamie cc = rqst->rq_clntcred; 1287197583Sjamie cc->cc_client = client; 1288197583Sjamie cc->cc_service = gc.gc_svc; 1289197583Sjamie cc->cc_seq = gc.gc_seq; 1290197583Sjamie 1291197583Sjamie /* 1292197583Sjamie * The service and sequence number must be ignored for 1293197583Sjamie * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 1294197583Sjamie */ 1295197583Sjamie if (gc.gc_proc != RPCSEC_GSS_INIT 1296197583Sjamie && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { 1297184588Sdfr /* 1298197583Sjamie * Check for sequence number overflow. 1299184588Sdfr */ 1300197583Sjamie if (gc.gc_seq >= MAXSEQ) { 1301197583Sjamie result = RPCSEC_GSS_CTXPROBLEM; 1302197583Sjamie goto out; 1303197583Sjamie } 1304197583Sjamie 1305197583Sjamie /* 1306197583Sjamie * Check for valid service. 1307197583Sjamie */ 1308197583Sjamie if (gc.gc_svc != rpc_gss_svc_none && 1309197583Sjamie gc.gc_svc != rpc_gss_svc_integrity && 1310197583Sjamie gc.gc_svc != rpc_gss_svc_privacy) { 1311197583Sjamie result = AUTH_BADCRED; 1312197583Sjamie goto out; 1313197583Sjamie } 1314184588Sdfr } 1315197583Sjamie 1316197583Sjamie /* Handle RPCSEC_GSS control procedure. */ 1317197583Sjamie switch (gc.gc_proc) { 1318197583Sjamie 1319197583Sjamie case RPCSEC_GSS_INIT: 1320197583Sjamie case RPCSEC_GSS_CONTINUE_INIT: 1321197583Sjamie if (rqst->rq_proc != NULLPROC) { 1322197583Sjamie result = AUTH_REJECTEDCRED; 1323197583Sjamie break; 1324197583Sjamie } 1325197583Sjamie 1326197583Sjamie memset(&gr, 0, sizeof(gr)); 1327197583Sjamie if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 1328197583Sjamie result = AUTH_REJECTEDCRED; 1329197583Sjamie break; 1330197583Sjamie } 1331197583Sjamie 1332197583Sjamie if (gr.gr_major == GSS_S_COMPLETE) { 1333197583Sjamie /* 1334197583Sjamie * We borrow the space for the call verf to 1335197583Sjamie * pack our reply verf. 1336197583Sjamie */ 1337197583Sjamie rqst->rq_verf = msg->rm_call.cb_verf; 1338197583Sjamie if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 1339197583Sjamie result = AUTH_REJECTEDCRED; 1340197583Sjamie break; 1341197583Sjamie } 1342197583Sjamie } else { 1343197583Sjamie rqst->rq_verf = _null_auth; 1344197583Sjamie } 1345197583Sjamie 1346197583Sjamie call_stat = svc_sendreply(rqst, 1347197583Sjamie (xdrproc_t) xdr_rpc_gss_init_res, 1348197583Sjamie (caddr_t) &gr); 1349197583Sjamie 1350197583Sjamie gss_release_buffer(&min_stat, &gr.gr_token); 1351197583Sjamie 1352197583Sjamie if (!call_stat) { 1353197583Sjamie result = AUTH_FAILED; 1354197583Sjamie break; 1355197583Sjamie } 1356197583Sjamie 1357197583Sjamie if (gr.gr_major == GSS_S_COMPLETE) 1358197583Sjamie client->cl_state = CLIENT_ESTABLISHED; 1359197583Sjamie 1360197583Sjamie result = RPCSEC_GSS_NODISPATCH; 1361197583Sjamie break; 1362197583Sjamie 1363197583Sjamie case RPCSEC_GSS_DATA: 1364197583Sjamie case RPCSEC_GSS_DESTROY: 1365197583Sjamie if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 1366197583Sjamie result = RPCSEC_GSS_NODISPATCH; 1367197583Sjamie break; 1368197583Sjamie } 1369197583Sjamie 1370241570Srmacklem if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) { 1371197583Sjamie result = RPCSEC_GSS_CREDPROBLEM; 1372197583Sjamie break; 1373197583Sjamie } 1374197583Sjamie 1375197583Sjamie /* 1376197583Sjamie * We borrow the space for the call verf to pack our 1377197583Sjamie * reply verf. 1378197583Sjamie */ 1379197583Sjamie rqst->rq_verf = msg->rm_call.cb_verf; 1380197583Sjamie if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 1381197583Sjamie result = RPCSEC_GSS_CTXPROBLEM; 1382197583Sjamie break; 1383197583Sjamie } 1384197583Sjamie 1385197583Sjamie svc_rpc_gss_update_seq(client, gc.gc_seq); 1386197583Sjamie 1387197583Sjamie /* 1388197583Sjamie * Change the SVCAUTH ops on the request to point at 1389197583Sjamie * our own code so that we can unwrap the arguments 1390197583Sjamie * and wrap the result. The caller will re-set this on 1391197583Sjamie * every request to point to a set of null wrap/unwrap 1392197583Sjamie * methods. Acquire an extra reference to the client 1393197583Sjamie * which will be released by svc_rpc_gss_release() 1394197583Sjamie * after the request has finished processing. 1395197583Sjamie */ 1396197583Sjamie refcount_acquire(&client->cl_refs); 1397197583Sjamie rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops; 1398197583Sjamie rqst->rq_auth.svc_ah_private = cc; 1399197583Sjamie 1400197583Sjamie if (gc.gc_proc == RPCSEC_GSS_DATA) { 1401197583Sjamie /* 1402197583Sjamie * We might be ready to do a callback to the server to 1403197583Sjamie * see if it wants to accept/reject the connection. 1404197583Sjamie */ 1405197583Sjamie sx_xlock(&client->cl_lock); 1406197583Sjamie if (!client->cl_done_callback) { 1407197583Sjamie client->cl_done_callback = TRUE; 1408197583Sjamie client->cl_qop = qop; 1409197583Sjamie client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1410197583Sjamie client->cl_rawcred.mechanism, qop); 1411197583Sjamie if (!svc_rpc_gss_callback(client, rqst)) { 1412197583Sjamie result = AUTH_REJECTEDCRED; 1413197583Sjamie sx_xunlock(&client->cl_lock); 1414197583Sjamie break; 1415197583Sjamie } 1416197583Sjamie } 1417197583Sjamie sx_xunlock(&client->cl_lock); 1418197583Sjamie 1419197583Sjamie /* 1420197583Sjamie * If the server has locked this client to a 1421197583Sjamie * particular service+qop pair, enforce that 1422197583Sjamie * restriction now. 1423197583Sjamie */ 1424197583Sjamie if (client->cl_locked) { 1425197583Sjamie if (client->cl_rawcred.service != gc.gc_svc) { 1426197583Sjamie result = AUTH_FAILED; 1427197583Sjamie break; 1428197583Sjamie } else if (client->cl_qop != qop) { 1429197583Sjamie result = AUTH_BADVERF; 1430197583Sjamie break; 1431197583Sjamie } 1432197583Sjamie } 1433197583Sjamie 1434197583Sjamie /* 1435197583Sjamie * If the qop changed, look up the new qop 1436197583Sjamie * name for rawcred. 1437197583Sjamie */ 1438197583Sjamie if (client->cl_qop != qop) { 1439197583Sjamie client->cl_qop = qop; 1440197583Sjamie client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1441197583Sjamie client->cl_rawcred.mechanism, qop); 1442197583Sjamie } 1443197583Sjamie 1444197583Sjamie /* 1445197583Sjamie * Make sure we use the right service value 1446197583Sjamie * for unwrap/wrap. 1447197583Sjamie */ 1448197583Sjamie if (client->cl_rawcred.service != gc.gc_svc) { 1449197583Sjamie client->cl_rawcred.service = gc.gc_svc; 1450197583Sjamie svc_rpc_gss_set_flavor(client); 1451197583Sjamie } 1452197583Sjamie 1453197583Sjamie result = AUTH_OK; 1454197583Sjamie } else { 1455197583Sjamie if (rqst->rq_proc != NULLPROC) { 1456197583Sjamie result = AUTH_REJECTEDCRED; 1457197583Sjamie break; 1458197583Sjamie } 1459197583Sjamie 1460197583Sjamie call_stat = svc_sendreply(rqst, 1461197583Sjamie (xdrproc_t) xdr_void, (caddr_t) NULL); 1462197583Sjamie 1463197583Sjamie if (!call_stat) { 1464197583Sjamie result = AUTH_FAILED; 1465197583Sjamie break; 1466197583Sjamie } 1467197583Sjamie 1468197583Sjamie svc_rpc_gss_forget_client(client); 1469197583Sjamie 1470197583Sjamie result = RPCSEC_GSS_NODISPATCH; 1471197583Sjamie break; 1472197583Sjamie } 1473197583Sjamie break; 1474197583Sjamie 1475197583Sjamie default: 1476197583Sjamie result = AUTH_BADCRED; 1477197583Sjamie break; 1478197583Sjamie } 1479197583Sjamieout: 1480197583Sjamie if (client) 1481197583Sjamie svc_rpc_gss_release_client(client); 1482197583Sjamie 1483197583Sjamie xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 1484197583Sjamie return (result); 1485184588Sdfr} 1486184588Sdfr 1487197583Sjamiestatic bool_t 1488197583Sjamiesvc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp) 1489197583Sjamie{ 1490197583Sjamie struct svc_rpc_gss_cookedcred *cc; 1491197583Sjamie struct svc_rpc_gss_client *client; 1492197583Sjamie 1493197583Sjamie rpc_gss_log_debug("in svc_rpc_gss_wrap()"); 1494184588Sdfr 1495197583Sjamie cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1496197583Sjamie client = cc->cc_client; 1497197583Sjamie if (client->cl_state != CLIENT_ESTABLISHED 1498197583Sjamie || cc->cc_service == rpc_gss_svc_none || *mp == NULL) { 1499197583Sjamie return (TRUE); 1500197583Sjamie } 1501197583Sjamie 1502197583Sjamie return (xdr_rpc_gss_wrap_data(mp, 1503197583Sjamie client->cl_ctx, client->cl_qop, 1504197583Sjamie cc->cc_service, cc->cc_seq)); 1505197583Sjamie} 1506197583Sjamie 1507197583Sjamiestatic bool_t 1508197583Sjamiesvc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp) 1509184588Sdfr{ 1510197583Sjamie struct svc_rpc_gss_cookedcred *cc; 1511197583Sjamie struct svc_rpc_gss_client *client; 1512184588Sdfr 1513197583Sjamie rpc_gss_log_debug("in svc_rpc_gss_unwrap()"); 1514197583Sjamie 1515197583Sjamie cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1516197583Sjamie client = cc->cc_client; 1517197583Sjamie if (client->cl_state != CLIENT_ESTABLISHED 1518197583Sjamie || cc->cc_service == rpc_gss_svc_none) { 1519197583Sjamie return (TRUE); 1520184588Sdfr } 1521197583Sjamie 1522197583Sjamie return (xdr_rpc_gss_unwrap_data(mp, 1523197583Sjamie client->cl_ctx, client->cl_qop, 1524197583Sjamie cc->cc_service, cc->cc_seq)); 1525184588Sdfr} 1526184588Sdfr 1527197583Sjamiestatic void 1528197583Sjamiesvc_rpc_gss_release(SVCAUTH *auth) 1529197583Sjamie{ 1530197583Sjamie struct svc_rpc_gss_cookedcred *cc; 1531197583Sjamie struct svc_rpc_gss_client *client; 1532197583Sjamie 1533197583Sjamie rpc_gss_log_debug("in svc_rpc_gss_release()"); 1534197583Sjamie 1535197583Sjamie cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1536197583Sjamie client = cc->cc_client; 1537197583Sjamie svc_rpc_gss_release_client(client); 1538197583Sjamie} 1539