/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* nis_common.c
*
* Common code and structures used by name-service-switch "nis" backends.
*/
#include "nis_common.h"
#include <string.h>
#include <synch.h>
#include <rpcsvc/ypclnt.h>
#include <rpcsvc/yp_prot.h>
#include <thread.h>
#include <ctype.h>
#include <stdlib.h>
#include <signal.h>
#ifndef MT_UNSAFE_YP /* Is the libnsl YP client code MT-unsafe? */
#define MT_UNSAFE_YP 0 /* No, not any longer */
#endif
#if MT_UNSAFE_YP
static mutex_t one_lane = DEFAULTMUTEX;
#endif
/* <rpcsvc/ypclnt.h> uses (char *) where it should use (const char *) */
typedef char *grrr;
/*
* The YP client code thinks it's being helpful by appending '\n' and '\0'
* to the values returned by yp_match() et al. In order to do this it
* ends up doing more malloc()ing and data copying than would otherwise
* be necessary. If we're interested in performance we should provide
* alternative library interfaces that skip the helpfulness and instead
* let the XDR routines dump the value directly into the buffer where
* we really want it. For now, though, we just use the vanilla interface.
*/
static nss_status_t
switch_err(ypstatus, ismatch)
int ypstatus;
int ismatch;
{
switch (ypstatus) {
case 0:
errno = 0;
return (NSS_SUCCESS);
case YPERR_BADARGS:
case YPERR_KEY:
errno = 0;
return (NSS_NOTFOUND);
/*
* When the YP server is running in DNS forwarding mode,
* the forwarder will return YPERR_NOMORE to us if it
* is unable to contact a server (i.e., it has timed out).
* The NSS_NISSERVDNS_TRYAGAIN is returned for timeout errors.
*/
case YPERR_NOMORE:
if (ismatch)
return (NSS_NISSERVDNS_TRYAGAIN);
else
return (NSS_NOTFOUND);
case YPERR_DOMAIN:
case YPERR_YPSERV:
case YPERR_BUSY:
return (NSS_TRYAGAIN);
default:
return (NSS_UNAVAIL);
}
}
/*ARGSUSED*/
nss_status_t
_nss_nis_setent(be, dummy)
nis_backend_ptr_t be;
void *dummy;
{
if (be->enum_key != 0) {
free(be->enum_key);
be->enum_key = 0;
}
be->enum_keylen = 0;
return (NSS_SUCCESS);
}
nss_status_t
_nss_nis_endent(be, dummy)
nis_backend_ptr_t be;
void *dummy;
{
return (_nss_nis_setent(be, dummy));
/* Nothing else we can clean up, is there? */
}
void
massage_netdb(const char **valp, int *vallenp)
{
const char *first;
const char *last;
const char *val = *valp;
int vallen = *vallenp;
if ((last = memchr(val, '#', vallen)) == 0) {
last = val + vallen;
}
for (first = val; first < last && isspace(*first); first++) {
;
}
for (/* cstyle */; first < last && isspace(last[-1]); last--) {
;
}
/*
* Don't check for an empty line because it shouldn't ever
* have made it into the YP map.
*/
*valp = first;
*vallenp = (int)(last - first);
}
nss_status_t
_nss_nis_ypmatch(domain, map, key, valp, vallenp, ypstatusp)
const char *domain;
const char *map;
const char *key;
char **valp;
int *vallenp;
int *ypstatusp;
{
int ypstatus;
#if MT_UNSAFE_YP
sigset_t oldmask, newmask;
(void) sigfillset(&newmask);
(void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
(void) mutex_lock(&one_lane);
#endif
ypstatus = __yp_match_cflookup((grrr)domain, (grrr)map,
(grrr)key, (int)strlen(key), valp, vallenp, 0);
#if MT_UNSAFE_YP
(void) mutex_unlock(&one_lane);
(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
#endif
if (ypstatusp != 0) {
*ypstatusp = ypstatus;
}
return (switch_err(ypstatus, 1));
}
/*
* XXX special version of _nss_nis_ypmatch() for handling C2 (passwd.adjunct)
* lookups when we need a reserved port.
*/
static nss_status_t
_nss_nis_ypmatch_rsvdport(domain, map, key, valp, vallenp, ypstatusp)
const char *domain;
const char *map;
const char *key;
char **valp;
int *vallenp;
int *ypstatusp;
{
int ypstatus;
#if MT_UNSAFE_YP
sigset_t oldmask, newmask;
(void) sigfillset(&newmask);
(void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
(void) mutex_lock(&one_lane);
#endif
ypstatus = __yp_match_rsvdport_cflookup((grrr)domain, (grrr)map,
(grrr)key, strlen(key), valp, vallenp, 0);
#if MT_UNSAFE_YP
(void) mutex_unlock(&one_lane);
(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
#endif
if (ypstatusp != 0) {
*ypstatusp = ypstatus;
}
return (switch_err(ypstatus, 1));
}
nss_status_t
_nss_nis_lookup(be, args, netdb, map, key, ypstatusp)
nis_backend_ptr_t be;
nss_XbyY_args_t *args;
int netdb;
const char *map;
const char *key;
int *ypstatusp;
{
nss_status_t res;
int vallen;
char *val;
char *free_ptr;
int parsestat;
if ((res = _nss_nis_ypmatch(be->domain, map, key, &val, &vallen,
ypstatusp)) != NSS_SUCCESS) {
return (res);
}
parsestat = NSS_STR_PARSE_SUCCESS;
if (strcmp(map, "passwd.byname") == 0 ||
strcmp(map, "passwd.byuid") == 0) {
parsestat = validate_passwd_ids(&val, &vallen, 1);
} else if (strcmp(map, "group.byname") == 0)
parsestat = validate_group_ids(&val, &vallen, 1);
if (parsestat != NSS_STR_PARSE_SUCCESS) {
free(val);
return (NSS_NOTFOUND);
}
free_ptr = val;
if (netdb) {
massage_netdb((const char **)&val, &vallen);
}
args->returnval = NULL;
args->returnlen = 0;
parsestat = (*args->str2ent)(val, vallen,
args->buf.result, args->buf.buffer, args->buf.buflen);
if (parsestat == NSS_STR_PARSE_SUCCESS) {
args->returnval = args->buf.result;
args->returnlen = vallen;
res = NSS_SUCCESS;
} else if (parsestat == NSS_STR_PARSE_ERANGE) {
args->erange = 1;
/* We won't find this otherwise, anyway */
res = NSS_NOTFOUND;
} /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */
free(free_ptr);
return (res);
}
nss_status_t
_nss_nis_lookup_rsvdport(be, args, netdb, map, key, ypstatusp)
nis_backend_ptr_t be;
nss_XbyY_args_t *args;
int netdb;
const char *map;
const char *key;
int *ypstatusp;
{
nss_status_t res;
int vallen;
char *val;
char *free_ptr;
int parsestat;
if ((res = _nss_nis_ypmatch_rsvdport(be->domain, map, key, &val,
&vallen, ypstatusp)) != NSS_SUCCESS) {
return (res);
}
free_ptr = val;
if (netdb) {
massage_netdb((const char **)&val, &vallen);
}
args->returnval = NULL;
args->returnlen = 0;
parsestat = (*args->str2ent)(val, vallen,
args->buf.result, args->buf.buffer, args->buf.buflen);
if (parsestat == NSS_STR_PARSE_SUCCESS) {
args->returnval = args->buf.result;
args->returnlen = vallen;
res = NSS_SUCCESS;
} else if (parsestat == NSS_STR_PARSE_ERANGE) {
args->erange = 1;
/* We won't find this otherwise, anyway */
res = NSS_NOTFOUND;
} /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */
free(free_ptr);
return (res);
}
static nss_status_t
do_getent(be, args, netdb)
nis_backend_ptr_t be;
nss_XbyY_args_t *args;
int netdb;
{
nss_status_t res;
int ypstatus;
int outkeylen, outvallen;
char *outkey, *outval;
char *free_ptr;
int parsestat;
#if MT_UNSAFE_YP
sigset_t oldmask, newmask;
(void) sigfillset(&newmask);
(void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
(void) mutex_lock(&one_lane);
#endif
if (be->enum_key == 0) {
ypstatus = __yp_first_cflookup((grrr)be->domain,
(grrr)be->enum_map, &outkey,
&outkeylen, &outval,
&outvallen, 0);
} else {
ypstatus = __yp_next_cflookup((grrr)be->domain,
(grrr)be->enum_map, be->enum_key,
be->enum_keylen, &outkey,
&outkeylen, &outval,
&outvallen, 0);
}
#if MT_UNSAFE_YP
(void) mutex_unlock(&one_lane);
(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
#endif
if ((res = switch_err(ypstatus, 0)) != NSS_SUCCESS) {
return (res);
}
free_ptr = outval;
if (netdb) {
massage_netdb((const char **)&outval, &outvallen);
}
args->returnval = NULL;
args->returnlen = 0;
parsestat = (*args->str2ent)(outval, outvallen,
args->buf.result, args->buf.buffer, args->buf.buflen);
if (parsestat == NSS_STR_PARSE_SUCCESS) {
args->returnval = args->buf.result;
args->returnlen = outvallen;
res = NSS_SUCCESS;
} else if (parsestat == NSS_STR_PARSE_ERANGE) {
args->erange = 1;
/* We won't find this otherwise, anyway */
res = NSS_NOTFOUND;
} /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */
free(free_ptr);
if (be->enum_key != 0) {
free(be->enum_key);
}
be->enum_key = outkey;
be->enum_keylen = outkeylen;
return (res);
}
nss_status_t
_nss_nis_getent_rigid(be, args)
nis_backend_ptr_t be;
void *args;
{
return (do_getent(be, (nss_XbyY_args_t *)args, 0));
}
nss_status_t
_nss_nis_getent_netdb(be, args)
nis_backend_ptr_t be;
void *args;
{
return (do_getent(be, (nss_XbyY_args_t *)args, 1));
}
struct cb_data {
void *args;
const char *filter;
nis_do_all_func_t func;
nss_status_t result;
};
enum { ITER_NEXT = 0, ITER_STOP = 1 }; /* Should be in <rpcsvc/ypclnt.h> */
/*ARGSUSED*/
static int
do_cback(instatus, inkey, inkeylen, inval, invallen, indata)
int instatus;
const char *inkey;
int inkeylen;
const char *inval;
int invallen;
struct cb_data *indata;
{
nss_status_t res;
if (instatus != YP_TRUE) {
return (ITER_NEXT); /* yp_all may decide otherwise... */
}
if (indata->filter != 0 && strstr(inval, indata->filter) == 0) {
/*
* Optimization: if the entry doesn't contain the filter
* string then it can't be the entry we want, so don't
* bother looking more closely at it.
*/
return (ITER_NEXT);
}
res = (*indata->func)(inval, invallen, indata->args);
if (res == NSS_NOTFOUND) {
return (ITER_NEXT);
} else {
indata->result = res;
return (ITER_STOP);
}
}
nss_status_t
_nss_nis_do_all(be, args, filter, func)
nis_backend_ptr_t be;
void *args;
const char *filter;
nis_do_all_func_t func;
{
int ypall_status;
struct cb_data data;
struct ypall_callback cback;
data.args = args;
data.filter = filter;
data.func = func;
data.result = NSS_NOTFOUND;
cback.foreach = do_cback;
cback.data = (char *)&data;
#if MT_UNSAFE_YP
sigset_t oldmask, newmask;
(void) sigfillset(&newmask);
(void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
(void) mutex_lock(&one_lane);
#endif
ypall_status = __yp_all_cflookup((grrr)be->domain,
(grrr) be->enum_map, &cback, 0);
#if MT_UNSAFE_YP
(void) mutex_unlock(&one_lane);
(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
#endif
switch (ypall_status) {
case 0:
return (data.result);
case YPERR_DOMAIN:
case YPERR_YPSERV:
case YPERR_BUSY: /* Probably never get this, but... */
return (NSS_TRYAGAIN);
default:
return (NSS_UNAVAIL);
}
}
struct XbyY_data {
nss_XbyY_args_t *args;
nis_XY_check_func func;
int netdb;
};
static nss_status_t
XbyY_iterator(instr, instr_len, a)
const char *instr;
int instr_len;
void *a;
{
struct XbyY_data *xydata = (struct XbyY_data *)a;
nss_XbyY_args_t *args = xydata->args;
nss_status_t res;
int parsestat;
if (xydata->netdb) {
massage_netdb(&instr, &instr_len);
}
args->returnval = NULL;
args->returnlen = 0;
parsestat = (*args->str2ent)(instr, instr_len,
args->buf.result, args->buf.buffer, args->buf.buflen);
if (parsestat == NSS_STR_PARSE_SUCCESS) {
args->returnval = args->buf.result;
if ((*xydata->func)(args)) {
res = NSS_SUCCESS;
args->returnlen = instr_len;
} else {
res = NSS_NOTFOUND;
args->returnval = 0;
}
} else if (parsestat == NSS_STR_PARSE_ERANGE) {
/*
* If we got here because (*str2ent)() found that the buffer
* wasn't big enough, maybe we should quit and return erange.
* Instead we'll keep looking and eventually return "not
* found" -- it's a bug, but not an earth-shattering one.
*/
args->erange = 1; /* <== Is this a good idea? */
res = NSS_NOTFOUND;
} /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */
return (res);
}
nss_status_t
_nss_nis_XY_all(be, args, netdb, filter, func)
nis_backend_ptr_t be;
nss_XbyY_args_t *args;
int netdb;
const char *filter;
nis_XY_check_func func;
{
struct XbyY_data data;
data.args = args;
data.func = func;
data.netdb = netdb;
return (_nss_nis_do_all(be, &data, filter, XbyY_iterator));
/* Now how many levels of callbacks was that? */
}
/*ARGSUSED*/
nss_status_t
_nss_nis_destr(be, dummy)
nis_backend_ptr_t be;
void *dummy;
{
if (be != 0) {
/* === Should change to invoke ops[ENDENT] ? */
(void) _nss_nis_endent(be, 0);
free(be);
}
return (NSS_SUCCESS); /* In case anyone is dumb enough to check */
}
/* We want to lock this even if the YP routines are MT-safe */
static mutex_t yp_domain_lock = DEFAULTMUTEX;
static char *yp_domain;
const char *
_nss_nis_domain()
{
char *domain;
/*
* This much locking is probably more "by the book" than necessary...
*/
sigset_t oldmask, newmask;
(void) sigfillset(&newmask);
(void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
(void) mutex_lock(&yp_domain_lock);
if ((domain = yp_domain) == 0) {
#if MT_UNSAFE_YP
(void) mutex_lock(&one_lane);
#endif
if (yp_get_default_domain(&yp_domain) == 0) {
domain = yp_domain;
}
#if MT_UNSAFE_YP
(void) mutex_unlock(&one_lane);
#endif
}
(void) mutex_unlock(&yp_domain_lock);
(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
return (domain);
}
nss_backend_t *
_nss_nis_constr(ops, n_ops, enum_map)
nis_backend_op_t ops[];
int n_ops;
const char *enum_map;
{
const char *domain;
nis_backend_ptr_t be;
if ((domain = _nss_nis_domain()) == 0 ||
(be = (nis_backend_ptr_t)malloc(sizeof (*be))) == 0) {
return (0);
}
be->ops = ops;
be->n_ops = n_ops;
be->domain = domain;
be->enum_map = enum_map; /* Don't strdup, assume valid forever */
be->enum_key = 0;
be->enum_keylen = 0;
return ((nss_backend_t *)be);
}
/*
* This routine is used to parse lines of the form:
* name number aliases
* It returns 1 if the key in argp matches any one of the
* names in the line, otherwise 0
* Used by rpc
*/
int
_nss_nis_check_name_aliases(nss_XbyY_args_t *argp, const char *line,
int linelen)
{
const char *limit, *linep, *keyp;
linep = line;
limit = line + linelen;
keyp = argp->key.name;
/* compare name */
while (*keyp && linep < limit && !isspace(*linep) && *keyp == *linep) {
keyp++;
linep++;
}
if (*keyp == '\0' && linep < limit && isspace(*linep))
return (1);
/* skip remainder of the name, if any */
while (linep < limit && !isspace(*linep))
linep++;
/* skip the delimiting spaces */
while (linep < limit && isspace(*linep))
linep++;
/* compare with the aliases */
while (linep < limit) {
/*
* 1st pass: skip number
* Other passes: skip remainder of the alias name, if any
*/
while (linep < limit && !isspace(*linep))
linep++;
/* skip the delimiting spaces */
while (linep < limit && isspace(*linep))
linep++;
/* compare with the alias name */
keyp = argp->key.name;
while (*keyp && linep < limit && !isspace(*linep) &&
*keyp == *linep) {
keyp++;
linep++;
}
if (*keyp == '\0' && (linep == limit || isspace(*linep)))
return (1);
}
return (0);
}