1a9148abdSDoug Rabson /*- 251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 351369649SPedro F. Giffuni * Copyright (c) 1990 The Regents of the University of California. 451369649SPedro F. Giffuni * 52e92ac56SJamie Gritton * Copyright (c) 2008 Doug Rabson 62e92ac56SJamie Gritton * All rights reserved. 7a9148abdSDoug Rabson * 8a9148abdSDoug Rabson * Redistribution and use in source and binary forms, with or without 9a9148abdSDoug Rabson * modification, are permitted provided that the following conditions 10a9148abdSDoug Rabson * are met: 11a9148abdSDoug Rabson * 1. Redistributions of source code must retain the above copyright 12a9148abdSDoug Rabson * notice, this list of conditions and the following disclaimer. 13a9148abdSDoug Rabson * 2. Redistributions in binary form must reproduce the above copyright 14a9148abdSDoug Rabson * notice, this list of conditions and the following disclaimer in the 15a9148abdSDoug Rabson * documentation and/or other materials provided with the distribution. 16a9148abdSDoug Rabson * 172e92ac56SJamie Gritton * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18a9148abdSDoug Rabson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19a9148abdSDoug Rabson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 202e92ac56SJamie Gritton * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21a9148abdSDoug Rabson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22a9148abdSDoug Rabson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23a9148abdSDoug Rabson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24a9148abdSDoug Rabson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25a9148abdSDoug Rabson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26a9148abdSDoug Rabson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27a9148abdSDoug Rabson * SUCH DAMAGE. 282e92ac56SJamie Gritton */ 292e92ac56SJamie Gritton /* 302e92ac56SJamie Gritton svc_rpcsec_gss.c 312e92ac56SJamie Gritton 322e92ac56SJamie Gritton Copyright (c) 2000 The Regents of the University of Michigan. 332e92ac56SJamie Gritton All rights reserved. 342e92ac56SJamie Gritton 352e92ac56SJamie Gritton Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 362e92ac56SJamie Gritton All rights reserved, all wrongs reversed. 372e92ac56SJamie Gritton 382e92ac56SJamie Gritton Redistribution and use in source and binary forms, with or without 392e92ac56SJamie Gritton modification, are permitted provided that the following conditions 402e92ac56SJamie Gritton are met: 412e92ac56SJamie Gritton 422e92ac56SJamie Gritton 1. Redistributions of source code must retain the above copyright 432e92ac56SJamie Gritton notice, this list of conditions and the following disclaimer. 442e92ac56SJamie Gritton 2. Redistributions in binary form must reproduce the above copyright 452e92ac56SJamie Gritton notice, this list of conditions and the following disclaimer in the 462e92ac56SJamie Gritton documentation and/or other materials provided with the distribution. 472e92ac56SJamie Gritton 3. Neither the name of the University nor the names of its 482e92ac56SJamie Gritton contributors may be used to endorse or promote products derived 492e92ac56SJamie Gritton from this software without specific prior written permission. 502e92ac56SJamie Gritton 512e92ac56SJamie Gritton THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 522e92ac56SJamie Gritton WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 532e92ac56SJamie Gritton MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 542e92ac56SJamie Gritton DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 552e92ac56SJamie Gritton FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 562e92ac56SJamie Gritton CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 572e92ac56SJamie Gritton SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 582e92ac56SJamie Gritton BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 592e92ac56SJamie Gritton LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 602e92ac56SJamie Gritton NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 612e92ac56SJamie Gritton SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 622e92ac56SJamie Gritton 632e92ac56SJamie Gritton $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $ 64a9148abdSDoug Rabson */ 65a9148abdSDoug Rabson 66a9148abdSDoug Rabson #include <sys/cdefs.h> 67a9148abdSDoug Rabson __FBSDID("$FreeBSD$"); 68a9148abdSDoug Rabson 69a9148abdSDoug Rabson #include <sys/param.h> 702e92ac56SJamie Gritton #include <sys/systm.h> 71ae883d55SRick Macklem #include <sys/jail.h> 72a9148abdSDoug Rabson #include <sys/kernel.h> 732e92ac56SJamie Gritton #include <sys/kobj.h> 74a9148abdSDoug Rabson #include <sys/lock.h> 75a9148abdSDoug Rabson #include <sys/malloc.h> 76a9148abdSDoug Rabson #include <sys/mbuf.h> 77a9148abdSDoug Rabson #include <sys/mutex.h> 782e92ac56SJamie Gritton #include <sys/proc.h> 792e92ac56SJamie Gritton #include <sys/sx.h> 802e92ac56SJamie Gritton #include <sys/ucred.h> 81a9148abdSDoug Rabson 822e92ac56SJamie Gritton #include <rpc/rpc.h> 832e92ac56SJamie Gritton #include <rpc/rpcsec_gss.h> 84a9148abdSDoug Rabson 852e92ac56SJamie Gritton #include "rpcsec_gss_int.h" 86a9148abdSDoug Rabson 872e92ac56SJamie Gritton static bool_t svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **); 882e92ac56SJamie Gritton static bool_t svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **); 892e92ac56SJamie Gritton static void svc_rpc_gss_release(SVCAUTH *); 902e92ac56SJamie Gritton static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *); 912e92ac56SJamie Gritton static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *); 92a9148abdSDoug Rabson 9320d728b5SMark Johnston static const struct svc_auth_ops svc_auth_gss_ops = { 9420d728b5SMark Johnston .svc_ah_wrap = svc_rpc_gss_wrap, 9520d728b5SMark Johnston .svc_ah_unwrap = svc_rpc_gss_unwrap, 9620d728b5SMark Johnston .svc_ah_release = svc_rpc_gss_release, 97a9148abdSDoug Rabson }; 98a9148abdSDoug Rabson 992e92ac56SJamie Gritton struct sx svc_rpc_gss_lock; 1002e92ac56SJamie Gritton 1012e92ac56SJamie Gritton struct svc_rpc_gss_callback { 1022e92ac56SJamie Gritton SLIST_ENTRY(svc_rpc_gss_callback) cb_link; 1032e92ac56SJamie Gritton rpc_gss_callback_t cb_callback; 1042e92ac56SJamie Gritton }; 1052e92ac56SJamie Gritton static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback) 10613e403fdSAntoine Brodin svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks); 1072e92ac56SJamie Gritton 1082e92ac56SJamie Gritton struct svc_rpc_gss_svc_name { 1092e92ac56SJamie Gritton SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link; 1102e92ac56SJamie Gritton char *sn_principal; 1112e92ac56SJamie Gritton gss_OID sn_mech; 1122e92ac56SJamie Gritton u_int sn_req_time; 1132e92ac56SJamie Gritton gss_cred_id_t sn_cred; 1142e92ac56SJamie Gritton u_int sn_program; 1152e92ac56SJamie Gritton u_int sn_version; 1162e92ac56SJamie Gritton }; 1172e92ac56SJamie Gritton static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name) 11813e403fdSAntoine Brodin svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names); 1192e92ac56SJamie Gritton 1202e92ac56SJamie Gritton enum svc_rpc_gss_client_state { 1212e92ac56SJamie Gritton CLIENT_NEW, /* still authenticating */ 1222e92ac56SJamie Gritton CLIENT_ESTABLISHED, /* context established */ 1232e92ac56SJamie Gritton CLIENT_STALE /* garbage to collect */ 124d4468577SJamie Gritton }; 125a9148abdSDoug Rabson 1262e92ac56SJamie Gritton #define SVC_RPC_GSS_SEQWINDOW 128 1272e92ac56SJamie Gritton 1282e92ac56SJamie Gritton struct svc_rpc_gss_clientid { 1292e92ac56SJamie Gritton unsigned long ci_hostid; 1302e92ac56SJamie Gritton uint32_t ci_boottime; 1312e92ac56SJamie Gritton uint32_t ci_id; 1322e92ac56SJamie Gritton }; 1332e92ac56SJamie Gritton 1342e92ac56SJamie Gritton struct svc_rpc_gss_client { 1352e92ac56SJamie Gritton TAILQ_ENTRY(svc_rpc_gss_client) cl_link; 1362e92ac56SJamie Gritton TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink; 1372e92ac56SJamie Gritton volatile u_int cl_refs; 1382e92ac56SJamie Gritton struct sx cl_lock; 1392e92ac56SJamie Gritton struct svc_rpc_gss_clientid cl_id; 1402e92ac56SJamie Gritton time_t cl_expiration; /* when to gc */ 1412e92ac56SJamie Gritton enum svc_rpc_gss_client_state cl_state; /* client state */ 1422e92ac56SJamie Gritton bool_t cl_locked; /* fixed service+qop */ 1432e92ac56SJamie Gritton gss_ctx_id_t cl_ctx; /* context id */ 1442e92ac56SJamie Gritton gss_cred_id_t cl_creds; /* delegated creds */ 1452e92ac56SJamie Gritton gss_name_t cl_cname; /* client name */ 1462e92ac56SJamie Gritton struct svc_rpc_gss_svc_name *cl_sname; /* server name used */ 1472e92ac56SJamie Gritton rpc_gss_rawcred_t cl_rawcred; /* raw credentials */ 1482e92ac56SJamie Gritton rpc_gss_ucred_t cl_ucred; /* unix-style credentials */ 1492e92ac56SJamie Gritton struct ucred *cl_cred; /* kernel-style credentials */ 1502e92ac56SJamie Gritton int cl_rpcflavor; /* RPC pseudo sec flavor */ 1512e92ac56SJamie Gritton bool_t cl_done_callback; /* TRUE after call */ 1522e92ac56SJamie Gritton void *cl_cookie; /* user cookie from callback */ 1535eff3ec6SJosh Paetzel gid_t cl_gid_storage[NGROUPS]; 1542e92ac56SJamie Gritton gss_OID cl_mech; /* mechanism */ 1552e92ac56SJamie Gritton gss_qop_t cl_qop; /* quality of protection */ 1562e92ac56SJamie Gritton uint32_t cl_seqlast; /* sequence window origin */ 1572e92ac56SJamie Gritton uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */ 1582e92ac56SJamie Gritton }; 1592e92ac56SJamie Gritton TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client); 1602e92ac56SJamie Gritton 161a9148abdSDoug Rabson /* 1622e92ac56SJamie Gritton * This structure holds enough information to unwrap arguments or wrap 1632e92ac56SJamie Gritton * results for a given request. We use the rq_clntcred area for this 1642e92ac56SJamie Gritton * (which is a per-request buffer). 1652e92ac56SJamie Gritton */ 1662e92ac56SJamie Gritton struct svc_rpc_gss_cookedcred { 1672e92ac56SJamie Gritton struct svc_rpc_gss_client *cc_client; 1682e92ac56SJamie Gritton rpc_gss_service_t cc_service; 1692e92ac56SJamie Gritton uint32_t cc_seq; 1702e92ac56SJamie Gritton }; 1712e92ac56SJamie Gritton 1722e92ac56SJamie Gritton #define CLIENT_HASH_SIZE 256 173e998861bSEdward Tomasz Napierala #define CLIENT_MAX 1024 174b329fb28SEdward Tomasz Napierala u_int svc_rpc_gss_client_max = CLIENT_MAX; 17552eb4995SEdward Tomasz Napierala u_int svc_rpc_gss_client_hash_size = CLIENT_HASH_SIZE; 176b329fb28SEdward Tomasz Napierala 1777029da5cSPawel Biernacki SYSCTL_NODE(_kern, OID_AUTO, rpc, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 1787029da5cSPawel Biernacki "RPC"); 1797029da5cSPawel Biernacki SYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 1807029da5cSPawel Biernacki "GSS"); 181b329fb28SEdward Tomasz Napierala 182b329fb28SEdward Tomasz Napierala SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW, 183b329fb28SEdward Tomasz Napierala &svc_rpc_gss_client_max, 0, 184b329fb28SEdward Tomasz Napierala "Max number of rpc-gss clients"); 185b329fb28SEdward Tomasz Napierala 18652eb4995SEdward Tomasz Napierala SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_hash, CTLFLAG_RDTUN, 18752eb4995SEdward Tomasz Napierala &svc_rpc_gss_client_hash_size, 0, 18852eb4995SEdward Tomasz Napierala "Size of rpc-gss client hash table"); 18952eb4995SEdward Tomasz Napierala 190841c3621SRick Macklem static u_int svc_rpc_gss_lifetime_max = 0; 19104cb0c38SRick Macklem SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, lifetime_max, CTLFLAG_RW, 19204cb0c38SRick Macklem &svc_rpc_gss_lifetime_max, 0, 19304cb0c38SRick Macklem "Maximum lifetime (seconds) of rpc-gss clients"); 19404cb0c38SRick Macklem 195b329fb28SEdward Tomasz Napierala static u_int svc_rpc_gss_client_count; 196b329fb28SEdward Tomasz Napierala SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD, 197b329fb28SEdward Tomasz Napierala &svc_rpc_gss_client_count, 0, 198b329fb28SEdward Tomasz Napierala "Number of rpc-gss clients"); 199b329fb28SEdward Tomasz Napierala 20052eb4995SEdward Tomasz Napierala struct svc_rpc_gss_client_list *svc_rpc_gss_client_hash; 2012e92ac56SJamie Gritton struct svc_rpc_gss_client_list svc_rpc_gss_clients; 2022e92ac56SJamie Gritton static uint32_t svc_rpc_gss_next_clientid = 1; 2032e92ac56SJamie Gritton 2042e92ac56SJamie Gritton static void 2052e92ac56SJamie Gritton svc_rpc_gss_init(void *arg) 2062e92ac56SJamie Gritton { 2072e92ac56SJamie Gritton int i; 2082e92ac56SJamie Gritton 20952eb4995SEdward Tomasz Napierala svc_rpc_gss_client_hash = mem_alloc(sizeof(struct svc_rpc_gss_client_list) * svc_rpc_gss_client_hash_size); 21052eb4995SEdward Tomasz Napierala for (i = 0; i < svc_rpc_gss_client_hash_size; i++) 2112e92ac56SJamie Gritton TAILQ_INIT(&svc_rpc_gss_client_hash[i]); 2122e92ac56SJamie Gritton TAILQ_INIT(&svc_rpc_gss_clients); 2132e92ac56SJamie Gritton svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred); 2142e92ac56SJamie Gritton sx_init(&svc_rpc_gss_lock, "gsslock"); 2152e92ac56SJamie Gritton } 2162e92ac56SJamie Gritton SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL); 2172e92ac56SJamie Gritton 2182e92ac56SJamie Gritton bool_t 2192e92ac56SJamie Gritton rpc_gss_set_callback(rpc_gss_callback_t *cb) 2202e92ac56SJamie Gritton { 2212e92ac56SJamie Gritton struct svc_rpc_gss_callback *scb; 2222e92ac56SJamie Gritton 2232e92ac56SJamie Gritton scb = mem_alloc(sizeof(struct svc_rpc_gss_callback)); 2242e92ac56SJamie Gritton if (!scb) { 2252e92ac56SJamie Gritton _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 2262e92ac56SJamie Gritton return (FALSE); 2272e92ac56SJamie Gritton } 2282e92ac56SJamie Gritton scb->cb_callback = *cb; 2292e92ac56SJamie Gritton sx_xlock(&svc_rpc_gss_lock); 2302e92ac56SJamie Gritton SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link); 2312e92ac56SJamie Gritton sx_xunlock(&svc_rpc_gss_lock); 2322e92ac56SJamie Gritton 2332e92ac56SJamie Gritton return (TRUE); 2342e92ac56SJamie Gritton } 2352e92ac56SJamie Gritton 2362e92ac56SJamie Gritton void 2372e92ac56SJamie Gritton rpc_gss_clear_callback(rpc_gss_callback_t *cb) 2382e92ac56SJamie Gritton { 2392e92ac56SJamie Gritton struct svc_rpc_gss_callback *scb; 2402e92ac56SJamie Gritton 2412e92ac56SJamie Gritton sx_xlock(&svc_rpc_gss_lock); 2422e92ac56SJamie Gritton SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 2432e92ac56SJamie Gritton if (scb->cb_callback.program == cb->program 2442e92ac56SJamie Gritton && scb->cb_callback.version == cb->version 2452e92ac56SJamie Gritton && scb->cb_callback.callback == cb->callback) { 2462e92ac56SJamie Gritton SLIST_REMOVE(&svc_rpc_gss_callbacks, scb, 2472e92ac56SJamie Gritton svc_rpc_gss_callback, cb_link); 2482e92ac56SJamie Gritton sx_xunlock(&svc_rpc_gss_lock); 2492e92ac56SJamie Gritton mem_free(scb, sizeof(*scb)); 2502e92ac56SJamie Gritton return; 2512e92ac56SJamie Gritton } 2522e92ac56SJamie Gritton } 2532e92ac56SJamie Gritton sx_xunlock(&svc_rpc_gss_lock); 2542e92ac56SJamie Gritton } 2552e92ac56SJamie Gritton 2562e92ac56SJamie Gritton static bool_t 2572e92ac56SJamie Gritton rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname) 2582e92ac56SJamie Gritton { 2592e92ac56SJamie Gritton OM_uint32 maj_stat, min_stat; 2602e92ac56SJamie Gritton gss_buffer_desc namebuf; 2612e92ac56SJamie Gritton gss_name_t name; 2622e92ac56SJamie Gritton gss_OID_set_desc oid_set; 2632e92ac56SJamie Gritton 2642e92ac56SJamie Gritton oid_set.count = 1; 2652e92ac56SJamie Gritton oid_set.elements = sname->sn_mech; 2662e92ac56SJamie Gritton 2672e92ac56SJamie Gritton namebuf.value = (void *) sname->sn_principal; 2682e92ac56SJamie Gritton namebuf.length = strlen(sname->sn_principal); 2692e92ac56SJamie Gritton 2702e92ac56SJamie Gritton maj_stat = gss_import_name(&min_stat, &namebuf, 2712e92ac56SJamie Gritton GSS_C_NT_HOSTBASED_SERVICE, &name); 2722e92ac56SJamie Gritton if (maj_stat != GSS_S_COMPLETE) 2732e92ac56SJamie Gritton return (FALSE); 2742e92ac56SJamie Gritton 2752e92ac56SJamie Gritton if (sname->sn_cred != GSS_C_NO_CREDENTIAL) 2762e92ac56SJamie Gritton gss_release_cred(&min_stat, &sname->sn_cred); 2772e92ac56SJamie Gritton 2782e92ac56SJamie Gritton maj_stat = gss_acquire_cred(&min_stat, name, 2792e92ac56SJamie Gritton sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred, 2802e92ac56SJamie Gritton NULL, NULL); 2812e92ac56SJamie Gritton if (maj_stat != GSS_S_COMPLETE) { 2822e92ac56SJamie Gritton gss_release_name(&min_stat, &name); 2832e92ac56SJamie Gritton return (FALSE); 2842e92ac56SJamie Gritton } 2852e92ac56SJamie Gritton gss_release_name(&min_stat, &name); 2862e92ac56SJamie Gritton 2872e92ac56SJamie Gritton return (TRUE); 2882e92ac56SJamie Gritton } 2892e92ac56SJamie Gritton 2902e92ac56SJamie Gritton bool_t 2912e92ac56SJamie Gritton rpc_gss_set_svc_name(const char *principal, const char *mechanism, 2922e92ac56SJamie Gritton u_int req_time, u_int program, u_int version) 2932e92ac56SJamie Gritton { 2942e92ac56SJamie Gritton struct svc_rpc_gss_svc_name *sname; 2952e92ac56SJamie Gritton gss_OID mech_oid; 2962e92ac56SJamie Gritton 2972e92ac56SJamie Gritton if (!rpc_gss_mech_to_oid(mechanism, &mech_oid)) 2982e92ac56SJamie Gritton return (FALSE); 2992e92ac56SJamie Gritton 3002e92ac56SJamie Gritton sname = mem_alloc(sizeof(*sname)); 3012e92ac56SJamie Gritton if (!sname) 3022e92ac56SJamie Gritton return (FALSE); 3032e92ac56SJamie Gritton sname->sn_principal = strdup(principal, M_RPC); 3042e92ac56SJamie Gritton sname->sn_mech = mech_oid; 3052e92ac56SJamie Gritton sname->sn_req_time = req_time; 3062e92ac56SJamie Gritton sname->sn_cred = GSS_C_NO_CREDENTIAL; 3072e92ac56SJamie Gritton sname->sn_program = program; 3082e92ac56SJamie Gritton sname->sn_version = version; 3092e92ac56SJamie Gritton 3102e92ac56SJamie Gritton if (!rpc_gss_acquire_svc_cred(sname)) { 3112e92ac56SJamie Gritton free(sname->sn_principal, M_RPC); 3122e92ac56SJamie Gritton mem_free(sname, sizeof(*sname)); 3132e92ac56SJamie Gritton return (FALSE); 3142e92ac56SJamie Gritton } 3152e92ac56SJamie Gritton 3162e92ac56SJamie Gritton sx_xlock(&svc_rpc_gss_lock); 3172e92ac56SJamie Gritton SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link); 3182e92ac56SJamie Gritton sx_xunlock(&svc_rpc_gss_lock); 3192e92ac56SJamie Gritton 3202e92ac56SJamie Gritton return (TRUE); 3212e92ac56SJamie Gritton } 3222e92ac56SJamie Gritton 3232e92ac56SJamie Gritton void 3242e92ac56SJamie Gritton rpc_gss_clear_svc_name(u_int program, u_int version) 3252e92ac56SJamie Gritton { 3262e92ac56SJamie Gritton OM_uint32 min_stat; 3272e92ac56SJamie Gritton struct svc_rpc_gss_svc_name *sname; 3282e92ac56SJamie Gritton 3292e92ac56SJamie Gritton sx_xlock(&svc_rpc_gss_lock); 3302e92ac56SJamie Gritton SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 3312e92ac56SJamie Gritton if (sname->sn_program == program 3322e92ac56SJamie Gritton && sname->sn_version == version) { 3332e92ac56SJamie Gritton SLIST_REMOVE(&svc_rpc_gss_svc_names, sname, 3342e92ac56SJamie Gritton svc_rpc_gss_svc_name, sn_link); 3352e92ac56SJamie Gritton sx_xunlock(&svc_rpc_gss_lock); 3362e92ac56SJamie Gritton gss_release_cred(&min_stat, &sname->sn_cred); 3372e92ac56SJamie Gritton free(sname->sn_principal, M_RPC); 3382e92ac56SJamie Gritton mem_free(sname, sizeof(*sname)); 3392e92ac56SJamie Gritton return; 3402e92ac56SJamie Gritton } 3412e92ac56SJamie Gritton } 3422e92ac56SJamie Gritton sx_xunlock(&svc_rpc_gss_lock); 3432e92ac56SJamie Gritton } 3442e92ac56SJamie Gritton 3452e92ac56SJamie Gritton bool_t 3462e92ac56SJamie Gritton rpc_gss_get_principal_name(rpc_gss_principal_t *principal, 3472e92ac56SJamie Gritton const char *mech, const char *name, const char *node, const char *domain) 3482e92ac56SJamie Gritton { 3492e92ac56SJamie Gritton OM_uint32 maj_stat, min_stat; 3502e92ac56SJamie Gritton gss_OID mech_oid; 3512e92ac56SJamie Gritton size_t namelen; 3522e92ac56SJamie Gritton gss_buffer_desc buf; 3532e92ac56SJamie Gritton gss_name_t gss_name, gss_mech_name; 3542e92ac56SJamie Gritton rpc_gss_principal_t result; 3552e92ac56SJamie Gritton 3562e92ac56SJamie Gritton if (!rpc_gss_mech_to_oid(mech, &mech_oid)) 3572e92ac56SJamie Gritton return (FALSE); 3582e92ac56SJamie Gritton 3592e92ac56SJamie Gritton /* 3602e92ac56SJamie Gritton * Construct a gss_buffer containing the full name formatted 3612e92ac56SJamie Gritton * as "name/node@domain" where node and domain are optional. 3622e92ac56SJamie Gritton */ 363e3081f7eSConrad Meyer namelen = strlen(name) + 1; 3642e92ac56SJamie Gritton if (node) { 3652e92ac56SJamie Gritton namelen += strlen(node) + 1; 3662e92ac56SJamie Gritton } 3672e92ac56SJamie Gritton if (domain) { 3682e92ac56SJamie Gritton namelen += strlen(domain) + 1; 3692e92ac56SJamie Gritton } 3702e92ac56SJamie Gritton 3712e92ac56SJamie Gritton buf.value = mem_alloc(namelen); 3722e92ac56SJamie Gritton buf.length = namelen; 3732e92ac56SJamie Gritton strcpy((char *) buf.value, name); 3742e92ac56SJamie Gritton if (node) { 3752e92ac56SJamie Gritton strcat((char *) buf.value, "/"); 3762e92ac56SJamie Gritton strcat((char *) buf.value, node); 3772e92ac56SJamie Gritton } 3782e92ac56SJamie Gritton if (domain) { 3792e92ac56SJamie Gritton strcat((char *) buf.value, "@"); 3802e92ac56SJamie Gritton strcat((char *) buf.value, domain); 3812e92ac56SJamie Gritton } 3822e92ac56SJamie Gritton 3832e92ac56SJamie Gritton /* 3842e92ac56SJamie Gritton * Convert that to a gss_name_t and then convert that to a 3852e92ac56SJamie Gritton * mechanism name in the selected mechanism. 3862e92ac56SJamie Gritton */ 3872e92ac56SJamie Gritton maj_stat = gss_import_name(&min_stat, &buf, 3882e92ac56SJamie Gritton GSS_C_NT_USER_NAME, &gss_name); 3892e92ac56SJamie Gritton mem_free(buf.value, buf.length); 3902e92ac56SJamie Gritton if (maj_stat != GSS_S_COMPLETE) { 3912e92ac56SJamie Gritton rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat); 3922e92ac56SJamie Gritton return (FALSE); 3932e92ac56SJamie Gritton } 3942e92ac56SJamie Gritton maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid, 3952e92ac56SJamie Gritton &gss_mech_name); 3962e92ac56SJamie Gritton if (maj_stat != GSS_S_COMPLETE) { 3972e92ac56SJamie Gritton rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat, 3982e92ac56SJamie Gritton min_stat); 3992e92ac56SJamie Gritton gss_release_name(&min_stat, &gss_name); 4002e92ac56SJamie Gritton return (FALSE); 4012e92ac56SJamie Gritton } 4022e92ac56SJamie Gritton gss_release_name(&min_stat, &gss_name); 4032e92ac56SJamie Gritton 4042e92ac56SJamie Gritton /* 4052e92ac56SJamie Gritton * Export the mechanism name and use that to construct the 4062e92ac56SJamie Gritton * rpc_gss_principal_t result. 4072e92ac56SJamie Gritton */ 4082e92ac56SJamie Gritton maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf); 4092e92ac56SJamie Gritton if (maj_stat != GSS_S_COMPLETE) { 4102e92ac56SJamie Gritton rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat); 4112e92ac56SJamie Gritton gss_release_name(&min_stat, &gss_mech_name); 4122e92ac56SJamie Gritton return (FALSE); 4132e92ac56SJamie Gritton } 4142e92ac56SJamie Gritton gss_release_name(&min_stat, &gss_mech_name); 4152e92ac56SJamie Gritton 4162e92ac56SJamie Gritton result = mem_alloc(sizeof(int) + buf.length); 4172e92ac56SJamie Gritton if (!result) { 4182e92ac56SJamie Gritton gss_release_buffer(&min_stat, &buf); 4192e92ac56SJamie Gritton return (FALSE); 4202e92ac56SJamie Gritton } 4212e92ac56SJamie Gritton result->len = buf.length; 4222e92ac56SJamie Gritton memcpy(result->name, buf.value, buf.length); 4232e92ac56SJamie Gritton gss_release_buffer(&min_stat, &buf); 4242e92ac56SJamie Gritton 4252e92ac56SJamie Gritton *principal = result; 4262e92ac56SJamie Gritton return (TRUE); 4272e92ac56SJamie Gritton } 4282e92ac56SJamie Gritton 4292e92ac56SJamie Gritton bool_t 4302e92ac56SJamie Gritton rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred, 4312e92ac56SJamie Gritton rpc_gss_ucred_t **ucred, void **cookie) 4322e92ac56SJamie Gritton { 4332e92ac56SJamie Gritton struct svc_rpc_gss_cookedcred *cc; 4342e92ac56SJamie Gritton struct svc_rpc_gss_client *client; 4352e92ac56SJamie Gritton 4362e92ac56SJamie Gritton if (req->rq_cred.oa_flavor != RPCSEC_GSS) 4372e92ac56SJamie Gritton return (FALSE); 4382e92ac56SJamie Gritton 4392e92ac56SJamie Gritton cc = req->rq_clntcred; 4402e92ac56SJamie Gritton client = cc->cc_client; 4412e92ac56SJamie Gritton if (rcred) 4422e92ac56SJamie Gritton *rcred = &client->cl_rawcred; 4432e92ac56SJamie Gritton if (ucred) 4442e92ac56SJamie Gritton *ucred = &client->cl_ucred; 4452e92ac56SJamie Gritton if (cookie) 4462e92ac56SJamie Gritton *cookie = client->cl_cookie; 4472e92ac56SJamie Gritton return (TRUE); 4482e92ac56SJamie Gritton } 4492e92ac56SJamie Gritton 4502e92ac56SJamie Gritton /* 4512e92ac56SJamie Gritton * This simpler interface is used by svc_getcred to copy the cred data 4522e92ac56SJamie Gritton * into a kernel cred structure. 453a9148abdSDoug Rabson */ 454a9148abdSDoug Rabson static int 4552e92ac56SJamie Gritton rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp) 456a9148abdSDoug Rabson { 4572e92ac56SJamie Gritton struct ucred *cr; 4582e92ac56SJamie Gritton struct svc_rpc_gss_cookedcred *cc; 4592e92ac56SJamie Gritton struct svc_rpc_gss_client *client; 4602e92ac56SJamie Gritton rpc_gss_ucred_t *uc; 461a9148abdSDoug Rabson 4622e92ac56SJamie Gritton if (req->rq_cred.oa_flavor != RPCSEC_GSS) 4632e92ac56SJamie Gritton return (FALSE); 4642e92ac56SJamie Gritton 4652e92ac56SJamie Gritton cc = req->rq_clntcred; 4662e92ac56SJamie Gritton client = cc->cc_client; 4672e92ac56SJamie Gritton 4682e92ac56SJamie Gritton if (flavorp) 4692e92ac56SJamie Gritton *flavorp = client->cl_rpcflavor; 4702e92ac56SJamie Gritton 4712e92ac56SJamie Gritton if (client->cl_cred) { 4722e92ac56SJamie Gritton *crp = crhold(client->cl_cred); 4732e92ac56SJamie Gritton return (TRUE); 474a9148abdSDoug Rabson } 475a9148abdSDoug Rabson 4762e92ac56SJamie Gritton uc = &client->cl_ucred; 4772e92ac56SJamie Gritton cr = client->cl_cred = crget(); 4782e92ac56SJamie Gritton cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid; 4792e92ac56SJamie Gritton cr->cr_rgid = cr->cr_svgid = uc->gid; 4802e92ac56SJamie Gritton crsetgroups(cr, uc->gidlen, uc->gidlist); 481c408f06bSJamie Gritton cr->cr_prison = &prison0; 482c408f06bSJamie Gritton prison_hold(cr->cr_prison); 4832e92ac56SJamie Gritton *crp = crhold(cr); 4842e92ac56SJamie Gritton 4852e92ac56SJamie Gritton return (TRUE); 486a9148abdSDoug Rabson } 4872e92ac56SJamie Gritton 4882e92ac56SJamie Gritton int 4892e92ac56SJamie Gritton rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len) 4902e92ac56SJamie Gritton { 4912e92ac56SJamie Gritton struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred; 4922e92ac56SJamie Gritton struct svc_rpc_gss_client *client = cc->cc_client; 4932e92ac56SJamie Gritton int want_conf; 4942e92ac56SJamie Gritton OM_uint32 max; 4952e92ac56SJamie Gritton OM_uint32 maj_stat, min_stat; 4962e92ac56SJamie Gritton int result; 4972e92ac56SJamie Gritton 4982e92ac56SJamie Gritton switch (client->cl_rawcred.service) { 4992e92ac56SJamie Gritton case rpc_gss_svc_none: 5002e92ac56SJamie Gritton return (max_tp_unit_len); 5012e92ac56SJamie Gritton break; 5022e92ac56SJamie Gritton 5032e92ac56SJamie Gritton case rpc_gss_svc_default: 5042e92ac56SJamie Gritton case rpc_gss_svc_integrity: 5052e92ac56SJamie Gritton want_conf = FALSE; 5062e92ac56SJamie Gritton break; 5072e92ac56SJamie Gritton 5082e92ac56SJamie Gritton case rpc_gss_svc_privacy: 5092e92ac56SJamie Gritton want_conf = TRUE; 5102e92ac56SJamie Gritton break; 5112e92ac56SJamie Gritton 5122e92ac56SJamie Gritton default: 513a9148abdSDoug Rabson return (0); 514a9148abdSDoug Rabson } 515a9148abdSDoug Rabson 5162e92ac56SJamie Gritton maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf, 5172e92ac56SJamie Gritton client->cl_qop, max_tp_unit_len, &max); 5182e92ac56SJamie Gritton 5192e92ac56SJamie Gritton if (maj_stat == GSS_S_COMPLETE) { 5202e92ac56SJamie Gritton result = (int) max; 5212e92ac56SJamie Gritton if (result < 0) 5222e92ac56SJamie Gritton result = 0; 5232e92ac56SJamie Gritton return (result); 5242e92ac56SJamie Gritton } else { 5252e92ac56SJamie Gritton rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech, 5262e92ac56SJamie Gritton maj_stat, min_stat); 5272e92ac56SJamie Gritton return (0); 5282e92ac56SJamie Gritton } 5292e92ac56SJamie Gritton } 5302e92ac56SJamie Gritton 5312e92ac56SJamie Gritton static struct svc_rpc_gss_client * 5322e92ac56SJamie Gritton svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id) 5332e92ac56SJamie Gritton { 5342e92ac56SJamie Gritton struct svc_rpc_gss_client *client; 5352e92ac56SJamie Gritton struct svc_rpc_gss_client_list *list; 536584b675eSKonstantin Belousov struct timeval boottime; 5372e92ac56SJamie Gritton unsigned long hostid; 5382e92ac56SJamie Gritton 5392e92ac56SJamie Gritton rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id); 5402e92ac56SJamie Gritton 5412e92ac56SJamie Gritton getcredhostid(curthread->td_ucred, &hostid); 542584b675eSKonstantin Belousov getboottime(&boottime); 5432e92ac56SJamie Gritton if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec) 5442e92ac56SJamie Gritton return (NULL); 5452e92ac56SJamie Gritton 54652eb4995SEdward Tomasz Napierala list = &svc_rpc_gss_client_hash[id->ci_id % svc_rpc_gss_client_hash_size]; 5472e92ac56SJamie Gritton sx_xlock(&svc_rpc_gss_lock); 5482e92ac56SJamie Gritton TAILQ_FOREACH(client, list, cl_link) { 5492e92ac56SJamie Gritton if (client->cl_id.ci_id == id->ci_id) { 5502e92ac56SJamie Gritton /* 5512e92ac56SJamie Gritton * Move this client to the front of the LRU 5522e92ac56SJamie Gritton * list. 5532e92ac56SJamie Gritton */ 5542e92ac56SJamie Gritton TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 5552e92ac56SJamie Gritton TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, 5562e92ac56SJamie Gritton cl_alllink); 5572e92ac56SJamie Gritton refcount_acquire(&client->cl_refs); 5582e92ac56SJamie Gritton break; 5592e92ac56SJamie Gritton } 5602e92ac56SJamie Gritton } 5612e92ac56SJamie Gritton sx_xunlock(&svc_rpc_gss_lock); 5622e92ac56SJamie Gritton 5632e92ac56SJamie Gritton return (client); 5642e92ac56SJamie Gritton } 5652e92ac56SJamie Gritton 5662e92ac56SJamie Gritton static struct svc_rpc_gss_client * 5672e92ac56SJamie Gritton svc_rpc_gss_create_client(void) 5682e92ac56SJamie Gritton { 5692e92ac56SJamie Gritton struct svc_rpc_gss_client *client; 5702e92ac56SJamie Gritton struct svc_rpc_gss_client_list *list; 571584b675eSKonstantin Belousov struct timeval boottime; 5722e92ac56SJamie Gritton unsigned long hostid; 5732e92ac56SJamie Gritton 5742e92ac56SJamie Gritton rpc_gss_log_debug("in svc_rpc_gss_create_client()"); 5752e92ac56SJamie Gritton 5762e92ac56SJamie Gritton client = mem_alloc(sizeof(struct svc_rpc_gss_client)); 5772e92ac56SJamie Gritton memset(client, 0, sizeof(struct svc_rpc_gss_client)); 57814068959SRick Macklem 57914068959SRick Macklem /* 58014068959SRick Macklem * Set the initial value of cl_refs to two. One for the caller 58114068959SRick Macklem * and the other to hold onto the client structure until it expires. 58214068959SRick Macklem */ 583b0e14530SRick Macklem refcount_init(&client->cl_refs, 2); 5842e92ac56SJamie Gritton sx_init(&client->cl_lock, "GSS-client"); 5852e92ac56SJamie Gritton getcredhostid(curthread->td_ucred, &hostid); 5862e92ac56SJamie Gritton client->cl_id.ci_hostid = hostid; 587584b675eSKonstantin Belousov getboottime(&boottime); 5882e92ac56SJamie Gritton client->cl_id.ci_boottime = boottime.tv_sec; 5892e92ac56SJamie Gritton client->cl_id.ci_id = svc_rpc_gss_next_clientid++; 5902e92ac56SJamie Gritton 5912e92ac56SJamie Gritton /* 5922e92ac56SJamie Gritton * Start the client off with a short expiration time. We will 5932e92ac56SJamie Gritton * try to get a saner value from the client creds later. 5942e92ac56SJamie Gritton */ 5952e92ac56SJamie Gritton client->cl_state = CLIENT_NEW; 5962e92ac56SJamie Gritton client->cl_locked = FALSE; 5972e92ac56SJamie Gritton client->cl_expiration = time_uptime + 5*60; 5982e92ac56SJamie Gritton 599b0e14530SRick Macklem list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size]; 600b0e14530SRick Macklem sx_xlock(&svc_rpc_gss_lock); 601b0e14530SRick Macklem TAILQ_INSERT_HEAD(list, client, cl_link); 602b0e14530SRick Macklem TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink); 603b0e14530SRick Macklem svc_rpc_gss_client_count++; 604b0e14530SRick Macklem sx_xunlock(&svc_rpc_gss_lock); 6052e92ac56SJamie Gritton return (client); 6062e92ac56SJamie Gritton } 6072e92ac56SJamie Gritton 6082e92ac56SJamie Gritton static void 6092e92ac56SJamie Gritton svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client) 6102e92ac56SJamie Gritton { 6112e92ac56SJamie Gritton OM_uint32 min_stat; 6122e92ac56SJamie Gritton 6132e92ac56SJamie Gritton rpc_gss_log_debug("in svc_rpc_gss_destroy_client()"); 6142e92ac56SJamie Gritton 6152e92ac56SJamie Gritton if (client->cl_ctx) 6162e92ac56SJamie Gritton gss_delete_sec_context(&min_stat, 6172e92ac56SJamie Gritton &client->cl_ctx, GSS_C_NO_BUFFER); 6182e92ac56SJamie Gritton 6192e92ac56SJamie Gritton if (client->cl_cname) 6202e92ac56SJamie Gritton gss_release_name(&min_stat, &client->cl_cname); 6212e92ac56SJamie Gritton 6222e92ac56SJamie Gritton if (client->cl_rawcred.client_principal) 6232e92ac56SJamie Gritton mem_free(client->cl_rawcred.client_principal, 6242e92ac56SJamie Gritton sizeof(*client->cl_rawcred.client_principal) 6252e92ac56SJamie Gritton + client->cl_rawcred.client_principal->len); 6262e92ac56SJamie Gritton 6272e92ac56SJamie Gritton if (client->cl_cred) 6282e92ac56SJamie Gritton crfree(client->cl_cred); 6292e92ac56SJamie Gritton 6302e92ac56SJamie Gritton sx_destroy(&client->cl_lock); 6312e92ac56SJamie Gritton mem_free(client, sizeof(*client)); 6322e92ac56SJamie Gritton } 6332e92ac56SJamie Gritton 6342e92ac56SJamie Gritton /* 6352e92ac56SJamie Gritton * Drop a reference to a client and free it if that was the last reference. 6362e92ac56SJamie Gritton */ 6372e92ac56SJamie Gritton static void 6382e92ac56SJamie Gritton svc_rpc_gss_release_client(struct svc_rpc_gss_client *client) 6392e92ac56SJamie Gritton { 6402e92ac56SJamie Gritton 6412e92ac56SJamie Gritton if (!refcount_release(&client->cl_refs)) 6422e92ac56SJamie Gritton return; 6432e92ac56SJamie Gritton svc_rpc_gss_destroy_client(client); 6442e92ac56SJamie Gritton } 6452e92ac56SJamie Gritton 6462e92ac56SJamie Gritton /* 6475328a32eSRick Macklem * Remove a client from our global lists. 6485328a32eSRick Macklem * Must be called with svc_rpc_gss_lock held. 6495328a32eSRick Macklem */ 6505328a32eSRick Macklem static void 6515328a32eSRick Macklem svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client) 6525328a32eSRick Macklem { 6535328a32eSRick Macklem struct svc_rpc_gss_client_list *list; 6545328a32eSRick Macklem 6555328a32eSRick Macklem sx_assert(&svc_rpc_gss_lock, SX_XLOCKED); 65652eb4995SEdward Tomasz Napierala list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size]; 6575328a32eSRick Macklem TAILQ_REMOVE(list, client, cl_link); 6585328a32eSRick Macklem TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 6595328a32eSRick Macklem svc_rpc_gss_client_count--; 6605328a32eSRick Macklem } 6615328a32eSRick Macklem 6625328a32eSRick Macklem /* 6632e92ac56SJamie Gritton * Remove a client from our global lists and free it if we can. 6642e92ac56SJamie Gritton */ 6652e92ac56SJamie Gritton static void 6662e92ac56SJamie Gritton svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client) 6672e92ac56SJamie Gritton { 6682e92ac56SJamie Gritton struct svc_rpc_gss_client_list *list; 6695328a32eSRick Macklem struct svc_rpc_gss_client *tclient; 6702e92ac56SJamie Gritton 67152eb4995SEdward Tomasz Napierala list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size]; 6722e92ac56SJamie Gritton sx_xlock(&svc_rpc_gss_lock); 6735328a32eSRick Macklem TAILQ_FOREACH(tclient, list, cl_link) { 6745328a32eSRick Macklem /* 6755328a32eSRick Macklem * Make sure this client has not already been removed 6765328a32eSRick Macklem * from the lists by svc_rpc_gss_forget_client() or 677cbf06947SRick Macklem * svc_rpc_gss_forget_client_locked(). 6785328a32eSRick Macklem */ 6795328a32eSRick Macklem if (client == tclient) { 6805328a32eSRick Macklem svc_rpc_gss_forget_client_locked(client); 6812e92ac56SJamie Gritton sx_xunlock(&svc_rpc_gss_lock); 6822e92ac56SJamie Gritton svc_rpc_gss_release_client(client); 6835328a32eSRick Macklem return; 6845328a32eSRick Macklem } 6855328a32eSRick Macklem } 6865328a32eSRick Macklem sx_xunlock(&svc_rpc_gss_lock); 6872e92ac56SJamie Gritton } 6882e92ac56SJamie Gritton 6892e92ac56SJamie Gritton static void 6902e92ac56SJamie Gritton svc_rpc_gss_timeout_clients(void) 6912e92ac56SJamie Gritton { 6922e92ac56SJamie Gritton struct svc_rpc_gss_client *client; 6932e92ac56SJamie Gritton time_t now = time_uptime; 6942e92ac56SJamie Gritton 6952e92ac56SJamie Gritton rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()"); 6962e92ac56SJamie Gritton 6972e92ac56SJamie Gritton /* 6982e92ac56SJamie Gritton * First enforce the max client limit. We keep 6992e92ac56SJamie Gritton * svc_rpc_gss_clients in LRU order. 7002e92ac56SJamie Gritton */ 7015328a32eSRick Macklem sx_xlock(&svc_rpc_gss_lock); 7025328a32eSRick Macklem client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list); 703b329fb28SEdward Tomasz Napierala while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) { 7045328a32eSRick Macklem svc_rpc_gss_forget_client_locked(client); 7055328a32eSRick Macklem sx_xunlock(&svc_rpc_gss_lock); 7065328a32eSRick Macklem svc_rpc_gss_release_client(client); 7075328a32eSRick Macklem sx_xlock(&svc_rpc_gss_lock); 7085328a32eSRick Macklem client = TAILQ_LAST(&svc_rpc_gss_clients, 7095328a32eSRick Macklem svc_rpc_gss_client_list); 7105328a32eSRick Macklem } 7115328a32eSRick Macklem again: 7125328a32eSRick Macklem TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) { 7132e92ac56SJamie Gritton if (client->cl_state == CLIENT_STALE 7142e92ac56SJamie Gritton || now > client->cl_expiration) { 7155328a32eSRick Macklem svc_rpc_gss_forget_client_locked(client); 7165328a32eSRick Macklem sx_xunlock(&svc_rpc_gss_lock); 7172e92ac56SJamie Gritton rpc_gss_log_debug("expiring client %p", client); 7185328a32eSRick Macklem svc_rpc_gss_release_client(client); 7195328a32eSRick Macklem sx_xlock(&svc_rpc_gss_lock); 7205328a32eSRick Macklem goto again; 7212e92ac56SJamie Gritton } 7222e92ac56SJamie Gritton } 7235328a32eSRick Macklem sx_xunlock(&svc_rpc_gss_lock); 7242e92ac56SJamie Gritton } 7252e92ac56SJamie Gritton 7262e92ac56SJamie Gritton #ifdef DEBUG 7272e92ac56SJamie Gritton /* 7282e92ac56SJamie Gritton * OID<->string routines. These are uuuuugly. 7292e92ac56SJamie Gritton */ 7302e92ac56SJamie Gritton static OM_uint32 7312e92ac56SJamie Gritton gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) 7322e92ac56SJamie Gritton { 7332e92ac56SJamie Gritton char numstr[128]; 7342e92ac56SJamie Gritton unsigned long number; 7352e92ac56SJamie Gritton int numshift; 7362e92ac56SJamie Gritton size_t string_length; 7372e92ac56SJamie Gritton size_t i; 7382e92ac56SJamie Gritton unsigned char *cp; 7392e92ac56SJamie Gritton char *bp; 7402e92ac56SJamie Gritton 7412e92ac56SJamie Gritton /* Decoded according to krb5/gssapi_krb5.c */ 7422e92ac56SJamie Gritton 7432e92ac56SJamie Gritton /* First determine the size of the string */ 7442e92ac56SJamie Gritton string_length = 0; 7452e92ac56SJamie Gritton number = 0; 7462e92ac56SJamie Gritton numshift = 0; 7472e92ac56SJamie Gritton cp = (unsigned char *) oid->elements; 7482e92ac56SJamie Gritton number = (unsigned long) cp[0]; 7492e92ac56SJamie Gritton sprintf(numstr, "%ld ", number/40); 7502e92ac56SJamie Gritton string_length += strlen(numstr); 7512e92ac56SJamie Gritton sprintf(numstr, "%ld ", number%40); 7522e92ac56SJamie Gritton string_length += strlen(numstr); 7532e92ac56SJamie Gritton for (i=1; i<oid->length; i++) { 7542e92ac56SJamie Gritton if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { 7552e92ac56SJamie Gritton number = (number << 7) | (cp[i] & 0x7f); 7562e92ac56SJamie Gritton numshift += 7; 7572e92ac56SJamie Gritton } 7582e92ac56SJamie Gritton else { 7592e92ac56SJamie Gritton *minor_status = 0; 7602e92ac56SJamie Gritton return(GSS_S_FAILURE); 7612e92ac56SJamie Gritton } 7622e92ac56SJamie Gritton if ((cp[i] & 0x80) == 0) { 7632e92ac56SJamie Gritton sprintf(numstr, "%ld ", number); 7642e92ac56SJamie Gritton string_length += strlen(numstr); 7652e92ac56SJamie Gritton number = 0; 7662e92ac56SJamie Gritton numshift = 0; 7672e92ac56SJamie Gritton } 7682e92ac56SJamie Gritton } 7692e92ac56SJamie Gritton /* 7702e92ac56SJamie Gritton * If we get here, we've calculated the length of "n n n ... n ". Add 4 7712e92ac56SJamie Gritton * here for "{ " and "}\0". 7722e92ac56SJamie Gritton */ 7732e92ac56SJamie Gritton string_length += 4; 77452cab12cSRick Macklem if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) { 7752e92ac56SJamie Gritton strcpy(bp, "{ "); 7762e92ac56SJamie Gritton number = (unsigned long) cp[0]; 7772e92ac56SJamie Gritton sprintf(numstr, "%ld ", number/40); 7782e92ac56SJamie Gritton strcat(bp, numstr); 7792e92ac56SJamie Gritton sprintf(numstr, "%ld ", number%40); 7802e92ac56SJamie Gritton strcat(bp, numstr); 7812e92ac56SJamie Gritton number = 0; 7822e92ac56SJamie Gritton cp = (unsigned char *) oid->elements; 7832e92ac56SJamie Gritton for (i=1; i<oid->length; i++) { 7842e92ac56SJamie Gritton number = (number << 7) | (cp[i] & 0x7f); 7852e92ac56SJamie Gritton if ((cp[i] & 0x80) == 0) { 7862e92ac56SJamie Gritton sprintf(numstr, "%ld ", number); 7872e92ac56SJamie Gritton strcat(bp, numstr); 7882e92ac56SJamie Gritton number = 0; 7892e92ac56SJamie Gritton } 7902e92ac56SJamie Gritton } 7912e92ac56SJamie Gritton strcat(bp, "}"); 7922e92ac56SJamie Gritton oid_str->length = strlen(bp)+1; 7932e92ac56SJamie Gritton oid_str->value = (void *) bp; 7942e92ac56SJamie Gritton *minor_status = 0; 7952e92ac56SJamie Gritton return(GSS_S_COMPLETE); 7962e92ac56SJamie Gritton } 7972e92ac56SJamie Gritton *minor_status = 0; 7982e92ac56SJamie Gritton return(GSS_S_FAILURE); 799a9148abdSDoug Rabson } 800a9148abdSDoug Rabson #endif 801a9148abdSDoug Rabson 802a9148abdSDoug Rabson static void 8032e92ac56SJamie Gritton svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, 8042e92ac56SJamie Gritton const gss_name_t name) 805a9148abdSDoug Rabson { 8062e92ac56SJamie Gritton OM_uint32 maj_stat, min_stat; 8072e92ac56SJamie Gritton rpc_gss_ucred_t *uc = &client->cl_ucred; 8082e92ac56SJamie Gritton int numgroups; 809a9148abdSDoug Rabson 8102e92ac56SJamie Gritton uc->uid = 65534; 8112e92ac56SJamie Gritton uc->gid = 65534; 8122e92ac56SJamie Gritton uc->gidlist = client->cl_gid_storage; 813d4468577SJamie Gritton 8145eff3ec6SJosh Paetzel numgroups = NGROUPS; 8152e92ac56SJamie Gritton maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech, 8162e92ac56SJamie Gritton &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]); 8172e92ac56SJamie Gritton if (GSS_ERROR(maj_stat)) 8182e92ac56SJamie Gritton uc->gidlen = 0; 8192e92ac56SJamie Gritton else 8202e92ac56SJamie Gritton uc->gidlen = numgroups; 821d4468577SJamie Gritton } 822d4468577SJamie Gritton 8232e92ac56SJamie Gritton static void 8242e92ac56SJamie Gritton svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client) 825a9148abdSDoug Rabson { 8262e92ac56SJamie Gritton static gss_OID_desc krb5_mech_oid = 8272e92ac56SJamie Gritton {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; 828a9148abdSDoug Rabson 829d4468577SJamie Gritton /* 8302e92ac56SJamie Gritton * Attempt to translate mech type and service into a 8312e92ac56SJamie Gritton * 'pseudo flavor'. Hardwire in krb5 support for now. 832d4468577SJamie Gritton */ 8332e92ac56SJamie Gritton if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) { 8342e92ac56SJamie Gritton switch (client->cl_rawcred.service) { 8352e92ac56SJamie Gritton case rpc_gss_svc_default: 8362e92ac56SJamie Gritton case rpc_gss_svc_none: 8372e92ac56SJamie Gritton client->cl_rpcflavor = RPCSEC_GSS_KRB5; 8382e92ac56SJamie Gritton break; 8392e92ac56SJamie Gritton case rpc_gss_svc_integrity: 8402e92ac56SJamie Gritton client->cl_rpcflavor = RPCSEC_GSS_KRB5I; 8412e92ac56SJamie Gritton break; 8422e92ac56SJamie Gritton case rpc_gss_svc_privacy: 8432e92ac56SJamie Gritton client->cl_rpcflavor = RPCSEC_GSS_KRB5P; 8442e92ac56SJamie Gritton break; 8452e92ac56SJamie Gritton } 8462e92ac56SJamie Gritton } else { 8472e92ac56SJamie Gritton client->cl_rpcflavor = RPCSEC_GSS; 8482e92ac56SJamie Gritton } 8492e92ac56SJamie Gritton } 8502e92ac56SJamie Gritton 8512e92ac56SJamie Gritton static bool_t 8522e92ac56SJamie Gritton svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, 8532e92ac56SJamie Gritton struct svc_req *rqst, 8542e92ac56SJamie Gritton struct rpc_gss_init_res *gr, 8552e92ac56SJamie Gritton struct rpc_gss_cred *gc) 856a9148abdSDoug Rabson { 8572e92ac56SJamie Gritton gss_buffer_desc recv_tok; 8582e92ac56SJamie Gritton gss_OID mech; 8592e92ac56SJamie Gritton OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; 8602e92ac56SJamie Gritton OM_uint32 cred_lifetime; 8612e92ac56SJamie Gritton struct svc_rpc_gss_svc_name *sname; 862a9148abdSDoug Rabson 8632e92ac56SJamie Gritton rpc_gss_log_debug("in svc_rpc_gss_accept_context()"); 8642e92ac56SJamie Gritton 8652e92ac56SJamie Gritton /* Deserialize arguments. */ 8662e92ac56SJamie Gritton memset(&recv_tok, 0, sizeof(recv_tok)); 8672e92ac56SJamie Gritton 8682e92ac56SJamie Gritton if (!svc_getargs(rqst, 8692e92ac56SJamie Gritton (xdrproc_t) xdr_gss_buffer_desc, 8702e92ac56SJamie Gritton (caddr_t) &recv_tok)) { 8712e92ac56SJamie Gritton client->cl_state = CLIENT_STALE; 8722e92ac56SJamie Gritton return (FALSE); 873a9148abdSDoug Rabson } 874a9148abdSDoug Rabson 875d4468577SJamie Gritton /* 8762e92ac56SJamie Gritton * First time round, try all the server names we have until 8772e92ac56SJamie Gritton * one matches. Afterwards, stick with that one. 878d4468577SJamie Gritton */ 8792e92ac56SJamie Gritton sx_xlock(&svc_rpc_gss_lock); 8802e92ac56SJamie Gritton if (!client->cl_sname) { 8812e92ac56SJamie Gritton SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 8822e92ac56SJamie Gritton if (sname->sn_program == rqst->rq_prog 8832e92ac56SJamie Gritton && sname->sn_version == rqst->rq_vers) { 8842e92ac56SJamie Gritton retry: 8852e92ac56SJamie Gritton gr->gr_major = gss_accept_sec_context( 8862e92ac56SJamie Gritton &gr->gr_minor, 8872e92ac56SJamie Gritton &client->cl_ctx, 8882e92ac56SJamie Gritton sname->sn_cred, 8892e92ac56SJamie Gritton &recv_tok, 8902e92ac56SJamie Gritton GSS_C_NO_CHANNEL_BINDINGS, 8912e92ac56SJamie Gritton &client->cl_cname, 8922e92ac56SJamie Gritton &mech, 8932e92ac56SJamie Gritton &gr->gr_token, 8942e92ac56SJamie Gritton &ret_flags, 8952e92ac56SJamie Gritton &cred_lifetime, 8962e92ac56SJamie Gritton &client->cl_creds); 8972e92ac56SJamie Gritton if (gr->gr_major == 8982e92ac56SJamie Gritton GSS_S_CREDENTIALS_EXPIRED) { 899d4468577SJamie Gritton /* 9002e92ac56SJamie Gritton * Either our creds really did 9012e92ac56SJamie Gritton * expire or gssd was 9022e92ac56SJamie Gritton * restarted. 903d4468577SJamie Gritton */ 9042e92ac56SJamie Gritton if (rpc_gss_acquire_svc_cred(sname)) 9052e92ac56SJamie Gritton goto retry; 9062e92ac56SJamie Gritton } 9072e92ac56SJamie Gritton client->cl_sname = sname; 908d4468577SJamie Gritton break; 909d4468577SJamie Gritton } 910d4468577SJamie Gritton } 9112e92ac56SJamie Gritton if (!sname) { 9122e92ac56SJamie Gritton xdr_free((xdrproc_t) xdr_gss_buffer_desc, 9132e92ac56SJamie Gritton (char *) &recv_tok); 9142e92ac56SJamie Gritton sx_xunlock(&svc_rpc_gss_lock); 9152e92ac56SJamie Gritton return (FALSE); 916d4468577SJamie Gritton } 9172e92ac56SJamie Gritton } else { 9182e92ac56SJamie Gritton gr->gr_major = gss_accept_sec_context( 9192e92ac56SJamie Gritton &gr->gr_minor, 9202e92ac56SJamie Gritton &client->cl_ctx, 9212e92ac56SJamie Gritton client->cl_sname->sn_cred, 9222e92ac56SJamie Gritton &recv_tok, 9232e92ac56SJamie Gritton GSS_C_NO_CHANNEL_BINDINGS, 9242e92ac56SJamie Gritton &client->cl_cname, 9252e92ac56SJamie Gritton &mech, 9262e92ac56SJamie Gritton &gr->gr_token, 9272e92ac56SJamie Gritton &ret_flags, 9282e92ac56SJamie Gritton &cred_lifetime, 9292e92ac56SJamie Gritton NULL); 930d4468577SJamie Gritton } 9312e92ac56SJamie Gritton sx_xunlock(&svc_rpc_gss_lock); 932a9148abdSDoug Rabson 9332e92ac56SJamie Gritton xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); 934d4468577SJamie Gritton 935d4468577SJamie Gritton /* 9362e92ac56SJamie Gritton * If we get an error from gss_accept_sec_context, send the 9372e92ac56SJamie Gritton * reply anyway so that the client gets a chance to see what 9382e92ac56SJamie Gritton * is wrong. 939d4468577SJamie Gritton */ 9402e92ac56SJamie Gritton if (gr->gr_major != GSS_S_COMPLETE && 9412e92ac56SJamie Gritton gr->gr_major != GSS_S_CONTINUE_NEEDED) { 9422e92ac56SJamie Gritton rpc_gss_log_status("accept_sec_context", client->cl_mech, 9432e92ac56SJamie Gritton gr->gr_major, gr->gr_minor); 9442e92ac56SJamie Gritton client->cl_state = CLIENT_STALE; 9452e92ac56SJamie Gritton return (TRUE); 9462e92ac56SJamie Gritton } 9472e92ac56SJamie Gritton 9482e92ac56SJamie Gritton gr->gr_handle.value = &client->cl_id; 9492e92ac56SJamie Gritton gr->gr_handle.length = sizeof(client->cl_id); 9502e92ac56SJamie Gritton gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 9512e92ac56SJamie Gritton 9522e92ac56SJamie Gritton /* Save client info. */ 9532e92ac56SJamie Gritton client->cl_mech = mech; 9542e92ac56SJamie Gritton client->cl_qop = GSS_C_QOP_DEFAULT; 9552e92ac56SJamie Gritton client->cl_done_callback = FALSE; 9562e92ac56SJamie Gritton 9572e92ac56SJamie Gritton if (gr->gr_major == GSS_S_COMPLETE) { 9582e92ac56SJamie Gritton gss_buffer_desc export_name; 9592e92ac56SJamie Gritton 9602e92ac56SJamie Gritton /* 9612e92ac56SJamie Gritton * Change client expiration time to be near when the 9622e92ac56SJamie Gritton * client creds expire (or 24 hours if we can't figure 9632e92ac56SJamie Gritton * that out). 9642e92ac56SJamie Gritton */ 9652e92ac56SJamie Gritton if (cred_lifetime == GSS_C_INDEFINITE) 96604cb0c38SRick Macklem cred_lifetime = 24*60*60; 96704cb0c38SRick Macklem 96804cb0c38SRick Macklem /* 96904cb0c38SRick Macklem * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set. 97004cb0c38SRick Macklem */ 97104cb0c38SRick Macklem if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime > 97204cb0c38SRick Macklem svc_rpc_gss_lifetime_max) 97304cb0c38SRick Macklem cred_lifetime = svc_rpc_gss_lifetime_max; 9742e92ac56SJamie Gritton 9752e92ac56SJamie Gritton client->cl_expiration = time_uptime + cred_lifetime; 9762e92ac56SJamie Gritton 9772e92ac56SJamie Gritton /* 9782e92ac56SJamie Gritton * Fill in cred details in the rawcred structure. 9792e92ac56SJamie Gritton */ 9802e92ac56SJamie Gritton client->cl_rawcred.version = RPCSEC_GSS_VERSION; 9812e92ac56SJamie Gritton rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 9822e92ac56SJamie Gritton maj_stat = gss_export_name(&min_stat, client->cl_cname, 9832e92ac56SJamie Gritton &export_name); 9842e92ac56SJamie Gritton if (maj_stat != GSS_S_COMPLETE) { 9852e92ac56SJamie Gritton rpc_gss_log_status("gss_export_name", client->cl_mech, 9862e92ac56SJamie Gritton maj_stat, min_stat); 9872e92ac56SJamie Gritton return (FALSE); 9882e92ac56SJamie Gritton } 9892e92ac56SJamie Gritton client->cl_rawcred.client_principal = 9902e92ac56SJamie Gritton mem_alloc(sizeof(*client->cl_rawcred.client_principal) 9912e92ac56SJamie Gritton + export_name.length); 9922e92ac56SJamie Gritton client->cl_rawcred.client_principal->len = export_name.length; 9932e92ac56SJamie Gritton memcpy(client->cl_rawcred.client_principal->name, 9942e92ac56SJamie Gritton export_name.value, export_name.length); 9952e92ac56SJamie Gritton gss_release_buffer(&min_stat, &export_name); 9962e92ac56SJamie Gritton client->cl_rawcred.svc_principal = 9972e92ac56SJamie Gritton client->cl_sname->sn_principal; 9982e92ac56SJamie Gritton client->cl_rawcred.service = gc->gc_svc; 9992e92ac56SJamie Gritton 10002e92ac56SJamie Gritton /* 10012e92ac56SJamie Gritton * Use gss_pname_to_uid to map to unix creds. For 10022e92ac56SJamie Gritton * kerberos5, this uses krb5_aname_to_localname. 10032e92ac56SJamie Gritton */ 10042e92ac56SJamie Gritton svc_rpc_gss_build_ucred(client, client->cl_cname); 10052e92ac56SJamie Gritton svc_rpc_gss_set_flavor(client); 10062e92ac56SJamie Gritton gss_release_name(&min_stat, &client->cl_cname); 10072e92ac56SJamie Gritton 10082e92ac56SJamie Gritton #ifdef DEBUG 1009a9148abdSDoug Rabson { 10102e92ac56SJamie Gritton gss_buffer_desc mechname; 1011a9148abdSDoug Rabson 10122e92ac56SJamie Gritton gss_oid_to_str(&min_stat, mech, &mechname); 10132e92ac56SJamie Gritton 10142e92ac56SJamie Gritton rpc_gss_log_debug("accepted context for %s with " 10152e92ac56SJamie Gritton "<mech %.*s, qop %d, svc %d>", 10162e92ac56SJamie Gritton client->cl_rawcred.client_principal->name, 10172e92ac56SJamie Gritton mechname.length, (char *)mechname.value, 10183d26cd60SBrooks Davis client->cl_qop, client->cl_rawcred.service); 10192e92ac56SJamie Gritton 10202e92ac56SJamie Gritton gss_release_buffer(&min_stat, &mechname); 1021d4468577SJamie Gritton } 10222e92ac56SJamie Gritton #endif /* DEBUG */ 1023d4468577SJamie Gritton } 10242e92ac56SJamie Gritton return (TRUE); 1025a9148abdSDoug Rabson } 1026a9148abdSDoug Rabson 10272e92ac56SJamie Gritton static bool_t 10282e92ac56SJamie Gritton svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 102905496254SRick Macklem gss_qop_t *qop, rpc_gss_proc_t gcproc) 1030a9148abdSDoug Rabson { 10312e92ac56SJamie Gritton struct opaque_auth *oa; 10322e92ac56SJamie Gritton gss_buffer_desc rpcbuf, checksum; 10332e92ac56SJamie Gritton OM_uint32 maj_stat, min_stat; 10342e92ac56SJamie Gritton gss_qop_t qop_state; 10352e92ac56SJamie Gritton int32_t rpchdr[128 / sizeof(int32_t)]; 10362e92ac56SJamie Gritton int32_t *buf; 1037a9148abdSDoug Rabson 10382e92ac56SJamie Gritton rpc_gss_log_debug("in svc_rpc_gss_validate()"); 10392e92ac56SJamie Gritton 10402e92ac56SJamie Gritton memset(rpchdr, 0, sizeof(rpchdr)); 10412e92ac56SJamie Gritton 10422e92ac56SJamie Gritton /* Reconstruct RPC header for signing (from xdr_callmsg). */ 10432e92ac56SJamie Gritton buf = rpchdr; 10442e92ac56SJamie Gritton IXDR_PUT_LONG(buf, msg->rm_xid); 10452e92ac56SJamie Gritton IXDR_PUT_ENUM(buf, msg->rm_direction); 10462e92ac56SJamie Gritton IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 10472e92ac56SJamie Gritton IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 10482e92ac56SJamie Gritton IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 10492e92ac56SJamie Gritton IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 10502e92ac56SJamie Gritton oa = &msg->rm_call.cb_cred; 10512e92ac56SJamie Gritton IXDR_PUT_ENUM(buf, oa->oa_flavor); 10522e92ac56SJamie Gritton IXDR_PUT_LONG(buf, oa->oa_length); 10532e92ac56SJamie Gritton if (oa->oa_length) { 10542e92ac56SJamie Gritton memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 10552e92ac56SJamie Gritton buf += RNDUP(oa->oa_length) / sizeof(int32_t); 1056a9148abdSDoug Rabson } 10572e92ac56SJamie Gritton rpcbuf.value = rpchdr; 10582e92ac56SJamie Gritton rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 10592e92ac56SJamie Gritton 10602e92ac56SJamie Gritton checksum.value = msg->rm_call.cb_verf.oa_base; 10612e92ac56SJamie Gritton checksum.length = msg->rm_call.cb_verf.oa_length; 10622e92ac56SJamie Gritton 10632e92ac56SJamie Gritton maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 10642e92ac56SJamie Gritton &qop_state); 10652e92ac56SJamie Gritton 10662e92ac56SJamie Gritton if (maj_stat != GSS_S_COMPLETE) { 10672e92ac56SJamie Gritton rpc_gss_log_status("gss_verify_mic", client->cl_mech, 10682e92ac56SJamie Gritton maj_stat, min_stat); 1069798a34feSRick Macklem /* 10701e0706fdSRick Macklem * A bug in some versions of the Linux client generates a 10711e0706fdSRick Macklem * Destroy operation with a bogus encrypted checksum. Deleting 10721e0706fdSRick Macklem * the credential handle for that case causes the mount to fail. 1073798a34feSRick Macklem * Since the checksum is bogus (gss_verify_mic() failed), it 1074798a34feSRick Macklem * doesn't make sense to destroy the handle and not doing so 1075798a34feSRick Macklem * fixes the Linux mount. 1076798a34feSRick Macklem */ 107705496254SRick Macklem if (gcproc != RPCSEC_GSS_DESTROY) 10782e92ac56SJamie Gritton client->cl_state = CLIENT_STALE; 10792e92ac56SJamie Gritton return (FALSE); 1080d4468577SJamie Gritton } 1081d4468577SJamie Gritton 10822e92ac56SJamie Gritton *qop = qop_state; 10832e92ac56SJamie Gritton return (TRUE); 10842e92ac56SJamie Gritton } 10852e92ac56SJamie Gritton 10862e92ac56SJamie Gritton static bool_t 10872e92ac56SJamie Gritton svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 10882e92ac56SJamie Gritton struct svc_req *rqst, u_int seq) 10892e92ac56SJamie Gritton { 10902e92ac56SJamie Gritton gss_buffer_desc signbuf; 10912e92ac56SJamie Gritton gss_buffer_desc mic; 10922e92ac56SJamie Gritton OM_uint32 maj_stat, min_stat; 10932e92ac56SJamie Gritton uint32_t nseq; 10942e92ac56SJamie Gritton 10952e92ac56SJamie Gritton rpc_gss_log_debug("in svc_rpc_gss_nextverf()"); 10962e92ac56SJamie Gritton 10972e92ac56SJamie Gritton nseq = htonl(seq); 10982e92ac56SJamie Gritton signbuf.value = &nseq; 10992e92ac56SJamie Gritton signbuf.length = sizeof(nseq); 11002e92ac56SJamie Gritton 11012e92ac56SJamie Gritton maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 11022e92ac56SJamie Gritton &signbuf, &mic); 11032e92ac56SJamie Gritton 11042e92ac56SJamie Gritton if (maj_stat != GSS_S_COMPLETE) { 11052e92ac56SJamie Gritton rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 11062e92ac56SJamie Gritton client->cl_state = CLIENT_STALE; 11072e92ac56SJamie Gritton return (FALSE); 11082e92ac56SJamie Gritton } 11092e92ac56SJamie Gritton 11102e92ac56SJamie Gritton KASSERT(mic.length <= MAX_AUTH_BYTES, 11112e92ac56SJamie Gritton ("MIC too large for RPCSEC_GSS")); 11122e92ac56SJamie Gritton 11132e92ac56SJamie Gritton rqst->rq_verf.oa_flavor = RPCSEC_GSS; 11142e92ac56SJamie Gritton rqst->rq_verf.oa_length = mic.length; 11152e92ac56SJamie Gritton bcopy(mic.value, rqst->rq_verf.oa_base, mic.length); 11162e92ac56SJamie Gritton 11172e92ac56SJamie Gritton gss_release_buffer(&min_stat, &mic); 11182e92ac56SJamie Gritton 11192e92ac56SJamie Gritton return (TRUE); 11202e92ac56SJamie Gritton } 11212e92ac56SJamie Gritton 11222e92ac56SJamie Gritton static bool_t 11232e92ac56SJamie Gritton svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 11242e92ac56SJamie Gritton { 11252e92ac56SJamie Gritton struct svc_rpc_gss_callback *scb; 11262e92ac56SJamie Gritton rpc_gss_lock_t lock; 11272e92ac56SJamie Gritton void *cookie; 11282e92ac56SJamie Gritton bool_t cb_res; 11292e92ac56SJamie Gritton bool_t result; 11302e92ac56SJamie Gritton 11312e92ac56SJamie Gritton /* 11322e92ac56SJamie Gritton * See if we have a callback for this guy. 11332e92ac56SJamie Gritton */ 11342e92ac56SJamie Gritton result = TRUE; 11352e92ac56SJamie Gritton SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 11362e92ac56SJamie Gritton if (scb->cb_callback.program == rqst->rq_prog 11372e92ac56SJamie Gritton && scb->cb_callback.version == rqst->rq_vers) { 11382e92ac56SJamie Gritton /* 11392e92ac56SJamie Gritton * This one matches. Call the callback and see 11402e92ac56SJamie Gritton * if it wants to veto or something. 11412e92ac56SJamie Gritton */ 11422e92ac56SJamie Gritton lock.locked = FALSE; 11432e92ac56SJamie Gritton lock.raw_cred = &client->cl_rawcred; 11442e92ac56SJamie Gritton cb_res = scb->cb_callback.callback(rqst, 11452e92ac56SJamie Gritton client->cl_creds, 11462e92ac56SJamie Gritton client->cl_ctx, 11472e92ac56SJamie Gritton &lock, 11482e92ac56SJamie Gritton &cookie); 11492e92ac56SJamie Gritton 11502e92ac56SJamie Gritton if (!cb_res) { 11512e92ac56SJamie Gritton client->cl_state = CLIENT_STALE; 11522e92ac56SJamie Gritton result = FALSE; 11532e92ac56SJamie Gritton break; 11542e92ac56SJamie Gritton } 11552e92ac56SJamie Gritton 11562e92ac56SJamie Gritton /* 11572e92ac56SJamie Gritton * The callback accepted the connection - it 11582e92ac56SJamie Gritton * is responsible for freeing client->cl_creds 11592e92ac56SJamie Gritton * now. 11602e92ac56SJamie Gritton */ 11612e92ac56SJamie Gritton client->cl_creds = GSS_C_NO_CREDENTIAL; 11622e92ac56SJamie Gritton client->cl_locked = lock.locked; 11632e92ac56SJamie Gritton client->cl_cookie = cookie; 11642e92ac56SJamie Gritton return (TRUE); 11652e92ac56SJamie Gritton } 11662e92ac56SJamie Gritton } 11672e92ac56SJamie Gritton 11682e92ac56SJamie Gritton /* 11692e92ac56SJamie Gritton * Either no callback exists for this program/version or one 11702e92ac56SJamie Gritton * of the callbacks rejected the connection. We just need to 11712e92ac56SJamie Gritton * clean up the delegated client creds, if any. 11722e92ac56SJamie Gritton */ 11732e92ac56SJamie Gritton if (client->cl_creds) { 11742e92ac56SJamie Gritton OM_uint32 min_ver; 11752e92ac56SJamie Gritton gss_release_cred(&min_ver, &client->cl_creds); 11762e92ac56SJamie Gritton } 11772e92ac56SJamie Gritton return (result); 11782e92ac56SJamie Gritton } 11792e92ac56SJamie Gritton 11802e92ac56SJamie Gritton static bool_t 11812e92ac56SJamie Gritton svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 11822e92ac56SJamie Gritton { 1183*bcd0e31dSJohn Baldwin uint32_t offset; 11842e92ac56SJamie Gritton int word, bit; 11852e92ac56SJamie Gritton bool_t result; 11862e92ac56SJamie Gritton 11872e92ac56SJamie Gritton sx_xlock(&client->cl_lock); 11882e92ac56SJamie Gritton if (seq <= client->cl_seqlast) { 11892e92ac56SJamie Gritton /* 11902e92ac56SJamie Gritton * The request sequence number is less than 11912e92ac56SJamie Gritton * the largest we have seen so far. If it is 11922e92ac56SJamie Gritton * outside the window or if we have seen a 11932e92ac56SJamie Gritton * request with this sequence before, silently 11942e92ac56SJamie Gritton * discard it. 11952e92ac56SJamie Gritton */ 11962e92ac56SJamie Gritton offset = client->cl_seqlast - seq; 11972e92ac56SJamie Gritton if (offset >= SVC_RPC_GSS_SEQWINDOW) { 11982e92ac56SJamie Gritton result = FALSE; 11992e92ac56SJamie Gritton goto out; 12002e92ac56SJamie Gritton } 12012e92ac56SJamie Gritton word = offset / 32; 12022e92ac56SJamie Gritton bit = offset % 32; 12032e92ac56SJamie Gritton if (client->cl_seqmask[word] & (1 << bit)) { 12042e92ac56SJamie Gritton result = FALSE; 12052e92ac56SJamie Gritton goto out; 12062e92ac56SJamie Gritton } 12072e92ac56SJamie Gritton } 12082e92ac56SJamie Gritton 12092e92ac56SJamie Gritton result = TRUE; 12102e92ac56SJamie Gritton out: 12112e92ac56SJamie Gritton sx_xunlock(&client->cl_lock); 12122e92ac56SJamie Gritton return (result); 12132e92ac56SJamie Gritton } 12142e92ac56SJamie Gritton 12152e92ac56SJamie Gritton static void 12162e92ac56SJamie Gritton svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) 12172e92ac56SJamie Gritton { 12182e92ac56SJamie Gritton int offset, i, word, bit; 12192e92ac56SJamie Gritton uint32_t carry, newcarry; 12202e92ac56SJamie Gritton 12212e92ac56SJamie Gritton sx_xlock(&client->cl_lock); 12222e92ac56SJamie Gritton if (seq > client->cl_seqlast) { 12232e92ac56SJamie Gritton /* 12242e92ac56SJamie Gritton * This request has a sequence number greater 12252e92ac56SJamie Gritton * than any we have seen so far. Advance the 12262e92ac56SJamie Gritton * seq window and set bit zero of the window 12272e92ac56SJamie Gritton * (which corresponds to the new sequence 12282e92ac56SJamie Gritton * number) 12292e92ac56SJamie Gritton */ 12302e92ac56SJamie Gritton offset = seq - client->cl_seqlast; 12312e92ac56SJamie Gritton while (offset > 32) { 12322e92ac56SJamie Gritton for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 12332e92ac56SJamie Gritton i > 0; i--) { 12342e92ac56SJamie Gritton client->cl_seqmask[i] = client->cl_seqmask[i-1]; 12352e92ac56SJamie Gritton } 12362e92ac56SJamie Gritton client->cl_seqmask[0] = 0; 12372e92ac56SJamie Gritton offset -= 32; 12382e92ac56SJamie Gritton } 12392e92ac56SJamie Gritton carry = 0; 12402e92ac56SJamie Gritton for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 12412e92ac56SJamie Gritton newcarry = client->cl_seqmask[i] >> (32 - offset); 12422e92ac56SJamie Gritton client->cl_seqmask[i] = 12432e92ac56SJamie Gritton (client->cl_seqmask[i] << offset) | carry; 12442e92ac56SJamie Gritton carry = newcarry; 12452e92ac56SJamie Gritton } 12462e92ac56SJamie Gritton client->cl_seqmask[0] |= 1; 12472e92ac56SJamie Gritton client->cl_seqlast = seq; 12482e92ac56SJamie Gritton } else { 12492e92ac56SJamie Gritton offset = client->cl_seqlast - seq; 12502e92ac56SJamie Gritton word = offset / 32; 12512e92ac56SJamie Gritton bit = offset % 32; 12522e92ac56SJamie Gritton client->cl_seqmask[word] |= (1 << bit); 12532e92ac56SJamie Gritton } 12542e92ac56SJamie Gritton sx_xunlock(&client->cl_lock); 12552e92ac56SJamie Gritton } 12562e92ac56SJamie Gritton 12572e92ac56SJamie Gritton enum auth_stat 12582e92ac56SJamie Gritton svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 12592e92ac56SJamie Gritton 12602e92ac56SJamie Gritton { 12612e92ac56SJamie Gritton OM_uint32 min_stat; 12622e92ac56SJamie Gritton XDR xdrs; 12632e92ac56SJamie Gritton struct svc_rpc_gss_cookedcred *cc; 12642e92ac56SJamie Gritton struct svc_rpc_gss_client *client; 12652e92ac56SJamie Gritton struct rpc_gss_cred gc; 12662e92ac56SJamie Gritton struct rpc_gss_init_res gr; 12672e92ac56SJamie Gritton gss_qop_t qop; 12682e92ac56SJamie Gritton int call_stat; 12692e92ac56SJamie Gritton enum auth_stat result; 12702e92ac56SJamie Gritton 12712e92ac56SJamie Gritton rpc_gss_log_debug("in svc_rpc_gss()"); 12722e92ac56SJamie Gritton 12732e92ac56SJamie Gritton /* Garbage collect old clients. */ 12742e92ac56SJamie Gritton svc_rpc_gss_timeout_clients(); 12752e92ac56SJamie Gritton 12762e92ac56SJamie Gritton /* Initialize reply. */ 12772e92ac56SJamie Gritton rqst->rq_verf = _null_auth; 12782e92ac56SJamie Gritton 12792e92ac56SJamie Gritton /* Deserialize client credentials. */ 12802e92ac56SJamie Gritton if (rqst->rq_cred.oa_length <= 0) 12812e92ac56SJamie Gritton return (AUTH_BADCRED); 12822e92ac56SJamie Gritton 12832e92ac56SJamie Gritton memset(&gc, 0, sizeof(gc)); 12842e92ac56SJamie Gritton 12852e92ac56SJamie Gritton xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 12862e92ac56SJamie Gritton rqst->rq_cred.oa_length, XDR_DECODE); 12872e92ac56SJamie Gritton 12882e92ac56SJamie Gritton if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 12892e92ac56SJamie Gritton XDR_DESTROY(&xdrs); 12902e92ac56SJamie Gritton return (AUTH_BADCRED); 12912e92ac56SJamie Gritton } 12922e92ac56SJamie Gritton XDR_DESTROY(&xdrs); 12932e92ac56SJamie Gritton 12942e92ac56SJamie Gritton client = NULL; 12952e92ac56SJamie Gritton 12962e92ac56SJamie Gritton /* Check version. */ 12972e92ac56SJamie Gritton if (gc.gc_version != RPCSEC_GSS_VERSION) { 12982e92ac56SJamie Gritton result = AUTH_BADCRED; 12992e92ac56SJamie Gritton goto out; 13002e92ac56SJamie Gritton } 13012e92ac56SJamie Gritton 13022e92ac56SJamie Gritton /* Check the proc and find the client (or create it) */ 13032e92ac56SJamie Gritton if (gc.gc_proc == RPCSEC_GSS_INIT) { 13042e92ac56SJamie Gritton if (gc.gc_handle.length != 0) { 13052e92ac56SJamie Gritton result = AUTH_BADCRED; 13062e92ac56SJamie Gritton goto out; 13072e92ac56SJamie Gritton } 13082e92ac56SJamie Gritton client = svc_rpc_gss_create_client(); 13092e92ac56SJamie Gritton } else { 13102e92ac56SJamie Gritton struct svc_rpc_gss_clientid *p; 13112e92ac56SJamie Gritton if (gc.gc_handle.length != sizeof(*p)) { 13122e92ac56SJamie Gritton result = AUTH_BADCRED; 13132e92ac56SJamie Gritton goto out; 13142e92ac56SJamie Gritton } 13152e92ac56SJamie Gritton p = gc.gc_handle.value; 13162e92ac56SJamie Gritton client = svc_rpc_gss_find_client(p); 13172e92ac56SJamie Gritton if (!client) { 13182e92ac56SJamie Gritton /* 13192e92ac56SJamie Gritton * Can't find the client - we may have 13202e92ac56SJamie Gritton * destroyed it - tell the other side to 13212e92ac56SJamie Gritton * re-authenticate. 13222e92ac56SJamie Gritton */ 13232e92ac56SJamie Gritton result = RPCSEC_GSS_CREDPROBLEM; 13242e92ac56SJamie Gritton goto out; 13252e92ac56SJamie Gritton } 13262e92ac56SJamie Gritton } 13272e92ac56SJamie Gritton cc = rqst->rq_clntcred; 13282e92ac56SJamie Gritton cc->cc_client = client; 13292e92ac56SJamie Gritton cc->cc_service = gc.gc_svc; 13302e92ac56SJamie Gritton cc->cc_seq = gc.gc_seq; 13312e92ac56SJamie Gritton 13322e92ac56SJamie Gritton /* 13332e92ac56SJamie Gritton * The service and sequence number must be ignored for 13342e92ac56SJamie Gritton * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 13352e92ac56SJamie Gritton */ 13362e92ac56SJamie Gritton if (gc.gc_proc != RPCSEC_GSS_INIT 13372e92ac56SJamie Gritton && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { 13382e92ac56SJamie Gritton /* 13392e92ac56SJamie Gritton * Check for sequence number overflow. 13402e92ac56SJamie Gritton */ 13412e92ac56SJamie Gritton if (gc.gc_seq >= MAXSEQ) { 13422e92ac56SJamie Gritton result = RPCSEC_GSS_CTXPROBLEM; 13432e92ac56SJamie Gritton goto out; 13442e92ac56SJamie Gritton } 13452e92ac56SJamie Gritton 13462e92ac56SJamie Gritton /* 13472e92ac56SJamie Gritton * Check for valid service. 13482e92ac56SJamie Gritton */ 13492e92ac56SJamie Gritton if (gc.gc_svc != rpc_gss_svc_none && 13502e92ac56SJamie Gritton gc.gc_svc != rpc_gss_svc_integrity && 13512e92ac56SJamie Gritton gc.gc_svc != rpc_gss_svc_privacy) { 13522e92ac56SJamie Gritton result = AUTH_BADCRED; 13532e92ac56SJamie Gritton goto out; 13542e92ac56SJamie Gritton } 13552e92ac56SJamie Gritton } 13562e92ac56SJamie Gritton 13572e92ac56SJamie Gritton /* Handle RPCSEC_GSS control procedure. */ 13582e92ac56SJamie Gritton switch (gc.gc_proc) { 13592e92ac56SJamie Gritton 13602e92ac56SJamie Gritton case RPCSEC_GSS_INIT: 13612e92ac56SJamie Gritton case RPCSEC_GSS_CONTINUE_INIT: 13622e92ac56SJamie Gritton if (rqst->rq_proc != NULLPROC) { 13632e92ac56SJamie Gritton result = AUTH_REJECTEDCRED; 13642e92ac56SJamie Gritton break; 13652e92ac56SJamie Gritton } 13662e92ac56SJamie Gritton 13672e92ac56SJamie Gritton memset(&gr, 0, sizeof(gr)); 13682e92ac56SJamie Gritton if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 13692e92ac56SJamie Gritton result = AUTH_REJECTEDCRED; 13702e92ac56SJamie Gritton break; 13712e92ac56SJamie Gritton } 13722e92ac56SJamie Gritton 13732e92ac56SJamie Gritton if (gr.gr_major == GSS_S_COMPLETE) { 13742e92ac56SJamie Gritton /* 13752e92ac56SJamie Gritton * We borrow the space for the call verf to 13762e92ac56SJamie Gritton * pack our reply verf. 13772e92ac56SJamie Gritton */ 13782e92ac56SJamie Gritton rqst->rq_verf = msg->rm_call.cb_verf; 13792e92ac56SJamie Gritton if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 13802e92ac56SJamie Gritton result = AUTH_REJECTEDCRED; 13812e92ac56SJamie Gritton break; 13822e92ac56SJamie Gritton } 13832e92ac56SJamie Gritton } else { 13842e92ac56SJamie Gritton rqst->rq_verf = _null_auth; 13852e92ac56SJamie Gritton } 13862e92ac56SJamie Gritton 13872e92ac56SJamie Gritton call_stat = svc_sendreply(rqst, 13882e92ac56SJamie Gritton (xdrproc_t) xdr_rpc_gss_init_res, 13892e92ac56SJamie Gritton (caddr_t) &gr); 13902e92ac56SJamie Gritton 13912e92ac56SJamie Gritton gss_release_buffer(&min_stat, &gr.gr_token); 13922e92ac56SJamie Gritton 13932e92ac56SJamie Gritton if (!call_stat) { 13942e92ac56SJamie Gritton result = AUTH_FAILED; 13952e92ac56SJamie Gritton break; 13962e92ac56SJamie Gritton } 13972e92ac56SJamie Gritton 13982e92ac56SJamie Gritton if (gr.gr_major == GSS_S_COMPLETE) 13992e92ac56SJamie Gritton client->cl_state = CLIENT_ESTABLISHED; 14002e92ac56SJamie Gritton 14012e92ac56SJamie Gritton result = RPCSEC_GSS_NODISPATCH; 14022e92ac56SJamie Gritton break; 14032e92ac56SJamie Gritton 14042e92ac56SJamie Gritton case RPCSEC_GSS_DATA: 14052e92ac56SJamie Gritton case RPCSEC_GSS_DESTROY: 14062e92ac56SJamie Gritton if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 14072e92ac56SJamie Gritton result = RPCSEC_GSS_NODISPATCH; 14082e92ac56SJamie Gritton break; 14092e92ac56SJamie Gritton } 14102e92ac56SJamie Gritton 141105496254SRick Macklem if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) { 14122e92ac56SJamie Gritton result = RPCSEC_GSS_CREDPROBLEM; 14132e92ac56SJamie Gritton break; 14142e92ac56SJamie Gritton } 14152e92ac56SJamie Gritton 14162e92ac56SJamie Gritton /* 14172e92ac56SJamie Gritton * We borrow the space for the call verf to pack our 14182e92ac56SJamie Gritton * reply verf. 14192e92ac56SJamie Gritton */ 14202e92ac56SJamie Gritton rqst->rq_verf = msg->rm_call.cb_verf; 14212e92ac56SJamie Gritton if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 14222e92ac56SJamie Gritton result = RPCSEC_GSS_CTXPROBLEM; 14232e92ac56SJamie Gritton break; 14242e92ac56SJamie Gritton } 14252e92ac56SJamie Gritton 14262e92ac56SJamie Gritton svc_rpc_gss_update_seq(client, gc.gc_seq); 14272e92ac56SJamie Gritton 14282e92ac56SJamie Gritton /* 14292e92ac56SJamie Gritton * Change the SVCAUTH ops on the request to point at 14302e92ac56SJamie Gritton * our own code so that we can unwrap the arguments 14312e92ac56SJamie Gritton * and wrap the result. The caller will re-set this on 14322e92ac56SJamie Gritton * every request to point to a set of null wrap/unwrap 14332e92ac56SJamie Gritton * methods. Acquire an extra reference to the client 14342e92ac56SJamie Gritton * which will be released by svc_rpc_gss_release() 14352e92ac56SJamie Gritton * after the request has finished processing. 14362e92ac56SJamie Gritton */ 14372e92ac56SJamie Gritton refcount_acquire(&client->cl_refs); 14382e92ac56SJamie Gritton rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops; 14392e92ac56SJamie Gritton rqst->rq_auth.svc_ah_private = cc; 14402e92ac56SJamie Gritton 14412e92ac56SJamie Gritton if (gc.gc_proc == RPCSEC_GSS_DATA) { 14422e92ac56SJamie Gritton /* 14432e92ac56SJamie Gritton * We might be ready to do a callback to the server to 14442e92ac56SJamie Gritton * see if it wants to accept/reject the connection. 14452e92ac56SJamie Gritton */ 14462e92ac56SJamie Gritton sx_xlock(&client->cl_lock); 14472e92ac56SJamie Gritton if (!client->cl_done_callback) { 14482e92ac56SJamie Gritton client->cl_done_callback = TRUE; 14492e92ac56SJamie Gritton client->cl_qop = qop; 14502e92ac56SJamie Gritton client->cl_rawcred.qop = _rpc_gss_num_to_qop( 14512e92ac56SJamie Gritton client->cl_rawcred.mechanism, qop); 14522e92ac56SJamie Gritton if (!svc_rpc_gss_callback(client, rqst)) { 14532e92ac56SJamie Gritton result = AUTH_REJECTEDCRED; 14542e92ac56SJamie Gritton sx_xunlock(&client->cl_lock); 14552e92ac56SJamie Gritton break; 14562e92ac56SJamie Gritton } 14572e92ac56SJamie Gritton } 14582e92ac56SJamie Gritton sx_xunlock(&client->cl_lock); 14592e92ac56SJamie Gritton 14602e92ac56SJamie Gritton /* 14612e92ac56SJamie Gritton * If the server has locked this client to a 14622e92ac56SJamie Gritton * particular service+qop pair, enforce that 14632e92ac56SJamie Gritton * restriction now. 14642e92ac56SJamie Gritton */ 14652e92ac56SJamie Gritton if (client->cl_locked) { 14662e92ac56SJamie Gritton if (client->cl_rawcred.service != gc.gc_svc) { 14672e92ac56SJamie Gritton result = AUTH_FAILED; 14682e92ac56SJamie Gritton break; 14692e92ac56SJamie Gritton } else if (client->cl_qop != qop) { 14702e92ac56SJamie Gritton result = AUTH_BADVERF; 14712e92ac56SJamie Gritton break; 14722e92ac56SJamie Gritton } 14732e92ac56SJamie Gritton } 14742e92ac56SJamie Gritton 14752e92ac56SJamie Gritton /* 14762e92ac56SJamie Gritton * If the qop changed, look up the new qop 14772e92ac56SJamie Gritton * name for rawcred. 14782e92ac56SJamie Gritton */ 14792e92ac56SJamie Gritton if (client->cl_qop != qop) { 14802e92ac56SJamie Gritton client->cl_qop = qop; 14812e92ac56SJamie Gritton client->cl_rawcred.qop = _rpc_gss_num_to_qop( 14822e92ac56SJamie Gritton client->cl_rawcred.mechanism, qop); 14832e92ac56SJamie Gritton } 14842e92ac56SJamie Gritton 14852e92ac56SJamie Gritton /* 14862e92ac56SJamie Gritton * Make sure we use the right service value 14872e92ac56SJamie Gritton * for unwrap/wrap. 14882e92ac56SJamie Gritton */ 14892e92ac56SJamie Gritton if (client->cl_rawcred.service != gc.gc_svc) { 14902e92ac56SJamie Gritton client->cl_rawcred.service = gc.gc_svc; 14912e92ac56SJamie Gritton svc_rpc_gss_set_flavor(client); 14922e92ac56SJamie Gritton } 14932e92ac56SJamie Gritton 14942e92ac56SJamie Gritton result = AUTH_OK; 14952e92ac56SJamie Gritton } else { 14962e92ac56SJamie Gritton if (rqst->rq_proc != NULLPROC) { 14972e92ac56SJamie Gritton result = AUTH_REJECTEDCRED; 14982e92ac56SJamie Gritton break; 14992e92ac56SJamie Gritton } 15002e92ac56SJamie Gritton 15012e92ac56SJamie Gritton call_stat = svc_sendreply(rqst, 15022e92ac56SJamie Gritton (xdrproc_t) xdr_void, (caddr_t) NULL); 15032e92ac56SJamie Gritton 15042e92ac56SJamie Gritton if (!call_stat) { 15052e92ac56SJamie Gritton result = AUTH_FAILED; 15062e92ac56SJamie Gritton break; 15072e92ac56SJamie Gritton } 15082e92ac56SJamie Gritton 15092e92ac56SJamie Gritton svc_rpc_gss_forget_client(client); 15102e92ac56SJamie Gritton 15112e92ac56SJamie Gritton result = RPCSEC_GSS_NODISPATCH; 15122e92ac56SJamie Gritton break; 15132e92ac56SJamie Gritton } 15142e92ac56SJamie Gritton break; 15152e92ac56SJamie Gritton 15162e92ac56SJamie Gritton default: 15172e92ac56SJamie Gritton result = AUTH_BADCRED; 15182e92ac56SJamie Gritton break; 15192e92ac56SJamie Gritton } 15202e92ac56SJamie Gritton out: 15212e92ac56SJamie Gritton if (client) 15222e92ac56SJamie Gritton svc_rpc_gss_release_client(client); 15232e92ac56SJamie Gritton 15242e92ac56SJamie Gritton xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 15252e92ac56SJamie Gritton return (result); 15262e92ac56SJamie Gritton } 15272e92ac56SJamie Gritton 15282e92ac56SJamie Gritton static bool_t 15292e92ac56SJamie Gritton svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp) 15302e92ac56SJamie Gritton { 15312e92ac56SJamie Gritton struct svc_rpc_gss_cookedcred *cc; 15322e92ac56SJamie Gritton struct svc_rpc_gss_client *client; 15332e92ac56SJamie Gritton 15342e92ac56SJamie Gritton rpc_gss_log_debug("in svc_rpc_gss_wrap()"); 15352e92ac56SJamie Gritton 15362e92ac56SJamie Gritton cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 15372e92ac56SJamie Gritton client = cc->cc_client; 15382e92ac56SJamie Gritton if (client->cl_state != CLIENT_ESTABLISHED 15392e92ac56SJamie Gritton || cc->cc_service == rpc_gss_svc_none || *mp == NULL) { 15402e92ac56SJamie Gritton return (TRUE); 15412e92ac56SJamie Gritton } 15422e92ac56SJamie Gritton 15432e92ac56SJamie Gritton return (xdr_rpc_gss_wrap_data(mp, 15442e92ac56SJamie Gritton client->cl_ctx, client->cl_qop, 15452e92ac56SJamie Gritton cc->cc_service, cc->cc_seq)); 15462e92ac56SJamie Gritton } 15472e92ac56SJamie Gritton 15482e92ac56SJamie Gritton static bool_t 15492e92ac56SJamie Gritton svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp) 15502e92ac56SJamie Gritton { 15512e92ac56SJamie Gritton struct svc_rpc_gss_cookedcred *cc; 15522e92ac56SJamie Gritton struct svc_rpc_gss_client *client; 15532e92ac56SJamie Gritton 15542e92ac56SJamie Gritton rpc_gss_log_debug("in svc_rpc_gss_unwrap()"); 15552e92ac56SJamie Gritton 15562e92ac56SJamie Gritton cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 15572e92ac56SJamie Gritton client = cc->cc_client; 15582e92ac56SJamie Gritton if (client->cl_state != CLIENT_ESTABLISHED 15592e92ac56SJamie Gritton || cc->cc_service == rpc_gss_svc_none) { 15602e92ac56SJamie Gritton return (TRUE); 15612e92ac56SJamie Gritton } 15622e92ac56SJamie Gritton 15632e92ac56SJamie Gritton return (xdr_rpc_gss_unwrap_data(mp, 15642e92ac56SJamie Gritton client->cl_ctx, client->cl_qop, 15652e92ac56SJamie Gritton cc->cc_service, cc->cc_seq)); 15662e92ac56SJamie Gritton } 15672e92ac56SJamie Gritton 15682e92ac56SJamie Gritton static void 15692e92ac56SJamie Gritton svc_rpc_gss_release(SVCAUTH *auth) 15702e92ac56SJamie Gritton { 15712e92ac56SJamie Gritton struct svc_rpc_gss_cookedcred *cc; 15722e92ac56SJamie Gritton struct svc_rpc_gss_client *client; 15732e92ac56SJamie Gritton 15742e92ac56SJamie Gritton rpc_gss_log_debug("in svc_rpc_gss_release()"); 15752e92ac56SJamie Gritton 15762e92ac56SJamie Gritton cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 15772e92ac56SJamie Gritton client = cc->cc_client; 15782e92ac56SJamie Gritton svc_rpc_gss_release_client(client); 15792e92ac56SJamie Gritton } 1580