Merge commit 'ea01a15a654b9e1c7b37d958f4d1911882ed7781'
[unleashed.git] / kernel / fs / nfs / nfs4_client_secinfo.c
blob7b562da83582573a7f197683842f627131c93e47
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * NFS Version 4 client side SECINFO code.
30 #include <nfs/nfs4_clnt.h>
31 #include <nfs/nfs4.h>
32 #include <nfs/nfs_clnt.h>
33 #include <nfs/rnode4.h>
34 #include <sys/cmn_err.h>
35 #include <sys/cred.h>
36 #include <sys/systm.h>
39 * Set up the security flavors supported in this release.
40 * In the order of potential usage.
42 #define SECINFO_SUPPORT_COUNT 6 /* sys, krb5, krb5i, krb5p, none, dh */
43 static char krb5_val[] = {'\x2A', '\x86', '\x48', '\x86', '\xF7', \
44 '\x12', '\x01', '\x02', '\x02'};
45 static sec_oid4 krb5_oid = {9, krb5_val};
46 static SECINFO4res *secinfo_support;
48 /* XXX should come from auth.h, do the cleanup someday */
49 extern void sec_clnt_freeinfo(struct sec_data *);
52 * "nfsstat -m" needs to print out what flavor is used for a mount
53 * point. V3 kernel gets the nfs pseudo flavor from the userland and provides
54 * nfsstat with such information. However, in V4, we do not have nfs pseudo
55 * flavors mapping in the kernel for the rpcsec_gss data negotiated from
56 * the nfs server.
58 * XXX
59 * Hard coded the mapping in V4 for now. We should look into a possibility
60 * to return the rpcsec_gss mechanism and service information to nfsstat and
61 * perhaps have nfsstat print out the mech and service seperately...
63 * We should avoid referring to nfssec.conf file in V4. The original reason
64 * for having /etc/nfssec.conf file is because V3 MOUNT protocol can only
65 * return an integer for a flavor, thus the term "nfs pseudo flavor" is
66 * defined and the nfssec.conf file is used to map the nfs pseudo flavor
67 * to rpcsec_gss data (mech, service, default-qop). Now, V4 can return the
68 * rpcsec_gss data instead of an integer, so in theory, V4 should not need
69 * to depend on the nfssec.conf file anymore.
71 #define NFS_FLAVOR_KRB5 390003
72 #define NFS_FLAVOR_KRB5I 390004
73 #define NFS_FLAVOR_KRB5P 390005
76 * Currently, 6 flavors are supported: sys, krb5, krb5i, krb5p, dh, none.
77 * Without proper keys, krb5* or dh will fail.
79 * XXX kgss_indicate_mechs() should be able to tell us what gss mechanisms
80 * are supported on this host (/etc/gss/mech), thus nfs should be able to
81 * use them. However, the dh640 and dh1024 implementation are not nfs tested.
82 * Should look into using kgss_indicate_mechs when new gss mechanism is added.
84 void
85 nfs4_secinfo_init(void)
87 secinfo4 *val;
88 int i;
90 secinfo_support = kmem_alloc(sizeof (SECINFO4res), KM_SLEEP);
91 secinfo_support->SECINFO4resok_len = SECINFO_SUPPORT_COUNT;
92 val = kmem_alloc(
93 secinfo_support->SECINFO4resok_len * sizeof (secinfo4),
94 KM_SLEEP);
96 val[0].flavor = AUTH_SYS;
97 val[0].flavor_info.oid.sec_oid4_len = 0;
98 val[0].flavor_info.oid.sec_oid4_val = NULL;
99 val[0].flavor_info.service = 0;
100 val[0].flavor_info.qop = 0;
102 /* add krb5, krb5i, krb5p */
103 for (i = 1; i <= 3; i++) {
104 val[i].flavor = RPCSEC_GSS;
105 val[i].flavor_info.oid = krb5_oid; /* struct copy */
106 val[i].flavor_info.service = i;
107 val[i].flavor_info.qop = 0;
110 val[4].flavor = AUTH_DH;
111 val[4].flavor_info.oid.sec_oid4_len = 0;
112 val[4].flavor_info.oid.sec_oid4_val = NULL;
113 val[4].flavor_info.service = 0;
114 val[4].flavor_info.qop = 0;
116 val[5].flavor = AUTH_NONE;
117 val[5].flavor_info.oid.sec_oid4_len = 0;
118 val[5].flavor_info.oid.sec_oid4_val = NULL;
119 val[5].flavor_info.service = 0;
120 val[5].flavor_info.qop = 0;
122 ASSERT(SECINFO_SUPPORT_COUNT == 6);
124 secinfo_support->SECINFO4resok_val = val;
128 * clean up secinfo_support
130 void
131 nfs4_secinfo_fini(void)
134 kmem_free(secinfo_support->SECINFO4resok_val,
135 secinfo_support->SECINFO4resok_len * sizeof (secinfo4));
136 kmem_free(secinfo_support, sizeof (SECINFO4res));
140 * Map RPCSEC_GSS data to a nfs pseudo flavor number defined
141 * in the nfssec.conf file.
143 * mechanism service qop nfs-pseudo-flavor
144 * ----------------------------------------------------
145 * kerberos_v5 none default 390003/krb5
146 * kerberos_v5 integrity default 390004/krb5i
147 * kerberos_v5 privacy default 390005/krb5p
149 * XXX need to re-visit the mapping semantics when a new
150 * security mechanism is to be added.
153 secinfo2nfsflavor(sec_oid4 *mech_oid, rpc_gss_svc_t service)
155 /* Is this kerberos_v5? */
156 if (bcmp(mech_oid->sec_oid4_val, krb5_oid.sec_oid4_val,
157 krb5_oid.sec_oid4_len) != 0) {
158 return (0);
161 /* for krb5, krb5i, krb5p mapping */
162 switch (service) {
163 case RPC_GSS_SVC_NONE:
164 return (NFS_FLAVOR_KRB5);
165 case RPC_GSS_SVC_INTEGRITY:
166 return (NFS_FLAVOR_KRB5I);
167 case RPC_GSS_SVC_PRIVACY:
168 return (NFS_FLAVOR_KRB5P);
169 default:
170 break;
173 /* no mapping */
174 return (0);
178 * secinfo_create() maps the secinfo4 data coming over the wire
179 * to sv_secinfo data structure in servinfo4_t
181 static sv_secinfo_t *
182 secinfo_create(servinfo4_t *svp, SECINFO4res *sec_info, char *servname)
184 uint_t i, seccnt, scnt;
185 sec_data_t *sdata;
186 sv_secinfo_t *sinfo;
187 uint_t len = sec_info->SECINFO4resok_len;
188 secinfo4 *value = sec_info->SECINFO4resok_val;
190 if (len == 0)
191 return (NULL);
193 seccnt = len;
196 * If there is no valid sv_dhsec data available but an AUTH_DH
197 * is in the list, skip AUTH_DH flavor.
199 if (!svp->sv_dhsec) {
200 for (i = 0; i < len; i++) {
201 if (value[i].flavor == AUTH_DH)
202 seccnt--;
206 if (seccnt == 0)
207 return (NULL);
209 sdata = kmem_alloc(sizeof (sec_data_t) * seccnt, KM_SLEEP);
210 scnt = 0;
211 for (i = 0; i < len; i++) {
212 secinfo4 *val = &value[i];
213 gss_clntdata_t *data;
214 rpcsec_gss_info *info;
216 sdata[scnt].flags = 0;
217 sdata[scnt].rpcflavor = val->flavor;
219 switch (val->flavor) {
220 case RPCSEC_GSS:
221 data = kmem_alloc(sizeof (gss_clntdata_t), KM_SLEEP);
222 data->realm[0] = '\0';
223 info = &val->flavor_info;
224 data->service = (rpc_gss_service_t)info->service;
225 data->qop = (uint_t)info->qop;
226 data->mechanism.length = info->oid.sec_oid4_len;
227 data->mechanism.elements =
228 kmem_alloc(info->oid.sec_oid4_len, KM_SLEEP);
229 bcopy(info->oid.sec_oid4_val,
230 data->mechanism.elements, info->oid.sec_oid4_len);
231 data->uname[0] = 'n'; data->uname[1] = 'f';
232 data->uname[2] = 's'; data->uname[3] = '\0';
233 (void) strcpy(data->inst, servname);
235 sdata[scnt].data = (caddr_t)data;
236 sdata[scnt].secmod =
237 secinfo2nfsflavor(&info->oid, info->service);
238 scnt++;
239 break;
240 case AUTH_DH:
241 if (svp->sv_dhsec) {
242 sdata[scnt] = *svp->sv_dhsec;
243 scnt++;
244 break;
246 /* no auth_dh data on the client, skip auth_dh */
247 continue;
248 default:
249 sdata[scnt].secmod = val->flavor;
250 sdata[scnt].data = NULL;
251 scnt++;
252 break;
256 ASSERT(seccnt == scnt);
257 sinfo = kmem_alloc(sizeof (sv_secinfo_t), KM_SLEEP);
258 sinfo->count = seccnt;
259 sinfo->sdata = sdata;
261 return (sinfo);
265 * secinfo_free() frees the malloc'd portion of a sv_secinfo_t in servinfo4_t.
267 * This is similar to sec_clnt_freeinfo() offered from rpcsec module,
268 * except that sec_clnt_freeinfo() frees up an individual secdata.
270 void
271 secinfo_free(sv_secinfo_t *secinfo)
273 int i;
275 if (secinfo == NULL)
276 return;
278 for (i = 0; i < secinfo->count; i++) {
279 if (secinfo->sdata[i].rpcflavor == RPCSEC_GSS) {
280 gss_clntdata_t *data = (gss_clntdata_t *)
281 secinfo->sdata[i].data;
284 * An auth handle may already cached in rpcsec_gss
285 * module per this secdata. Purge the cache entry
286 * before freeing up this secdata. Can't use
287 * sec_clnt_freeinfo since the allocation of secinfo
288 * is different from sec_data.
290 (void) rpc_gss_secpurge((void *)&secinfo->sdata[i]);
292 kmem_free(data->mechanism.elements,
293 data->mechanism.length);
294 kmem_free(data, sizeof (gss_clntdata_t));
297 if (secinfo->sdata[i].rpcflavor == AUTH_DH) {
299 /* release ref to sv_dhsec */
300 secinfo->sdata[i].data = NULL;
303 * No need to purge the auth_dh cache entry (e.g. call
304 * purge_authtab()) since the AUTH_DH data used here
305 * are always the same.
309 kmem_free(secinfo->sdata, sizeof (sec_data_t) * secinfo->count);
310 kmem_free(secinfo, sizeof (sv_secinfo_t));
314 * Check if there is more secinfo to try.
315 * If TRUE, try again.
317 static bool_t
318 secinfo_check(servinfo4_t *svp)
321 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
322 if (svp->sv_secinfo == NULL) {
323 nfs_rw_exit(&svp->sv_lock);
324 return (FALSE);
327 svp->sv_secinfo->index++;
328 if (svp->sv_secinfo->index < svp->sv_secinfo->count) {
329 svp->sv_flags |= SV4_TRYSECINFO;
330 svp->sv_currsec =
331 &svp->sv_secinfo->sdata[svp->sv_secinfo->index];
332 nfs_rw_exit(&svp->sv_lock);
333 return (TRUE);
334 } else {
335 svp->sv_secinfo->index = 0;
336 svp->sv_flags &= ~SV4_TRYSECINFO;
337 svp->sv_currsec = NULL;
338 nfs_rw_exit(&svp->sv_lock);
339 return (FALSE);
344 * Update the secinfo related fields in svp.
346 * secinfo_update will free the previous sv_secinfo and update with
347 * the new secinfo. However, if the sv_secinfo is saved into sv_save_secinfo
348 * before the recovery starts via save_mnt_secinfo(), sv_secinfo will not
349 * be freed until the recovery is done.
351 static void
352 secinfo_update(servinfo4_t *svp, SECINFO4res *sec_info)
355 sv_secinfo_t *newsecinfo;
358 * Create secinfo before freeing the old one to make sure
359 * they are not using the same address.
361 newsecinfo = secinfo_create(svp, sec_info, svp->sv_hostname);
363 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
364 if (svp->sv_secinfo && svp->sv_secinfo != svp->sv_save_secinfo) {
365 secinfo_free(svp->sv_secinfo);
368 svp->sv_secinfo = newsecinfo;
369 if (svp->sv_secinfo) {
370 svp->sv_secinfo->index = 0;
371 svp->sv_flags |= SV4_TRYSECINFO;
372 svp->sv_currsec =
373 &svp->sv_secinfo->sdata[svp->sv_secinfo->index];
374 } else {
375 svp->sv_flags &= ~SV4_TRYSECINFO;
376 svp->sv_currsec = NULL;
378 nfs_rw_exit(&svp->sv_lock);
382 * Save the original mount point security information.
384 * sv_savesec saves the pointer of sv_currsec which points to one of the
385 * secinfo data in the sv_secinfo list. i.e. sv_currsec == &sv_secinfo[index].
387 * sv_save_secinfo saves the pointer of sv_secinfo which is the list of
388 * secinfo data returned by the server.
390 void
391 save_mnt_secinfo(servinfo4_t *svp)
393 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
394 if (svp->sv_currsec) {
395 svp->sv_savesec = svp->sv_currsec;
396 svp->sv_save_secinfo = svp->sv_secinfo;
397 } else {
398 ASSERT(svp->sv_save_secinfo == NULL);
399 svp->sv_savesec = svp->sv_secdata;
401 nfs_rw_exit(&svp->sv_lock);
405 * Check if we need to restore what is saved in sv_savesec and sv_save_secinfo
406 * to be the current secinfo information - sv_currsec and sv_secinfo.
408 * If op a node that is a stub for a crossed mount point,
409 * keep the original secinfo flavor for the current file system,
410 * not the crossed one.
412 void
413 check_mnt_secinfo(servinfo4_t *svp, vnode_t *vp)
415 bool_t is_restore;
417 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
419 is_restore = (vp == NULL || (RP_ISSTUB(VTOR4(vp)))) &&
420 svp->sv_save_secinfo &&
421 (svp->sv_secinfo != svp->sv_save_secinfo);
423 if (is_restore) {
424 secinfo_free(svp->sv_secinfo);
425 if (svp->sv_savesec == svp->sv_secdata) {
426 ASSERT(svp->sv_save_secinfo == NULL);
427 svp->sv_secinfo = NULL;
428 svp->sv_currsec = NULL;
429 } else {
430 ASSERT(svp->sv_save_secinfo != NULL);
431 svp->sv_secinfo = svp->sv_save_secinfo;
432 svp->sv_currsec = svp->sv_savesec;
434 } else {
435 if (svp->sv_save_secinfo &&
436 svp->sv_save_secinfo != svp->sv_secinfo)
437 secinfo_free(svp->sv_save_secinfo);
440 svp->sv_save_secinfo = NULL;
441 svp->sv_savesec = NULL;
443 nfs_rw_exit(&svp->sv_lock);
447 * Use the security flavors supported on the client to try
448 * PUTROOTFH until a flavor is found.
450 * PUTROOTFH could return NFS4ERR_RESOURCE and NFS4ERR_WRONGSEC that
451 * may need a recovery action. This routine only handles NFS4ERR_WRONGSEC.
452 * For other recovery action, it returns ok to the caller for retry.
454 static int
455 secinfo_tryroot_otw(mntinfo4_t *mi, cred_t *cr)
457 COMPOUND4args_clnt args;
458 COMPOUND4res_clnt res;
459 nfs_argop4 argop;
460 int doqueue = 1;
461 bool_t needrecov = FALSE;
462 nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS };
464 /* use the flavors supported on the client */
465 secinfo_update(mi->mi_curr_serv, secinfo_support);
467 /* Compound {Putroofh} */
468 args.ctag = TAG_PUTROOTFH;
470 args.array_len = 1;
471 args.array = &argop;
473 argop.argop = OP_PUTROOTFH;
474 retry:
475 NFS4_DEBUG(nfs4_client_call_debug, (CE_NOTE,
476 "secinfo_tryroot_otw: %s call, mi 0x%p",
477 needrecov ? "recov" : "first", (void*)mi));
479 rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, &e);
481 needrecov = nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp);
482 if (e.error && !needrecov) {
483 return (e.error);
486 if (res.status == NFS4ERR_WRONGSEC) {
487 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
488 if (secinfo_check(mi->mi_curr_serv))
489 goto retry;
491 * Have tried all flavors supported on the client,
492 * but still get NFS4ERR_WRONGSEC. Nothing more can
493 * be done.
495 return (geterrno4(res.status));
498 if (needrecov) {
499 NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
500 "secinfo_tryroot_otw: let the caller retry\n"));
502 if (!e.error)
503 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
504 return (0);
507 if (res.status) {
508 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
509 return (geterrno4(res.status));
513 * Done.
515 * Now, mi->sv_curr_server->sv_currsec points to the flavor found.
516 * SV4_TRYSECINFO has been cleared in rfs4call.
517 * sv_currsec will be used.
519 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
520 return (e.error);
524 * Caculate the total number of components within a given pathname.
525 * Assuming the given pathname is not null.
526 * e.g. returns 5 for "/a/b/c/d/e" or "a/b/c/d/e"
527 * returns 0 for "/"
529 static int
530 comp_total(char *inpath)
532 int tnum = 0;
533 char *slash;
535 while (*inpath != '\0') {
537 if (*inpath == '/') {
538 inpath++;
539 continue;
541 if ((slash = (char *)strchr(inpath, '/')) == NULL) {
542 tnum++;
543 break;
544 } else {
545 tnum++;
546 inpath = slash + 1;
550 return (tnum);
554 * Get the pointer of the n-th component in the given path.
555 * Mark the preceeding '/' of the component to be '\0' when done.
556 * Assuming nth is > 0.
558 static void
559 comp_getn(char *inpath, int nth, component4 *comp)
561 char *path = inpath, *comp_start, *slash = NULL;
562 int count = 0;
564 while ((count != nth) && (*path != '\0')) {
566 comp_start = path;
568 /* ignore slashes prior to the component name */
569 while (*path == '/')
570 path++;
572 if (*path != '\0') {
573 comp_start = path;
574 count++;
577 if ((slash = strchr(path, '/')) == NULL)
578 break;
579 else
580 path = slash + 1;
583 if (count == nth) {
584 if (slash)
585 *slash = '\0';
586 comp->utf8string_len = strlen(comp_start);
587 comp->utf8string_val = comp_start;
589 if (comp_start != inpath) {
590 comp_start--;
591 *comp_start = '\0';
593 } else {
594 comp->utf8string_len = 0;
595 comp->utf8string_val = NULL;
600 * SECINFO over the wire compound operation
602 * compound {PUTROOTFH, {LOOKUP parent-path}, SECINFO component}
604 * This routine assumes there is a component to work on, thus the
605 * given pathname (svp->sv_path) has to have at least 1 component.
607 * isrecov - TRUE if this routine is called from a recovery thread.
609 * nfs4secinfo_otw() only deals with NFS4ERR_WRONGSEC recovery. If this
610 * is already in a recovery thread, then setup the non-wrongsec recovery
611 * action thru nfs4_start_recovery and return to the outer loop in
612 * nfs4_recov_thread() for recovery. If this is not called from a recovery
613 * thread, then error out and let the caller decide what to do.
615 static int
616 nfs4secinfo_otw(mntinfo4_t *mi, cred_t *cr, servinfo4_t *svp, int isrecov)
618 COMPOUND4args_clnt args;
619 COMPOUND4res_clnt res;
620 nfs_argop4 *argop;
621 nfs_resop4 *resop;
622 lookup4_param_t lookuparg;
623 uint_t path_len;
624 int doqueue;
625 int numops, num_argops;
626 char *tmp_path;
627 component4 comp;
628 uint_t ncomp, tcomp;
629 bool_t needrecov = FALSE;
630 nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS };
632 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
633 ncomp = tcomp = comp_total(svp->sv_path);
634 path_len = strlen(svp->sv_path);
635 nfs_rw_exit(&svp->sv_lock);
636 ASSERT(ncomp > 0);
638 retry:
639 tmp_path = kmem_alloc(path_len + 1, KM_SLEEP);
640 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
641 bcopy(svp->sv_path, tmp_path, path_len + 1);
642 nfs_rw_exit(&svp->sv_lock);
643 comp_getn(tmp_path, ncomp, &comp);
645 args.ctag = TAG_SECINFO;
647 lookuparg.l4_getattrs = LKP4_NO_ATTRIBUTES;
648 lookuparg.argsp = &args;
649 lookuparg.resp = &res;
650 lookuparg.header_len = 1; /* Putrootfh */
651 lookuparg.trailer_len = 1; /* Secinfo */
652 lookuparg.ga_bits = 0;
653 lookuparg.mi = mi;
655 /* setup LOOKUPs for parent path */
656 (void) nfs4lookup_setup(tmp_path, &lookuparg, 0);
658 argop = args.array;
660 /* put root fh */
661 argop[0].argop = OP_PUTROOTFH;
663 /* setup SECINFO op */
664 num_argops = args.array_len;
665 argop[num_argops - 1].argop = OP_SECINFO;
666 argop[num_argops - 1].nfs_argop4_u.opsecinfo.name.utf8string_len =
667 comp.utf8string_len;
668 argop[num_argops - 1].nfs_argop4_u.opsecinfo.name.utf8string_val =
669 comp.utf8string_val;
671 doqueue = 1;
673 NFS4_DEBUG(nfs4_client_call_debug, (CE_NOTE,
674 "nfs4secinfo_otw: %s call, mi 0x%p",
675 needrecov ? "recov" : "first", (void*)mi));
677 rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, &e);
679 needrecov = nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp);
680 if (e.error && !needrecov) {
681 nfs4args_lookup_free(argop, num_argops);
682 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
683 kmem_free(tmp_path, path_len + 1);
684 return (e.error);
688 * Secinfo compound op may fail with NFS4ERR_WRONGSEC from
689 * PUTROOTFH or LOOKUP. Special handling here to recover it.
691 if (res.status == NFS4ERR_WRONGSEC) {
693 if (res.array_len == 1) {
695 * If a flavor can not be found via trying
696 * all supported flavors on the client, no
697 * more operations.
699 ncomp = tcomp;
700 nfs4args_lookup_free(argop, num_argops);
701 kmem_free(argop,
702 lookuparg.arglen * sizeof (nfs_argop4));
703 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
704 kmem_free(tmp_path, path_len + 1);
706 if (e.error = secinfo_tryroot_otw(mi, cr)) {
707 return (e.error);
709 goto retry;
711 ncomp = res.array_len - 1;
712 nfs4args_lookup_free(argop, num_argops);
713 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
714 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
715 kmem_free(tmp_path, path_len + 1);
716 goto retry;
720 * This routine does not do recovery for non NFS4ERR_WRONGSEC error.
721 * However, if this is already in a recovery thread, then
722 * set up the recovery action thru nfs4_start_recovery and
723 * return ok back to the outer loop in nfs4_recov_thread for
724 * recovery.
726 if (needrecov) {
727 bool_t abort;
729 /* If not in a recovery thread, bail out */
730 if (!isrecov) {
731 if (!e.error) {
732 e.error = geterrno4(res.status);
733 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
736 nfs4args_lookup_free(argop, num_argops);
737 kmem_free(argop,
738 lookuparg.arglen * sizeof (nfs_argop4));
739 kmem_free(tmp_path, path_len + 1);
740 return (e.error);
743 NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
744 "nfs4secinfo_otw: recovery in a recovery thread\n"));
746 abort = nfs4_start_recovery(&e, mi, NULL,
747 NULL, NULL, NULL, OP_SECINFO, NULL, NULL, NULL);
748 if (!e.error) {
749 e.error = geterrno4(res.status);
750 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
752 nfs4args_lookup_free(argop, num_argops);
753 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
754 kmem_free(tmp_path, path_len + 1);
755 if (abort == FALSE) {
757 * Return ok to let the outer loop in
758 * nfs4_recov_thread continue with the recovery action.
760 return (0);
762 return (e.error);
765 if (res.status) {
766 nfs4args_lookup_free(argop, num_argops);
767 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
768 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
769 kmem_free(tmp_path, path_len + 1);
770 return (geterrno4(res.status));
774 * Success! Now get the SECINFO result.
776 numops = res.array_len;
777 resop = &res.array[numops-1]; /* secinfo res */
778 ASSERT(resop->resop == OP_SECINFO);
780 if (resop->nfs_resop4_u.opsecinfo.SECINFO4resok_len == 0) {
782 * Server does not return any flavor for this export point.
783 * Return EACCES.
785 nfs4args_lookup_free(argop, num_argops);
786 kmem_free(tmp_path, path_len + 1);
787 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
788 kmem_free(argop, num_argops * sizeof (nfs_argop4));
789 return (EACCES);
792 secinfo_update(mi->mi_curr_serv, &resop->nfs_resop4_u.opsecinfo);
794 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
795 if (svp->sv_secinfo == NULL) {
796 nfs_rw_exit(&svp->sv_lock);
798 * This could be because the server requires AUTH_DH, but
799 * the client does not have netname/syncaddr data
800 * from sv_dhsec.
802 nfs4args_lookup_free(argop, num_argops);
803 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
804 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
805 kmem_free(tmp_path, path_len + 1);
806 return (EACCES);
808 nfs_rw_exit(&svp->sv_lock);
811 * If this is not the original request, try again using the
812 * new secinfo data in mi.
814 if (ncomp != tcomp) {
816 ncomp = tcomp;
817 nfs4args_lookup_free(argop, num_argops);
818 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
819 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
820 kmem_free(tmp_path, path_len + 1);
821 goto retry;
824 /* Done! */
825 nfs4args_lookup_free(argop, num_argops);
826 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
827 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
828 kmem_free(tmp_path, path_len + 1);
830 return (0); /* got the secinfo */
834 * Get the security information per mount point.
835 * Use the server pathname to get the secinfo.
838 nfs4_secinfo_path(mntinfo4_t *mi, cred_t *cr, int isrecov)
840 int error = 0;
841 int ncomp;
842 servinfo4_t *svp = mi->mi_curr_serv;
845 * Get the server pathname that is being mounted on.
847 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
848 ASSERT(svp->sv_path != NULL);
850 /* returns 0 for root, no matter how many leading /'s */
851 ncomp = comp_total(svp->sv_path);
854 * If mounting server rootdir, use available secinfo list
855 * on the client. No SECINFO call here since SECINFO op
856 * expects a component name.
858 if (ncomp == 0) {
859 if (svp->sv_secinfo == NULL) {
860 nfs_rw_exit(&svp->sv_lock);
861 secinfo_update(svp, secinfo_support);
862 return (0);
864 nfs_rw_exit(&svp->sv_lock);
866 if (secinfo_check(svp))
867 return (0); /* try again */
869 /* no flavors in sv_secinfo work */
870 return (EACCES);
872 nfs_rw_exit(&svp->sv_lock);
875 * Get the secinfo from the server.
877 error = nfs4secinfo_otw(mi, cr, svp, isrecov);
879 if (error) {
881 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
882 if (svp->sv_secinfo) {
883 if (svp->sv_save_secinfo == svp->sv_secinfo) {
884 svp->sv_save_secinfo = NULL;
885 svp->sv_savesec = NULL;
887 secinfo_free(svp->sv_secinfo);
888 svp->sv_secinfo = NULL;
889 svp->sv_currsec = NULL;
890 svp->sv_flags &= ~SV4_TRYSECINFO;
893 if (svp->sv_save_secinfo) {
894 secinfo_free(svp->sv_save_secinfo);
895 svp->sv_save_secinfo = NULL;
896 svp->sv_savesec = NULL;
898 nfs_rw_exit(&svp->sv_lock);
901 return (error);
905 * (secinfo) compound based on a given filehandle and component name.
907 * i.e. (secinfo) PUTFH (fh), SECINFO nm
910 nfs4_secinfo_fh_otw(mntinfo4_t *mi, nfs4_sharedfh_t *fh, char *nm, cred_t *cr)
912 COMPOUND4args_clnt args;
913 COMPOUND4res_clnt res;
914 nfs_argop4 argop[2];
915 nfs_resop4 *resop;
916 int num_argops, doqueue;
917 nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS };
918 servinfo4_t *svp;
920 ASSERT(strlen(nm) > 0);
922 num_argops = 2; /* Putfh, Secinfo nm */
923 args.ctag = TAG_SECINFO;
924 args.array_len = num_argops;
925 args.array = argop;
927 /* putfh fh */
928 argop[0].argop = OP_CPUTFH;
929 argop[0].nfs_argop4_u.opcputfh.sfh = fh;
931 /* setup SECINFO op */
932 argop[1].argop = OP_CSECINFO;
933 argop[1].nfs_argop4_u.opcsecinfo.cname = nm;
935 doqueue = 1;
937 rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, &e);
939 if (e.error)
940 return (e.error);
942 if (res.status) {
943 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
944 return (geterrno4(res.status));
948 * Success! Now get the SECINFO result.
950 resop = &res.array[1]; /* secinfo res */
951 ASSERT(resop->resop == OP_SECINFO);
953 if (resop->nfs_resop4_u.opsecinfo.SECINFO4resok_len == 0) {
955 * Server does not return any flavor for this export point.
956 * Return EACCES.
958 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
959 return (EACCES);
962 secinfo_update(mi->mi_curr_serv, &resop->nfs_resop4_u.opsecinfo);
964 svp = mi->mi_curr_serv;
965 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
966 if (mi->mi_curr_serv->sv_secinfo == NULL) {
967 nfs_rw_exit(&svp->sv_lock);
969 * This could be because the server requires AUTH_DH, but
970 * the client does not have netname/syncaddr data
971 * from sv_dhsec.
973 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
974 return (EACCES);
976 nfs_rw_exit(&svp->sv_lock);
978 /* Done! */
979 xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
981 return (0); /* got the secinfo */
985 * Making secinfo operation with a given vnode.
987 * This routine is not used by the recovery thread.
988 * Mainly used in response to NFS4ERR_WRONGSEC from lookup.
991 nfs4_secinfo_vnode_otw(vnode_t *dvp, char *nm, cred_t *cr)
993 ASSERT(strlen(nm) > 0);
995 return (nfs4_secinfo_fh_otw(VTOMI4(dvp), VTOR4(dvp)->r_fh, nm, cr));
999 * Making secinfo operation with a given vnode if this vnode
1000 * has a parent node. If the given vnode is a root node, use
1001 * the pathname from the mntinfor4_t to do the secinfo call.
1003 * This routine is mainly used by the recovery thread.
1006 nfs4_secinfo_vnode(vnode_t *vp, cred_t *cr, int isrecov)
1008 svnode_t *svp = VTOSV(vp);
1009 char *nm;
1010 int error = 0;
1013 * If there is a parent filehandle, use it to get the secinfo,
1014 * otherwise, use mntinfo4_t pathname to get the secinfo.
1016 if (svp->sv_dfh) {
1017 nm = fn_name(svp->sv_name); /* get the actual component name */
1018 error = nfs4_secinfo_fh_otw(VTOMI4(vp), svp->sv_dfh, nm, cr);
1019 kmem_free(nm, MAXNAMELEN);
1020 } else {
1021 error = nfs4_secinfo_path(VTOMI4(vp), cr, isrecov);
1024 return (error);
1028 * We are here because the client gets NFS4ERR_WRONGSEC.
1030 * Get the security information from the server and indicate
1031 * a set of new security information is here to try.
1032 * Start with the server path that's mounted.
1035 nfs4_secinfo_recov(mntinfo4_t *mi, vnode_t *vp1, vnode_t *vp2)
1037 int error = 0;
1038 cred_t *cr, *lcr = NULL;
1039 servinfo4_t *svp = mi->mi_curr_serv;
1042 * If the client explicitly specifies a preferred flavor to use
1043 * and gets NFS4ERR_WRONGSEC back, there is no need to negotiate
1044 * the flavor.
1046 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
1047 if (! (svp->sv_flags & SV4_TRYSECDEFAULT)) {
1048 error = geterrno4(NFS4ERR_WRONGSEC);
1049 nfs_rw_exit(&svp->sv_lock);
1050 } else {
1051 cr = crgetcred();
1053 if (svp->sv_secdata->uid != 0) {
1054 lcr = crdup(cr);
1055 (void) crsetugid(lcr, svp->sv_secdata->uid,
1056 crgetgid(cr));
1058 nfs_rw_exit(&svp->sv_lock);
1060 if (vp1 == NULL && vp2 == NULL) {
1061 error = nfs4_secinfo_path(mi, cr, TRUE);
1063 if (lcr && error == EACCES)
1064 error = nfs4_secinfo_path(mi, lcr, TRUE);
1065 } else if (vp1) {
1066 error = nfs4_secinfo_vnode(vp1, cr, TRUE);
1068 if (lcr && error == EACCES)
1069 error = nfs4_secinfo_vnode(vp1, lcr, TRUE);
1070 } /* else */
1071 /* ??? */
1073 crfree(cr);
1074 if (lcr != NULL)
1075 crfree(lcr);
1078 mutex_enter(&mi->mi_lock);
1079 mi->mi_recovflags &= ~MI4R_NEED_SECINFO;
1080 mutex_exit(&mi->mi_lock);
1082 return (error);