nfs_sec.c revision bfa62c284402a2e7ee6a349df0ef8d1aa2e908e8
/*
* 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
*/
/* LINTLIBRARY */
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* nfs security related library routines.
*
* Some of the routines in this file are adopted from
* lib/libnsl/netselect/netselect.c and are modified to be
* used for accessing /etc/nfssec.conf.
*/
/* SVr4.0 1.18 */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <syslog.h>
#include <synch.h>
#include <rpc/rpcsec_gss.h>
#ifdef WNFS_SEC_NEGO
#include "webnfs.h"
#endif
#define GETBYNAME 1
#define GETBYNUM 2
/*
* mapping for /etc/nfssec.conf
*/
struct sc_data {
char *string;
int value;
};
static struct sc_data sc_service[] = {
"default", rpc_gss_svc_default,
"-", rpc_gss_svc_none,
"none", rpc_gss_svc_none,
"integrity", rpc_gss_svc_integrity,
"privacy", rpc_gss_svc_privacy,
};
static char *gettoken(char *, int);
char *, char *, char *);
/*
* blank() returns true if the line is a blank line, 0 otherwise
*/
static int
char *cp;
{
cp++;
}
return (*cp == '\0');
}
/*
* comment() returns true if the line is a comment, 0 otherwise.
*/
static int
char *cp;
{
cp++;
}
return (*cp == '#');
}
/*
* getvalue() searches for the given string in the given array,
* and returns the integer value associated with the string.
*/
static unsigned long
char *cp;
{
int i; /* used to index through the given struct sc_data array */
break;
}
}
}
/*
* shift1left() moves all characters in the string over 1 to
* the left.
*/
static void
shift1left(p)
char *p;
{
for (; *p; p++)
*p = *(p + 1);
}
/*
* gettoken() behaves much like strtok(), except that
* it knows about escaped space characters (i.e., space characters
* preceeded by a '\' are taken literally).
*
* XXX We should make this MT-hot by making it more like strtok_r().
*/
static char *
char *cp;
int skip;
{
static char *savep; /* the place where we left off */
register char *p; /* the beginning of the new token */
register char *retp; /* the token to be returned */
/* Determine if first or subsequent call */
/* Return if no tokens remain. */
if (p == 0) {
return (NULL);
}
while (isspace(*p))
p++;
if (*p == '\0') {
return (NULL);
}
/*
* Save the location of the token and then skip past it
*/
retp = p;
while (*p) {
if (isspace(*p))
shift1left(p);
continue;
} else
break;
/*
* Only process the escape of the space separator;
* since the token may contain other separators,
* let the other routines handle the escape of
* specific characters in the token.
*/
shift1left(p);
}
p++;
}
if (*p == '\0') {
savep = 0; /* indicate this is last token */
} else {
*p = '\0';
savep = ++p;
}
return (retp);
}
/*
* matchname() parses a line of the /etc/nfssec.conf file
* and match the sc_name with the given name.
* If there is a match, it fills the information into the given
* pointer of the seconfig_t structure.
*
* Returns TRUE if a match is found.
*/
static bool_t
{
/* bad line */
return (FALSE);
}
return (FALSE);
}
== SC_FAILURE)) {
return (FALSE);
}
return (FALSE);
}
}
return (TRUE);
}
/*
* matchnum() parses a line of the /etc/nfssec.conf file
* and match the sc_nfsnum with the given number.
* If it is a match, it fills the information in the given pointer
* of the seconfig_t structure.
*
* Returns TRUE if a match is found.
*/
static bool_t
{
/* bad line */
return (FALSE);
}
/* bad line */
return (FALSE);
}
return (FALSE);
}
== SC_FAILURE)) {
return (FALSE);
}
return (FALSE);
}
}
return (TRUE);
}
/*
* Fill in the RPC Protocol security flavor number
* into the sc_rpcnum of seconfig_t structure.
*
* Mainly to map NFS secmod number to RPCSEC_GSS if
* a mechanism name is specified.
*/
static void
{
} else {
}
}
/*
* Parse a given hostname (nodename[.domain@realm]) to
* instant name (nodename[.domain]) and realm.
*
* Assuming user has allocated the space for inst and realm.
*/
static int
{
char *h, *r;
if (!hostname)
return (0);
if (!h) {
return (0);
}
r = (char *)strchr(h, '@');
if (!r) {
} else {
*r++ = '\0';
}
free(h);
return (1);
}
/*
* Get the name corresponding to a qop num.
*/
char *
{
char *tok; /* holds a token from the line */
return (NULL);
}
(void) mutex_lock(&matching_lock);
/* bad line */
continue;
}
/* bad line */
goto err;
}
goto err;
== NULL)) {
goto err;
}
break;
}
}
}
err:
(void) mutex_unlock(&matching_lock);
return (gss_qop);
}
/*
* This routine creates an auth handle assocaited with the
* negotiated security flavor contained in nfs_sec. The auth
* handle will be used in the next LOOKUP request to fetch
* the filehandle.
*/
AUTH *
{
char *gss_qop;
static int window = 60;
goto err;
case AUTH_UNIX:
case AUTH_NONE:
return (NULL);
case AUTH_DES:
goto err;
NULL));
case RPCSEC_GSS:
goto err;
"nfs_create_ah: need mechanism information\n");
goto err;
}
/*
* RPCSEC_GSS service names are of the form svc@host.dom
*/
goto err;
default:
return (NULL);
}
err:
return (NULL);
}
#ifdef WNFS_SEC_NEGO
/*
* This routine negotiates sec flavors with server and returns:
* SNEGO_SUCCESS: successful; sec flavors are
* returned in snego,
* SNEGO_DEF_VALID: default sec flavor valid; no need
* to negotiate flavors,
* SNEGO_ARRAY_TOO_SMALL: array too small,
* SNEGO_FAILURE: failure
*/
/*
* The following depicts how sec flavors are placed in an
* overloaded V2 fhandle:
*
* Note that the first four octets contain the length octet,
* the status octet, and two padded octets to make them XDR
* four-octet aligned.
*
* 1 2 3 4 32
* +---+---+---+---+---+---+---+---+ +---+---+---+---+ +---+
* | l | s | | | sec_1 |...| sec_n |...| |
* +---+---+---+---+---+---+---+---+ +---+---+---+---+ +---+
*
* where
*
* the status octet s indicates whether there are more security
* flavors(1 means yes, 0 means no) that require the client to
* perform another 0x81 LOOKUP to get them,
*
* the length octet l is the length describing the number of
* valid octets that follow. (l = 4 * n, where n is the number
*
* The following depicts how sec flavors are placed in an
* overloaded V3 fhandle:
*
* 1 4
* +--+--+--+--+
* | len |
* +--+--+--+--+
* up to 64
* +--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+
* |s | | | | sec_1 | sec_2 | ... | sec_n |
* +--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+
*
* len = 4 * (n+1), where n is the number of security flavors
* sent in the current overloaded filehandle.
*
* the status octet s indicates whether there are more security
* mechanisms(1 means yes, 0 means no) that require the client
* to perform another 0x81 LOOKUP to get them.
*
* Three octets are padded after the status octet.
*/
enum snego_stat
{
int status;
return (SNEGO_FAILURE);
TIMEOUT);
return (SNEGO_DEF_VALID);
if (rpc_stat != RPC_AUTHERROR)
return (SNEGO_FAILURE);
{
struct rpc_err e;
char *p;
int tot = 0;
CLNT_GETERR(clnt, &e);
if (e.re_why != AUTH_TOOWEAK)
return (SNEGO_FAILURE);
return (SNEGO_FAILURE);
}
/*
* Do an x81 LOOKUP
*/
p[0] = (char)WNL_SEC_NEGO;
do {
free(p);
return (SNEGO_FAILURE);
}
/*
* retrieve flavors from filehandle:
* 1st byte: length
* 2nd byte: status
* 3rd & 4th: pad
* 5th and after: sec flavors.
*/
{
char *c = (char *)&res.wnl_diropres_u.
int ii;
/* LINTED pointer alignment */
int *ip = (int *)(c+sizeof (int));
if (tot >= MAX_FLAVORS) {
free(p);
return (SNEGO_ARRAY_TOO_SMALL);
}
status = (int)*(c+1);
free(p);
return (SNEGO_FAILURE);
}
}
} while (status);
free(p);
return (SNEGO_SUCCESS);
}
TIMEOUT);
return (SNEGO_DEF_VALID);
if (rpc_stat != RPC_AUTHERROR)
return (SNEGO_FAILURE);
{
struct rpc_err e;
char *p;
int tot = 0;
CLNT_GETERR(clnt, &e);
if (e.re_why != AUTH_TOOWEAK)
return (SNEGO_FAILURE);
return (SNEGO_FAILURE);
}
/*
* Do an x81 LOOKUP
*/
p[0] = (char)WNL_SEC_NEGO;
do {
sizeof (WNL_LOOKUP3res));
free(p);
return (SNEGO_FAILURE);
}
/*
* retrieve flavors from filehandle:
*
* 1st byte: status
* 2nd thru 4th: pad
* 5th and after: sec flavors.
*/
{
char *c = (char *)&res.WNL_LOOKUP3res_u.
int ii;
int cnt;
/* LINTED pointer alignment */
int *ip = (int *)(c+sizeof (int));
if (tot >= MAX_FLAVORS) {
free(p);
return (SNEGO_ARRAY_TOO_SMALL);
}
status = (int)(*c);
free(p);
return (SNEGO_FAILURE);
}
}
} while (status);
free(p);
return (SNEGO_SUCCESS);
}
}
return (SNEGO_FAILURE);
}
#endif
/*
* Get seconfig from /etc/nfssec.conf by name or by number or
* by descriptior.
*/
/* ARGSUSED */
static int
{
return (SC_NOTFOUND);
return (SC_OPENFAIL);
}
(void) mutex_lock(&matching_lock);
switch (whichway) {
case GETBYNAME:
goto found;
}
break;
case GETBYNUM:
goto found;
}
break;
default:
break;
}
}
}
(void) mutex_unlock(&matching_lock);
return (SC_NOTFOUND);
(void) mutex_unlock(&matching_lock);
(void) get_rpcnum(entryp);
return (SC_NOERROR);
}
/*
* NFS project private API.
* Get a seconfig entry from /etc/nfssec.conf by nfs specific sec name,
* e.g. des, krb5p, etc.
*/
int
{
if (!entryp)
return (SC_NOMEM);
entryp));
}
/*
* NFS project private API.
*
* Get a seconfig entry from /etc/nfssec.conf by nfs specific sec number,
* e.g. AUTH_DES, AUTH_KRB5_P, etc.
*/
int
{
if (!entryp)
return (SC_NOMEM);
entryp));
}
/*
* NFS project private API.
*
* Get a seconfig_t entry used as the default for NFS operations.
* The default flavor entry is defined in /etc/nfssec.conf.
*
* Assume user has allocate spaces for secp.
*/
int
{
return (SC_NOMEM);
}
/*
* NFS project private API.
*
* Free an sec_data structure.
* Free the parts that nfs_clnt_secdata allocates.
*/
void
{
if (!secdata)
return;
case AUTH_UNIX:
case AUTH_NONE:
break;
case AUTH_DES:
/* LINTED pointer alignment */
if (dkdata) {
}
break;
case RPCSEC_GSS:
/* LINTED pointer alignment */
if (gdata) {
}
break;
default:
break;
}
}
/*
* Make an client side sec_data structure and fill in appropriate value
* based on its rpc security flavor.
*
* It is caller's responsibility to allocate space for seconfig_t,
* and this routine will allocate space for the sec_data structure
* and related data field.
*
* Return the sec_data_t on success.
* If fail, return NULL pointer.
*/
{
if (!secdata) {
return (NULL);
}
/*
* Now, fill in the information for client side secdata :
*
* For AUTH_UNIX, AUTH_DES
* hostname can be in the form of
* nodename or
* nodename.domain
*
* For RPCSEC_GSS security flavor
* hostname can be in the form of
* nodename or
* nodename.domain or
* nodename@realm (realm can be the same as the domain) or
* nodename.domain@realm
*/
case AUTH_UNIX:
case AUTH_NONE:
break;
case AUTH_DES:
/*
* If hostname is in the format of host.nisdomain
* the netname will be constructed with
* this nisdomain name rather than the default
* domain of the machine.
*/
hostname);
goto err_out;
}
if (!dkdata) {
"nfs_clnt_secdata: no memory\n");
goto err_out;
}
sizeof (dh_k4_clntdata_t));
"nfs_clnt_secdata: no memory\n");
goto err_out;
}
"nfs_clnt_secdata: no memory\n");
goto err_out;
}
break;
case RPCSEC_GSS:
"nfs_clnt_secdata: need mechanism information\n");
goto err_out;
}
if (!gdata) {
"nfs_clnt_secdata: no memory\n");
goto err_out;
}
"nfs_clnt_secdata: bad host name\n");
goto err_out;
}
"nfs_clnt_secdata: no memory\n");
goto err_out;
}
break;
default:
goto err_out;
}
return (secdata);
return (NULL);
}
/*
* nfs_get_root_principal() maps a host name to its principal name
* based on the given security information.
*
* input : seconfig - security configuration information
* host - the host name which could be in the following forms:
* node
* node.namedomain
* node@secdomain (e.g. kerberos realm is a secdomain)
* node.namedomain@secdomain
* output : rootname_p - address of the principal name for the host
*
* Currently, this routine is only used by share program.
*
*/
{
char secdomain[MAX_NAME_LEN];
case AUTH_DES:
"nfs_get_root_principal: unknown host: %s\n", host);
return (FALSE);
}
if (!*rootname_p) {
"nfs_get_root_principal: no memory\n");
return (FALSE);
}
break;
case RPCSEC_GSS:
"nfs_get_root_principal: bad host name\n");
return (FALSE);
}
"nfs_get_root_principal: can not get principal name : %s\n", host);
return (FALSE);
}
break;
default:
return (FALSE);
}
return (TRUE);
}
/*
* SYSLOG SC_* errors.
*/
int
{
switch (scerror) {
case SC_NOMEM :
return (0);
case SC_OPENFAIL :
return (0);
case SC_NOTFOUND :
return (0);
case SC_BADENTRIES :
return (0);
default:
msg[0] = '\0';
return (-1);
}
}