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]
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>
32 #include <nfs/nfs_clnt.h>
33 #include <nfs/rnode4.h>
34 #include <sys/cmn_err.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
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.
85 nfs4_secinfo_init(void)
90 secinfo_support
= kmem_alloc(sizeof (SECINFO4res
), KM_SLEEP
);
91 secinfo_support
->SECINFO4resok_len
= SECINFO_SUPPORT_COUNT
;
93 secinfo_support
->SECINFO4resok_len
* sizeof (secinfo4
),
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
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) {
161 /* for krb5, krb5i, krb5p mapping */
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
);
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
;
187 uint_t len
= sec_info
->SECINFO4resok_len
;
188 secinfo4
*value
= sec_info
->SECINFO4resok_val
;
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
)
209 sdata
= kmem_alloc(sizeof (sec_data_t
) * seccnt
, KM_SLEEP
);
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
) {
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
;
237 secinfo2nfsflavor(&info
->oid
, info
->service
);
242 sdata
[scnt
] = *svp
->sv_dhsec
;
246 /* no auth_dh data on the client, skip auth_dh */
249 sdata
[scnt
].secmod
= val
->flavor
;
250 sdata
[scnt
].data
= NULL
;
256 ASSERT(seccnt
== scnt
);
257 sinfo
= kmem_alloc(sizeof (sv_secinfo_t
), KM_SLEEP
);
258 sinfo
->count
= seccnt
;
259 sinfo
->sdata
= sdata
;
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.
271 secinfo_free(sv_secinfo_t
*secinfo
)
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.
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
);
327 svp
->sv_secinfo
->index
++;
328 if (svp
->sv_secinfo
->index
< svp
->sv_secinfo
->count
) {
329 svp
->sv_flags
|= SV4_TRYSECINFO
;
331 &svp
->sv_secinfo
->sdata
[svp
->sv_secinfo
->index
];
332 nfs_rw_exit(&svp
->sv_lock
);
335 svp
->sv_secinfo
->index
= 0;
336 svp
->sv_flags
&= ~SV4_TRYSECINFO
;
337 svp
->sv_currsec
= NULL
;
338 nfs_rw_exit(&svp
->sv_lock
);
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.
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
;
373 &svp
->sv_secinfo
->sdata
[svp
->sv_secinfo
->index
];
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.
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
;
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.
413 check_mnt_secinfo(servinfo4_t
*svp
, vnode_t
*vp
)
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
);
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
;
430 ASSERT(svp
->sv_save_secinfo
!= NULL
);
431 svp
->sv_secinfo
= svp
->sv_save_secinfo
;
432 svp
->sv_currsec
= svp
->sv_savesec
;
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.
455 secinfo_tryroot_otw(mntinfo4_t
*mi
, cred_t
*cr
)
457 COMPOUND4args_clnt args
;
458 COMPOUND4res_clnt res
;
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
;
473 argop
.argop
= OP_PUTROOTFH
;
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
) {
486 if (res
.status
== NFS4ERR_WRONGSEC
) {
487 xdr_free(xdr_COMPOUND4res_clnt
, (caddr_t
)&res
);
488 if (secinfo_check(mi
->mi_curr_serv
))
491 * Have tried all flavors supported on the client,
492 * but still get NFS4ERR_WRONGSEC. Nothing more can
495 return (geterrno4(res
.status
));
499 NFS4_DEBUG(nfs4_client_recov_debug
, (CE_NOTE
,
500 "secinfo_tryroot_otw: let the caller retry\n"));
503 xdr_free(xdr_COMPOUND4res_clnt
, (caddr_t
)&res
);
508 xdr_free(xdr_COMPOUND4res_clnt
, (caddr_t
)&res
);
509 return (geterrno4(res
.status
));
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
);
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"
530 comp_total(char *inpath
)
535 while (*inpath
!= '\0') {
537 if (*inpath
== '/') {
541 if ((slash
= (char *)strchr(inpath
, '/')) == NULL
) {
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.
559 comp_getn(char *inpath
, int nth
, component4
*comp
)
561 char *path
= inpath
, *comp_start
, *slash
= NULL
;
564 while ((count
!= nth
) && (*path
!= '\0')) {
568 /* ignore slashes prior to the component name */
577 if ((slash
= strchr(path
, '/')) == NULL
)
586 comp
->utf8string_len
= strlen(comp_start
);
587 comp
->utf8string_val
= comp_start
;
589 if (comp_start
!= inpath
) {
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.
616 nfs4secinfo_otw(mntinfo4_t
*mi
, cred_t
*cr
, servinfo4_t
*svp
, int isrecov
)
618 COMPOUND4args_clnt args
;
619 COMPOUND4res_clnt res
;
622 lookup4_param_t lookuparg
;
625 int numops
, num_argops
;
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
);
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;
655 /* setup LOOKUPs for parent path */
656 (void) nfs4lookup_setup(tmp_path
, &lookuparg
, 0);
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
=
668 argop
[num_argops
- 1].nfs_argop4_u
.opsecinfo
.name
.utf8string_val
=
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);
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
700 nfs4args_lookup_free(argop
, num_argops
);
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
)) {
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);
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
729 /* If not in a recovery thread, bail out */
732 e
.error
= geterrno4(res
.status
);
733 xdr_free(xdr_COMPOUND4res_clnt
, (caddr_t
)&res
);
736 nfs4args_lookup_free(argop
, num_argops
);
738 lookuparg
.arglen
* sizeof (nfs_argop4
));
739 kmem_free(tmp_path
, path_len
+ 1);
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
);
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.
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.
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
));
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
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);
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
) {
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);
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
)
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.
859 if (svp
->sv_secinfo
== NULL
) {
860 nfs_rw_exit(&svp
->sv_lock
);
861 secinfo_update(svp
, secinfo_support
);
864 nfs_rw_exit(&svp
->sv_lock
);
866 if (secinfo_check(svp
))
867 return (0); /* try again */
869 /* no flavors in sv_secinfo work */
872 nfs_rw_exit(&svp
->sv_lock
);
875 * Get the secinfo from the server.
877 error
= nfs4secinfo_otw(mi
, cr
, svp
, isrecov
);
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
);
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
;
916 int num_argops
, doqueue
;
917 nfs4_error_t e
= { 0, NFS4_OK
, RPC_SUCCESS
};
920 ASSERT(strlen(nm
) > 0);
922 num_argops
= 2; /* Putfh, Secinfo nm */
923 args
.ctag
= TAG_SECINFO
;
924 args
.array_len
= num_argops
;
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
;
937 rfs4call(mi
, &args
, &res
, cr
, &doqueue
, RFSCALL_SOFT
, &e
);
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.
958 xdr_free(xdr_COMPOUND4res_clnt
, (caddr_t
)&res
);
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
973 xdr_free(xdr_COMPOUND4res_clnt
, (caddr_t
)&res
);
976 nfs_rw_exit(&svp
->sv_lock
);
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
);
1013 * If there is a parent filehandle, use it to get the secinfo,
1014 * otherwise, use mntinfo4_t pathname to get the secinfo.
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
);
1021 error
= nfs4_secinfo_path(VTOMI4(vp
), cr
, isrecov
);
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
)
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
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
);
1053 if (svp
->sv_secdata
->uid
!= 0) {
1055 (void) crsetugid(lcr
, svp
->sv_secdata
->uid
,
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
);
1066 error
= nfs4_secinfo_vnode(vp1
, cr
, TRUE
);
1068 if (lcr
&& error
== EACCES
)
1069 error
= nfs4_secinfo_vnode(vp1
, lcr
, TRUE
);
1078 mutex_enter(&mi
->mi_lock
);
1079 mi
->mi_recovflags
&= ~MI4R_NEED_SECINFO
;
1080 mutex_exit(&mi
->mi_lock
);