diff options
Diffstat (limited to 'lib/nss_wrapper')
-rw-r--r-- | lib/nss_wrapper/config.m4 | 19 | ||||
-rw-r--r-- | lib/nss_wrapper/config.mk | 7 | ||||
-rw-r--r-- | lib/nss_wrapper/nss_wrapper.c | 1130 | ||||
-rw-r--r-- | lib/nss_wrapper/nss_wrapper.h | 165 | ||||
-rw-r--r-- | lib/nss_wrapper/nss_wrapper.pl | 265 |
5 files changed, 1586 insertions, 0 deletions
diff --git a/lib/nss_wrapper/config.m4 b/lib/nss_wrapper/config.m4 new file mode 100644 index 0000000000..45423788d1 --- /dev/null +++ b/lib/nss_wrapper/config.m4 @@ -0,0 +1,19 @@ +AC_ARG_ENABLE(nss-wrapper, +[ --enable-nss-wrapper Turn on nss wrapper library (default=no)]) + +HAVE_NSS_WRAPPER=no + +if eval "test x$developer = xyes"; then + enable_nss_wrapper=yes +fi + +if eval "test x$enable_nss_wrapper = xyes"; then + AC_DEFINE(NSS_WRAPPER,1,[Use nss wrapper library]) + HAVE_NSS_WRAPPER=yes + + # this is only used for samba3 + NSS_WRAPPER_OBJS="../lib/nss_wrapper/nss_wrapper.o" +fi + +AC_SUBST(HAVE_NSS_WRAPPER) +AC_SUBST(NSS_WRAPPER_OBJS) diff --git a/lib/nss_wrapper/config.mk b/lib/nss_wrapper/config.mk new file mode 100644 index 0000000000..015fbe511c --- /dev/null +++ b/lib/nss_wrapper/config.mk @@ -0,0 +1,7 @@ +############################## +# Start SUBSYSTEM NSS_WRAPPER +[SUBSYSTEM::NSS_WRAPPER] +# End SUBSYSTEM NSS_WRAPPER +############################## + +NSS_WRAPPER_OBJ_FILES = $(nsswrappersrcdir)/nss_wrapper.o diff --git a/lib/nss_wrapper/nss_wrapper.c b/lib/nss_wrapper/nss_wrapper.c new file mode 100644 index 0000000000..da090832b0 --- /dev/null +++ b/lib/nss_wrapper/nss_wrapper.c @@ -0,0 +1,1130 @@ +/* + * Copyright (C) Stefan Metzmacher 2007 <metze@samba.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef _SAMBA_BUILD_ + +#define NSS_WRAPPER_NOT_REPLACE +#include "../replace/replace.h" +#include "system/passwd.h" +#include "system/filesys.h" + +#else /* _SAMBA_BUILD_ */ + +#error nss_wrapper_only_supported_in_samba_yet + +#endif + +#ifndef _PUBLIC_ +#define _PUBLIC_ +#endif + +/* not all systems have _r functions... */ +#ifndef HAVE_GETPWNAM_R +#define getpwnam_r(name, pwdst, buf, buflen, pwdstp) ENOSYS +#endif +#ifndef HAVE_GETPWUID_R +#define getpwuid_r(uid, pwdst, buf, buflen, pwdstp) ENOSYS +#endif +#ifndef HAVE_GETPWENT_R +#define getpwent_r(pwdst, buf, buflen, pwdstp) ENOSYS +#endif +#ifndef HAVE_GETGRNAM_R +#define getgrnam_r(name, grdst, buf, buflen, grdstp) ENOSYS +#endif +#ifndef HAVE_GETGRUID_R +#define getgrgid_r(uid, grdst, buf, buflen, grdstp) ENOSYS +#endif +#ifndef HAVE_GETGRENT_R +#define getgrent_r(grdst, buf, buflen, grdstp) ENOSYS +#endif + +/* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support + * for now */ +#define REWRITE_CALLS + +#ifdef REWRITE_CALLS + +#define real_getpwnam getpwnam +#define real_getpwnam_r getpwnam_r +#define real_getpwuid getpwuid +#define real_getpwuid_r getpwuid_r + +#define real_setpwent setpwent +#define real_getpwent getpwent +#define real_getpwent_r getpwent_r +#define real_endpwent endpwent + +/* +#define real_getgrlst getgrlst +#define real_getgrlst_r getgrlst_r +#define real_initgroups_dyn initgroups_dyn +*/ +#define real_initgroups initgroups + +#define real_getgrnam getgrnam +#define real_getgrnam_r getgrnam_r +#define real_getgrgid getgrgid +#define real_getgrgid_r getgrgid_r + +#define real_setgrent setgrent +#define real_getgrent getgrent +#define real_getgrent_r getgrent_r +#define real_endgrent endgrent + +#endif + +#if 0 +# ifdef DEBUG +# define NWRAP_ERROR(args) DEBUG(0, args) +# else +# define NWRAP_ERROR(args) printf args +# endif +#else +#define NWRAP_ERROR(args) +#endif + +#if 0 +# ifdef DEBUG +# define NWRAP_DEBUG(args) DEBUG(0, args) +# else +# define NWRAP_DEBUG(args) printf args +# endif +#else +#define NWRAP_DEBUG(args) +#endif + +#if 0 +# ifdef DEBUG +# define NWRAP_VERBOSE(args) DEBUG(0, args) +# else +# define NWRAP_VERBOSE(args) printf args +# endif +#else +#define NWRAP_VERBOSE(args) +#endif + +struct nwrap_cache { + const char *path; + int fd; + struct stat st; + uint8_t *buf; + void *private_data; + bool (*parse_line)(struct nwrap_cache *, char *line); + void (*unload)(struct nwrap_cache *); +}; + +struct nwrap_pw { + struct nwrap_cache *cache; + + struct passwd *list; + int num; + int idx; +}; + +struct nwrap_cache __nwrap_cache_pw; +struct nwrap_pw nwrap_pw_global; + +static bool nwrap_pw_parse_line(struct nwrap_cache *nwrap, char *line); +static void nwrap_pw_unload(struct nwrap_cache *nwrap); + +struct nwrap_gr { + struct nwrap_cache *cache; + + struct group *list; + int num; + int idx; +}; + +struct nwrap_cache __nwrap_cache_gr; +struct nwrap_gr nwrap_gr_global; + +static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line); +static void nwrap_gr_unload(struct nwrap_cache *nwrap); + +static void nwrap_init(void) +{ + static bool initialized; + + if (initialized) return; + initialized = true; + + nwrap_pw_global.cache = &__nwrap_cache_pw; + + nwrap_pw_global.cache->path = getenv("NSS_WRAPPER_PASSWD"); + nwrap_pw_global.cache->fd = -1; + nwrap_pw_global.cache->private_data = &nwrap_pw_global; + nwrap_pw_global.cache->parse_line = nwrap_pw_parse_line; + nwrap_pw_global.cache->unload = nwrap_pw_unload; + + nwrap_gr_global.cache = &__nwrap_cache_gr; + + nwrap_gr_global.cache->path = getenv("NSS_WRAPPER_GROUP"); + nwrap_gr_global.cache->fd = -1; + nwrap_gr_global.cache->private_data = &nwrap_gr_global; + nwrap_gr_global.cache->parse_line = nwrap_gr_parse_line; + nwrap_gr_global.cache->unload = nwrap_gr_unload; +} + +static bool nwrap_enabled(void) +{ + nwrap_init(); + + if (!nwrap_pw_global.cache->path) { + return false; + } + if (nwrap_pw_global.cache->path[0] == '\0') { + return false; + } + if (!nwrap_gr_global.cache->path) { + return false; + } + if (nwrap_gr_global.cache->path[0] == '\0') { + return false; + } + + return true; +} + +static bool nwrap_parse_file(struct nwrap_cache *nwrap) +{ + int ret; + uint8_t *buf = NULL; + char *nline; + + if (nwrap->st.st_size == 0) { + NWRAP_DEBUG(("%s: size == 0\n", + __location__)); + goto done; + } + + if (nwrap->st.st_size > INT32_MAX) { + NWRAP_ERROR(("%s: size[%u] larger than INT32_MAX\n", + __location__, (unsigned)nwrap->st.st_size)); + goto failed; + } + + ret = lseek(nwrap->fd, 0, SEEK_SET); + if (ret != 0) { + NWRAP_ERROR(("%s: lseek - %d\n",__location__,ret)); + goto failed; + } + + buf = (uint8_t *)malloc(nwrap->st.st_size + 1); + if (!buf) { + NWRAP_ERROR(("%s: malloc failed\n",__location__)); + goto failed; + } + + ret = read(nwrap->fd, buf, nwrap->st.st_size); + if (ret != nwrap->st.st_size) { + NWRAP_ERROR(("%s: read(%u) gave %d\n", + __location__, (unsigned)nwrap->st.st_size, ret)); + goto failed; + } + + buf[nwrap->st.st_size] = '\0'; + + nline = (char *)buf; + while (nline && nline[0]) { + char *line; + char *e; + bool ok; + + line = nline; + nline = NULL; + + e = strchr(line, '\n'); + if (e) { + e[0] = '\0'; + e++; + if (e[0] == '\r') { + e[0] = '\0'; + e++; + } + nline = e; + } + + NWRAP_VERBOSE(("%s:'%s'\n",__location__, line)); + + if (strlen(line) == 0) { + continue; + } + + ok = nwrap->parse_line(nwrap, line); + if (!ok) { + goto failed; + } + } + +done: + nwrap->buf = buf; + return true; + +failed: + if (buf) free(buf); + return false; +} + +static void nwrap_cache_unload(struct nwrap_cache *nwrap) +{ + nwrap->unload(nwrap); + + if (nwrap->buf) free(nwrap->buf); + + nwrap->buf = NULL; +} + +static void nwrap_cache_reload(struct nwrap_cache *nwrap) +{ + struct stat st; + int ret; + bool ok; + bool retried = false; + +reopen: + if (nwrap->fd < 0) { + nwrap->fd = open(nwrap->path, O_RDONLY); + if (nwrap->fd < 0) { + NWRAP_ERROR(("%s: unable to open '%s' readonly %d:%s\n", + __location__, + nwrap->path, nwrap->fd, + strerror(errno))); + return; + } + NWRAP_VERBOSE(("%s: open '%s'\n", __location__, nwrap->path)); + } + + ret = fstat(nwrap->fd, &st); + if (ret != 0) { + NWRAP_ERROR(("%s: fstat(%s) - %d:%s\n", + __location__, + nwrap->path, + ret, strerror(errno))); + return; + } + + if (retried == false && st.st_nlink == 0) { + /* maybe someone has replaced the file... */ + NWRAP_DEBUG(("%s: st_nlink == 0, reopen %s\n", + __location__, nwrap->path)); + retried = true; + memset(&nwrap->st, 0, sizeof(nwrap->st)); + close(nwrap->fd); + nwrap->fd = -1; + goto reopen; + } + + if (st.st_mtime == nwrap->st.st_mtime) { + NWRAP_VERBOSE(("%s: st_mtime[%u] hasn't changed, skip reload\n", + __location__, (unsigned)st.st_mtime)); + return; + } + NWRAP_DEBUG(("%s: st_mtime has changed [%u] => [%u], start reload\n", + __location__, (unsigned)st.st_mtime, + (unsigned)nwrap->st.st_mtime)); + + nwrap->st = st; + + nwrap_cache_unload(nwrap); + + ok = nwrap_parse_file(nwrap); + if (!ok) { + NWRAP_ERROR(("%s: failed to reload %s\n", + __location__, nwrap->path)); + nwrap_cache_unload(nwrap); + } + NWRAP_DEBUG(("%s: reloaded %s\n", + __location__, nwrap->path)); +} + +/* + * the caller has to call nwrap_unload() on failure + */ +static bool nwrap_pw_parse_line(struct nwrap_cache *nwrap, char *line) +{ + struct nwrap_pw *nwrap_pw; + char *c; + char *p; + char *e; + struct passwd *pw; + size_t list_size; + + nwrap_pw = (struct nwrap_pw *)nwrap->private_data; + + list_size = sizeof(*nwrap_pw->list) * (nwrap_pw->num+1); + pw = (struct passwd *)realloc(nwrap_pw->list, list_size); + if (!pw) { + NWRAP_ERROR(("%s:realloc(%u) failed\n", + __location__, list_size)); + return false; + } + nwrap_pw->list = pw; + + pw = &nwrap_pw->list[nwrap_pw->num]; + + c = line; + + /* name */ + p = strchr(c, ':'); + if (!p) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n", + __location__, line, c)); + return false; + } + *p = '\0'; + p++; + pw->pw_name = c; + c = p; + + NWRAP_VERBOSE(("name[%s]\n", pw->pw_name)); + + /* password */ + p = strchr(c, ':'); + if (!p) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n", + __location__, line, c)); + return false; + } + *p = '\0'; + p++; + pw->pw_passwd = c; + c = p; + + NWRAP_VERBOSE(("password[%s]\n", pw->pw_passwd)); + + /* uid */ + p = strchr(c, ':'); + if (!p) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n", + __location__, line, c)); + return false; + } + *p = '\0'; + p++; + e = NULL; + pw->pw_uid = (uid_t)strtoul(c, &e, 10); + if (c == e) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n", + __location__, line, c, strerror(errno))); + return false; + } + if (e == NULL) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n", + __location__, line, c, strerror(errno))); + return false; + } + if (e[0] != '\0') { + NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n", + __location__, line, c, strerror(errno))); + return false; + } + c = p; + + NWRAP_VERBOSE(("uid[%u]\n", pw->pw_uid)); + + /* gid */ + p = strchr(c, ':'); + if (!p) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n", + __location__, line, c)); + return false; + } + *p = '\0'; + p++; + e = NULL; + pw->pw_gid = (gid_t)strtoul(c, &e, 10); + if (c == e) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n", + __location__, line, c, strerror(errno))); + return false; + } + if (e == NULL) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n", + __location__, line, c, strerror(errno))); + return false; + } + if (e[0] != '\0') { + NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n", + __location__, line, c, strerror(errno))); + return false; + } + c = p; + + NWRAP_VERBOSE(("gid[%u]\n", pw->pw_gid)); + + /* gecos */ + p = strchr(c, ':'); + if (!p) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n", + __location__, line, c)); + return false; + } + *p = '\0'; + p++; + pw->pw_gecos = c; + c = p; + + NWRAP_VERBOSE(("gecos[%s]\n", pw->pw_gecos)); + + /* dir */ + p = strchr(c, ':'); + if (!p) { + NWRAP_ERROR(("%s:'%s'\n",__location__,c)); + return false; + } + *p = '\0'; + p++; + pw->pw_dir = c; + c = p; + + NWRAP_VERBOSE(("dir[%s]\n", pw->pw_dir)); + + /* shell */ + pw->pw_shell = c; + NWRAP_VERBOSE(("shell[%s]\n", pw->pw_shell)); + + NWRAP_DEBUG(("add user[%s:%s:%u:%u:%s:%s:%s]\n", + pw->pw_name, pw->pw_passwd, + pw->pw_uid, pw->pw_gid, + pw->pw_gecos, pw->pw_dir, pw->pw_shell)); + + nwrap_pw->num++; + return true; +} + +static void nwrap_pw_unload(struct nwrap_cache *nwrap) +{ + struct nwrap_pw *nwrap_pw; + nwrap_pw = (struct nwrap_pw *)nwrap->private_data; + + if (nwrap_pw->list) free(nwrap_pw->list); + + nwrap_pw->list = NULL; + nwrap_pw->num = 0; + nwrap_pw->idx = 0; +} + +static int nwrap_pw_copy_r(const struct passwd *src, struct passwd *dst, + char *buf, size_t buflen, struct passwd **dstp) +{ + char *first; + char *last; + off_t ofs; + + first = src->pw_name; + + last = src->pw_shell; + while (*last) last++; + + ofs = PTR_DIFF(last + 1, first); + + if (ofs > buflen) { + return ERANGE; + } + + memcpy(buf, first, ofs); + + ofs = PTR_DIFF(src->pw_name, first); + dst->pw_name = buf + ofs; + ofs = PTR_DIFF(src->pw_passwd, first); + dst->pw_passwd = buf + ofs; + dst->pw_uid = src->pw_uid; + dst->pw_gid = src->pw_gid; + ofs = PTR_DIFF(src->pw_gecos, first); + dst->pw_gecos = buf + ofs; + ofs = PTR_DIFF(src->pw_dir, first); + dst->pw_dir = buf + ofs; + ofs = PTR_DIFF(src->pw_shell, first); + dst->pw_shell = buf + ofs; + + if (dstp) { + *dstp = dst; + } + + return 0; +} + +/* + * the caller has to call nwrap_unload() on failure + */ +static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line) +{ + struct nwrap_gr *nwrap_gr; + char *c; + char *p; + char *e; + struct group *gr; + size_t list_size; + unsigned nummem; + + nwrap_gr = (struct nwrap_gr *)nwrap->private_data; + + list_size = sizeof(*nwrap_gr->list) * (nwrap_gr->num+1); + gr = (struct group *)realloc(nwrap_gr->list, list_size); + if (!gr) { + NWRAP_ERROR(("%s:realloc failed\n",__location__)); + return false; + } + nwrap_gr->list = gr; + + gr = &nwrap_gr->list[nwrap_gr->num]; + + c = line; + + /* name */ + p = strchr(c, ':'); + if (!p) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n", + __location__, line, c)); + return false; + } + *p = '\0'; + p++; + gr->gr_name = c; + c = p; + + NWRAP_VERBOSE(("name[%s]\n", gr->gr_name)); + + /* password */ + p = strchr(c, ':'); + if (!p) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n", + __location__, line, c)); + return false; + } + *p = '\0'; + p++; + gr->gr_passwd = c; + c = p; + + NWRAP_VERBOSE(("password[%s]\n", gr->gr_passwd)); + + /* gid */ + p = strchr(c, ':'); + if (!p) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n", + __location__, line, c)); + return false; + } + *p = '\0'; + p++; + e = NULL; + gr->gr_gid = (gid_t)strtoul(c, &e, 10); + if (c == e) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n", + __location__, line, c, strerror(errno))); + return false; + } + if (e == NULL) { + NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n", + __location__, line, c, strerror(errno))); + return false; + } + if (e[0] != '\0') { + NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n", + __location__, line, c, strerror(errno))); + return false; + } + c = p; + + NWRAP_VERBOSE(("gid[%u]\n", gr->gr_gid)); + + /* members */ + gr->gr_mem = (char **)malloc(sizeof(char *)); + if (!gr->gr_mem) { + NWRAP_ERROR(("%s:calloc failed\n",__location__)); + return false; + } + gr->gr_mem[0] = NULL; + + for(nummem=0; p; nummem++) { + char **m; + size_t m_size; + c = p; + p = strchr(c, ','); + if (p) { + *p = '\0'; + p++; + } + + if (strlen(c) == 0) { + break; + } + + m_size = sizeof(char *) * (nummem+2); + m = (char **)realloc(gr->gr_mem, m_size); + if (!m) { + NWRAP_ERROR(("%s:realloc(%u) failed\n", + __location__, m_size)); + return false; + } + gr->gr_mem = m; + gr->gr_mem[nummem] = c; + gr->gr_mem[nummem+1] = NULL; + + NWRAP_VERBOSE(("member[%u]: '%s'\n", nummem, gr->gr_mem[nummem])); + } + + NWRAP_DEBUG(("add group[%s:%s:%u:] with %u members\n", + gr->gr_name, gr->gr_passwd, gr->gr_gid, nummem)); + + nwrap_gr->num++; + return true; +} + +static void nwrap_gr_unload(struct nwrap_cache *nwrap) +{ + int i; + struct nwrap_gr *nwrap_gr; + nwrap_gr = (struct nwrap_gr *)nwrap->private_data; + + if (nwrap_gr->list) { + for (i=0; i < nwrap_gr->num; i++) { + if (nwrap_gr->list[i].gr_mem) { + free(nwrap_gr->list[i].gr_mem); + } + } + free(nwrap_gr->list); + } + + nwrap_gr->list = NULL; + nwrap_gr->num = 0; + nwrap_gr->idx = 0; +} + +static int nwrap_gr_copy_r(const struct group *src, struct group *dst, + char *buf, size_t buflen, struct group **dstp) +{ + char *first; + char **lastm; + char *last; + off_t ofsb; + off_t ofsm; + off_t ofs; + unsigned i; + + first = src->gr_name; + + lastm = src->gr_mem; + while (*lastm) lastm++; + + last = *lastm; + while (*last) last++; + + ofsb = PTR_DIFF(last + 1, first); + ofsm = PTR_DIFF(lastm + 1, src->gr_mem); + + if ((ofsb + ofsm) > buflen) { + return ERANGE; + } + + memcpy(buf, first, ofsb); + memcpy(buf + ofsb, src->gr_mem, ofsm); + + ofs = PTR_DIFF(src->gr_name, first); + dst->gr_name = buf + ofs; + ofs = PTR_DIFF(src->gr_passwd, first); + dst->gr_passwd = buf + ofs; + dst->gr_gid = src->gr_gid; + + dst->gr_mem = (char **)(buf + ofsb); + for (i=0; src->gr_mem[i]; i++) { + ofs = PTR_DIFF(src->gr_mem[i], first); + dst->gr_mem[i] = buf + ofs; + } + + if (dstp) { + *dstp = dst; + } + + return 0; +} + +/* user functions */ +_PUBLIC_ struct passwd *nwrap_getpwnam(const char *name) +{ + int i; + + if (!nwrap_enabled()) { + return real_getpwnam(name); + } + + nwrap_cache_reload(nwrap_pw_global.cache); + + for (i=0; i<nwrap_pw_global.num; i++) { + if (strcmp(nwrap_pw_global.list[i].pw_name, name) == 0) { + NWRAP_DEBUG(("%s: user[%s] found\n", + __location__, name)); + return &nwrap_pw_global.list[i]; + } + NWRAP_VERBOSE(("%s: user[%s] does not match [%s]\n", + __location__, name, + nwrap_pw_global.list[i].pw_name)); + } + + NWRAP_DEBUG(("%s: user[%s] not found\n", __location__, name)); + + errno = ENOENT; + return NULL; +} + +_PUBLIC_ int nwrap_getpwnam_r(const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +{ + struct passwd *pw; + + if (!nwrap_enabled()) { + return real_getpwnam_r(name, pwdst, buf, buflen, pwdstp); + } + + pw = nwrap_getpwnam(name); + if (!pw) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + + return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp); +} + +_PUBLIC_ struct passwd *nwrap_getpwuid(uid_t uid) +{ + int i; + + if (!nwrap_enabled()) { + return real_getpwuid(uid); + } + + nwrap_cache_reload(nwrap_pw_global.cache); + + for (i=0; i<nwrap_pw_global.num; i++) { + if (nwrap_pw_global.list[i].pw_uid == uid) { + NWRAP_DEBUG(("%s: uid[%u] found\n", + __location__, uid)); + return &nwrap_pw_global.list[i]; + } + NWRAP_VERBOSE(("%s: uid[%u] does not match [%u]\n", + __location__, uid, + nwrap_pw_global.list[i].pw_uid)); + } + + NWRAP_DEBUG(("%s: uid[%u] not found\n", __location__, uid)); + + errno = ENOENT; + return NULL; +} + +_PUBLIC_ int nwrap_getpwuid_r(uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +{ + struct passwd *pw; + + if (!nwrap_enabled()) { + return real_getpwuid_r(uid, pwdst, buf, buflen, pwdstp); + } + + pw = nwrap_getpwuid(uid); + if (!pw) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + + return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp); +} + +/* user enum functions */ +_PUBLIC_ void nwrap_setpwent(void) +{ + if (!nwrap_enabled()) { + real_setpwent(); + } + + nwrap_pw_global.idx = 0; +} + +_PUBLIC_ struct passwd *nwrap_getpwent(void) +{ + struct passwd *pw; + + if (!nwrap_enabled()) { + return real_getpwent(); + } + + if (nwrap_pw_global.idx == 0) { + nwrap_cache_reload(nwrap_pw_global.cache); + } + + if (nwrap_pw_global.idx >= nwrap_pw_global.num) { + errno = ENOENT; + return NULL; + } + + pw = &nwrap_pw_global.list[nwrap_pw_global.idx++]; + + NWRAP_VERBOSE(("%s: return user[%s] uid[%u]\n", + __location__, pw->pw_name, pw->pw_uid)); + + return pw; +} + +_PUBLIC_ int nwrap_getpwent_r(struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp) +{ + struct passwd *pw; + + if (!nwrap_enabled()) { +#ifdef SOLARIS_GETPWENT_R + pw = real_getpwent_r(pwdst, buf, buflen); + if (!pw) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + if (pwdstp) { + *pwdstp = pw; + } + return 0; +#else + return real_getpwent_r(pwdst, buf, buflen, pwdstp); +#endif + } + + pw = nwrap_getpwent(); + if (!pw) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + + return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp); +} + +_PUBLIC_ void nwrap_endpwent(void) +{ + if (!nwrap_enabled()) { + real_endpwent(); + } + + nwrap_pw_global.idx = 0; +} + +/* misc functions */ +_PUBLIC_ int nwrap_initgroups(const char *user, gid_t group) +{ + if (!nwrap_enabled()) { + return real_initgroups(user, group); + } + + /* TODO: maybe we should also fake this... */ + return EPERM; +} + +/* group functions */ +_PUBLIC_ struct group *nwrap_getgrnam(const char *name) +{ + int i; + + if (!nwrap_enabled()) { + return real_getgrnam(name); + } + + nwrap_cache_reload(nwrap_gr_global.cache); + + for (i=0; i<nwrap_gr_global.num; i++) { + if (strcmp(nwrap_gr_global.list[i].gr_name, name) == 0) { + NWRAP_DEBUG(("%s: group[%s] found\n", + __location__, name)); + return &nwrap_gr_global.list[i]; + } + NWRAP_VERBOSE(("%s: group[%s] does not match [%s]\n", + __location__, name, + nwrap_gr_global.list[i].gr_name)); + } + + NWRAP_DEBUG(("%s: group[%s] not found\n", __location__, name)); + + errno = ENOENT; + return NULL; +} + +_PUBLIC_ int nwrap_getgrnam_r(const char *name, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +{ + struct group *gr; + + if (!nwrap_enabled()) { + return real_getgrnam_r(name, grdst, buf, buflen, grdstp); + } + + gr = nwrap_getgrnam(name); + if (!gr) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + + return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp); +} + +_PUBLIC_ struct group *nwrap_getgrgid(gid_t gid) +{ + int i; + + if (!nwrap_enabled()) { + return real_getgrgid(gid); + } + + nwrap_cache_reload(nwrap_gr_global.cache); + + for (i=0; i<nwrap_gr_global.num; i++) { + if (nwrap_gr_global.list[i].gr_gid == gid) { + NWRAP_DEBUG(("%s: gid[%u] found\n", + __location__, gid)); + return &nwrap_gr_global.list[i]; + } + NWRAP_VERBOSE(("%s: gid[%u] does not match [%u]\n", + __location__, gid, + nwrap_gr_global.list[i].gr_gid)); + } + + NWRAP_DEBUG(("%s: gid[%u] not found\n", __location__, gid)); + + errno = ENOENT; + return NULL; +} + +_PUBLIC_ int nwrap_getgrgid_r(gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +{ + struct group *gr; + + if (!nwrap_enabled()) { + return real_getgrgid_r(gid, grdst, buf, buflen, grdstp); + } + + gr = nwrap_getgrgid(gid); + if (!gr) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + + return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp); + + return ENOENT; +} + +/* group enum functions */ +_PUBLIC_ void nwrap_setgrent(void) +{ + if (!nwrap_enabled()) { + real_setgrent(); + } + + nwrap_gr_global.idx = 0; +} + +_PUBLIC_ struct group *nwrap_getgrent(void) +{ + struct group *gr; + + if (!nwrap_enabled()) { + return real_getgrent(); + } + + if (nwrap_gr_global.idx == 0) { + nwrap_cache_reload(nwrap_gr_global.cache); + } + + if (nwrap_gr_global.idx >= nwrap_gr_global.num) { + errno = ENOENT; + return NULL; + } + + gr = &nwrap_gr_global.list[nwrap_gr_global.idx++]; + + NWRAP_VERBOSE(("%s: return group[%s] gid[%u]\n", + __location__, gr->gr_name, gr->gr_gid)); + + return gr; +} + +_PUBLIC_ int nwrap_getgrent_r(struct group *grdst, char *buf, + size_t buflen, struct group **grdstp) +{ + struct group *gr; + + if (!nwrap_enabled()) { +#ifdef SOLARIS_GETGRENT_R + gr = real_getgrent_r(grdst, buf, buflen); + if (!gr) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + if (grdstp) { + *grdstp = gr; + } + return 0; +#else + return real_getgrent_r(grdst, buf, buflen, grdstp); +#endif + } + + gr = nwrap_getgrent(); + if (!gr) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + + return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp); +} + +_PUBLIC_ void nwrap_endgrent(void) +{ + if (!nwrap_enabled()) { + real_endgrent(); + } + + nwrap_gr_global.idx = 0; +} diff --git a/lib/nss_wrapper/nss_wrapper.h b/lib/nss_wrapper/nss_wrapper.h new file mode 100644 index 0000000000..35a47348a8 --- /dev/null +++ b/lib/nss_wrapper/nss_wrapper.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) Stefan Metzmacher 2007 <metze@samba.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __NSS_WRAPPER_H__ +#define __NSS_WRAPPER_H__ + +struct passwd *nwrap_getpwnam(const char *name); +int nwrap_getpwnam_r(const char *name, struct passwd *pwbuf, + char *buf, size_t buflen, struct passwd **pwbufp); +struct passwd *nwrap_getpwuid(uid_t uid); +int nwrap_getpwuid_r(uid_t uid, struct passwd *pwbuf, + char *buf, size_t buflen, struct passwd **pwbufp); +void nwrap_setpwent(void); +struct passwd *nwrap_getpwent(void); +int nwrap_getpwent_r(struct passwd *pwbuf, char *buf, + size_t buflen, struct passwd **pwbufp); +void nwrap_endpwent(void); +int nwrap_initgroups(const char *user, gid_t group); +struct group *nwrap_getgrnam(const char *name); +int nwrap_getgrnam_r(const char *name, struct group *gbuf, + char *buf, size_t buflen, struct group **gbufp); +struct group *nwrap_getgrgid(gid_t gid); +int nwrap_getgrgid_r(gid_t gid, struct group *gbuf, + char *buf, size_t buflen, struct group **gbufp); +void nwrap_setgrent(void); +struct group *nwrap_getgrent(void); +int nwrap_getgrent_r(struct group *gbuf, char *buf, + size_t buflen, struct group **gbufp); +void nwrap_endgrent(void); + +#ifdef NSS_WRAPPER_REPLACE + +#ifdef getpwnam +#undef getpwnam +#endif +#define getpwnam nwrap_getpwnam + +#ifdef getpwnam_r +#undef getpwnam_r +#endif +#define getpwnam_r nwrap_getpwnam_r + +#ifdef getpwuid +#undef getpwuid +#endif +#define getpwuid nwrap_getpwuid + +#ifdef getpwuid_r +#undef getpwuid_r +#endif +#define getpwuid_r nwrap_getpwuid_r + +#ifdef setpwent +#undef setpwent +#endif +#define setpwent nwrap_setpwent + +#ifdef getpwent +#undef getpwent +#endif +#define getpwent nwrap_getpwent + +#ifdef getpwent_r +#undef getpwent_r +#endif +#define getpwent_r nwrap_getpwent_r + +#ifdef endpwent +#undef endpwent +#endif +#define endpwent nwrap_endpwent + +#ifdef getgrlst +#undef getgrlst +#endif +#define getgrlst __none_nwrap_getgrlst + +#ifdef getgrlst_r +#undef getgrlst_r +#endif +#define getgrlst_r __none_nwrap_getgrlst_r + +#ifdef initgroups_dyn +#undef initgroups_dyn +#endif +#define initgroups_dyn __none_nwrap_initgroups_dyn + +#ifdef initgroups +#undef initgroups +#endif +#define initgroups nwrap_initgroups + +#ifdef getgrnam +#undef getgrnam +#endif +#define getgrnam nwrap_getgrnam + +#ifdef getgrnam_r +#undef getgrnam_r +#endif +#define getgrnam_r nwrap_getgrnam_r + +#ifdef getgrgid +#undef getgrgid +#endif +#define getgrgid nwrap_getgrgid + +#ifdef getgrgid_r +#undef getgrgid_r +#endif +#define getgrgid_r nwrap_getgrgid_r + +#ifdef setgrent +#undef setgrent +#endif +#define setgrent nwrap_setgrent + +#ifdef getgrent +#undef getgrent +#endif +#define getgrent nwrap_getgrent + +#ifdef getgrent_r +#undef getgrent_r +#endif +#define getgrent_r nwrap_getgrent_r + +#ifdef endgrent +#undef endgrent +#endif +#define endgrent nwrap_endgrent + +#endif /* NSS_WRAPPER_REPLACE */ + +#endif /* __NSS_WRAPPER_H__ */ diff --git a/lib/nss_wrapper/nss_wrapper.pl b/lib/nss_wrapper/nss_wrapper.pl new file mode 100644 index 0000000000..b1c9be5365 --- /dev/null +++ b/lib/nss_wrapper/nss_wrapper.pl @@ -0,0 +1,265 @@ +#!/usr/bin/perl +# + +use strict; + +use Getopt::Long; +use Cwd qw(abs_path); + +my $opt_help = 0; +my $opt_path = undef; +my $opt_action = undef; +my $opt_type = undef; +my $opt_name = undef; + +my $passwdfn = undef; +my $groupfn = undef; +my $actionfn = undef; + +sub passwd_add($$); +sub passwd_delete($$); +sub group_add($$); +sub group_delete($$); + +my $result = GetOptions( + 'help|h|?' => \$opt_help, + 'path=s' => \$opt_path, + 'action=s' => \$opt_action, + 'type=s' => \$opt_type, + 'name=s' => \$opt_name +); + +sub usage($;$) +{ + my ($ret, $msg) = @_; + + print $msg."\n\n" if defined($msg); + + print "usage: + + --help|-h|-? Show this help. + + --path <path> Path of the 'passwd' or 'group' file. + + --type <type> Only 'passwd' is supported yet, + but 'group' and maybe 'member' will be added + in future. + + --action <action> 'add' or 'delete'. + + --name <name> The name of the object. +"; + exit($ret); +} + +usage(1) if (not $result); + +usage(0) if ($opt_help); + +if (not defined($opt_path)) { + usage(1, "missing: --path <path>"); +} +if ($opt_path eq "" or $opt_path eq "/") { + usage(1, "invalid: --path <path>: '$opt_path'"); +} +my $opt_fullpath = abs_path($opt_path); +if (not defined($opt_fullpath)) { + usage(1, "invalid: --path <path>: '$opt_path'"); +} + + +if (not defined($opt_action)) { + usage(1, "missing: --action [add|delete]"); +} +if ($opt_action eq "add") { + $passwdfn = \&passwd_add; + $groupfn = \&group_add; +} elsif ($opt_action eq "delete") { + $passwdfn = \&passwd_delete; + $groupfn = \&group_delete; +} else { + usage(1, "invalid: --action [add|delete]: '$opt_action'"); +} + +if (not defined($opt_type)) { + usage(1, "missing: --type [passwd|group]"); +} +if ($opt_type eq "passwd") { + $actionfn = $passwdfn; +} elsif ($opt_type eq "group") { + $actionfn = $groupfn; +} else { + usage(1, "invalid: --type [passwd|group]: '$opt_type'") +} + +if (not defined($opt_name)) { + usage(1, "missing: --name <name>"); +} +if ($opt_name eq "") { + usage(1, "invalid: --name <name>"); +} + +exit $actionfn->($opt_fullpath, $opt_name); + +sub passwd_add_entry($$); + +sub passwd_load($) +{ + my ($path) = @_; + my @lines; + my $passwd = undef; + + open(PWD, "<$path") or die("Unable to open '$path' for read"); + @lines = <PWD>; + close(PWD); + + $passwd->{array} = (); + $passwd->{name} = {}; + $passwd->{uid} = {}; + $passwd->{path} = $path; + + foreach my $line (@lines) { + passwd_add_entry($passwd, $line); + } + + return $passwd; +} + +sub passwd_lookup_name($$) +{ + my ($passwd, $name) = @_; + + return undef unless defined($passwd->{name}{$name}); + + return $passwd->{name}{$name}; +} + +sub passwd_lookup_uid($$) +{ + my ($passwd, $uid) = @_; + + return undef unless defined($passwd->{uid}{$uid}); + + return $passwd->{uid}{$uid}; +} + +sub passwd_get_free_uid($) +{ + my ($passwd) = @_; + my $uid = 1000; + + while (passwd_lookup_uid($passwd, $uid)) { + $uid++; + } + + return $uid; +} + +sub passwd_add_entry($$) +{ + my ($passwd, $str) = @_; + + chomp $str; + my @e = split(':', $str); + + push(@{$passwd->{array}}, \@e); + $passwd->{name}{$e[0]} = \@e; + $passwd->{uid}{$e[2]} = \@e; +} + +sub passwd_remove_entry($$) +{ + my ($passwd, $eref) = @_; + + for(my $i; defined($passwd->{array}[$i]); $i++) { + if ($eref == $passwd->{array}[$i]) { + $passwd->{array}[$i] = undef; + } + } + + delete $passwd->{name}{${$eref}[0]}; + delete $passwd->{uid}{${$eref}[2]}; +} + +sub passwd_save($) +{ + my ($passwd) = @_; + my @lines = (); + my $path = $passwd->{path}; + my $tmppath = $path.$$; + + foreach my $eref (@{$passwd->{array}}) { + next unless defined($eref); + + my $line = join(':', @{$eref}); + push(@lines, $line); + } + + open(PWD, ">$tmppath") or die("Unable to open '$tmppath' for write"); + print PWD join("\n", @lines)."\n"; + close(PWD); + rename($tmppath, $path) or die("Unable to rename $tmppath => $path"); +} + +sub passwd_add($$) +{ + my ($path, $name) = @_; + + #print "passwd_add: '$name' in '$path'\n"; + + my $passwd = passwd_load($path); + + my $e = passwd_lookup_name($passwd, $name); + die("account[$name] already exists in '$path'") if defined($e); + + my $uid = passwd_get_free_uid($passwd); + my $gid = 65534;# nogroup gid + + my $pwent = $name.":x:".$uid.":".$gid.":".$name." gecos:/nodir:/bin/false"; + + passwd_add_entry($passwd, $pwent); + + passwd_save($passwd); + + return 0; +} + +sub passwd_delete($$) +{ + my ($path, $name) = @_; + + #print "passwd_delete: '$name' in '$path'\n"; + + my $passwd = passwd_load($path); + + my $e = passwd_lookup_name($passwd, $name); + die("account[$name] does not exists in '$path'") unless defined($e); + + passwd_remove_entry($passwd, $e); + + passwd_save($passwd); + + return 0; +} + +sub group_add($$) +{ + my ($path, $name) = @_; + + #print "group_add: '$name' in '$path'\n"; + + die("group_add: not implemented yet!"); + + return 0; +} + +sub group_delete($$) +{ + my ($path, $name) = @_; + + #print "group_delete: '$name' in '$path'\n"; + + die("group_delete: not implemented yet!"); + + return 0; +} |