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