xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs4_client_secinfo.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 }
1085