1/*-
| 1/*-
|
2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * (c) UNIX System Laboratories, Inc. 5 * All or some portions of this file are derived from material licensed 6 * to the University of California by American Telephone and Telegraph 7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8 * the permission of UNIX System Laboratories, Inc.
| 2 * Copyright (c) 2008 Doug Rabson 3 * All rights reserved.
|
9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution.
| 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution.
|
18 * 4. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission.
| |
21 *
| 13 *
|
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
| 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
| 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
| 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE.
| 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE.
|
33 * 34 * @(#)vfs_subr.c 8.31 (Berkeley) 5/26/95
| |
35 */
| 25 */
|
| 26/* 27 svc_rpcsec_gss.c 28 29 Copyright (c) 2000 The Regents of the University of Michigan. 30 All rights reserved.
|
36
| 31
|
| 32 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 33 All rights reserved, all wrongs reversed. 34 35 Redistribution and use in source and binary forms, with or without 36 modification, are permitted provided that the following conditions 37 are met: 38 39 1. Redistributions of source code must retain the above copyright 40 notice, this list of conditions and the following disclaimer. 41 2. Redistributions in binary form must reproduce the above copyright 42 notice, this list of conditions and the following disclaimer in the 43 documentation and/or other materials provided with the distribution. 44 3. Neither the name of the University nor the names of its 45 contributors may be used to endorse or promote products derived 46 from this software without specific prior written permission. 47 48 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 49 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 50 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 51 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 53 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 54 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 55 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 56 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 57 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 58 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59 60 $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $ 61 */ 62
|
37#include <sys/cdefs.h>
| 63#include <sys/cdefs.h>
|
38__FBSDID("$FreeBSD: head/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c 197581 2009-09-28 18:07:16Z jamie $");
| 64__FBSDID("$FreeBSD: head/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c 197583 2009-09-28 18:54:26Z jamie $");
|
39 40#include <sys/param.h>
| 65 66#include <sys/param.h>
|
41#include <sys/dirent.h> 42#include <sys/domain.h>
| 67#include <sys/systm.h>
|
43#include <sys/jail.h> 44#include <sys/kernel.h>
| 68#include <sys/jail.h> 69#include <sys/kernel.h>
|
| 70#include <sys/kobj.h>
|
45#include <sys/lock.h> 46#include <sys/malloc.h> 47#include <sys/mbuf.h>
| 71#include <sys/lock.h> 72#include <sys/malloc.h> 73#include <sys/mbuf.h>
|
48#include <sys/mount.h>
| |
49#include <sys/mutex.h>
| 74#include <sys/mutex.h>
|
50#include <sys/rwlock.h> 51#include <sys/refcount.h> 52#include <sys/socket.h> 53#include <sys/systm.h> 54#include <sys/vnode.h>
| 75#include <sys/proc.h> 76#include <sys/sx.h> 77#include <sys/ucred.h>
|
55
| 78
|
56#include <net/radix.h>
| 79#include <rpc/rpc.h> 80#include <rpc/rpcsec_gss.h>
|
57
| 81
|
58static MALLOC_DEFINE(M_NETADDR, "export_host", "Export host address structure");
| 82#include "rpcsec_gss_int.h"
|
59
| 83
|
60static void vfs_free_addrlist(struct netexport *nep); 61static int vfs_free_netcred(struct radix_node *rn, void *w); 62static int vfs_hang_addrlist(struct mount *mp, struct netexport *nep, 63 struct export_args *argp); 64static struct netcred *vfs_export_lookup(struct mount *, struct sockaddr *);
| 84static bool_t svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **); 85static bool_t svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **); 86static void svc_rpc_gss_release(SVCAUTH *); 87static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *); 88static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
|
65
| 89
|
66/* 67 * Network address lookup element 68 */ 69struct netcred { 70 struct radix_node netc_rnodes[2]; 71 int netc_exflags; 72 struct ucred *netc_anon; 73 int netc_numsecflavors; 74 int netc_secflavors[MAXSECFLAVORS];
| 90static struct svc_auth_ops svc_auth_gss_ops = { 91 svc_rpc_gss_wrap, 92 svc_rpc_gss_unwrap, 93 svc_rpc_gss_release,
|
75}; 76
| 94}; 95
|
77/* 78 * Network export information 79 */ 80struct netexport { 81 struct netcred ne_defexported; /* Default export */ 82 struct radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */
| 96struct sx svc_rpc_gss_lock; 97 98struct svc_rpc_gss_callback { 99 SLIST_ENTRY(svc_rpc_gss_callback) cb_link; 100 rpc_gss_callback_t cb_callback;
|
83};
| 101};
|
| 102static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback) 103 svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(&svc_rpc_gss_callbacks);
|
84
| 104
|
| 105struct svc_rpc_gss_svc_name { 106 SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link; 107 char *sn_principal; 108 gss_OID sn_mech; 109 u_int sn_req_time; 110 gss_cred_id_t sn_cred; 111 u_int sn_program; 112 u_int sn_version; 113}; 114static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name) 115 svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(&svc_rpc_gss_svc_names); 116 117enum svc_rpc_gss_client_state { 118 CLIENT_NEW, /* still authenticating */ 119 CLIENT_ESTABLISHED, /* context established */ 120 CLIENT_STALE /* garbage to collect */ 121}; 122 123#define SVC_RPC_GSS_SEQWINDOW 128 124 125struct svc_rpc_gss_clientid { 126 unsigned long ci_hostid; 127 uint32_t ci_boottime; 128 uint32_t ci_id; 129}; 130 131struct svc_rpc_gss_client { 132 TAILQ_ENTRY(svc_rpc_gss_client) cl_link; 133 TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink; 134 volatile u_int cl_refs; 135 struct sx cl_lock; 136 struct svc_rpc_gss_clientid cl_id; 137 time_t cl_expiration; /* when to gc */ 138 enum svc_rpc_gss_client_state cl_state; /* client state */ 139 bool_t cl_locked; /* fixed service+qop */ 140 gss_ctx_id_t cl_ctx; /* context id */ 141 gss_cred_id_t cl_creds; /* delegated creds */ 142 gss_name_t cl_cname; /* client name */ 143 struct svc_rpc_gss_svc_name *cl_sname; /* server name used */ 144 rpc_gss_rawcred_t cl_rawcred; /* raw credentials */ 145 rpc_gss_ucred_t cl_ucred; /* unix-style credentials */ 146 struct ucred *cl_cred; /* kernel-style credentials */ 147 int cl_rpcflavor; /* RPC pseudo sec flavor */ 148 bool_t cl_done_callback; /* TRUE after call */ 149 void *cl_cookie; /* user cookie from callback */ 150 gid_t cl_gid_storage[NGROUPS]; 151 gss_OID cl_mech; /* mechanism */ 152 gss_qop_t cl_qop; /* quality of protection */ 153 uint32_t cl_seqlast; /* sequence window origin */ 154 uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */ 155}; 156TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client); 157
|
85/*
| 158/*
|
86 * Build hash lists of net addresses and hang them off the mount point. 87 * Called by vfs_export() to set up the lists of export addresses.
| 159 * This structure holds enough information to unwrap arguments or wrap 160 * results for a given request. We use the rq_clntcred area for this 161 * (which is a per-request buffer).
|
88 */
| 162 */
|
89static int 90vfs_hang_addrlist(struct mount *mp, struct netexport *nep, 91 struct export_args *argp)
| 163struct svc_rpc_gss_cookedcred { 164 struct svc_rpc_gss_client *cc_client; 165 rpc_gss_service_t cc_service; 166 uint32_t cc_seq; 167}; 168 169#define CLIENT_HASH_SIZE 256 170#define CLIENT_MAX 128 171struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE]; 172struct svc_rpc_gss_client_list svc_rpc_gss_clients; 173static size_t svc_rpc_gss_client_count; 174static uint32_t svc_rpc_gss_next_clientid = 1; 175 176static void 177svc_rpc_gss_init(void *arg)
|
92{
| 178{
|
93 register struct netcred *np; 94 register struct radix_node_head *rnh; 95 register int i; 96 struct radix_node *rn; 97 struct sockaddr *saddr, *smask = 0; 98 struct domain *dom; 99 int error;
| 179 int i;
|
100
| 180
|
101 /* 102 * XXX: This routine converts from a `struct xucred' 103 * (argp->ex_anon) to a `struct ucred' (np->netc_anon). This 104 * operation is questionable; for example, what should be done 105 * with fields like cr_uidinfo and cr_prison? Currently, this 106 * routine does not touch them (leaves them as NULL). 107 */ 108 if (argp->ex_anon.cr_version != XUCRED_VERSION) { 109 vfs_mount_error(mp, "ex_anon.cr_version: %d != %d", 110 argp->ex_anon.cr_version, XUCRED_VERSION); 111 return (EINVAL);
| 181 for (i = 0; i < CLIENT_HASH_SIZE; i++) 182 TAILQ_INIT(&svc_rpc_gss_client_hash[i]); 183 TAILQ_INIT(&svc_rpc_gss_clients); 184 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred); 185 sx_init(&svc_rpc_gss_lock, "gsslock"); 186} 187SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL); 188 189bool_t 190rpc_gss_set_callback(rpc_gss_callback_t *cb) 191{ 192 struct svc_rpc_gss_callback *scb; 193 194 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback)); 195 if (!scb) { 196 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 197 return (FALSE);
|
112 }
| 198 }
|
| 199 scb->cb_callback = *cb; 200 sx_xlock(&svc_rpc_gss_lock); 201 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link); 202 sx_xunlock(&svc_rpc_gss_lock);
|
113
| 203
|
114 if (argp->ex_addrlen == 0) { 115 if (mp->mnt_flag & MNT_DEFEXPORTED) { 116 vfs_mount_error(mp, 117 "MNT_DEFEXPORTED already set for mount %p", mp); 118 return (EPERM);
| 204 return (TRUE); 205} 206 207void 208rpc_gss_clear_callback(rpc_gss_callback_t *cb) 209{ 210 struct svc_rpc_gss_callback *scb; 211 212 sx_xlock(&svc_rpc_gss_lock); 213 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 214 if (scb->cb_callback.program == cb->program 215 && scb->cb_callback.version == cb->version 216 && scb->cb_callback.callback == cb->callback) { 217 SLIST_REMOVE(&svc_rpc_gss_callbacks, scb, 218 svc_rpc_gss_callback, cb_link); 219 sx_xunlock(&svc_rpc_gss_lock); 220 mem_free(scb, sizeof(*scb)); 221 return;
|
119 }
| 222 }
|
120 np = &nep->ne_defexported; 121 np->netc_exflags = argp->ex_flags; 122 np->netc_anon = crget(); 123 np->netc_anon->cr_uid = argp->ex_anon.cr_uid; 124 crsetgroups(np->netc_anon, argp->ex_anon.cr_ngroups, 125 argp->ex_anon.cr_groups); 126 np->netc_anon->cr_prison = &prison0; 127 prison_hold(np->netc_anon->cr_prison); 128 np->netc_numsecflavors = argp->ex_numsecflavors; 129 bcopy(argp->ex_secflavors, np->netc_secflavors, 130 sizeof(np->netc_secflavors)); 131 MNT_ILOCK(mp); 132 mp->mnt_flag |= MNT_DEFEXPORTED; 133 MNT_IUNLOCK(mp); 134 return (0);
| |
135 }
| 223 }
|
| 224 sx_xunlock(&svc_rpc_gss_lock); 225}
|
136
| 226
|
137#if MSIZE <= 256 138 if (argp->ex_addrlen > MLEN) { 139 vfs_mount_error(mp, "ex_addrlen %d is greater than %d", 140 argp->ex_addrlen, MLEN); 141 return (EINVAL);
| 227static bool_t 228rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname) 229{ 230 OM_uint32 maj_stat, min_stat; 231 gss_buffer_desc namebuf; 232 gss_name_t name; 233 gss_OID_set_desc oid_set; 234 235 oid_set.count = 1; 236 oid_set.elements = sname->sn_mech; 237 238 namebuf.value = (void *) sname->sn_principal; 239 namebuf.length = strlen(sname->sn_principal); 240 241 maj_stat = gss_import_name(&min_stat, &namebuf, 242 GSS_C_NT_HOSTBASED_SERVICE, &name); 243 if (maj_stat != GSS_S_COMPLETE) 244 return (FALSE); 245 246 if (sname->sn_cred != GSS_C_NO_CREDENTIAL) 247 gss_release_cred(&min_stat, &sname->sn_cred); 248 249 maj_stat = gss_acquire_cred(&min_stat, name, 250 sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred, 251 NULL, NULL); 252 if (maj_stat != GSS_S_COMPLETE) { 253 gss_release_name(&min_stat, &name); 254 return (FALSE);
|
142 }
| 255 }
|
143#endif
| 256 gss_release_name(&min_stat, &name);
|
144
| 257
|
145 i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen; 146 np = (struct netcred *) malloc(i, M_NETADDR, M_WAITOK | M_ZERO); 147 saddr = (struct sockaddr *) (np + 1); 148 if ((error = copyin(argp->ex_addr, saddr, argp->ex_addrlen))) 149 goto out; 150 if (saddr->sa_family == AF_UNSPEC || saddr->sa_family > AF_MAX) { 151 error = EINVAL; 152 vfs_mount_error(mp, "Invalid saddr->sa_family: %d"); 153 goto out;
| 258 return (TRUE); 259} 260 261bool_t 262rpc_gss_set_svc_name(const char *principal, const char *mechanism, 263 u_int req_time, u_int program, u_int version) 264{ 265 struct svc_rpc_gss_svc_name *sname; 266 gss_OID mech_oid; 267 268 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid)) 269 return (FALSE); 270 271 sname = mem_alloc(sizeof(*sname)); 272 if (!sname) 273 return (FALSE); 274 sname->sn_principal = strdup(principal, M_RPC); 275 sname->sn_mech = mech_oid; 276 sname->sn_req_time = req_time; 277 sname->sn_cred = GSS_C_NO_CREDENTIAL; 278 sname->sn_program = program; 279 sname->sn_version = version; 280 281 if (!rpc_gss_acquire_svc_cred(sname)) { 282 free(sname->sn_principal, M_RPC); 283 mem_free(sname, sizeof(*sname)); 284 return (FALSE);
|
154 }
| 285 }
|
155 if (saddr->sa_len > argp->ex_addrlen) 156 saddr->sa_len = argp->ex_addrlen; 157 if (argp->ex_masklen) { 158 smask = (struct sockaddr *)((caddr_t)saddr + argp->ex_addrlen); 159 error = copyin(argp->ex_mask, smask, argp->ex_masklen); 160 if (error) 161 goto out; 162 if (smask->sa_len > argp->ex_masklen) 163 smask->sa_len = argp->ex_masklen; 164 } 165 i = saddr->sa_family; 166 if ((rnh = nep->ne_rtable[i]) == NULL) { 167 /* 168 * Seems silly to initialize every AF when most are not used, 169 * do so on demand here 170 */ 171 for (dom = domains; dom; dom = dom->dom_next) { 172 KASSERT(((i == AF_INET) || (i == AF_INET6)), 173 ("unexpected protocol in vfs_hang_addrlist")); 174 if (dom->dom_family == i && dom->dom_rtattach) { 175 /* 176 * XXX MRT 177 * The INET and INET6 domains know the 178 * offset already. We don't need to send it 179 * So we just use it as a flag to say that 180 * we are or are not setting up a real routing 181 * table. Only IP and IPV6 need have this 182 * be 0 so all other protocols can stay the 183 * same (ABI compatible). 184 */ 185 dom->dom_rtattach( 186 (void **) &nep->ne_rtable[i], 0); 187 break; 188 }
| 286 287 sx_xlock(&svc_rpc_gss_lock); 288 SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link); 289 sx_xunlock(&svc_rpc_gss_lock); 290 291 return (TRUE); 292} 293 294void 295rpc_gss_clear_svc_name(u_int program, u_int version) 296{ 297 OM_uint32 min_stat; 298 struct svc_rpc_gss_svc_name *sname; 299 300 sx_xlock(&svc_rpc_gss_lock); 301 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 302 if (sname->sn_program == program 303 && sname->sn_version == version) { 304 SLIST_REMOVE(&svc_rpc_gss_svc_names, sname, 305 svc_rpc_gss_svc_name, sn_link); 306 sx_xunlock(&svc_rpc_gss_lock); 307 gss_release_cred(&min_stat, &sname->sn_cred); 308 free(sname->sn_principal, M_RPC); 309 mem_free(sname, sizeof(*sname)); 310 return;
|
189 }
| 311 }
|
190 if ((rnh = nep->ne_rtable[i]) == NULL) { 191 error = ENOBUFS; 192 vfs_mount_error(mp, "%s %s %d", 193 "Unable to initialize radix node head ", 194 "for address family", i); 195 goto out; 196 }
| |
197 }
| 312 }
|
198 RADIX_NODE_HEAD_LOCK(rnh); 199 rn = (*rnh->rnh_addaddr)(saddr, smask, rnh, np->netc_rnodes); 200 RADIX_NODE_HEAD_UNLOCK(rnh); 201 if (rn == NULL || np != (struct netcred *)rn) { /* already exists */ 202 error = EPERM; 203 vfs_mount_error(mp, "Invalid radix node head, rn: %p %p", 204 rn, np); 205 goto out;
| 313 sx_xunlock(&svc_rpc_gss_lock); 314} 315 316bool_t 317rpc_gss_get_principal_name(rpc_gss_principal_t *principal, 318 const char *mech, const char *name, const char *node, const char *domain) 319{ 320 OM_uint32 maj_stat, min_stat; 321 gss_OID mech_oid; 322 size_t namelen; 323 gss_buffer_desc buf; 324 gss_name_t gss_name, gss_mech_name; 325 rpc_gss_principal_t result; 326 327 if (!rpc_gss_mech_to_oid(mech, &mech_oid)) 328 return (FALSE); 329 330 /* 331 * Construct a gss_buffer containing the full name formatted 332 * as "name/node@domain" where node and domain are optional. 333 */ 334 namelen = strlen(name); 335 if (node) { 336 namelen += strlen(node) + 1;
|
206 }
| 337 }
|
207 np->netc_exflags = argp->ex_flags; 208 np->netc_anon = crget(); 209 np->netc_anon->cr_uid = argp->ex_anon.cr_uid; 210 crsetgroups(np->netc_anon, argp->ex_anon.cr_ngroups, 211 np->netc_anon->cr_groups); 212 np->netc_anon->cr_prison = &prison0; 213 prison_hold(np->netc_anon->cr_prison); 214 np->netc_numsecflavors = argp->ex_numsecflavors; 215 bcopy(argp->ex_secflavors, np->netc_secflavors, 216 sizeof(np->netc_secflavors)); 217 return (0); 218out: 219 free(np, M_NETADDR); 220 return (error);
| 338 if (domain) { 339 namelen += strlen(domain) + 1; 340 } 341 342 buf.value = mem_alloc(namelen); 343 buf.length = namelen; 344 strcpy((char *) buf.value, name); 345 if (node) { 346 strcat((char *) buf.value, "/"); 347 strcat((char *) buf.value, node); 348 } 349 if (domain) { 350 strcat((char *) buf.value, "@"); 351 strcat((char *) buf.value, domain); 352 } 353 354 /* 355 * Convert that to a gss_name_t and then convert that to a 356 * mechanism name in the selected mechanism. 357 */ 358 maj_stat = gss_import_name(&min_stat, &buf, 359 GSS_C_NT_USER_NAME, &gss_name); 360 mem_free(buf.value, buf.length); 361 if (maj_stat != GSS_S_COMPLETE) { 362 rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat); 363 return (FALSE); 364 } 365 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid, 366 &gss_mech_name); 367 if (maj_stat != GSS_S_COMPLETE) { 368 rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat, 369 min_stat); 370 gss_release_name(&min_stat, &gss_name); 371 return (FALSE); 372 } 373 gss_release_name(&min_stat, &gss_name); 374 375 /* 376 * Export the mechanism name and use that to construct the 377 * rpc_gss_principal_t result. 378 */ 379 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf); 380 if (maj_stat != GSS_S_COMPLETE) { 381 rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat); 382 gss_release_name(&min_stat, &gss_mech_name); 383 return (FALSE); 384 } 385 gss_release_name(&min_stat, &gss_mech_name); 386 387 result = mem_alloc(sizeof(int) + buf.length); 388 if (!result) { 389 gss_release_buffer(&min_stat, &buf); 390 return (FALSE); 391 } 392 result->len = buf.length; 393 memcpy(result->name, buf.value, buf.length); 394 gss_release_buffer(&min_stat, &buf); 395 396 *principal = result; 397 return (TRUE);
|
221} 222
| 398} 399
|
223/* Helper for vfs_free_addrlist. */ 224/* ARGSUSED */
| 400bool_t 401rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred, 402 rpc_gss_ucred_t **ucred, void **cookie) 403{ 404 struct svc_rpc_gss_cookedcred *cc; 405 struct svc_rpc_gss_client *client; 406 407 if (req->rq_cred.oa_flavor != RPCSEC_GSS) 408 return (FALSE); 409 410 cc = req->rq_clntcred; 411 client = cc->cc_client; 412 if (rcred) 413 *rcred = &client->cl_rawcred; 414 if (ucred) 415 *ucred = &client->cl_ucred; 416 if (cookie) 417 *cookie = client->cl_cookie; 418 return (TRUE); 419} 420 421/* 422 * This simpler interface is used by svc_getcred to copy the cred data 423 * into a kernel cred structure. 424 */
|
225static int
| 425static int
|
226vfs_free_netcred(struct radix_node *rn, void *w)
| 426rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
|
227{
| 427{
|
228 struct radix_node_head *rnh = (struct radix_node_head *) w; 229 struct ucred *cred;
| 428 struct ucred *cr; 429 struct svc_rpc_gss_cookedcred *cc; 430 struct svc_rpc_gss_client *client; 431 rpc_gss_ucred_t *uc;
|
230
| 432
|
231 (*rnh->rnh_deladdr) (rn->rn_key, rn->rn_mask, rnh); 232 cred = ((struct netcred *)rn)->netc_anon; 233 if (cred != NULL) 234 crfree(cred); 235 free(rn, M_NETADDR); 236 return (0);
| 433 if (req->rq_cred.oa_flavor != RPCSEC_GSS) 434 return (FALSE); 435 436 cc = req->rq_clntcred; 437 client = cc->cc_client; 438 439 if (flavorp) 440 *flavorp = client->cl_rpcflavor; 441 442 if (client->cl_cred) { 443 *crp = crhold(client->cl_cred); 444 return (TRUE); 445 } 446 447 uc = &client->cl_ucred; 448 cr = client->cl_cred = crget(); 449 cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid; 450 cr->cr_rgid = cr->cr_svgid = uc->gid; 451 crsetgroups(cr, uc->gidlen, uc->gidlist); 452 *crp = crhold(cr); 453 454 return (TRUE);
|
237} 238
| 455} 456
|
| 457int 458rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len) 459{ 460 struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred; 461 struct svc_rpc_gss_client *client = cc->cc_client; 462 int want_conf; 463 OM_uint32 max; 464 OM_uint32 maj_stat, min_stat; 465 int result; 466 467 switch (client->cl_rawcred.service) { 468 case rpc_gss_svc_none: 469 return (max_tp_unit_len); 470 break; 471 472 case rpc_gss_svc_default: 473 case rpc_gss_svc_integrity: 474 want_conf = FALSE; 475 break; 476 477 case rpc_gss_svc_privacy: 478 want_conf = TRUE; 479 break; 480 481 default: 482 return (0); 483 } 484 485 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf, 486 client->cl_qop, max_tp_unit_len, &max); 487 488 if (maj_stat == GSS_S_COMPLETE) { 489 result = (int) max; 490 if (result < 0) 491 result = 0; 492 return (result); 493 } else { 494 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech, 495 maj_stat, min_stat); 496 return (0); 497 } 498} 499 500static struct svc_rpc_gss_client * 501svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id) 502{ 503 struct svc_rpc_gss_client *client; 504 struct svc_rpc_gss_client_list *list; 505 unsigned long hostid; 506 507 rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id); 508 509 getcredhostid(curthread->td_ucred, &hostid); 510 if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec) 511 return (NULL); 512 513 list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE]; 514 sx_xlock(&svc_rpc_gss_lock); 515 TAILQ_FOREACH(client, list, cl_link) { 516 if (client->cl_id.ci_id == id->ci_id) { 517 /* 518 * Move this client to the front of the LRU 519 * list. 520 */ 521 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 522 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, 523 cl_alllink); 524 refcount_acquire(&client->cl_refs); 525 break; 526 } 527 } 528 sx_xunlock(&svc_rpc_gss_lock); 529 530 return (client); 531} 532 533static struct svc_rpc_gss_client * 534svc_rpc_gss_create_client(void) 535{ 536 struct svc_rpc_gss_client *client; 537 struct svc_rpc_gss_client_list *list; 538 unsigned long hostid; 539 540 rpc_gss_log_debug("in svc_rpc_gss_create_client()"); 541 542 client = mem_alloc(sizeof(struct svc_rpc_gss_client)); 543 memset(client, 0, sizeof(struct svc_rpc_gss_client)); 544 refcount_init(&client->cl_refs, 1); 545 sx_init(&client->cl_lock, "GSS-client"); 546 getcredhostid(curthread->td_ucred, &hostid); 547 client->cl_id.ci_hostid = hostid; 548 client->cl_id.ci_boottime = boottime.tv_sec; 549 client->cl_id.ci_id = svc_rpc_gss_next_clientid++; 550 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; 551 sx_xlock(&svc_rpc_gss_lock); 552 TAILQ_INSERT_HEAD(list, client, cl_link); 553 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink); 554 svc_rpc_gss_client_count++; 555 sx_xunlock(&svc_rpc_gss_lock); 556 557 /* 558 * Start the client off with a short expiration time. We will 559 * try to get a saner value from the client creds later. 560 */ 561 client->cl_state = CLIENT_NEW; 562 client->cl_locked = FALSE; 563 client->cl_expiration = time_uptime + 5*60; 564 565 return (client); 566} 567 568static void 569svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client) 570{ 571 OM_uint32 min_stat; 572 573 rpc_gss_log_debug("in svc_rpc_gss_destroy_client()"); 574 575 if (client->cl_ctx) 576 gss_delete_sec_context(&min_stat, 577 &client->cl_ctx, GSS_C_NO_BUFFER); 578 579 if (client->cl_cname) 580 gss_release_name(&min_stat, &client->cl_cname); 581 582 if (client->cl_rawcred.client_principal) 583 mem_free(client->cl_rawcred.client_principal, 584 sizeof(*client->cl_rawcred.client_principal) 585 + client->cl_rawcred.client_principal->len); 586 587 if (client->cl_cred) 588 crfree(client->cl_cred); 589 590 sx_destroy(&client->cl_lock); 591 mem_free(client, sizeof(*client)); 592} 593
|
239/*
| 594/*
|
240 * Free the net address hash lists that are hanging off the mount points.
| 595 * Drop a reference to a client and free it if that was the last reference.
|
241 */ 242static void
| 596 */ 597static void
|
243vfs_free_addrlist(struct netexport *nep)
| 598svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
|
244{
| 599{
|
245 int i; 246 struct radix_node_head *rnh; 247 struct ucred *cred;
| |
248
| 600
|
249 for (i = 0; i <= AF_MAX; i++) { 250 if ((rnh = nep->ne_rtable[i])) { 251 RADIX_NODE_HEAD_LOCK(rnh); 252 (*rnh->rnh_walktree) (rnh, vfs_free_netcred, rnh); 253 RADIX_NODE_HEAD_UNLOCK(rnh); 254 RADIX_NODE_HEAD_DESTROY(rnh); 255 free(rnh, M_RTABLE); 256 nep->ne_rtable[i] = NULL; /* not SMP safe XXX */
| 601 if (!refcount_release(&client->cl_refs)) 602 return; 603 svc_rpc_gss_destroy_client(client); 604} 605 606/* 607 * Remove a client from our global lists and free it if we can. 608 */ 609static void 610svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client) 611{ 612 struct svc_rpc_gss_client_list *list; 613 614 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; 615 sx_xlock(&svc_rpc_gss_lock); 616 TAILQ_REMOVE(list, client, cl_link); 617 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 618 svc_rpc_gss_client_count--; 619 sx_xunlock(&svc_rpc_gss_lock); 620 svc_rpc_gss_release_client(client); 621} 622 623static void 624svc_rpc_gss_timeout_clients(void) 625{ 626 struct svc_rpc_gss_client *client; 627 struct svc_rpc_gss_client *nclient; 628 time_t now = time_uptime; 629 630 rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()"); 631 632 /* 633 * First enforce the max client limit. We keep 634 * svc_rpc_gss_clients in LRU order. 635 */ 636 while (svc_rpc_gss_client_count > CLIENT_MAX) 637 svc_rpc_gss_forget_client(TAILQ_LAST(&svc_rpc_gss_clients, 638 svc_rpc_gss_client_list)); 639 TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) { 640 if (client->cl_state == CLIENT_STALE 641 || now > client->cl_expiration) { 642 rpc_gss_log_debug("expiring client %p", client); 643 svc_rpc_gss_forget_client(client);
|
257 } 258 }
| 644 } 645 }
|
259 cred = nep->ne_defexported.netc_anon; 260 if (cred != NULL) 261 crfree(cred); 262
| |
263} 264
| 646} 647
|
| 648#ifdef DEBUG
|
265/*
| 649/*
|
266 * High level function to manipulate export options on a mount point 267 * and the passed in netexport. 268 * Struct export_args *argp is the variable used to twiddle options, 269 * the structure is described in sys/mount.h
| 650 * OID<->string routines. These are uuuuugly.
|
270 */
| 651 */
|
271int 272vfs_export(struct mount *mp, struct export_args *argp)
| 652static OM_uint32 653gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
|
273{
| 654{
|
274 struct netexport *nep; 275 int error;
| 655 char numstr[128]; 656 unsigned long number; 657 int numshift; 658 size_t string_length; 659 size_t i; 660 unsigned char *cp; 661 char *bp;
|
276
| 662
|
277 if (argp->ex_numsecflavors < 0 278 || argp->ex_numsecflavors >= MAXSECFLAVORS) 279 return (EINVAL);
| 663 /* Decoded according to krb5/gssapi_krb5.c */
|
280
| 664
|
281 error = 0; 282 lockmgr(&mp->mnt_explock, LK_EXCLUSIVE, NULL); 283 nep = mp->mnt_export; 284 if (argp->ex_flags & MNT_DELEXPORT) { 285 if (nep == NULL) { 286 error = ENOENT; 287 goto out;
| 665 /* First determine the size of the string */ 666 string_length = 0; 667 number = 0; 668 numshift = 0; 669 cp = (unsigned char *) oid->elements; 670 number = (unsigned long) cp[0]; 671 sprintf(numstr, "%ld ", number/40); 672 string_length += strlen(numstr); 673 sprintf(numstr, "%ld ", number%40); 674 string_length += strlen(numstr); 675 for (i=1; i<oid->length; i++) { 676 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { 677 number = (number << 7) | (cp[i] & 0x7f); 678 numshift += 7;
|
288 }
| 679 }
|
289 if (mp->mnt_flag & MNT_EXPUBLIC) { 290 vfs_setpublicfs(NULL, NULL, NULL); 291 MNT_ILOCK(mp); 292 mp->mnt_flag &= ~MNT_EXPUBLIC; 293 MNT_IUNLOCK(mp);
| 680 else { 681 *minor_status = 0; 682 return(GSS_S_FAILURE);
|
294 }
| 683 }
|
295 vfs_free_addrlist(nep); 296 mp->mnt_export = NULL; 297 free(nep, M_MOUNT); 298 nep = NULL; 299 MNT_ILOCK(mp); 300 mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED); 301 MNT_IUNLOCK(mp);
| 684 if ((cp[i] & 0x80) == 0) { 685 sprintf(numstr, "%ld ", number); 686 string_length += strlen(numstr); 687 number = 0; 688 numshift = 0; 689 }
|
302 }
| 690 }
|
303 if (argp->ex_flags & MNT_EXPORTED) { 304 if (nep == NULL) { 305 nep = malloc(sizeof(struct netexport), M_MOUNT, M_WAITOK | M_ZERO); 306 mp->mnt_export = nep;
| 691 /* 692 * If we get here, we've calculated the length of "n n n ... n ". Add 4 693 * here for "{ " and "}\0". 694 */ 695 string_length += 4; 696 if ((bp = (char *) mem_alloc(string_length))) { 697 strcpy(bp, "{ "); 698 number = (unsigned long) cp[0]; 699 sprintf(numstr, "%ld ", number/40); 700 strcat(bp, numstr); 701 sprintf(numstr, "%ld ", number%40); 702 strcat(bp, numstr); 703 number = 0; 704 cp = (unsigned char *) oid->elements; 705 for (i=1; i<oid->length; i++) { 706 number = (number << 7) | (cp[i] & 0x7f); 707 if ((cp[i] & 0x80) == 0) { 708 sprintf(numstr, "%ld ", number); 709 strcat(bp, numstr); 710 number = 0; 711 }
|
307 }
| 712 }
|
308 if (argp->ex_flags & MNT_EXPUBLIC) { 309 if ((error = vfs_setpublicfs(mp, nep, argp)) != 0) 310 goto out; 311 MNT_ILOCK(mp); 312 mp->mnt_flag |= MNT_EXPUBLIC; 313 MNT_IUNLOCK(mp); 314 } 315 if ((error = vfs_hang_addrlist(mp, nep, argp))) 316 goto out; 317 MNT_ILOCK(mp); 318 mp->mnt_flag |= MNT_EXPORTED; 319 MNT_IUNLOCK(mp);
| 713 strcat(bp, "}"); 714 oid_str->length = strlen(bp)+1; 715 oid_str->value = (void *) bp; 716 *minor_status = 0; 717 return(GSS_S_COMPLETE);
|
320 }
| 718 }
|
| 719 *minor_status = 0; 720 return(GSS_S_FAILURE); 721} 722#endif
|
321
| 723
|
322out: 323 lockmgr(&mp->mnt_explock, LK_RELEASE, NULL);
| 724static void 725svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, 726 const gss_name_t name) 727{ 728 OM_uint32 maj_stat, min_stat; 729 rpc_gss_ucred_t *uc = &client->cl_ucred; 730 int numgroups; 731 732 uc->uid = 65534; 733 uc->gid = 65534; 734 uc->gidlist = client->cl_gid_storage; 735 736 numgroups = NGROUPS; 737 maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech, 738 &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]); 739 if (GSS_ERROR(maj_stat)) 740 uc->gidlen = 0; 741 else 742 uc->gidlen = numgroups; 743} 744 745static void 746svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client) 747{ 748 static gss_OID_desc krb5_mech_oid = 749 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; 750
|
324 /*
| 751 /*
|
325 * Once we have executed the vfs_export() command, we do 326 * not want to keep the "export" option around in the 327 * options list, since that will cause subsequent MNT_UPDATE 328 * calls to fail. The export information is saved in 329 * mp->mnt_export, so we can safely delete the "export" mount option 330 * here.
| 752 * Attempt to translate mech type and service into a 753 * 'pseudo flavor'. Hardwire in krb5 support for now.
|
331 */
| 754 */
|
332 vfs_deleteopt(mp->mnt_optnew, "export"); 333 vfs_deleteopt(mp->mnt_opt, "export"); 334 return (error);
| 755 if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) { 756 switch (client->cl_rawcred.service) { 757 case rpc_gss_svc_default: 758 case rpc_gss_svc_none: 759 client->cl_rpcflavor = RPCSEC_GSS_KRB5; 760 break; 761 case rpc_gss_svc_integrity: 762 client->cl_rpcflavor = RPCSEC_GSS_KRB5I; 763 break; 764 case rpc_gss_svc_privacy: 765 client->cl_rpcflavor = RPCSEC_GSS_KRB5P; 766 break; 767 } 768 } else { 769 client->cl_rpcflavor = RPCSEC_GSS; 770 }
|
335} 336
| 771} 772
|
337/* 338 * Set the publicly exported filesystem (WebNFS). Currently, only 339 * one public filesystem is possible in the spec (RFC 2054 and 2055) 340 */ 341int 342vfs_setpublicfs(struct mount *mp, struct netexport *nep, 343 struct export_args *argp)
| 773static bool_t 774svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, 775 struct svc_req *rqst, 776 struct rpc_gss_init_res *gr, 777 struct rpc_gss_cred *gc)
|
344{
| 778{
|
345 int error; 346 struct vnode *rvp; 347 char *cp;
| 779 gss_buffer_desc recv_tok; 780 gss_OID mech; 781 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; 782 OM_uint32 cred_lifetime; 783 struct svc_rpc_gss_svc_name *sname;
|
348
| 784
|
| 785 rpc_gss_log_debug("in svc_rpc_gss_accept_context()"); 786 787 /* Deserialize arguments. */ 788 memset(&recv_tok, 0, sizeof(recv_tok)); 789 790 if (!svc_getargs(rqst, 791 (xdrproc_t) xdr_gss_buffer_desc, 792 (caddr_t) &recv_tok)) { 793 client->cl_state = CLIENT_STALE; 794 return (FALSE); 795 } 796
|
349 /*
| 797 /*
|
350 * mp == NULL -> invalidate the current info, the FS is 351 * no longer exported. May be called from either vfs_export 352 * or unmount, so check if it hasn't already been done.
| 798 * First time round, try all the server names we have until 799 * one matches. Afterwards, stick with that one.
|
353 */
| 800 */
|
354 if (mp == NULL) { 355 if (nfs_pub.np_valid) { 356 nfs_pub.np_valid = 0; 357 if (nfs_pub.np_index != NULL) { 358 free(nfs_pub.np_index, M_TEMP); 359 nfs_pub.np_index = NULL;
| 801 sx_xlock(&svc_rpc_gss_lock); 802 if (!client->cl_sname) { 803 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 804 if (sname->sn_program == rqst->rq_prog 805 && sname->sn_version == rqst->rq_vers) { 806 retry: 807 gr->gr_major = gss_accept_sec_context( 808 &gr->gr_minor, 809 &client->cl_ctx, 810 sname->sn_cred, 811 &recv_tok, 812 GSS_C_NO_CHANNEL_BINDINGS, 813 &client->cl_cname, 814 &mech, 815 &gr->gr_token, 816 &ret_flags, 817 &cred_lifetime, 818 &client->cl_creds); 819 if (gr->gr_major == 820 GSS_S_CREDENTIALS_EXPIRED) { 821 /* 822 * Either our creds really did 823 * expire or gssd was 824 * restarted. 825 */ 826 if (rpc_gss_acquire_svc_cred(sname)) 827 goto retry; 828 } 829 client->cl_sname = sname; 830 break;
|
360 } 361 }
| 831 } 832 }
|
362 return (0);
| 833 if (!sname) { 834 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 835 (char *) &recv_tok); 836 sx_xunlock(&svc_rpc_gss_lock); 837 return (FALSE); 838 } 839 } else { 840 gr->gr_major = gss_accept_sec_context( 841 &gr->gr_minor, 842 &client->cl_ctx, 843 client->cl_sname->sn_cred, 844 &recv_tok, 845 GSS_C_NO_CHANNEL_BINDINGS, 846 &client->cl_cname, 847 &mech, 848 &gr->gr_token, 849 &ret_flags, 850 &cred_lifetime, 851 NULL);
|
363 }
| 852 }
|
| 853 sx_xunlock(&svc_rpc_gss_lock); 854 855 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
|
364 365 /*
| 856 857 /*
|
366 * Only one allowed at a time.
| 858 * If we get an error from gss_accept_sec_context, send the 859 * reply anyway so that the client gets a chance to see what 860 * is wrong.
|
367 */
| 861 */
|
368 if (nfs_pub.np_valid != 0 && mp != nfs_pub.np_mount) 369 return (EBUSY);
| 862 if (gr->gr_major != GSS_S_COMPLETE && 863 gr->gr_major != GSS_S_CONTINUE_NEEDED) { 864 rpc_gss_log_status("accept_sec_context", client->cl_mech, 865 gr->gr_major, gr->gr_minor); 866 client->cl_state = CLIENT_STALE; 867 return (TRUE); 868 }
|
370
| 869
|
371 /* 372 * Get real filehandle for root of exported FS. 373 */ 374 bzero(&nfs_pub.np_handle, sizeof(nfs_pub.np_handle)); 375 nfs_pub.np_handle.fh_fsid = mp->mnt_stat.f_fsid;
| 870 gr->gr_handle.value = &client->cl_id; 871 gr->gr_handle.length = sizeof(client->cl_id); 872 gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 873 874 /* Save client info. */ 875 client->cl_mech = mech; 876 client->cl_qop = GSS_C_QOP_DEFAULT; 877 client->cl_done_callback = FALSE;
|
376
| 878
|
377 if ((error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp))) 378 return (error);
| 879 if (gr->gr_major == GSS_S_COMPLETE) { 880 gss_buffer_desc export_name;
|
379
| 881
|
380 if ((error = VOP_VPTOFH(rvp, &nfs_pub.np_handle.fh_fid))) 381 return (error);
| 882 /* 883 * Change client expiration time to be near when the 884 * client creds expire (or 24 hours if we can't figure 885 * that out). 886 */ 887 if (cred_lifetime == GSS_C_INDEFINITE) 888 cred_lifetime = time_uptime + 24*60*60;
|
382
| 889
|
383 vput(rvp);
| 890 client->cl_expiration = time_uptime + cred_lifetime;
|
384
| 891
|
| 892 /* 893 * Fill in cred details in the rawcred structure. 894 */ 895 client->cl_rawcred.version = RPCSEC_GSS_VERSION; 896 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 897 maj_stat = gss_export_name(&min_stat, client->cl_cname, 898 &export_name); 899 if (maj_stat != GSS_S_COMPLETE) { 900 rpc_gss_log_status("gss_export_name", client->cl_mech, 901 maj_stat, min_stat); 902 return (FALSE); 903 } 904 client->cl_rawcred.client_principal = 905 mem_alloc(sizeof(*client->cl_rawcred.client_principal) 906 + export_name.length); 907 client->cl_rawcred.client_principal->len = export_name.length; 908 memcpy(client->cl_rawcred.client_principal->name, 909 export_name.value, export_name.length); 910 gss_release_buffer(&min_stat, &export_name); 911 client->cl_rawcred.svc_principal = 912 client->cl_sname->sn_principal; 913 client->cl_rawcred.service = gc->gc_svc; 914 915 /* 916 * Use gss_pname_to_uid to map to unix creds. For 917 * kerberos5, this uses krb5_aname_to_localname. 918 */ 919 svc_rpc_gss_build_ucred(client, client->cl_cname); 920 svc_rpc_gss_set_flavor(client); 921 gss_release_name(&min_stat, &client->cl_cname); 922 923#ifdef DEBUG 924 { 925 gss_buffer_desc mechname; 926 927 gss_oid_to_str(&min_stat, mech, &mechname); 928 929 rpc_gss_log_debug("accepted context for %s with " 930 "<mech %.*s, qop %d, svc %d>", 931 client->cl_rawcred.client_principal->name, 932 mechname.length, (char *)mechname.value, 933 client->cl_qop, client->rawcred.service); 934 935 gss_release_buffer(&min_stat, &mechname); 936 } 937#endif /* DEBUG */ 938 } 939 return (TRUE); 940} 941 942static bool_t 943svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 944 gss_qop_t *qop) 945{ 946 struct opaque_auth *oa; 947 gss_buffer_desc rpcbuf, checksum; 948 OM_uint32 maj_stat, min_stat; 949 gss_qop_t qop_state; 950 int32_t rpchdr[128 / sizeof(int32_t)]; 951 int32_t *buf; 952 953 rpc_gss_log_debug("in svc_rpc_gss_validate()"); 954 955 memset(rpchdr, 0, sizeof(rpchdr)); 956 957 /* Reconstruct RPC header for signing (from xdr_callmsg). */ 958 buf = rpchdr; 959 IXDR_PUT_LONG(buf, msg->rm_xid); 960 IXDR_PUT_ENUM(buf, msg->rm_direction); 961 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 962 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 963 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 964 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 965 oa = &msg->rm_call.cb_cred; 966 IXDR_PUT_ENUM(buf, oa->oa_flavor); 967 IXDR_PUT_LONG(buf, oa->oa_length); 968 if (oa->oa_length) { 969 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 970 buf += RNDUP(oa->oa_length) / sizeof(int32_t); 971 } 972 rpcbuf.value = rpchdr; 973 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 974 975 checksum.value = msg->rm_call.cb_verf.oa_base; 976 checksum.length = msg->rm_call.cb_verf.oa_length; 977 978 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 979 &qop_state); 980 981 if (maj_stat != GSS_S_COMPLETE) { 982 rpc_gss_log_status("gss_verify_mic", client->cl_mech, 983 maj_stat, min_stat); 984 client->cl_state = CLIENT_STALE; 985 return (FALSE); 986 } 987 988 *qop = qop_state; 989 return (TRUE); 990} 991 992static bool_t 993svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 994 struct svc_req *rqst, u_int seq) 995{ 996 gss_buffer_desc signbuf; 997 gss_buffer_desc mic; 998 OM_uint32 maj_stat, min_stat; 999 uint32_t nseq; 1000 1001 rpc_gss_log_debug("in svc_rpc_gss_nextverf()"); 1002 1003 nseq = htonl(seq); 1004 signbuf.value = &nseq; 1005 signbuf.length = sizeof(nseq); 1006 1007 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 1008 &signbuf, &mic); 1009 1010 if (maj_stat != GSS_S_COMPLETE) { 1011 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 1012 client->cl_state = CLIENT_STALE; 1013 return (FALSE); 1014 } 1015 1016 KASSERT(mic.length <= MAX_AUTH_BYTES, 1017 ("MIC too large for RPCSEC_GSS")); 1018 1019 rqst->rq_verf.oa_flavor = RPCSEC_GSS; 1020 rqst->rq_verf.oa_length = mic.length; 1021 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length); 1022 1023 gss_release_buffer(&min_stat, &mic); 1024 1025 return (TRUE); 1026} 1027 1028static bool_t 1029svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 1030{ 1031 struct svc_rpc_gss_callback *scb; 1032 rpc_gss_lock_t lock; 1033 void *cookie; 1034 bool_t cb_res; 1035 bool_t result; 1036
|
385 /*
| 1037 /*
|
386 * If an indexfile was specified, pull it in.
| 1038 * See if we have a callback for this guy.
|
387 */
| 1039 */
|
388 if (argp->ex_indexfile != NULL) { 389 if (nfs_pub.np_index != NULL) 390 nfs_pub.np_index = malloc(MAXNAMLEN + 1, M_TEMP, 391 M_WAITOK); 392 error = copyinstr(argp->ex_indexfile, nfs_pub.np_index, 393 MAXNAMLEN, (size_t *)0); 394 if (!error) {
| 1040 result = TRUE; 1041 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 1042 if (scb->cb_callback.program == rqst->rq_prog 1043 && scb->cb_callback.version == rqst->rq_vers) {
|
395 /*
| 1044 /*
|
396 * Check for illegal filenames.
| 1045 * This one matches. Call the callback and see 1046 * if it wants to veto or something.
|
397 */
| 1047 */
|
398 for (cp = nfs_pub.np_index; *cp; cp++) { 399 if (*cp == '/') { 400 error = EINVAL; 401 break; 402 }
| 1048 lock.locked = FALSE; 1049 lock.raw_cred = &client->cl_rawcred; 1050 cb_res = scb->cb_callback.callback(rqst, 1051 client->cl_creds, 1052 client->cl_ctx, 1053 &lock, 1054 &cookie); 1055 1056 if (!cb_res) { 1057 client->cl_state = CLIENT_STALE; 1058 result = FALSE; 1059 break;
|
403 }
| 1060 }
|
| 1061 1062 /* 1063 * The callback accepted the connection - it 1064 * is responsible for freeing client->cl_creds 1065 * now. 1066 */ 1067 client->cl_creds = GSS_C_NO_CREDENTIAL; 1068 client->cl_locked = lock.locked; 1069 client->cl_cookie = cookie; 1070 return (TRUE);
|
404 }
| 1071 }
|
405 if (error) { 406 free(nfs_pub.np_index, M_TEMP); 407 nfs_pub.np_index = NULL; 408 return (error);
| 1072 } 1073 1074 /* 1075 * Either no callback exists for this program/version or one 1076 * of the callbacks rejected the connection. We just need to 1077 * clean up the delegated client creds, if any. 1078 */ 1079 if (client->cl_creds) { 1080 OM_uint32 min_ver; 1081 gss_release_cred(&min_ver, &client->cl_creds); 1082 } 1083 return (result); 1084} 1085 1086static bool_t 1087svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 1088{ 1089 u_int32_t offset; 1090 int word, bit; 1091 bool_t result; 1092 1093 sx_xlock(&client->cl_lock); 1094 if (seq <= client->cl_seqlast) { 1095 /* 1096 * The request sequence number is less than 1097 * the largest we have seen so far. If it is 1098 * outside the window or if we have seen a 1099 * request with this sequence before, silently 1100 * discard it. 1101 */ 1102 offset = client->cl_seqlast - seq; 1103 if (offset >= SVC_RPC_GSS_SEQWINDOW) { 1104 result = FALSE; 1105 goto out;
|
409 }
| 1106 }
|
| 1107 word = offset / 32; 1108 bit = offset % 32; 1109 if (client->cl_seqmask[word] & (1 << bit)) { 1110 result = FALSE; 1111 goto out; 1112 }
|
410 } 411
| 1113 } 1114
|
412 nfs_pub.np_mount = mp; 413 nfs_pub.np_valid = 1; 414 return (0);
| 1115 result = TRUE; 1116out: 1117 sx_xunlock(&client->cl_lock); 1118 return (result);
|
415} 416
| 1119} 1120
|
417/* 418 * Used by the filesystems to determine if a given network address 419 * (passed in 'nam') is present in their exports list, returns a pointer 420 * to struct netcred so that the filesystem can examine it for 421 * access rights (read/write/etc). 422 */ 423static struct netcred * 424vfs_export_lookup(struct mount *mp, struct sockaddr *nam)
| 1121static void 1122svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
|
425{
| 1123{
|
426 struct netexport *nep; 427 register struct netcred *np; 428 register struct radix_node_head *rnh; 429 struct sockaddr *saddr;
| 1124 int offset, i, word, bit; 1125 uint32_t carry, newcarry;
|
430
| 1126
|
431 nep = mp->mnt_export; 432 if (nep == NULL) 433 return (NULL); 434 np = NULL; 435 if (mp->mnt_flag & MNT_EXPORTED) {
| 1127 sx_xlock(&client->cl_lock); 1128 if (seq > client->cl_seqlast) {
|
436 /*
| 1129 /*
|
437 * Lookup in the export list first.
| 1130 * This request has a sequence number greater 1131 * than any we have seen so far. Advance the 1132 * seq window and set bit zero of the window 1133 * (which corresponds to the new sequence 1134 * number)
|
438 */
| 1135 */
|
439 if (nam != NULL) { 440 saddr = nam; 441 rnh = nep->ne_rtable[saddr->sa_family]; 442 if (rnh != NULL) { 443 RADIX_NODE_HEAD_RLOCK(rnh); 444 np = (struct netcred *) 445 (*rnh->rnh_matchaddr)(saddr, rnh); 446 RADIX_NODE_HEAD_RUNLOCK(rnh); 447 if (np && np->netc_rnodes->rn_flags & RNF_ROOT) 448 np = NULL;
| 1136 offset = seq - client->cl_seqlast; 1137 while (offset > 32) { 1138 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 1139 i > 0; i--) { 1140 client->cl_seqmask[i] = client->cl_seqmask[i-1];
|
449 }
| 1141 }
|
| 1142 client->cl_seqmask[0] = 0; 1143 offset -= 32;
|
450 }
| 1144 }
|
| 1145 carry = 0; 1146 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 1147 newcarry = client->cl_seqmask[i] >> (32 - offset); 1148 client->cl_seqmask[i] = 1149 (client->cl_seqmask[i] << offset) | carry; 1150 carry = newcarry; 1151 } 1152 client->cl_seqmask[0] |= 1; 1153 client->cl_seqlast = seq; 1154 } else { 1155 offset = client->cl_seqlast - seq; 1156 word = offset / 32; 1157 bit = offset % 32; 1158 client->cl_seqmask[word] |= (1 << bit); 1159 } 1160 sx_xunlock(&client->cl_lock); 1161} 1162 1163enum auth_stat 1164svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 1165 1166{ 1167 OM_uint32 min_stat; 1168 XDR xdrs; 1169 struct svc_rpc_gss_cookedcred *cc; 1170 struct svc_rpc_gss_client *client; 1171 struct rpc_gss_cred gc; 1172 struct rpc_gss_init_res gr; 1173 gss_qop_t qop; 1174 int call_stat; 1175 enum auth_stat result; 1176 1177 rpc_gss_log_debug("in svc_rpc_gss()"); 1178 1179 /* Garbage collect old clients. */ 1180 svc_rpc_gss_timeout_clients(); 1181 1182 /* Initialize reply. */ 1183 rqst->rq_verf = _null_auth; 1184 1185 /* Deserialize client credentials. */ 1186 if (rqst->rq_cred.oa_length <= 0) 1187 return (AUTH_BADCRED); 1188 1189 memset(&gc, 0, sizeof(gc)); 1190 1191 xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 1192 rqst->rq_cred.oa_length, XDR_DECODE); 1193 1194 if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 1195 XDR_DESTROY(&xdrs); 1196 return (AUTH_BADCRED); 1197 } 1198 XDR_DESTROY(&xdrs); 1199 1200 client = NULL; 1201 1202 /* Check version. */ 1203 if (gc.gc_version != RPCSEC_GSS_VERSION) { 1204 result = AUTH_BADCRED; 1205 goto out; 1206 } 1207 1208 /* Check the proc and find the client (or create it) */ 1209 if (gc.gc_proc == RPCSEC_GSS_INIT) { 1210 if (gc.gc_handle.length != 0) { 1211 result = AUTH_BADCRED; 1212 goto out; 1213 } 1214 client = svc_rpc_gss_create_client(); 1215 refcount_acquire(&client->cl_refs); 1216 } else { 1217 struct svc_rpc_gss_clientid *p; 1218 if (gc.gc_handle.length != sizeof(*p)) { 1219 result = AUTH_BADCRED; 1220 goto out; 1221 } 1222 p = gc.gc_handle.value; 1223 client = svc_rpc_gss_find_client(p); 1224 if (!client) { 1225 /* 1226 * Can't find the client - we may have 1227 * destroyed it - tell the other side to 1228 * re-authenticate. 1229 */ 1230 result = RPCSEC_GSS_CREDPROBLEM; 1231 goto out; 1232 } 1233 } 1234 cc = rqst->rq_clntcred; 1235 cc->cc_client = client; 1236 cc->cc_service = gc.gc_svc; 1237 cc->cc_seq = gc.gc_seq; 1238 1239 /* 1240 * The service and sequence number must be ignored for 1241 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 1242 */ 1243 if (gc.gc_proc != RPCSEC_GSS_INIT 1244 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
|
451 /*
| 1245 /*
|
452 * If no address match, use the default if it exists.
| 1246 * Check for sequence number overflow.
|
453 */
| 1247 */
|
454 if (np == NULL && mp->mnt_flag & MNT_DEFEXPORTED) 455 np = &nep->ne_defexported;
| 1248 if (gc.gc_seq >= MAXSEQ) { 1249 result = RPCSEC_GSS_CTXPROBLEM; 1250 goto out; 1251 } 1252 1253 /* 1254 * Check for valid service. 1255 */ 1256 if (gc.gc_svc != rpc_gss_svc_none && 1257 gc.gc_svc != rpc_gss_svc_integrity && 1258 gc.gc_svc != rpc_gss_svc_privacy) { 1259 result = AUTH_BADCRED; 1260 goto out; 1261 }
|
456 }
| 1262 }
|
457 return (np);
| 1263 1264 /* Handle RPCSEC_GSS control procedure. */ 1265 switch (gc.gc_proc) { 1266 1267 case RPCSEC_GSS_INIT: 1268 case RPCSEC_GSS_CONTINUE_INIT: 1269 if (rqst->rq_proc != NULLPROC) { 1270 result = AUTH_REJECTEDCRED; 1271 break; 1272 } 1273 1274 memset(&gr, 0, sizeof(gr)); 1275 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 1276 result = AUTH_REJECTEDCRED; 1277 break; 1278 } 1279 1280 if (gr.gr_major == GSS_S_COMPLETE) { 1281 /* 1282 * We borrow the space for the call verf to 1283 * pack our reply verf. 1284 */ 1285 rqst->rq_verf = msg->rm_call.cb_verf; 1286 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 1287 result = AUTH_REJECTEDCRED; 1288 break; 1289 } 1290 } else { 1291 rqst->rq_verf = _null_auth; 1292 } 1293 1294 call_stat = svc_sendreply(rqst, 1295 (xdrproc_t) xdr_rpc_gss_init_res, 1296 (caddr_t) &gr); 1297 1298 gss_release_buffer(&min_stat, &gr.gr_token); 1299 1300 if (!call_stat) { 1301 result = AUTH_FAILED; 1302 break; 1303 } 1304 1305 if (gr.gr_major == GSS_S_COMPLETE) 1306 client->cl_state = CLIENT_ESTABLISHED; 1307 1308 result = RPCSEC_GSS_NODISPATCH; 1309 break; 1310 1311 case RPCSEC_GSS_DATA: 1312 case RPCSEC_GSS_DESTROY: 1313 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 1314 result = RPCSEC_GSS_NODISPATCH; 1315 break; 1316 } 1317 1318 if (!svc_rpc_gss_validate(client, msg, &qop)) { 1319 result = RPCSEC_GSS_CREDPROBLEM; 1320 break; 1321 } 1322 1323 /* 1324 * We borrow the space for the call verf to pack our 1325 * reply verf. 1326 */ 1327 rqst->rq_verf = msg->rm_call.cb_verf; 1328 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 1329 result = RPCSEC_GSS_CTXPROBLEM; 1330 break; 1331 } 1332 1333 svc_rpc_gss_update_seq(client, gc.gc_seq); 1334 1335 /* 1336 * Change the SVCAUTH ops on the request to point at 1337 * our own code so that we can unwrap the arguments 1338 * and wrap the result. The caller will re-set this on 1339 * every request to point to a set of null wrap/unwrap 1340 * methods. Acquire an extra reference to the client 1341 * which will be released by svc_rpc_gss_release() 1342 * after the request has finished processing. 1343 */ 1344 refcount_acquire(&client->cl_refs); 1345 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops; 1346 rqst->rq_auth.svc_ah_private = cc; 1347 1348 if (gc.gc_proc == RPCSEC_GSS_DATA) { 1349 /* 1350 * We might be ready to do a callback to the server to 1351 * see if it wants to accept/reject the connection. 1352 */ 1353 sx_xlock(&client->cl_lock); 1354 if (!client->cl_done_callback) { 1355 client->cl_done_callback = TRUE; 1356 client->cl_qop = qop; 1357 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1358 client->cl_rawcred.mechanism, qop); 1359 if (!svc_rpc_gss_callback(client, rqst)) { 1360 result = AUTH_REJECTEDCRED; 1361 sx_xunlock(&client->cl_lock); 1362 break; 1363 } 1364 } 1365 sx_xunlock(&client->cl_lock); 1366 1367 /* 1368 * If the server has locked this client to a 1369 * particular service+qop pair, enforce that 1370 * restriction now. 1371 */ 1372 if (client->cl_locked) { 1373 if (client->cl_rawcred.service != gc.gc_svc) { 1374 result = AUTH_FAILED; 1375 break; 1376 } else if (client->cl_qop != qop) { 1377 result = AUTH_BADVERF; 1378 break; 1379 } 1380 } 1381 1382 /* 1383 * If the qop changed, look up the new qop 1384 * name for rawcred. 1385 */ 1386 if (client->cl_qop != qop) { 1387 client->cl_qop = qop; 1388 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1389 client->cl_rawcred.mechanism, qop); 1390 } 1391 1392 /* 1393 * Make sure we use the right service value 1394 * for unwrap/wrap. 1395 */ 1396 if (client->cl_rawcred.service != gc.gc_svc) { 1397 client->cl_rawcred.service = gc.gc_svc; 1398 svc_rpc_gss_set_flavor(client); 1399 } 1400 1401 result = AUTH_OK; 1402 } else { 1403 if (rqst->rq_proc != NULLPROC) { 1404 result = AUTH_REJECTEDCRED; 1405 break; 1406 } 1407 1408 call_stat = svc_sendreply(rqst, 1409 (xdrproc_t) xdr_void, (caddr_t) NULL); 1410 1411 if (!call_stat) { 1412 result = AUTH_FAILED; 1413 break; 1414 } 1415 1416 svc_rpc_gss_forget_client(client); 1417 1418 result = RPCSEC_GSS_NODISPATCH; 1419 break; 1420 } 1421 break; 1422 1423 default: 1424 result = AUTH_BADCRED; 1425 break; 1426 } 1427out: 1428 if (client) 1429 svc_rpc_gss_release_client(client); 1430 1431 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 1432 return (result);
|
458} 459
| 1433} 1434
|
460/* 461 * XXX: This comment comes from the deprecated ufs_check_export() 462 * XXX: and may not entirely apply, but lacking something better: 463 * This is the generic part of fhtovp called after the underlying 464 * filesystem has validated the file handle. 465 * 466 * Verify that a host should have access to a filesystem. 467 */
| 1435static bool_t 1436svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp) 1437{ 1438 struct svc_rpc_gss_cookedcred *cc; 1439 struct svc_rpc_gss_client *client; 1440 1441 rpc_gss_log_debug("in svc_rpc_gss_wrap()");
|
468
| 1442
|
469int 470vfs_stdcheckexp(struct mount *mp, struct sockaddr *nam, int *extflagsp, 471 struct ucred **credanonp, int *numsecflavors, int **secflavors)
| 1443 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1444 client = cc->cc_client; 1445 if (client->cl_state != CLIENT_ESTABLISHED 1446 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) { 1447 return (TRUE); 1448 } 1449 1450 return (xdr_rpc_gss_wrap_data(mp, 1451 client->cl_ctx, client->cl_qop, 1452 cc->cc_service, cc->cc_seq)); 1453} 1454 1455static bool_t 1456svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
|
472{
| 1457{
|
473 struct netcred *np;
| 1458 struct svc_rpc_gss_cookedcred *cc; 1459 struct svc_rpc_gss_client *client;
|
474
| 1460
|
475 lockmgr(&mp->mnt_explock, LK_SHARED, NULL); 476 np = vfs_export_lookup(mp, nam); 477 if (np == NULL) { 478 lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); 479 *credanonp = NULL; 480 return (EACCES);
| 1461 rpc_gss_log_debug("in svc_rpc_gss_unwrap()"); 1462 1463 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1464 client = cc->cc_client; 1465 if (client->cl_state != CLIENT_ESTABLISHED 1466 || cc->cc_service == rpc_gss_svc_none) { 1467 return (TRUE);
|
481 }
| 1468 }
|
482 *extflagsp = np->netc_exflags; 483 if ((*credanonp = np->netc_anon) != NULL) 484 crhold(*credanonp); 485 if (numsecflavors) 486 *numsecflavors = np->netc_numsecflavors; 487 if (secflavors) 488 *secflavors = np->netc_secflavors; 489 lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); 490 return (0);
| 1469 1470 return (xdr_rpc_gss_unwrap_data(mp, 1471 client->cl_ctx, client->cl_qop, 1472 cc->cc_service, cc->cc_seq));
|
491} 492
| 1473} 1474
|
| 1475static void 1476svc_rpc_gss_release(SVCAUTH *auth) 1477{ 1478 struct svc_rpc_gss_cookedcred *cc; 1479 struct svc_rpc_gss_client *client; 1480 1481 rpc_gss_log_debug("in svc_rpc_gss_release()"); 1482 1483 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1484 client = cc->cc_client; 1485 svc_rpc_gss_release_client(client); 1486}
|
| |