0
|
1 |
/*
|
|
2 |
* CDDL HEADER START
|
|
3 |
*
|
|
4 |
* The contents of this file are subject to the terms of the
|
|
5 |
* Common Development and Distribution License, Version 1.0 only
|
|
6 |
* (the "License"). You may not use this file except in compliance
|
|
7 |
* with the License.
|
|
8 |
*
|
|
9 |
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
10 |
* or http://www.opensolaris.org/os/licensing.
|
|
11 |
* See the License for the specific language governing permissions
|
|
12 |
* and limitations under the License.
|
|
13 |
*
|
|
14 |
* When distributing Covered Code, include this CDDL HEADER in each
|
|
15 |
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
16 |
* If applicable, add the following below this CDDL HEADER, with the
|
|
17 |
* fields enclosed by brackets "[]" replaced with your own identifying
|
|
18 |
* information: Portions Copyright [yyyy] [name of copyright owner]
|
|
19 |
*
|
|
20 |
* CDDL HEADER END
|
|
21 |
*/
|
|
22 |
/*
|
|
23 |
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
|
|
24 |
* Use is subject to license terms.
|
|
25 |
*/
|
|
26 |
|
|
27 |
#pragma ident "%Z%%M% %I% %E% SMI"
|
|
28 |
|
|
29 |
/*
|
|
30 |
* NFS Version 4 client side SECINFO code.
|
|
31 |
*/
|
|
32 |
|
|
33 |
#include <nfs/nfs4_clnt.h>
|
|
34 |
#include <nfs/nfs4.h>
|
|
35 |
#include <nfs/nfs_clnt.h>
|
|
36 |
#include <nfs/rnode4.h>
|
|
37 |
#include <sys/cmn_err.h>
|
|
38 |
#include <sys/cred.h>
|
|
39 |
#include <sys/systm.h>
|
|
40 |
|
|
41 |
/*
|
|
42 |
* Set up the security flavors supported in this release.
|
|
43 |
* In the order of potential usage.
|
|
44 |
*/
|
|
45 |
#define SECINFO_SUPPORT_COUNT 6 /* sys, krb5, krb5i, krb5p, none, dh */
|
|
46 |
static char krb5_val[] = {'\x2A', '\x86', '\x48', '\x86', '\xF7', \
|
|
47 |
'\x12', '\x01', '\x02', '\x02'};
|
|
48 |
static sec_oid4 krb5_oid = {9, krb5_val};
|
|
49 |
static SECINFO4res *secinfo_support;
|
|
50 |
|
|
51 |
/* XXX should come from auth.h, do the cleanup someday */
|
|
52 |
extern void sec_clnt_freeinfo(struct sec_data *);
|
|
53 |
|
|
54 |
/*
|
|
55 |
* "nfsstat -m" needs to print out what flavor is used for a mount
|
|
56 |
* point. V3 kernel gets the nfs pseudo flavor from the userland and provides
|
|
57 |
* nfsstat with such information. However, in V4, we do not have nfs pseudo
|
|
58 |
* flavors mapping in the kernel for the rpcsec_gss data negotiated from
|
|
59 |
* the nfs server.
|
|
60 |
*
|
|
61 |
* XXX
|
|
62 |
* Hard coded the mapping in V4 for now. We should look into a possibility
|
|
63 |
* to return the rpcsec_gss mechanism and service information to nfsstat and
|
|
64 |
* perhaps have nfsstat print out the mech and service seperately...
|
|
65 |
*
|
|
66 |
* We should avoid referring to nfssec.conf file in V4. The original reason
|
|
67 |
* for having /etc/nfssec.conf file is because V3 MOUNT protocol can only
|
|
68 |
* return an integer for a flavor, thus the term "nfs pseudo flavor" is
|
|
69 |
* defined and the nfssec.conf file is used to map the nfs pseudo flavor
|
|
70 |
* to rpcsec_gss data (mech, service, default-qop). Now, V4 can return the
|
|
71 |
* rpcsec_gss data instead of an integer, so in theory, V4 should not need
|
|
72 |
* to depend on the nfssec.conf file anymore.
|
|
73 |
*/
|
|
74 |
#define NFS_FLAVOR_KRB5 390003
|
|
75 |
#define NFS_FLAVOR_KRB5I 390004
|
|
76 |
#define NFS_FLAVOR_KRB5P 390005
|
|
77 |
|
|
78 |
/*
|
|
79 |
* Currently, 6 flavors are supported: sys, krb5, krb5i, krb5p, dh, none.
|
|
80 |
* Without proper keys, krb5* or dh will fail.
|
|
81 |
*
|
|
82 |
* XXX kgss_indicate_mechs() should be able to tell us what gss mechanisms
|
|
83 |
* are supported on this host (/etc/gss/mech), thus nfs should be able to
|
|
84 |
* use them. However, the dh640 and dh1024 implementation are not nfs tested.
|
|
85 |
* Should look into using kgss_indicate_mechs when new gss mechanism is added.
|
|
86 |
*/
|
|
87 |
void
|
|
88 |
nfs4_secinfo_init(void)
|
|
89 |
{
|
|
90 |
secinfo4 *val;
|
|
91 |
int i;
|
|
92 |
|
|
93 |
secinfo_support = kmem_alloc(sizeof (SECINFO4res), KM_SLEEP);
|
|
94 |
secinfo_support->SECINFO4resok_len = SECINFO_SUPPORT_COUNT;
|
|
95 |
val = kmem_alloc(
|
|
96 |
secinfo_support->SECINFO4resok_len * sizeof (secinfo4),
|
|
97 |
KM_SLEEP);
|
|
98 |
|
|
99 |
val[0].flavor = AUTH_SYS;
|
|
100 |
val[0].flavor_info.oid.sec_oid4_len = 0;
|
|
101 |
val[0].flavor_info.oid.sec_oid4_val = NULL;
|
|
102 |
val[0].flavor_info.service = 0;
|
|
103 |
val[0].flavor_info.qop = 0;
|
|
104 |
|
|
105 |
/* add krb5, krb5i, krb5p */
|
|
106 |
for (i = 1; i <= 3; i++) {
|
|
107 |
val[i].flavor = RPCSEC_GSS;
|
|
108 |
val[i].flavor_info.oid = krb5_oid; /* struct copy */
|
|
109 |
val[i].flavor_info.service = i;
|
|
110 |
val[i].flavor_info.qop = 0;
|
|
111 |
}
|
|
112 |
|
|
113 |
val[4].flavor = AUTH_DH;
|
|
114 |
val[4].flavor_info.oid.sec_oid4_len = 0;
|
|
115 |
val[4].flavor_info.oid.sec_oid4_val = NULL;
|
|
116 |
val[4].flavor_info.service = 0;
|
|
117 |
val[4].flavor_info.qop = 0;
|
|
118 |
|
|
119 |
val[5].flavor = AUTH_NONE;
|
|
120 |
val[5].flavor_info.oid.sec_oid4_len = 0;
|
|
121 |
val[5].flavor_info.oid.sec_oid4_val = NULL;
|
|
122 |
val[5].flavor_info.service = 0;
|
|
123 |
val[5].flavor_info.qop = 0;
|
|
124 |
|
|
125 |
#if !defined(lint)
|
|
126 |
ASSERT(SECINFO_SUPPORT_COUNT == 6);
|
|
127 |
#endif
|
|
128 |
|
|
129 |
secinfo_support->SECINFO4resok_val = val;
|
|
130 |
}
|
|
131 |
|
|
132 |
/*
|
|
133 |
* clean up secinfo_support
|
|
134 |
*/
|
|
135 |
void
|
|
136 |
nfs4_secinfo_fini(void)
|
|
137 |
{
|
|
138 |
|
|
139 |
kmem_free(secinfo_support->SECINFO4resok_val,
|
|
140 |
secinfo_support->SECINFO4resok_len * sizeof (secinfo4));
|
|
141 |
kmem_free(secinfo_support, sizeof (SECINFO4res));
|
|
142 |
}
|
|
143 |
|
|
144 |
/*
|
|
145 |
* Map RPCSEC_GSS data to a nfs pseudo flavor number defined
|
|
146 |
* in the nfssec.conf file.
|
|
147 |
*
|
|
148 |
* mechanism service qop nfs-pseudo-flavor
|
|
149 |
* ----------------------------------------------------
|
|
150 |
* kerberos_v5 none default 390003/krb5
|
|
151 |
* kerberos_v5 integrity default 390004/krb5i
|
|
152 |
* kerberos_v5 privacy default 390005/krb5p
|
|
153 |
*
|
|
154 |
* XXX need to re-visit the mapping semantics when a new
|
|
155 |
* security mechanism is to be added.
|
|
156 |
*/
|
|
157 |
int
|
|
158 |
secinfo2nfsflavor(sec_oid4 *mech_oid, rpc_gss_svc_t service)
|
|
159 |
{
|
|
160 |
/* Is this kerberos_v5? */
|
|
161 |
if (bcmp(mech_oid->sec_oid4_val, krb5_oid.sec_oid4_val,
|
|
162 |
krb5_oid.sec_oid4_len) != 0) {
|
|
163 |
return (0);
|
|
164 |
}
|
|
165 |
|
|
166 |
/* for krb5, krb5i, krb5p mapping */
|
|
167 |
switch (service) {
|
|
168 |
case RPC_GSS_SVC_NONE:
|
|
169 |
return (NFS_FLAVOR_KRB5);
|
|
170 |
case RPC_GSS_SVC_INTEGRITY:
|
|
171 |
return (NFS_FLAVOR_KRB5I);
|
|
172 |
case RPC_GSS_SVC_PRIVACY:
|
|
173 |
return (NFS_FLAVOR_KRB5P);
|
|
174 |
default:
|
|
175 |
break;
|
|
176 |
}
|
|
177 |
|
|
178 |
/* no mapping */
|
|
179 |
return (0);
|
|
180 |
}
|
|
181 |
|
|
182 |
/*
|
|
183 |
* secinfo_create() maps the secinfo4 data coming over the wire
|
|
184 |
* to sv_secinfo data structure in servinfo4_t
|
|
185 |
*/
|
|
186 |
static sv_secinfo_t *
|
|
187 |
secinfo_create(servinfo4_t *svp, SECINFO4res *sec_info, char *servname)
|
|
188 |
{
|
|
189 |
uint_t i, seccnt, scnt;
|
|
190 |
sec_data_t *sdata;
|
|
191 |
sv_secinfo_t *sinfo;
|
|
192 |
uint_t len = sec_info->SECINFO4resok_len;
|
|
193 |
secinfo4 *value = sec_info->SECINFO4resok_val;
|
|
194 |
|
|
195 |
if (len == 0)
|
|
196 |
return (NULL);
|
|
197 |
|
|
198 |
seccnt = len;
|
|
199 |
|
|
200 |
/*
|
|
201 |
* If there is no valid sv_dhsec data available but an AUTH_DH
|
|
202 |
* is in the list, skip AUTH_DH flavor.
|
|
203 |
*/
|
|
204 |
if (!svp->sv_dhsec) {
|
|
205 |
for (i = 0; i < len; i++) {
|
|
206 |
if (value[i].flavor == AUTH_DH)
|
|
207 |
seccnt--;
|
|
208 |
}
|
|
209 |
}
|
|
210 |
|
|
211 |
if (seccnt == 0)
|
|
212 |
return (NULL);
|
|
213 |
|
|
214 |
sdata = kmem_alloc(sizeof (sec_data_t) * seccnt, KM_SLEEP);
|
|
215 |
scnt = 0;
|
|
216 |
for (i = 0; i < len; i++) {
|
|
217 |
secinfo4 *val = &value[i];
|
|
218 |
gss_clntdata_t *data;
|
|
219 |
rpcsec_gss_info *info;
|
|
220 |
|
|
221 |
sdata[scnt].flags = 0;
|
|
222 |
sdata[scnt].rpcflavor = val->flavor;
|
|
223 |
|
|
224 |
switch (val->flavor) {
|
|
225 |
case RPCSEC_GSS:
|
|
226 |
data = kmem_alloc(sizeof (gss_clntdata_t), KM_SLEEP);
|
|
227 |
data->realm[0] = '\0';
|
|
228 |
info = &val->flavor_info;
|
|
229 |
data->service = (rpc_gss_service_t)info->service;
|
|
230 |
data->qop = (uint_t)info->qop;
|
|
231 |
data->mechanism.length = info->oid.sec_oid4_len;
|
|
232 |
data->mechanism.elements =
|
|
233 |
kmem_alloc(info->oid.sec_oid4_len, KM_SLEEP);
|
|
234 |
bcopy(info->oid.sec_oid4_val,
|
|
235 |
data->mechanism.elements, info->oid.sec_oid4_len);
|
|
236 |
data->uname[0] = 'n'; data->uname[1] = 'f';
|
|
237 |
data->uname[2] = 's'; data->uname[3] = '\0';
|
|
238 |
(void) strcpy(data->inst, servname);
|
|
239 |
|
|
240 |
sdata[scnt].data = (caddr_t)data;
|
|
241 |
sdata[scnt].secmod =
|
|
242 |
secinfo2nfsflavor(&info->oid, info->service);
|
|
243 |
scnt++;
|
|
244 |
break;
|
|
245 |
case AUTH_DH:
|
|
246 |
if (svp->sv_dhsec) {
|
|
247 |
sdata[scnt] = *svp->sv_dhsec;
|
|
248 |
scnt++;
|
|
249 |
break;
|
|
250 |
}
|
|
251 |
/* no auth_dh data on the client, skip auth_dh */
|
|
252 |
continue;
|
|
253 |
default:
|
|
254 |
sdata[scnt].secmod = val->flavor;
|
|
255 |
sdata[scnt].data = NULL;
|
|
256 |
scnt++;
|
|
257 |
break;
|
|
258 |
}
|
|
259 |
}
|
|
260 |
|
|
261 |
ASSERT(seccnt == scnt);
|
|
262 |
sinfo = kmem_alloc(sizeof (sv_secinfo_t), KM_SLEEP);
|
|
263 |
sinfo->count = seccnt;
|
|
264 |
sinfo->sdata = sdata;
|
|
265 |
|
|
266 |
return (sinfo);
|
|
267 |
}
|
|
268 |
|
|
269 |
/*
|
|
270 |
* secinfo_free() frees the malloc'd portion of a sv_secinfo_t in servinfo4_t.
|
|
271 |
*
|
|
272 |
* This is similar to sec_clnt_freeinfo() offered from rpcsec module,
|
|
273 |
* except that sec_clnt_freeinfo() frees up an individual secdata.
|
|
274 |
*/
|
|
275 |
void
|
|
276 |
secinfo_free(sv_secinfo_t *secinfo)
|
|
277 |
{
|
|
278 |
int i;
|
|
279 |
|
|
280 |
if (secinfo == NULL)
|
|
281 |
return;
|
|
282 |
|
|
283 |
for (i = 0; i < secinfo->count; i++) {
|
|
284 |
if (secinfo->sdata[i].rpcflavor == RPCSEC_GSS) {
|
|
285 |
gss_clntdata_t *data = (gss_clntdata_t *)
|
|
286 |
secinfo->sdata[i].data;
|
|
287 |
|
|
288 |
/*
|
|
289 |
* An auth handle may already cached in rpcsec_gss module
|
|
290 |
* per this secdata. Purge the cache entry before freeing
|
|
291 |
* up this secdata. Can't use sec_clnt_freeinfo since
|
|
292 |
* the allocation of secinfo is different from sec_data.
|
|
293 |
*/
|
|
294 |
(void) rpc_gss_secpurge((void *)&secinfo->sdata[i]);
|
|
295 |
|
|
296 |
kmem_free(data->mechanism.elements, data->mechanism.length);
|
|
297 |
kmem_free(data, sizeof (gss_clntdata_t));
|
|
298 |
}
|
|
299 |
|
|
300 |
if (secinfo->sdata[i].rpcflavor == AUTH_DH) {
|
|
301 |
|
|
302 |
secinfo->sdata[i].data = NULL; /* release ref to sv_dhsec */
|
|
303 |
|
|
304 |
/*
|
|
305 |
* No need to purge the auth_dh cache entry (e.g. call
|
|
306 |
* purge_authtab()) since the AUTH_DH data used here
|
|
307 |
* are always the same.
|
|
308 |
*/
|
|
309 |
}
|
|
310 |
}
|
|
311 |
kmem_free(secinfo->sdata, sizeof (sec_data_t) * secinfo->count);
|
|
312 |
kmem_free(secinfo, sizeof (sv_secinfo_t));
|
|
313 |
}
|
|
314 |
|
|
315 |
/*
|
|
316 |
* Check if there is more secinfo to try.
|
|
317 |
* If TRUE, try again.
|
|
318 |
*/
|
|
319 |
static bool_t
|
|
320 |
secinfo_check(servinfo4_t *svp)
|
|
321 |
{
|
|
322 |
|
|
323 |
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
|
|
324 |
if (svp->sv_secinfo == NULL) {
|
|
325 |
nfs_rw_exit(&svp->sv_lock);
|
|
326 |
return (FALSE);
|
|
327 |
}
|
|
328 |
|
|
329 |
svp->sv_secinfo->index++;
|
|
330 |
if (svp->sv_secinfo->index < svp->sv_secinfo->count) {
|
|
331 |
svp->sv_flags |= SV4_TRYSECINFO;
|
|
332 |
svp->sv_currsec =
|
|
333 |
&svp->sv_secinfo->sdata[svp->sv_secinfo->index];
|
|
334 |
nfs_rw_exit(&svp->sv_lock);
|
|
335 |
return (TRUE);
|
|
336 |
} else {
|
|
337 |
svp->sv_secinfo->index = 0;
|
|
338 |
svp->sv_flags &= ~SV4_TRYSECINFO;
|
|
339 |
svp->sv_currsec = NULL;
|
|
340 |
nfs_rw_exit(&svp->sv_lock);
|
|
341 |
return (FALSE);
|
|
342 |
}
|
|
343 |
}
|
|
344 |
|
|
345 |
/*
|
|
346 |
* Update the secinfo related fields in svp.
|
|
347 |
*
|
|
348 |
* secinfo_update will free the previous sv_secinfo and update with
|
|
349 |
* the new secinfo. However, if the sv_secinfo is saved into sv_save_secinfo
|
|
350 |
* before the recovery starts via save_mnt_secinfo(), sv_secinfo will not
|
|
351 |
* be freed until the recovery is done.
|
|
352 |
*/
|
|
353 |
static void
|
|
354 |
secinfo_update(servinfo4_t *svp, SECINFO4res *sec_info)
|
|
355 |
{
|
|
356 |
|
|
357 |
sv_secinfo_t *newsecinfo;
|
|
358 |
|
|
359 |
/*
|
|
360 |
* Create secinfo before freeing the old one to make sure
|
|
361 |
* they are not using the same address.
|
|
362 |
*/
|
|
363 |
newsecinfo = secinfo_create(svp, sec_info, svp->sv_hostname);
|
|
364 |
|
|
365 |
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
|
|
366 |
if (svp->sv_secinfo && svp->sv_secinfo != svp->sv_save_secinfo) {
|
|
367 |
secinfo_free(svp->sv_secinfo);
|
|
368 |
}
|
|
369 |
|
|
370 |
svp->sv_secinfo = newsecinfo;
|
|
371 |
if (svp->sv_secinfo) {
|
|
372 |
svp->sv_secinfo->index = 0;
|
|
373 |
svp->sv_flags |= SV4_TRYSECINFO;
|
|
374 |
svp->sv_currsec =
|
|
375 |
&svp->sv_secinfo->sdata[svp->sv_secinfo->index];
|
|
376 |
} else {
|
|
377 |
svp->sv_flags &= ~SV4_TRYSECINFO;
|
|
378 |
svp->sv_currsec = NULL;
|
|
379 |
}
|
|
380 |
nfs_rw_exit(&svp->sv_lock);
|
|
381 |
}
|
|
382 |
|
|
383 |
/*
|
|
384 |
* Save the original mount point security information.
|
|
385 |
*
|
|
386 |
* sv_savesec saves the pointer of sv_currsec which points to one of the
|
|
387 |
* secinfo data in the sv_secinfo list. i.e. sv_currsec == &sv_secinfo[index].
|
|
388 |
*
|
|
389 |
* sv_save_secinfo saves the pointer of sv_secinfo which is the list of
|
|
390 |
* secinfo data returned by the server.
|
|
391 |
*/
|
|
392 |
void
|
|
393 |
save_mnt_secinfo(servinfo4_t *svp)
|
|
394 |
{
|
|
395 |
|
|
396 |
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
|
|
397 |
if (svp->sv_currsec) {
|
|
398 |
svp->sv_savesec = svp->sv_currsec;
|
|
399 |
svp->sv_save_secinfo = svp->sv_secinfo;
|
|
400 |
} else {
|
|
401 |
ASSERT(svp->sv_save_secinfo == NULL);
|
|
402 |
svp->sv_savesec = svp->sv_secdata;
|
|
403 |
}
|
|
404 |
nfs_rw_exit(&svp->sv_lock);
|
|
405 |
}
|
|
406 |
|
|
407 |
/*
|
|
408 |
* Check if we need to restore what is saved in sv_savesec and sv_save_secinfo
|
|
409 |
* to be the current secinfo information - sv_currsec and sv_secinfo.
|
|
410 |
*
|
|
411 |
* If op a node that is a stub for a crossed mount point,
|
|
412 |
* keep the original secinfo flavor for the current file system,
|
|
413 |
* not the crossed one.
|
|
414 |
*/
|
|
415 |
void
|
|
416 |
check_mnt_secinfo(servinfo4_t *svp, vnode_t *vp)
|
|
417 |
{
|
|
418 |
bool_t is_restore;
|
|
419 |
|
|
420 |
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
|
|
421 |
|
|
422 |
is_restore = (vp == NULL || (VTOR4(vp)->r_flags & R4SRVSTUB)) &&
|
|
423 |
svp->sv_save_secinfo &&
|
|
424 |
(svp->sv_secinfo != svp->sv_save_secinfo);
|
|
425 |
|
|
426 |
if (is_restore) {
|
|
427 |
secinfo_free(svp->sv_secinfo);
|
|
428 |
if (svp->sv_savesec == svp->sv_secdata) {
|
|
429 |
ASSERT(svp->sv_save_secinfo == NULL);
|
|
430 |
svp->sv_secinfo = NULL;
|
|
431 |
svp->sv_currsec = NULL;
|
|
432 |
} else {
|
|
433 |
ASSERT(svp->sv_save_secinfo != NULL);
|
|
434 |
svp->sv_secinfo = svp->sv_save_secinfo;
|
|
435 |
svp->sv_currsec = svp->sv_savesec;
|
|
436 |
}
|
|
437 |
} else {
|
|
438 |
if (svp->sv_save_secinfo &&
|
|
439 |
svp->sv_save_secinfo != svp->sv_secinfo)
|
|
440 |
secinfo_free(svp->sv_save_secinfo);
|
|
441 |
}
|
|
442 |
|
|
443 |
svp->sv_save_secinfo = NULL;
|
|
444 |
svp->sv_savesec = NULL;
|
|
445 |
|
|
446 |
nfs_rw_exit(&svp->sv_lock);
|
|
447 |
}
|
|
448 |
|
|
449 |
/*
|
|
450 |
* Use the security flavors supported on the client to try
|
|
451 |
* PUTROOTFH until a flavor is found.
|
|
452 |
*
|
|
453 |
* PUTROOTFH could return NFS4ERR_RESOURCE and NFS4ERR_WRONGSEC that
|
|
454 |
* may need a recovery action. This routine only handles NFS4ERR_WRONGSEC.
|
|
455 |
* For other recovery action, it returns ok to the caller for retry.
|
|
456 |
*/
|
|
457 |
static int
|
|
458 |
secinfo_tryroot_otw(mntinfo4_t *mi, cred_t *cr)
|
|
459 |
{
|
|
460 |
COMPOUND4args_clnt args;
|
|
461 |
COMPOUND4res_clnt res;
|
|
462 |
nfs_argop4 argop;
|
|
463 |
int doqueue = 1;
|
|
464 |
bool_t needrecov = FALSE;
|
|
465 |
nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS };
|
|
466 |
|
|
467 |
/* use the flavors supported on the client */
|
|
468 |
secinfo_update(mi->mi_curr_serv, secinfo_support);
|
|
469 |
|
|
470 |
/* Compound {Putroofh} */
|
|
471 |
args.ctag = TAG_PUTROOTFH;
|
|
472 |
|
|
473 |
args.array_len = 1;
|
|
474 |
args.array = &argop;
|
|
475 |
|
|
476 |
argop.argop = OP_PUTROOTFH;
|
|
477 |
retry:
|
|
478 |
NFS4_DEBUG(nfs4_client_call_debug, (CE_NOTE,
|
|
479 |
"secinfo_tryroot_otw: %s call, mi 0x%p",
|
|
480 |
needrecov ? "recov" : "first", (void*)mi));
|
|
481 |
|
|
482 |
rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, &e);
|
|
483 |
|
|
484 |
needrecov = nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp);
|
|
485 |
if (e.error && !needrecov) {
|
|
486 |
return (e.error);
|
|
487 |
}
|
|
488 |
|
|
489 |
if (res.status == NFS4ERR_WRONGSEC) {
|
|
490 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
491 |
if (secinfo_check(mi->mi_curr_serv))
|
|
492 |
goto retry;
|
|
493 |
/*
|
|
494 |
* Have tried all flavors supported on the client,
|
|
495 |
* but still get NFS4ERR_WRONGSEC. Nothing more can
|
|
496 |
* be done.
|
|
497 |
*/
|
|
498 |
return (geterrno4(res.status));
|
|
499 |
}
|
|
500 |
|
|
501 |
if (needrecov) {
|
|
502 |
NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
|
|
503 |
"secinfo_tryroot_otw: let the caller retry\n"));
|
|
504 |
|
|
505 |
if (!e.error)
|
|
506 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
507 |
return (0);
|
|
508 |
}
|
|
509 |
|
|
510 |
if (res.status) {
|
|
511 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
512 |
return (geterrno4(res.status));
|
|
513 |
}
|
|
514 |
|
|
515 |
/*
|
|
516 |
* Done.
|
|
517 |
*
|
|
518 |
* Now, mi->sv_curr_server->sv_currsec points to the flavor found.
|
|
519 |
* SV4_TRYSECINFO has been cleared in rfs4call.
|
|
520 |
* sv_currsec will be used.
|
|
521 |
*/
|
|
522 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
523 |
return (e.error);
|
|
524 |
}
|
|
525 |
|
|
526 |
/*
|
|
527 |
* Caculate the total number of components within a given pathname.
|
|
528 |
* Assuming the given pathname is not null.
|
|
529 |
* e.g. returns 5 for "/a/b/c/d/e" or "a/b/c/d/e"
|
|
530 |
* returns 0 for "/"
|
|
531 |
*/
|
|
532 |
static int
|
|
533 |
comp_total(char *inpath)
|
|
534 |
{
|
|
535 |
int tnum = 0;
|
|
536 |
char *slash;
|
|
537 |
|
|
538 |
while (*inpath != '\0') {
|
|
539 |
|
|
540 |
if (*inpath == '/') {
|
|
541 |
inpath++;
|
|
542 |
continue;
|
|
543 |
}
|
|
544 |
if ((slash = (char *)strchr(inpath, '/')) == NULL) {
|
|
545 |
tnum++;
|
|
546 |
break;
|
|
547 |
} else {
|
|
548 |
tnum++;
|
|
549 |
inpath = slash + 1;
|
|
550 |
}
|
|
551 |
}
|
|
552 |
|
|
553 |
return (tnum);
|
|
554 |
}
|
|
555 |
|
|
556 |
/*
|
|
557 |
* Get the pointer of the n-th component in the given path.
|
|
558 |
* Mark the preceeding '/' of the component to be '\0' when done.
|
|
559 |
* Assuming nth is > 0.
|
|
560 |
*/
|
|
561 |
static void
|
|
562 |
comp_getn(char *inpath, int nth, component4 *comp)
|
|
563 |
{
|
|
564 |
char *path = inpath, *comp_start, *slash = NULL;
|
|
565 |
int count = 0;
|
|
566 |
|
|
567 |
while ((count != nth) && (*path != '\0')) {
|
|
568 |
|
|
569 |
comp_start = path;
|
|
570 |
|
|
571 |
/* ignore slashes prior to the component name */
|
|
572 |
while (*path == '/')
|
|
573 |
path++;
|
|
574 |
|
|
575 |
if (*path != '\0') {
|
|
576 |
comp_start = path;
|
|
577 |
count++;
|
|
578 |
}
|
|
579 |
|
|
580 |
if ((slash = strchr(path, '/')) == NULL)
|
|
581 |
break;
|
|
582 |
else
|
|
583 |
path = slash + 1;
|
|
584 |
}
|
|
585 |
|
|
586 |
if (count == nth) {
|
|
587 |
if (slash)
|
|
588 |
*slash = '\0';
|
|
589 |
comp->utf8string_len = strlen(comp_start);
|
|
590 |
comp->utf8string_val = comp_start;
|
|
591 |
|
|
592 |
if (comp_start != inpath) {
|
|
593 |
comp_start--;
|
|
594 |
*comp_start = '\0';
|
|
595 |
}
|
|
596 |
} else {
|
|
597 |
comp->utf8string_len = 0;
|
|
598 |
comp->utf8string_val = NULL;
|
|
599 |
}
|
|
600 |
}
|
|
601 |
|
|
602 |
/*
|
|
603 |
* SECINFO over the wire compound operation
|
|
604 |
*
|
|
605 |
* compound {PUTROOTFH, {LOOKUP parent-path}, SECINFO component}
|
|
606 |
*
|
|
607 |
* This routine assumes there is a component to work on, thus the
|
|
608 |
* given pathname (svp->sv_path) has to have at least 1 component.
|
|
609 |
*
|
|
610 |
* isrecov - TRUE if this routine is called from a recovery thread.
|
|
611 |
*
|
|
612 |
* nfs4secinfo_otw() only deals with NFS4ERR_WRONGSEC recovery. If this
|
|
613 |
* is already in a recovery thread, then setup the non-wrongsec recovery
|
|
614 |
* action thru nfs4_start_recovery and return to the outer loop in
|
|
615 |
* nfs4_recov_thread() for recovery. If this is not called from a recovery
|
|
616 |
* thread, then error out and let the caller decide what to do.
|
|
617 |
*/
|
|
618 |
static int
|
|
619 |
nfs4secinfo_otw(mntinfo4_t *mi, cred_t *cr, servinfo4_t *svp, int isrecov)
|
|
620 |
{
|
|
621 |
COMPOUND4args_clnt args;
|
|
622 |
COMPOUND4res_clnt res;
|
|
623 |
nfs_argop4 *argop;
|
|
624 |
nfs_resop4 *resop;
|
|
625 |
lookup4_param_t lookuparg;
|
|
626 |
uint_t path_len;
|
|
627 |
int doqueue;
|
|
628 |
int numops, num_argops;
|
|
629 |
char *tmp_path;
|
|
630 |
component4 comp;
|
|
631 |
uint_t ncomp, tcomp;
|
|
632 |
bool_t needrecov = FALSE;
|
|
633 |
nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS };
|
|
634 |
|
|
635 |
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
|
|
636 |
ncomp = tcomp = comp_total(svp->sv_path);
|
|
637 |
path_len = strlen(svp->sv_path);
|
|
638 |
nfs_rw_exit(&svp->sv_lock);
|
|
639 |
ASSERT(ncomp > 0);
|
|
640 |
|
|
641 |
retry:
|
|
642 |
tmp_path = kmem_alloc(path_len + 1, KM_SLEEP);
|
|
643 |
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
|
|
644 |
bcopy(svp->sv_path, tmp_path, path_len + 1);
|
|
645 |
nfs_rw_exit(&svp->sv_lock);
|
|
646 |
comp_getn(tmp_path, ncomp, &comp);
|
|
647 |
|
|
648 |
args.ctag = TAG_SECINFO;
|
|
649 |
|
|
650 |
lookuparg.l4_getattrs = LKP4_NO_ATTRIBUTES;
|
|
651 |
lookuparg.argsp = &args;
|
|
652 |
lookuparg.resp = &res;
|
|
653 |
lookuparg.header_len = 1; /* Putrootfh */
|
|
654 |
lookuparg.trailer_len = 1; /* Secinfo */
|
|
655 |
lookuparg.ga_bits = NULL;
|
|
656 |
lookuparg.mi = mi;
|
|
657 |
|
|
658 |
/* setup LOOKUPs for parent path */
|
|
659 |
(void) nfs4lookup_setup(tmp_path, &lookuparg, 0);
|
|
660 |
|
|
661 |
argop = args.array;
|
|
662 |
|
|
663 |
/* put root fh */
|
|
664 |
argop[0].argop = OP_PUTROOTFH;
|
|
665 |
|
|
666 |
/* setup SECINFO op */
|
|
667 |
num_argops = args.array_len;
|
|
668 |
argop[num_argops - 1].argop = OP_SECINFO;
|
|
669 |
argop[num_argops - 1].nfs_argop4_u.opsecinfo.name.utf8string_len =
|
|
670 |
comp.utf8string_len;
|
|
671 |
argop[num_argops - 1].nfs_argop4_u.opsecinfo.name.utf8string_val =
|
|
672 |
comp.utf8string_val;
|
|
673 |
|
|
674 |
doqueue = 1;
|
|
675 |
|
|
676 |
NFS4_DEBUG(nfs4_client_call_debug, (CE_NOTE,
|
|
677 |
"nfs4secinfo_otw: %s call, mi 0x%p",
|
|
678 |
needrecov ? "recov" : "first", (void*)mi));
|
|
679 |
|
|
680 |
rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, &e);
|
|
681 |
|
|
682 |
needrecov = nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp);
|
|
683 |
if (e.error && !needrecov) {
|
|
684 |
nfs4args_lookup_free(argop, num_argops);
|
|
685 |
kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
|
|
686 |
kmem_free(tmp_path, path_len + 1);
|
|
687 |
return (e.error);
|
|
688 |
}
|
|
689 |
|
|
690 |
/*
|
|
691 |
* Secinfo compound op may fail with NFS4ERR_WRONGSEC from
|
|
692 |
* PUTROOTFH or LOOKUP. Special handling here to recover it.
|
|
693 |
*/
|
|
694 |
if (res.status == NFS4ERR_WRONGSEC) {
|
|
695 |
|
|
696 |
if (res.array_len == 1) {
|
|
697 |
/*
|
|
698 |
* If a flavor can not be found via trying
|
|
699 |
* all supported flavors on the client, no
|
|
700 |
* more operations.
|
|
701 |
*/
|
|
702 |
ncomp = tcomp;
|
|
703 |
nfs4args_lookup_free(argop, num_argops);
|
|
704 |
kmem_free(argop,
|
|
705 |
lookuparg.arglen * sizeof (nfs_argop4));
|
|
706 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
707 |
kmem_free(tmp_path, path_len + 1);
|
|
708 |
|
|
709 |
if (e.error = secinfo_tryroot_otw(mi, cr)) {
|
|
710 |
return (e.error);
|
|
711 |
}
|
|
712 |
goto retry;
|
|
713 |
}
|
|
714 |
ncomp = res.array_len - 1;
|
|
715 |
nfs4args_lookup_free(argop, num_argops);
|
|
716 |
kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
|
|
717 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
718 |
kmem_free(tmp_path, path_len + 1);
|
|
719 |
goto retry;
|
|
720 |
}
|
|
721 |
|
|
722 |
/*
|
|
723 |
* This routine does not do recovery for non NFS4ERR_WRONGSEC error.
|
|
724 |
* However, if this is already in a recovery thread, then
|
|
725 |
* set up the recovery action thru nfs4_start_recovery and
|
|
726 |
* return ok back to the outer loop in nfs4_recov_thread for
|
|
727 |
* recovery.
|
|
728 |
*/
|
|
729 |
if (needrecov) {
|
|
730 |
bool_t abort;
|
|
731 |
|
|
732 |
/* If not in a recovery thread, bail out */
|
|
733 |
if (!isrecov) {
|
|
734 |
|
|
735 |
if (!e.error) {
|
|
736 |
e.error = geterrno4(res.status);
|
|
737 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
738 |
}
|
|
739 |
nfs4args_lookup_free(argop, num_argops);
|
|
740 |
kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
|
|
741 |
kmem_free(tmp_path, path_len + 1);
|
|
742 |
return (e.error);
|
|
743 |
}
|
|
744 |
|
|
745 |
NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
|
|
746 |
"nfs4secinfo_otw: recovery in a recovery thread\n"));
|
|
747 |
|
|
748 |
abort = nfs4_start_recovery(&e, mi, NULL,
|
|
749 |
NULL, NULL, NULL, OP_SECINFO, NULL);
|
|
750 |
if (!e.error) {
|
|
751 |
e.error = geterrno4(res.status);
|
|
752 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
753 |
}
|
|
754 |
nfs4args_lookup_free(argop, num_argops);
|
|
755 |
kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
|
|
756 |
kmem_free(tmp_path, path_len + 1);
|
|
757 |
if (abort == FALSE) {
|
|
758 |
/*
|
|
759 |
* Return ok to let the outer loop in
|
|
760 |
* nfs4_recov_thread continue with the recovery action.
|
|
761 |
*/
|
|
762 |
return (0);
|
|
763 |
}
|
|
764 |
return (e.error);
|
|
765 |
}
|
|
766 |
|
|
767 |
if (res.status) {
|
|
768 |
nfs4args_lookup_free(argop, num_argops);
|
|
769 |
kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
|
|
770 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
771 |
kmem_free(tmp_path, path_len + 1);
|
|
772 |
return (geterrno4(res.status));
|
|
773 |
}
|
|
774 |
|
|
775 |
/*
|
|
776 |
* Success! Now get the SECINFO result.
|
|
777 |
*/
|
|
778 |
numops = res.array_len;
|
|
779 |
resop = &res.array[numops-1]; /* secinfo res */
|
|
780 |
ASSERT(resop->resop == OP_SECINFO);
|
|
781 |
|
|
782 |
if (resop->nfs_resop4_u.opsecinfo.SECINFO4resok_len == 0) {
|
|
783 |
/*
|
|
784 |
* Server does not return any flavor for this export point.
|
|
785 |
* Return EACCES.
|
|
786 |
*/
|
|
787 |
nfs4args_lookup_free(argop, num_argops);
|
|
788 |
kmem_free(tmp_path, path_len + 1);
|
|
789 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
790 |
kmem_free(argop, num_argops * sizeof (nfs_argop4));
|
|
791 |
return (EACCES);
|
|
792 |
}
|
|
793 |
|
|
794 |
secinfo_update(mi->mi_curr_serv, &resop->nfs_resop4_u.opsecinfo);
|
|
795 |
|
|
796 |
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
|
|
797 |
if (svp->sv_secinfo == NULL) {
|
|
798 |
nfs_rw_exit(&svp->sv_lock);
|
|
799 |
/*
|
|
800 |
* This could be because the server requires AUTH_DH, but
|
|
801 |
* the client does not have netname/syncaddr data
|
|
802 |
* from sv_dhsec.
|
|
803 |
*/
|
|
804 |
nfs4args_lookup_free(argop, num_argops);
|
|
805 |
kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
|
|
806 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
807 |
kmem_free(tmp_path, path_len + 1);
|
|
808 |
return (EACCES);
|
|
809 |
}
|
|
810 |
nfs_rw_exit(&svp->sv_lock);
|
|
811 |
|
|
812 |
/*
|
|
813 |
* If this is not the original request, try again using the
|
|
814 |
* new secinfo data in mi.
|
|
815 |
*/
|
|
816 |
if (ncomp != tcomp) {
|
|
817 |
|
|
818 |
ncomp = tcomp;
|
|
819 |
nfs4args_lookup_free(argop, num_argops);
|
|
820 |
kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
|
|
821 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
822 |
kmem_free(tmp_path, path_len + 1);
|
|
823 |
goto retry;
|
|
824 |
}
|
|
825 |
|
|
826 |
/* Done! */
|
|
827 |
nfs4args_lookup_free(argop, num_argops);
|
|
828 |
kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
|
|
829 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
830 |
kmem_free(tmp_path, path_len + 1);
|
|
831 |
|
|
832 |
return (0); /* got the secinfo */
|
|
833 |
}
|
|
834 |
|
|
835 |
/*
|
|
836 |
* Get the security information per mount point.
|
|
837 |
* Use the server pathname to get the secinfo.
|
|
838 |
*/
|
|
839 |
int
|
|
840 |
nfs4_secinfo_path(mntinfo4_t *mi, cred_t *cr, int isrecov)
|
|
841 |
{
|
|
842 |
|
|
843 |
int error = 0;
|
|
844 |
int pathlen;
|
|
845 |
servinfo4_t *svp = mi->mi_curr_serv;
|
|
846 |
|
|
847 |
/*
|
|
848 |
* Get the server pathname that is being mounted on.
|
|
849 |
*/
|
|
850 |
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
|
|
851 |
ASSERT(svp->sv_path != NULL);
|
|
852 |
pathlen = strlen(svp->sv_path);
|
|
853 |
|
|
854 |
/*
|
|
855 |
* If mounting server rootdir, use available secinfo list
|
|
856 |
* on the client. No SECINFO call here since SECINFO op
|
|
857 |
* expects a component name.
|
|
858 |
*/
|
|
859 |
if (pathlen == 1 && svp->sv_path[0] == '/') {
|
|
860 |
if (svp->sv_secinfo == NULL) {
|
|
861 |
nfs_rw_exit(&svp->sv_lock);
|
|
862 |
secinfo_update(svp, secinfo_support);
|
|
863 |
return (0);
|
|
864 |
}
|
|
865 |
nfs_rw_exit(&svp->sv_lock);
|
|
866 |
|
|
867 |
if (secinfo_check(svp))
|
|
868 |
return (0); /* try again */
|
|
869 |
|
|
870 |
/* no flavors in sv_secinfo work */
|
|
871 |
return (EACCES);
|
|
872 |
}
|
|
873 |
nfs_rw_exit(&svp->sv_lock);
|
|
874 |
|
|
875 |
/*
|
|
876 |
* Get the secinfo from the server.
|
|
877 |
*/
|
|
878 |
error = nfs4secinfo_otw(mi, cr, svp, isrecov);
|
|
879 |
|
|
880 |
if (error) {
|
|
881 |
|
|
882 |
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
|
|
883 |
if (svp->sv_secinfo) {
|
|
884 |
if (svp->sv_save_secinfo == svp->sv_secinfo) {
|
|
885 |
svp->sv_save_secinfo = NULL;
|
|
886 |
svp->sv_savesec = NULL;
|
|
887 |
}
|
|
888 |
secinfo_free(svp->sv_secinfo);
|
|
889 |
svp->sv_secinfo = NULL;
|
|
890 |
svp->sv_currsec = NULL;
|
|
891 |
svp->sv_flags &= ~SV4_TRYSECINFO;
|
|
892 |
}
|
|
893 |
|
|
894 |
if (svp->sv_save_secinfo) {
|
|
895 |
secinfo_free(svp->sv_save_secinfo);
|
|
896 |
svp->sv_save_secinfo = NULL;
|
|
897 |
svp->sv_savesec = NULL;
|
|
898 |
}
|
|
899 |
nfs_rw_exit(&svp->sv_lock);
|
|
900 |
}
|
|
901 |
|
|
902 |
return (error);
|
|
903 |
}
|
|
904 |
|
|
905 |
/*
|
|
906 |
* (secinfo) compound based on a given filehandle and component name.
|
|
907 |
*
|
|
908 |
* i.e. (secinfo) PUTFH (fh), SECINFO nm
|
|
909 |
*/
|
|
910 |
int
|
|
911 |
nfs4_secinfo_fh_otw(mntinfo4_t *mi, nfs4_sharedfh_t *fh, char *nm, cred_t *cr)
|
|
912 |
{
|
|
913 |
COMPOUND4args_clnt args;
|
|
914 |
COMPOUND4res_clnt res;
|
|
915 |
nfs_argop4 argop[2];
|
|
916 |
nfs_resop4 *resop;
|
|
917 |
int num_argops, doqueue;
|
|
918 |
nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS };
|
|
919 |
servinfo4_t *svp;
|
|
920 |
|
|
921 |
ASSERT(strlen(nm) > 0);
|
|
922 |
|
|
923 |
num_argops = 2; /* Putfh, Secinfo nm */
|
|
924 |
args.ctag = TAG_SECINFO;
|
|
925 |
args.array_len = num_argops;
|
|
926 |
args.array = argop;
|
|
927 |
|
|
928 |
/* putfh fh */
|
|
929 |
argop[0].argop = OP_CPUTFH;
|
|
930 |
argop[0].nfs_argop4_u.opcputfh.sfh = fh;
|
|
931 |
|
|
932 |
/* setup SECINFO op */
|
|
933 |
argop[1].argop = OP_CSECINFO;
|
|
934 |
argop[1].nfs_argop4_u.opcsecinfo.cname = nm;
|
|
935 |
|
|
936 |
doqueue = 1;
|
|
937 |
|
|
938 |
rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, &e);
|
|
939 |
|
|
940 |
if (e.error)
|
|
941 |
return (e.error);
|
|
942 |
|
|
943 |
if (res.status) {
|
|
944 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
945 |
return (geterrno4(res.status));
|
|
946 |
}
|
|
947 |
|
|
948 |
/*
|
|
949 |
* Success! Now get the SECINFO result.
|
|
950 |
*/
|
|
951 |
resop = &res.array[1]; /* secinfo res */
|
|
952 |
ASSERT(resop->resop == OP_SECINFO);
|
|
953 |
|
|
954 |
if (resop->nfs_resop4_u.opsecinfo.SECINFO4resok_len == 0) {
|
|
955 |
/*
|
|
956 |
* Server does not return any flavor for this export point.
|
|
957 |
* Return EACCES.
|
|
958 |
*/
|
|
959 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
960 |
return (EACCES);
|
|
961 |
}
|
|
962 |
|
|
963 |
secinfo_update(mi->mi_curr_serv, &resop->nfs_resop4_u.opsecinfo);
|
|
964 |
|
|
965 |
svp = mi->mi_curr_serv;
|
|
966 |
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
|
|
967 |
if (mi->mi_curr_serv->sv_secinfo == NULL) {
|
|
968 |
nfs_rw_exit(&svp->sv_lock);
|
|
969 |
/*
|
|
970 |
* This could be because the server requires AUTH_DH, but
|
|
971 |
* the client does not have netname/syncaddr data
|
|
972 |
* from sv_dhsec.
|
|
973 |
*/
|
|
974 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
975 |
return (EACCES);
|
|
976 |
}
|
|
977 |
nfs_rw_exit(&svp->sv_lock);
|
|
978 |
|
|
979 |
/* Done! */
|
|
980 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
|
|
981 |
|
|
982 |
return (0); /* got the secinfo */
|
|
983 |
}
|
|
984 |
|
|
985 |
/*
|
|
986 |
* Making secinfo operation with a given vnode.
|
|
987 |
*
|
|
988 |
* This routine is not used by the recovery thread.
|
|
989 |
* Mainly used in response to NFS4ERR_WRONGSEC from lookup.
|
|
990 |
*/
|
|
991 |
int
|
|
992 |
nfs4_secinfo_vnode_otw(vnode_t *dvp, char *nm, cred_t *cr)
|
|
993 |
{
|
|
994 |
ASSERT(strlen(nm) > 0);
|
|
995 |
|
|
996 |
return (nfs4_secinfo_fh_otw(VTOMI4(dvp), VTOR4(dvp)->r_fh, nm, cr));
|
|
997 |
}
|
|
998 |
|
|
999 |
/*
|
|
1000 |
* Making secinfo operation with a given vnode if this vnode
|
|
1001 |
* has a parent node. If the given vnode is a root node, use
|
|
1002 |
* the pathname from the mntinfor4_t to do the secinfo call.
|
|
1003 |
*
|
|
1004 |
* This routine is mainly used by the recovery thread.
|
|
1005 |
*/
|
|
1006 |
int
|
|
1007 |
nfs4_secinfo_vnode(vnode_t *vp, cred_t *cr, int isrecov)
|
|
1008 |
{
|
|
1009 |
svnode_t *svp = VTOSV(vp);
|
|
1010 |
char *nm;
|
|
1011 |
int error = 0;
|
|
1012 |
|
|
1013 |
/*
|
|
1014 |
* If there is a parent filehandle, use it to get the secinfo,
|
|
1015 |
* otherwise, use mntinfo4_t pathname to get the secinfo.
|
|
1016 |
*/
|
|
1017 |
if (svp->sv_dfh) {
|
|
1018 |
nm = fn_name(svp->sv_name); /* get the actual component name */
|
|
1019 |
error = nfs4_secinfo_fh_otw(VTOMI4(vp), svp->sv_dfh, nm, cr);
|
|
1020 |
kmem_free(nm, MAXNAMELEN);
|
|
1021 |
} else {
|
|
1022 |
error = nfs4_secinfo_path(VTOMI4(vp), cr, isrecov);
|
|
1023 |
}
|
|
1024 |
|
|
1025 |
return (error);
|
|
1026 |
}
|
|
1027 |
|
|
1028 |
/*
|
|
1029 |
* We are here because the client gets NFS4ERR_WRONGSEC.
|
|
1030 |
*
|
|
1031 |
* Get the security information from the server and indicate
|
|
1032 |
* a set of new security information is here to try.
|
|
1033 |
* Start with the server path that's mounted.
|
|
1034 |
*/
|
|
1035 |
int
|
|
1036 |
nfs4_secinfo_recov(mntinfo4_t *mi, vnode_t *vp1, vnode_t *vp2)
|
|
1037 |
{
|
|
1038 |
int error = 0;
|
|
1039 |
cred_t *cr, *lcr = NULL;
|
|
1040 |
servinfo4_t *svp = mi->mi_curr_serv;
|
|
1041 |
|
|
1042 |
/*
|
|
1043 |
* If the client explicitly specifies a preferred flavor to use
|
|
1044 |
* and gets NFS4ERR_WRONGSEC back, there is no need to negotiate
|
|
1045 |
* the flavor.
|
|
1046 |
*/
|
|
1047 |
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
|
|
1048 |
if (! (svp->sv_flags & SV4_TRYSECDEFAULT)) {
|
|
1049 |
error = geterrno4(NFS4ERR_WRONGSEC);
|
|
1050 |
nfs_rw_exit(&svp->sv_lock);
|
|
1051 |
} else {
|
|
1052 |
cr = crgetcred();
|
|
1053 |
|
|
1054 |
if (svp->sv_secdata->uid != 0) {
|
|
1055 |
lcr = crdup(cr);
|
|
1056 |
(void) crsetugid(lcr, svp->sv_secdata->uid,
|
|
1057 |
crgetgid(cr));
|
|
1058 |
}
|
|
1059 |
nfs_rw_exit(&svp->sv_lock);
|
|
1060 |
|
|
1061 |
if (vp1 == NULL && vp2 == NULL) {
|
|
1062 |
error = nfs4_secinfo_path(mi, cr, TRUE);
|
|
1063 |
|
|
1064 |
if (lcr && error == EACCES)
|
|
1065 |
error = nfs4_secinfo_path(mi, lcr, TRUE);
|
|
1066 |
} else if (vp1) {
|
|
1067 |
error = nfs4_secinfo_vnode(vp1, cr, TRUE);
|
|
1068 |
|
|
1069 |
if (lcr && error == EACCES)
|
|
1070 |
error = nfs4_secinfo_vnode(vp1, lcr, TRUE);
|
|
1071 |
} /* else */
|
|
1072 |
/* ??? */
|
|
1073 |
|
|
1074 |
crfree(cr);
|
|
1075 |
if (lcr != NULL)
|
|
1076 |
crfree(lcr);
|
|
1077 |
}
|
|
1078 |
|
|
1079 |
mutex_enter(&mi->mi_lock);
|
|
1080 |
mi->mi_recovflags &= ~MI4R_NEED_SECINFO;
|
|
1081 |
mutex_exit(&mi->mi_lock);
|
|
1082 |
|
|
1083 |
return (error);
|
|
1084 |
}
|