libnss-extrausers-0.6/0000755000175100017510000000000011737064660013210 5ustar brlbrllibnss-extrausers-0.6/passwd.c0000644000175100017510000001342211737064660014657 0ustar brlbrl/* Copyright (C) 2001,2002,2009,2010,2012 Bernhard R. Link This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Please tell me, if you find errors or mistakes. Based on parts of the GNU C Library: Common code for file-based database parsers in nss_files module. Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include "s_config.h" enum nss_status _nss_extrausers_getpwuid_r(uid_t, struct passwd *, char *, size_t, int *); enum nss_status _nss_extrausers_setpwent(void); enum nss_status _nss_extrausers_endpwent(void); enum nss_status _nss_extrausers_getpwnam_r(const char *, struct passwd *, char *, size_t, int *); enum nss_status _nss_extrausers_getpwent_r(struct passwd *, char *, size_t, int *); static enum nss_status p_search(FILE *f, const char *name, const uid_t uid, struct passwd *pw, int *errnop, char *buffer, size_t buflen); static inline enum nss_status p_search(FILE *f, const char *name, const uid_t uid, struct passwd *pw, int *errnop, char *buffer, size_t buflen) { #define SANEQUIT {funlockfile(stream); if (f==NULL) fclose(stream);} #define TOCOLON(p, h) { while (*p && *p != ':') \ p++; \ h=p; \ if(!*p) { \ SANEQUIT \ *errnop = 0; \ return NSS_STATUS_UNAVAIL; \ } \ p++; \ *h='\0'; \ h--; \ } FILE *stream = f; char *p, *h; uid_t t_uid; gid_t t_gid; char *t_name, *t_passwd, *t_gecos, *t_shell, *t_dir; if (stream == NULL) { stream = fopen(USERSFILE, "r"); if (stream == NULL) { *errnop = errno; return NSS_STATUS_UNAVAIL; } } flockfile(stream); while (1) { buffer[buflen - 1] = '\xff'; p = fgets_unlocked(buffer, buflen, stream); if (p == NULL) { if (feof_unlocked(stream)) { SANEQUIT *errnop = ENOENT; return NSS_STATUS_NOTFOUND; } else { *errnop = errno; SANEQUIT return NSS_STATUS_UNAVAIL; } } h = index(p, '\n'); if (buffer[buflen - 1] != '\xff' || h == NULL) { SANEQUIT *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } while (isspace(*h) && h != p) { *h = '\0'; h--; } /* Ignore comments */ if (*p == '#') continue; /* extract name */ while (isspace(*p)) ++p; /* Ignore empty lines */ if (*p == '\0') continue; t_name = p; TOCOLON(p, h); if (name && strcmp(name, t_name)!=0) continue; /* passwd (should be "x" or "!!" or something...) */ while (isspace(*p)) ++p; t_passwd = p; TOCOLON(p, h); /* extract uid */ t_uid = strtol(p, &h, 10); if (*h != ':') { SANEQUIT *errnop = 0; return NSS_STATUS_UNAVAIL; } if (t_uid < MINUID) { continue; } if (uid != 0 && uid != t_uid) { continue; } p = ++h; /* extract gid */ t_gid = strtol(p, &h, 10); if (*h != ':') { SANEQUIT *errnop = 0; return NSS_STATUS_UNAVAIL; } # ifdef USERSGID if (t_gid < MINGID && t_gid != USERSGID) { # else if (t_gid < MINGID) { # endif continue; } p = ++h; /* extract gecos */ while (isspace(*p)) ++p; t_gecos = p; TOCOLON(p, h); /* extract dir */ while (isspace(*p)) ++p; t_dir = p; TOCOLON(p, h); /* extract shell */ while (isspace(*p)) ++p; t_shell = p; if (index(p, ':') != NULL) { SANEQUIT *errnop = 0; return NSS_STATUS_UNAVAIL; } SANEQUIT *errnop = 0; pw->pw_name = t_name; pw->pw_uid = t_uid; pw->pw_passwd = t_passwd; pw->pw_gid = t_gid; pw->pw_gecos = t_gecos; pw->pw_dir = t_dir; pw->pw_shell = t_shell; return NSS_STATUS_SUCCESS; } } enum nss_status _nss_extrausers_getpwuid_r(uid_t uid, struct passwd *result, char *buf, size_t buflen, int *errnop) { *errnop = 0; if (result) return p_search(NULL, NULL, uid, result, errnop, buf, buflen); else return NSS_STATUS_UNAVAIL; } enum nss_status _nss_extrausers_getpwnam_r(const char *name, struct passwd *result, char *buf, size_t buflen, int *errnop) { *errnop = 0; if (result) return p_search(NULL, name, 0, result, errnop, buf, buflen); else return NSS_STATUS_UNAVAIL; } static FILE *usersfile = NULL; enum nss_status _nss_extrausers_setpwent(void) { if (usersfile != NULL) { fclose(usersfile); usersfile = NULL; } usersfile = fopen(USERSFILE, "r"); if (usersfile == NULL) { return NSS_STATUS_UNAVAIL; } return NSS_STATUS_SUCCESS; } enum nss_status _nss_extrausers_endpwent(void) { if (usersfile != NULL) { fclose(usersfile); usersfile = NULL; } return NSS_STATUS_SUCCESS; } enum nss_status _nss_extrausers_getpwent_r(struct passwd *pw, char *buffer, size_t buflen, int *errnop) { *errnop = -1; if (pw == NULL) return NSS_STATUS_UNAVAIL; if (usersfile == NULL) return NSS_STATUS_UNAVAIL; return p_search(usersfile, NULL, 0, pw, errnop, buffer, buflen); } libnss-extrausers-0.6/group.c0000644000175100017510000001564711737064660014525 0ustar brlbrl/* Copyright (C) 2001,2002,2009,2012 Bernhard R. Link This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Please tell me, if you find errors or mistakes. Based on parts of the GNU C Library: Common code for file-based database parsers in nss_files module. Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ #define _GNU_SOURCE 1 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include "s_config.h" enum nss_status _nss_extrausers_setgrent(void); enum nss_status _nss_extrausers_endgrent(void); enum nss_status _nss_extrausers_getgrent_r(struct group *gr, char *buffer, size_t buflen, int *errnop); enum nss_status _nss_extrausers_getgrnam_r(const char *name, struct group *gr, char *buffer, size_t buflen, int *errnop); enum nss_status _nss_extrausers_getgrgid_r(const gid_t gid, struct group *gr, char *buffer, size_t buflen, int *errnop); static FILE *groupsfile = NULL; /* from clib/nss */ static inline char **parse_list(char *line, char *data, size_t datalen, int *errnop) { char *eol, **list, **p; if (line >= data && line < (char *) data + datalen) /* Find the end of the line buffer, we will use the space in DATA after * it for storing the vector of pointers. */ eol = strchr(line, '\0') + 1; else /* LINE does not point within DATA->linebuffer, so that space is * not being used for scratch space right now. We can use all of * it for the pointer vector storage. */ eol = data; /* Adjust the pointer so it is aligned for storing pointers. */ eol += __alignof__(char *) - 1; eol -= (eol - (char *)0) % __alignof__(char *); /* We will start the storage here for the vector of pointers. */ list = (char **)eol; p = list; while (1) { char *elt; if ((size_t) ((char *)&p[1] - (char *)data) > datalen) { /* We cannot fit another pointer in the buffer. */ *errnop = ERANGE; return NULL; } if (*line == '\0') break; /* Skip leading white space. This might not be portable but useful. */ while (isspace(*line)) ++line; elt = line; while (1) { if (*line == '\0' || *line == ',' ) { /* End of the next entry. */ if (line > elt) /* We really found some data. */ *p++ = elt; /* Terminate string if necessary. */ if (*line != '\0') *line++ = '\0'; break; } ++line; } } *p = NULL; return list; } #define TOCOLON(p, h) { \ while (*p && *p != ':') \ p++; \ h=p; \ if (*p) \ p++; \ *h='\0'; h--; \ while (isspace(*h)) { \ *h='\0'; h--; \ } \ } static inline enum nss_status g_search(FILE *stream, const char *name, const gid_t gid, struct group *gr, int *errnop, char *buffer, size_t buflen) { char *p, *h; gid_t t_gid; char *t_name, *t_passwd; char **t_mem; off_t last_position; if (gid != 0 && gid < MINGID) { *errnop = ENOENT; return NSS_STATUS_NOTFOUND; } last_position = ftello(stream); flockfile(stream); while (1) { buffer[buflen - 1] = '\xff'; p = fgets_unlocked(buffer, buflen, stream); if (p == NULL) { if (feof_unlocked(stream)) { funlockfile(stream); *errnop = ENOENT; return NSS_STATUS_NOTFOUND; } else { funlockfile(stream); *errnop = errno; return NSS_STATUS_UNAVAIL; } } h = index(p, '\n'); if (buffer[buflen - 1] != '\xff' || h == NULL) { funlockfile(stream); *errnop = ERANGE; fseeko(stream, last_position, SEEK_SET); return NSS_STATUS_TRYAGAIN; } while (isspace(*h) && h != p) { *h = '\0'; h--; } /* Ignore comments */ if (*p == '#') continue; /* extract name */ while (isspace(*p)) ++p; /* Ignore empty lines */ if (*p == '\0') continue; t_name = p; TOCOLON(p, h); if (name && strcmp(name, t_name) != 0) continue; /* passwd (should be "x" or "" or something...) */ while (isspace(*p)) ++p; t_passwd = p; TOCOLON(p, h); /* extract gid */ t_gid = strtol(p, &h, 10); if (*h != ':') { funlockfile(stream); *errnop = 0; return NSS_STATUS_UNAVAIL; } if (gid != 0 && gid != t_gid) { continue; } if (t_gid < MINGID) { continue; } p = h; /* extract members */ h++; // Over ':' t_mem = parse_list(h, buffer, buflen, errnop); if (t_mem == NULL){ funlockfile(stream); fseeko(stream, last_position, SEEK_SET); return NSS_STATUS_TRYAGAIN; } funlockfile(stream); *errnop = 0; gr->gr_name = t_name; gr->gr_passwd = t_passwd; gr->gr_gid = t_gid; gr->gr_mem = t_mem; return NSS_STATUS_SUCCESS; } } enum nss_status _nss_extrausers_setgrent(void) { groupsfile = fopen(GROUPSFILE, "r"); if (groupsfile == NULL) return NSS_STATUS_UNAVAIL; return NSS_STATUS_SUCCESS; } enum nss_status _nss_extrausers_endgrent(void) { if (groupsfile != NULL) { fclose(groupsfile); groupsfile = NULL; } return NSS_STATUS_SUCCESS; } enum nss_status _nss_extrausers_getgrent_r(struct group *gr, char *buffer, size_t buflen, int *errnop) { *errnop = 0; if (groupsfile == NULL) return NSS_STATUS_UNAVAIL; return g_search(groupsfile, NULL, 0, gr, errnop, buffer, buflen); } enum nss_status _nss_extrausers_getgrnam_r(const char *name, struct group *gr, char *buffer, size_t buflen, int *errnop) { enum nss_status e; FILE *f; *errnop = 0; if (gr == NULL || name == NULL) return NSS_STATUS_UNAVAIL; f = fopen(GROUPSFILE, "r"); if (f == NULL) { *errnop = errno; return NSS_STATUS_UNAVAIL; } e = g_search(f, name, 0, gr, errnop, buffer, buflen); fclose(f); return e; } enum nss_status _nss_extrausers_getgrgid_r(const gid_t gid, struct group *gr, char *buffer, size_t buflen, int *errnop) { enum nss_status e; FILE *f; *errnop = 0; if (gr == NULL) return NSS_STATUS_UNAVAIL; if (gid == 0 || gid < MINGID) return NSS_STATUS_NOTFOUND; f = fopen(GROUPSFILE, "r"); if (f == NULL) { *errnop = errno; return NSS_STATUS_UNAVAIL; } e = g_search(f, NULL, gid, gr, errnop, buffer, buflen); fclose(f); return e; } libnss-extrausers-0.6/README0000644000175100017510000000276311737064660014100 0ustar brlbrllibnss_extrausers: With the following lines in /etc/nsswitch.conf passwd: compat extrausers group: compat extrausers shadow: compat extrausers and /lib/libnss_extrausers.so.2 from this package, glibc will not only look in /etc/{passwd,shadow,group} but also in /var/lib/extrausers/{passwd,shadow,group}. It will limit itself to uids and gids of at least 500 - where detectable - to avoid root or system account access. (Except users with gid 100 are also allowed, see below). Since version 0.2, lines starting with # are ignored. Since version 0.3 gid 100 is also allowed, because that is the "users" group on Debian systems. (So people can put users in that group instead of their own (though I strongly discourage not every user having their own group by default) without needing to have a special group with gid >= 500 for that). Since version 0.5 behaviour if a group with too low gid is the same as it has with users before: They are ignored but no longer considered an error causing the file no longer being processed. Since version 0.6 empty lines are ignored (before they were considered broken lines and thus ended the processing of the file). Security considerations: Always use after compat in nsswitch.conf, otherwise it could overwrite the shadow-password for root. (shadow has no uids, so this cannot be ruled out) If someone is able to place terminals instead of the files, that could cause all programs to get a new controling terminal, making DoS attacks possible. libnss-extrausers-0.6/Makefile0000644000175100017510000000243611737064660014655 0ustar brlbrl# Makefile for nss-extrausers CC = gcc prefix = /usr exec_prefix = ${prefix} BITSOFS= libprefix = ${exec_prefix}/lib$(BITSOFS) DESTDIR= OBJSUFFIX=$(BITSOFS).o OBJECTS=shadow$(OBJSUFFIX) passwd$(OBJSUFFIX) group$(OBJSUFFIX) SHARED_OBJECT = libnss_extrausers$(BITSOFS).so.2 INSTALL_NAME = libnss_extrausers.so.2 # This only works sometimes, give manually when needed: BIT_CFLAGS = $(if $(BITSOFS),-m$(BITSOFS)) CFLAGS = $(BIT_CFLAGS) -g -O2 -Wall -Wstrict-prototypes -Wpointer-arith -Wmissing-prototypes CPPFLAGS = LIBS = -lc LDLIBFLAGS = -shared -Wl,-soname,$(INSTALL_NAME) LDFLAGS = -Wl,-z,defs VERSION = unreleased all: $(SHARED_OBJECT) $(SHARED_OBJECT): $(OBJECTS) $(CC) $(CFLAGS) $(LDLIBFLAGS) $(LDFLAGS) -o $(SHARED_OBJECT) $(OBJECTS) $(LIBS) %$(OBJSUFFIX): %.c s_config.h $(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -c -o $@ $< install: install -m755 -d $(DESTDIR)$(libprefix)/ install -m644 $(SHARED_OBJECT) $(DESTDIR)$(libprefix)/$(INSTALL_NAME) clean: rm -f $(OBJECTS) rm -f $(SHARED_OBJECT) distclean: clean dist: Makefile README s_config.h $(patsubst %$(OBJSUFFIX),%.c,$(OBJECTS)) mkdir libnss-extrausers-$(VERSION) install -m 644 $^ libnss-extrausers-$(VERSION) tar -cvvzf libnss-extrausers_$(VERSION).orig.tar.gz libnss-extrausers-$(VERSION) rm -r libnss-extrausers-$(VERSION) .PHONY: all libnss-extrausers-0.6/s_config.h0000644000175100017510000000031111737064660015143 0ustar brlbrl#define USERSFILE "/var/lib/extrausers/passwd" #define SHADOWFILE "/var/lib/extrausers/shadow" #define GROUPSFILE "/var/lib/extrausers/group" #define MINUID 500 #define MINGID 500 #define USERSGID 100 libnss-extrausers-0.6/shadow.c0000644000175100017510000001273411737064660014650 0ustar brlbrl/* Copyright (C) 2001,2002,2009,2012 Bernhard R. Link This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Please tell me, if you find errors or mistakes. Based on parts of the GNU C Library: Common code for file-based database parsers in nss_files module. Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include "s_config.h" enum nss_status _nss_extrausers_getspent_r(struct spwd *, char *, size_t, int *); enum nss_status _nss_extrausers_getspnam_r(const char *, struct spwd *, char *, size_t, int *); enum nss_status _nss_extrausers_setspent(void); enum nss_status _nss_extrausers_endspent(void); static enum nss_status shadow_search(FILE *stream, const char *name, struct spwd *spw, char *buffer, size_t buflen, int *errnop); enum nss_status _nss_extrausers_getspnam_r(const char *name, struct spwd *spw, char *buffer, size_t buflen, int *errnop) { FILE *stream; enum nss_status s; if (spw == NULL || name == NULL) { *errnop = EPERM; return NSS_STATUS_UNAVAIL; } stream = fopen(SHADOWFILE, "r"); if( stream == NULL ) { *errnop = errno; return NSS_STATUS_UNAVAIL; } flockfile(stream); s = shadow_search(stream, name, spw, buffer, buflen, errnop); funlockfile(stream); fclose(stream); return s; } static FILE *shadowfile = NULL; enum nss_status _nss_extrausers_setspent(void) { if (shadowfile != NULL) { fclose(shadowfile); shadowfile = NULL; } shadowfile = fopen(SHADOWFILE, "r"); if (shadowfile == NULL) { return NSS_STATUS_UNAVAIL; } return NSS_STATUS_SUCCESS; } enum nss_status _nss_extrausers_endspent(void) { if (shadowfile != NULL) { fclose(shadowfile); shadowfile = NULL; } return NSS_STATUS_SUCCESS; } enum nss_status _nss_extrausers_getspent_r(struct spwd *spw, char *buffer, size_t buflen, int *errnop) { enum nss_status s; if (spw == NULL) { *errnop = EPERM; return NSS_STATUS_UNAVAIL; } if (shadowfile == NULL) { shadowfile = fopen(SHADOWFILE, "r"); if( shadowfile == NULL ) { *errnop = errno; return NSS_STATUS_UNAVAIL; } } flockfile(shadowfile); s = shadow_search(shadowfile, NULL, spw, buffer, buflen, errnop); funlockfile(shadowfile); return s; } static enum nss_status shadow_search(FILE *stream, const char *name, struct spwd *spw, char *buffer, size_t buflen, int *errnop) { #define CHECKCOLON if(*p != ':' ) { \ *errnop = 0; \ return NSS_STATUS_UNAVAIL; \ } else { \ *(p++) = '\0'; \ } #define TOCOLON(p, h) { while( *p && *p != ':' ) p++; CHECKCOLON } char *p, *h; char *t_namp, *t_pwdp; long int t_lstchg, t_min, t_max, t_warn, t_inact, t_expire; unsigned long int t_flag; while( 1 ) { buffer[buflen - 1] = '\xff'; p = fgets_unlocked(buffer, buflen, stream); if( p == NULL ) { if( feof_unlocked(stream) ) { *errnop = ENOENT; return NSS_STATUS_NOTFOUND; } else { *errnop = errno; return NSS_STATUS_UNAVAIL; } } h = index(p, '\n'); if( buffer[buflen - 1] != '\xff' || h == NULL ) { *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } while( isspace(*h) && h >= p) { *h = '\0'; h--; } /* Ignore comments */ if( *p == '#') continue; /* extract name */ while (isspace(*p)) ++p; /* Ignore empty lines */ if (*p == '\0') continue; t_namp = p; TOCOLON(p, h); if( name && strcmp(name, t_namp) != 0 ) continue; /* passwd */ while (isspace(*p)) ++p; t_pwdp = p; TOCOLON(p, h); /* extract day of last changes */ #define PARSE_NUMBER(var) \ if (*p == ':') { \ var = -1; \ *(p++) = '\0'; \ } else { \ var = strtol(p, &h, 10); \ p = h; \ CHECKCOLON; \ } PARSE_NUMBER(t_lstchg); /* extract min */ PARSE_NUMBER(t_min); /* extract max */ PARSE_NUMBER(t_max); /* extract days of warning */ PARSE_NUMBER(t_warn); /* extract days of inactivity */ PARSE_NUMBER(t_inact); /* extract day of expire */ PARSE_NUMBER(t_expire); #undef PARSE_NUMBER /* extract reserved flags */ t_flag = strtoul(p, &h, 10); if( *h != '\0' ) { *errnop = 0; return NSS_STATUS_UNAVAIL; } else if (p == h) t_flag = -1; *errnop = 0; spw->sp_namp = t_namp; spw->sp_pwdp = t_pwdp; spw->sp_lstchg = t_lstchg; spw->sp_min = t_min; spw->sp_max = t_max; spw->sp_warn = t_warn; spw->sp_inact = t_inact; spw->sp_expire = t_expire; spw->sp_flag = t_flag; return NSS_STATUS_SUCCESS; } }