Implementing 'fallback' nsswitch source. 'fallback' source is used

when particular function can't be found in nsswitch-module. For
example, getgrouplist(3) will use module-supplied 'getgroupmembership'
function (which can work in an optimal way for such source as LDAP) and
will fall back to the stanard iterate-through-all-groups implementation
otherwise.

PR:		ports/114655
Submitted by:	Michael Hanselmann <freebsd AT hansmi DOT ch>
Reviewed by:	brooks (mentor)
This commit is contained in:
Michael Bushkov 2007-12-12 10:08:03 +00:00
parent 2992b5e82c
commit a59d6a8724
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=174547
4 changed files with 233 additions and 127 deletions

View File

@ -68,7 +68,8 @@
#define NSSRC_DNS "dns" /* DNS; IN for hosts, HS for others */
#define NSSRC_NIS "nis" /* YP/NIS */
#define NSSRC_COMPAT "compat" /* passwd,group in YP compat mode */
#define NSSRC_CACHE "cache" /* cache daemon */
#define NSSRC_CACHE "cache" /* nscd daemon */
#define NSSRC_FALLBACK "__fallback" /* internal fallback source */
/*
* currently implemented databases
@ -132,6 +133,7 @@ typedef struct _ns_dtab {
*/
#define NS_FILES_CB(F,C) { NSSRC_FILES, F, C },
#define NS_COMPAT_CB(F,C) { NSSRC_COMPAT, F, C },
#define NS_FALLBACK_CB(F) { NSSRC_FALLBACK, F, NULL },
#ifdef HESIOD
# define NS_DNS_CB(F,C) { NSSRC_DNS, F, C },

View File

@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#endif
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#ifdef HESIOD
@ -143,6 +144,9 @@ NSS_TLS_HANDLING(compat);
static int compat_setgrent(void *, void *, va_list);
static int compat_group(void *, void *, va_list);
static int gr_addgid(gid_t, gid_t *, int, int *);
static int getgroupmembership_fallback(void *, void *, va_list);
#ifdef NS_CACHING
static int grp_id_func(char *, size_t *, va_list, void *);
static int grp_marshal_func(char *, size_t *, void *, va_list, void *);
@ -361,32 +365,178 @@ grp_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
NSS_MP_CACHE_HANDLING(group);
#endif /* NS_CACHING */
#ifdef NS_CACHING
static const nss_cache_info setgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER(
group, (void *)nss_lt_all,
NULL, NULL);
#endif
static const ns_dtab setgrent_dtab[] = {
{ NSSRC_FILES, files_setgrent, (void *)SETGRENT },
#ifdef HESIOD
{ NSSRC_DNS, dns_setgrent, (void *)SETGRENT },
#endif
#ifdef YP
{ NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
#endif
{ NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
#ifdef NS_CACHING
NS_CACHE_CB(&setgrent_cache_info)
#endif
{ NULL, NULL, NULL }
};
#ifdef NS_CACHING
static const nss_cache_info endgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER(
group, (void *)nss_lt_all,
NULL, NULL);
#endif
static const ns_dtab endgrent_dtab[] = {
{ NSSRC_FILES, files_setgrent, (void *)ENDGRENT },
#ifdef HESIOD
{ NSSRC_DNS, dns_setgrent, (void *)ENDGRENT },
#endif
#ifdef YP
{ NSSRC_NIS, nis_setgrent, (void *)ENDGRENT },
#endif
{ NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT },
#ifdef NS_CACHING
NS_CACHE_CB(&endgrent_cache_info)
#endif
{ NULL, NULL, NULL }
};
#ifdef NS_CACHING
static const nss_cache_info getgrent_r_cache_info = NS_MP_CACHE_INFO_INITIALIZER(
group, (void *)nss_lt_all,
grp_marshal_func, grp_unmarshal_func);
#endif
static const ns_dtab getgrent_r_dtab[] = {
{ NSSRC_FILES, files_group, (void *)nss_lt_all },
#ifdef HESIOD
{ NSSRC_DNS, dns_group, (void *)nss_lt_all },
#endif
#ifdef YP
{ NSSRC_NIS, nis_group, (void *)nss_lt_all },
#endif
{ NSSRC_COMPAT, compat_group, (void *)nss_lt_all },
#ifdef NS_CACHING
NS_CACHE_CB(&getgrent_r_cache_info)
#endif
{ NULL, NULL, NULL }
};
static int
gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *grpcnt)
{
int ret, dupc;
for (dupc = 0; dupc < MIN(maxgrp, *grpcnt); dupc++) {
if (groups[dupc] == gid)
return 1;
}
ret = 1;
if (*grpcnt < maxgrp)
groups[*grpcnt] = gid;
else
ret = 0;
(*grpcnt)++;
return ret;
}
static int
getgroupmembership_fallback(void *retval, void *mdata, va_list ap)
{
const ns_src src[] = {
{ mdata, NS_SUCCESS },
{ NULL, 0}
};
struct group grp;
struct group *grp_p;
char *buf;
size_t bufsize;
const char *uname;
gid_t *groups;
gid_t agroup;
int maxgrp, *grpcnt;
int i, rv, ret_errno;
/*
* As this is a fallback method, only provided src
* list will be respected during methods search.
*/
assert(src[0].name != NULL);
uname = va_arg(ap, const char *);
agroup = va_arg(ap, gid_t);
groups = va_arg(ap, gid_t *);
maxgrp = va_arg(ap, int);
grpcnt = va_arg(ap, int *);
rv = NS_UNAVAIL;
buf = malloc(GRP_STORAGE_INITIAL);
if (buf == NULL)
goto out;
bufsize = GRP_STORAGE_INITIAL;
gr_addgid(agroup, groups, maxgrp, grpcnt);
_nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", src, 0);
for (;;) {
do {
ret_errno = 0;
grp_p = NULL;
rv = _nsdispatch(&grp_p, getgrent_r_dtab, NSDB_GROUP,
"getgrent_r", src, &grp, buf, bufsize, &ret_errno);
if (grp_p == NULL && ret_errno == ERANGE) {
free(buf);
if ((bufsize << 1) > GRP_STORAGE_MAX) {
buf = NULL;
errno = ERANGE;
goto out;
}
bufsize <<= 1;
buf = malloc(bufsize);
if (buf == NULL) {
goto out;
}
}
} while (grp_p == NULL && ret_errno == ERANGE);
if (ret_errno != 0) {
errno = ret_errno;
goto out;
}
if (grp_p == NULL)
break;
for (i = 0; grp.gr_mem[i]; i++) {
if (strcmp(grp.gr_mem[i], uname) == 0)
gr_addgid(grp.gr_gid, groups, maxgrp, grpcnt);
}
}
_nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", src);
out:
free(buf);
return (rv);
}
/* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */
int
setgrent(void)
{
#ifdef NS_CACHING
static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
group, (void *)nss_lt_all,
NULL, NULL);
#endif
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_setgrent, (void *)SETGRENT },
#ifdef HESIOD
{ NSSRC_DNS, dns_setgrent, (void *)SETGRENT },
#endif
#ifdef YP
{ NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
#endif
{ NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
#ifdef NS_CACHING
NS_CACHE_CB(&cache_info)
#endif
{ NULL, NULL, NULL }
};
(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, 0);
(void)_nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc, 0);
return (1);
}
@ -394,27 +544,7 @@ setgrent(void)
int
setgroupent(int stayopen)
{
#ifdef NS_CACHING
static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
group, (void *)nss_lt_all,
NULL, NULL);
#endif
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_setgrent, (void *)SETGRENT },
#ifdef HESIOD
{ NSSRC_DNS, dns_setgrent, (void *)SETGRENT },
#endif
#ifdef YP
{ NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
#endif
{ NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
#ifdef NS_CACHING
NS_CACHE_CB(&cache_info)
#endif
{ NULL, NULL, NULL }
};
(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc,
(void)_nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc,
stayopen);
return (1);
}
@ -423,27 +553,7 @@ setgroupent(int stayopen)
void
endgrent(void)
{
#ifdef NS_CACHING
static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
group, (void *)nss_lt_all,
NULL, NULL);
#endif
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_setgrent, (void *)ENDGRENT },
#ifdef HESIOD
{ NSSRC_DNS, dns_setgrent, (void *)ENDGRENT },
#endif
#ifdef YP
{ NSSRC_NIS, nis_setgrent, (void *)ENDGRENT },
#endif
{ NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT },
#ifdef NS_CACHING
NS_CACHE_CB(&cache_info)
#endif
{ NULL, NULL, NULL }
};
(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent", defaultsrc);
(void)_nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", defaultsrc);
}
@ -451,31 +561,11 @@ int
getgrent_r(struct group *grp, char *buffer, size_t bufsize,
struct group **result)
{
#ifdef NS_CACHING
static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
group, (void *)nss_lt_all,
grp_marshal_func, grp_unmarshal_func);
#endif
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_group, (void *)nss_lt_all },
#ifdef HESIOD
{ NSSRC_DNS, dns_group, (void *)nss_lt_all },
#endif
#ifdef YP
{ NSSRC_NIS, nis_group, (void *)nss_lt_all },
#endif
{ NSSRC_COMPAT, compat_group, (void *)nss_lt_all },
#ifdef NS_CACHING
NS_CACHE_CB(&cache_info)
#endif
{ NULL, NULL, NULL }
};
int rv, ret_errno;
ret_errno = 0;
*result = NULL;
rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrent_r", defaultsrc,
rv = _nsdispatch(result, getgrent_r_dtab, NSDB_GROUP, "getgrent_r", defaultsrc,
grp, buffer, bufsize, &ret_errno);
if (rv == NS_SUCCESS)
return (0);
@ -560,6 +650,30 @@ getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
}
int
__getgroupmembership(const char *uname, gid_t agroup, gid_t *groups,
int maxgrp, int *grpcnt)
{
static const ns_dtab dtab[] = {
NS_FALLBACK_CB(getgroupmembership_fallback)
{ NULL, NULL, NULL }
};
int rv;
assert(uname != NULL);
/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
assert(grpcnt != NULL);
*grpcnt = 0;
rv = _nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership",
defaultsrc, uname, agroup, groups, maxgrp, grpcnt);
/* too many groups found? */
return (*grpcnt > maxgrp ? -1 : 0);
}
static struct group grp;
static char *grp_storage;
static size_t grp_storage_size;
@ -1436,3 +1550,5 @@ __gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf,
return (NS_RETURN);
}
}

View File

@ -42,46 +42,11 @@ __FBSDID("$FreeBSD$");
#include <string.h>
#include <unistd.h>
extern int __getgroupmembership(const char *, gid_t, gid_t *, int, int *);
int
getgrouplist(const char *uname, gid_t agroup, gid_t *groups, int *grpcnt)
{
const struct group *grp;
int i, maxgroups, ngroups, ret;
ret = 0;
ngroups = 0;
maxgroups = *grpcnt;
/*
* When installing primary group, duplicate it;
* the first element of groups is the effective gid
* and will be overwritten when a setgid file is executed.
*/
groups[ngroups++] = agroup;
if (maxgroups > 1)
groups[ngroups++] = agroup;
/*
* Scan the group file to find additional groups.
*/
setgrent();
while ((grp = getgrent()) != NULL) {
for (i = 0; i < ngroups; i++) {
if (grp->gr_gid == groups[i])
goto skip;
}
for (i = 0; grp->gr_mem[i]; i++) {
if (!strcmp(grp->gr_mem[i], uname)) {
if (ngroups >= maxgroups) {
ret = -1;
break;
}
groups[ngroups++] = grp->gr_gid;
break;
}
}
skip:
;
}
endgrent();
*grpcnt = ngroups;
return (ret);
return __getgroupmembership(uname, agroup, groups, *grpcnt, grpcnt);
}

View File

@ -135,6 +135,15 @@ static void *nss_builtin_handle = &__nss_builtin_handle;
static void *nss_cache_cycle_prevention_func = NULL;
#endif
/*
* When this is set to 1, nsdispatch won't use nsswitch.conf
* but will consult the 'defaults' source list only.
* NOTE: nested fallbacks (when nsdispatch calls fallback functions,
* which in turn calls nsdispatch, which should call fallback
* function) are not supported
*/
static int fallback_dispatch = 0;
/*
* Attempt to spew relatively uniform messages to syslog.
*/
@ -600,7 +609,7 @@ _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database,
va_list ap;
const ns_dbt *dbt;
const ns_src *srclist;
nss_method method;
nss_method method, fb_method;
void *mdata;
int isthreaded, serrno, i, result, srclistsize;
@ -609,6 +618,9 @@ _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database,
nss_cache_data *cache_data_p;
int cache_flag;
#endif
dbt = NULL;
fb_method = NULL;
isthreaded = __isthreaded;
serrno = errno;
@ -624,8 +636,13 @@ _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database,
result = NS_UNAVAIL;
goto fin;
}
dbt = vector_search(&database, _nsmap, _nsmapsize, sizeof(*_nsmap),
string_compare);
if (fallback_dispatch == 0) {
dbt = vector_search(&database, _nsmap, _nsmapsize, sizeof(*_nsmap),
string_compare);
fb_method = nss_method_lookup(NSSRC_FALLBACK, database,
method_name, disp_tab, &mdata);
}
if (dbt != NULL) {
srclist = dbt->srclist;
srclistsize = dbt->srclistsize;
@ -684,6 +701,12 @@ _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database,
if (result & (srclist[i].flags))
break;
} else if (fb_method != NULL) {
fallback_dispatch = 1;
va_start(ap, defaults);
result = fb_method(retval, (void *)srclist[i].name, ap);
va_end(ap);
fallback_dispatch = 0;
}
}