Home | History | Annotate | Download | only in nfs
      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