/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* NFS Version 4 client side SECINFO code.
*/
#include <nfs/nfs4_clnt.h>
#include <nfs/nfs_clnt.h>
/*
* Set up the security flavors supported in this release.
* In the order of potential usage.
*/
'\x12', '\x01', '\x02', '\x02'};
/* XXX should come from auth.h, do the cleanup someday */
extern void sec_clnt_freeinfo(struct sec_data *);
/*
* "nfsstat -m" needs to print out what flavor is used for a mount
* point. V3 kernel gets the nfs pseudo flavor from the userland and provides
* nfsstat with such information. However, in V4, we do not have nfs pseudo
* flavors mapping in the kernel for the rpcsec_gss data negotiated from
* the nfs server.
*
* XXX
* Hard coded the mapping in V4 for now. We should look into a possibility
* to return the rpcsec_gss mechanism and service information to nfsstat and
* perhaps have nfsstat print out the mech and service seperately...
*
* We should avoid referring to nfssec.conf file in V4. The original reason
* for having /etc/nfssec.conf file is because V3 MOUNT protocol can only
* return an integer for a flavor, thus the term "nfs pseudo flavor" is
* defined and the nfssec.conf file is used to map the nfs pseudo flavor
* to rpcsec_gss data (mech, service, default-qop). Now, V4 can return the
* rpcsec_gss data instead of an integer, so in theory, V4 should not need
* to depend on the nfssec.conf file anymore.
*/
/*
* Currently, 6 flavors are supported: sys, krb5, krb5i, krb5p, dh, none.
* Without proper keys, krb5* or dh will fail.
*
* XXX kgss_indicate_mechs() should be able to tell us what gss mechanisms
* use them. However, the dh640 and dh1024 implementation are not nfs tested.
* Should look into using kgss_indicate_mechs when new gss mechanism is added.
*/
void
nfs4_secinfo_init(void)
{
int i;
val = kmem_alloc(
KM_SLEEP);
/* add krb5, krb5i, krb5p */
for (i = 1; i <= 3; i++) {
}
#if !defined(lint)
#endif
}
/*
* clean up secinfo_support
*/
void
nfs4_secinfo_fini(void)
{
}
/*
* Map RPCSEC_GSS data to a nfs pseudo flavor number defined
* in the nfssec.conf file.
*
* mechanism service qop nfs-pseudo-flavor
* ----------------------------------------------------
* kerberos_v5 none default 390003/krb5
* kerberos_v5 integrity default 390004/krb5i
* kerberos_v5 privacy default 390005/krb5p
*
* XXX need to re-visit the mapping semantics when a new
* security mechanism is to be added.
*/
int
{
/* Is this kerberos_v5? */
krb5_oid.sec_oid4_len) != 0) {
return (0);
}
/* for krb5, krb5i, krb5p mapping */
switch (service) {
case RPC_GSS_SVC_NONE:
return (NFS_FLAVOR_KRB5);
case RPC_GSS_SVC_INTEGRITY:
return (NFS_FLAVOR_KRB5I);
case RPC_GSS_SVC_PRIVACY:
return (NFS_FLAVOR_KRB5P);
default:
break;
}
/* no mapping */
return (0);
}
/*
* secinfo_create() maps the secinfo4 data coming over the wire
* to sv_secinfo data structure in servinfo4_t
*/
static sv_secinfo_t *
{
if (len == 0)
return (NULL);
/*
* If there is no valid sv_dhsec data available but an AUTH_DH
* is in the list, skip AUTH_DH flavor.
*/
for (i = 0; i < len; i++) {
seccnt--;
}
}
if (seccnt == 0)
return (NULL);
scnt = 0;
for (i = 0; i < len; i++) {
case RPCSEC_GSS:
scnt++;
break;
case AUTH_DH:
scnt++;
break;
}
/* no auth_dh data on the client, skip auth_dh */
continue;
default:
scnt++;
break;
}
}
return (sinfo);
}
/*
* secinfo_free() frees the malloc'd portion of a sv_secinfo_t in servinfo4_t.
*
* This is similar to sec_clnt_freeinfo() offered from rpcsec module,
* except that sec_clnt_freeinfo() frees up an individual secdata.
*/
void
{
int i;
return;
/*
* An auth handle may already cached in rpcsec_gss
* module per this secdata. Purge the cache entry
* before freeing up this secdata. Can't use
* sec_clnt_freeinfo since the allocation of secinfo
* is different from sec_data.
*/
}
/* release ref to sv_dhsec */
/*
* No need to purge the auth_dh cache entry (e.g. call
* purge_authtab()) since the AUTH_DH data used here
* are always the same.
*/
}
}
}
/*
* Check if there is more secinfo to try.
* If TRUE, try again.
*/
static bool_t
{
return (FALSE);
}
svp->sv_currsec =
return (TRUE);
} else {
return (FALSE);
}
}
/*
* Update the secinfo related fields in svp.
*
* secinfo_update will free the previous sv_secinfo and update with
* the new secinfo. However, if the sv_secinfo is saved into sv_save_secinfo
* before the recovery starts via save_mnt_secinfo(), sv_secinfo will not
* be freed until the recovery is done.
*/
static void
{
/*
* Create secinfo before freeing the old one to make sure
* they are not using the same address.
*/
}
if (svp->sv_secinfo) {
svp->sv_currsec =
} else {
}
}
/*
* Save the original mount point security information.
*
* sv_savesec saves the pointer of sv_currsec which points to one of the
* secinfo data in the sv_secinfo list. i.e. sv_currsec == &sv_secinfo[index].
*
* sv_save_secinfo saves the pointer of sv_secinfo which is the list of
* secinfo data returned by the server.
*/
void
{
if (svp->sv_currsec) {
} else {
}
}
/*
* Check if we need to restore what is saved in sv_savesec and sv_save_secinfo
* to be the current secinfo information - sv_currsec and sv_secinfo.
*
* If op a node that is a stub for a crossed mount point,
* keep the original secinfo flavor for the current file system,
* not the crossed one.
*/
void
{
svp->sv_save_secinfo &&
if (is_restore) {
} else {
}
} else {
if (svp->sv_save_secinfo &&
}
}
/*
* Use the security flavors supported on the client to try
* PUTROOTFH until a flavor is found.
*
* PUTROOTFH could return NFS4ERR_RESOURCE and NFS4ERR_WRONGSEC that
* may need a recovery action. This routine only handles NFS4ERR_WRONGSEC.
* For other recovery action, it returns ok to the caller for retry.
*/
static int
{
/* use the flavors supported on the client */
/* Compound {Putroofh} */
"secinfo_tryroot_otw: %s call, mi 0x%p",
return (e.error);
}
goto retry;
/*
* Have tried all flavors supported on the client,
* but still get NFS4ERR_WRONGSEC. Nothing more can
* be done.
*/
}
if (needrecov) {
"secinfo_tryroot_otw: let the caller retry\n"));
if (!e.error)
return (0);
}
}
/*
* Done.
*
* Now, mi->sv_curr_server->sv_currsec points to the flavor found.
* SV4_TRYSECINFO has been cleared in rfs4call.
* sv_currsec will be used.
*/
return (e.error);
}
/*
* Caculate the total number of components within a given pathname.
* Assuming the given pathname is not null.
* e.g. returns 5 for "/a/b/c/d/e" or "a/b/c/d/e"
* returns 0 for "/"
*/
static int
{
int tnum = 0;
char *slash;
while (*inpath != '\0') {
if (*inpath == '/') {
inpath++;
continue;
}
tnum++;
break;
} else {
tnum++;
}
}
return (tnum);
}
/*
* Get the pointer of the n-th component in the given path.
* Mark the preceeding '/' of the component to be '\0' when done.
* Assuming nth is > 0.
*/
static void
{
int count = 0;
comp_start = path;
/* ignore slashes prior to the component name */
while (*path == '/')
path++;
if (*path != '\0') {
comp_start = path;
count++;
}
break;
else
}
if (slash)
*slash = '\0';
if (comp_start != inpath) {
comp_start--;
*comp_start = '\0';
}
} else {
comp->utf8string_len = 0;
}
}
/*
* SECINFO over the wire compound operation
*
* compound {PUTROOTFH, {LOOKUP parent-path}, SECINFO component}
*
* This routine assumes there is a component to work on, thus the
* given pathname (svp->sv_path) has to have at least 1 component.
*
* isrecov - TRUE if this routine is called from a recovery thread.
*
* nfs4secinfo_otw() only deals with NFS4ERR_WRONGSEC recovery. If this
* is already in a recovery thread, then setup the non-wrongsec recovery
* action thru nfs4_start_recovery and return to the outer loop in
* nfs4_recov_thread() for recovery. If this is not called from a recovery
* thread, then error out and let the caller decide what to do.
*/
static int
{
int doqueue;
char *tmp_path;
/* setup LOOKUPs for parent path */
/* put root fh */
/* setup SECINFO op */
doqueue = 1;
"nfs4secinfo_otw: %s call, mi 0x%p",
return (e.error);
}
/*
* Secinfo compound op may fail with NFS4ERR_WRONGSEC from
* PUTROOTFH or LOOKUP. Special handling here to recover it.
*/
/*
* If a flavor can not be found via trying
* all supported flavors on the client, no
* more operations.
*/
return (e.error);
}
goto retry;
}
goto retry;
}
/*
* This routine does not do recovery for non NFS4ERR_WRONGSEC error.
* However, if this is already in a recovery thread, then
* set up the recovery action thru nfs4_start_recovery and
* return ok back to the outer loop in nfs4_recov_thread for
* recovery.
*/
if (needrecov) {
/* If not in a recovery thread, bail out */
if (!isrecov) {
if (!e.error) {
(void) xdr_free(xdr_COMPOUND4res_clnt,
}
return (e.error);
}
"nfs4secinfo_otw: recovery in a recovery thread\n"));
if (!e.error) {
}
/*
* Return ok to let the outer loop in
* nfs4_recov_thread continue with the recovery action.
*/
return (0);
}
return (e.error);
}
}
/*
* Success! Now get the SECINFO result.
*/
/*
* Server does not return any flavor for this export point.
* Return EACCES.
*/
return (EACCES);
}
/*
* This could be because the server requires AUTH_DH, but
* from sv_dhsec.
*/
return (EACCES);
}
/*
* If this is not the original request, try again using the
* new secinfo data in mi.
*/
goto retry;
}
/* Done! */
return (0); /* got the secinfo */
}
/*
* Get the security information per mount point.
* Use the server pathname to get the secinfo.
*/
int
{
int error = 0;
int ncomp;
/*
* Get the server pathname that is being mounted on.
*/
/* returns 0 for root, no matter how many leading /'s */
/*
* If mounting server rootdir, use available secinfo list
* on the client. No SECINFO call here since SECINFO op
* expects a component name.
*/
if (ncomp == 0) {
return (0);
}
if (secinfo_check(svp))
return (0); /* try again */
/* no flavors in sv_secinfo work */
return (EACCES);
}
/*
* Get the secinfo from the server.
*/
if (error) {
if (svp->sv_secinfo) {
}
}
if (svp->sv_save_secinfo) {
}
}
return (error);
}
/*
* (secinfo) compound based on a given filehandle and component name.
*
* i.e. (secinfo) PUTFH (fh), SECINFO nm
*/
int
{
/* putfh fh */
/* setup SECINFO op */
doqueue = 1;
if (e.error)
return (e.error);
}
/*
* Success! Now get the SECINFO result.
*/
/*
* Server does not return any flavor for this export point.
* Return EACCES.
*/
return (EACCES);
}
/*
* This could be because the server requires AUTH_DH, but
* from sv_dhsec.
*/
return (EACCES);
}
/* Done! */
return (0); /* got the secinfo */
}
/*
* Making secinfo operation with a given vnode.
*
* This routine is not used by the recovery thread.
* Mainly used in response to NFS4ERR_WRONGSEC from lookup.
*/
int
{
}
/*
* Making secinfo operation with a given vnode if this vnode
* has a parent node. If the given vnode is a root node, use
* the pathname from the mntinfor4_t to do the secinfo call.
*
* This routine is mainly used by the recovery thread.
*/
int
{
char *nm;
int error = 0;
/*
* If there is a parent filehandle, use it to get the secinfo,
* otherwise, use mntinfo4_t pathname to get the secinfo.
*/
} else {
}
return (error);
}
/*
* We are here because the client gets NFS4ERR_WRONGSEC.
*
* Get the security information from the server and indicate
* a set of new security information is here to try.
* Start with the server path that's mounted.
*/
int
{
int error = 0;
/*
* If the client explicitly specifies a preferred flavor to use
* and gets NFS4ERR_WRONGSEC back, there is no need to negotiate
* the flavor.
*/
} else {
}
} else if (vp1) {
} /* else */
/* ??? */
}
return (error);
}