freebsd-nq/lib/libc/gen/getgrent.c
Wolfram Schneider ebddb6b4fe Use dynamic allocated buffers instead static buffers. No member or
line length limit anymore - now 500 members or 5000 members are
possible. For security group lines longer than 256K will be count as
an error. 256K should be enough for 65536 users.

Support comments (lines that begin with a #) if compiled with
option -DGROUP_IGNORE_COMMENTS.

Fortunately it seems that all system utilities which use getgrent()
functions are dynamically linked executables. So you need only
rebuild libc.so.3.0 if you want this change. Note: if you have
an old X server which depend on libc.so.2.* you should rebuild
libc.so.2.* too.

Not a 2.2 candidate.
1996-12-25 21:51:24 +00:00

552 lines
11 KiB
C

/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94";
#endif /* LIBC_SCCS and not lint */
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <grp.h>
static FILE *_gr_fp;
static struct group _gr_group;
static int _gr_stayopen;
static int grscan(), start_gr();
#ifdef YP
#include <rpc/rpc.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
static int _gr_stepping_yp;
static int _gr_yp_enabled;
static int _getypgroup(struct group *, const char *, char *);
static int _nextypgroup(struct group *);
#endif
/* initial size for malloc and increase steps for realloc */
#define MAXGRP 64
#define MAXLINELENGTH 256
static char **members; /* list of group members */
static int maxgrp; /* current length of **mebers */
static char *line; /* temp buffer for group line */
static int maxlinelength; /* current length of *line */
/*
* Lines longer than MAXLINELENGTHLIMIT will be count as an error.
* <= 0 disable check for maximum line length
* 256K is enough for 64,000 uids
*/
#define MAXLINELENGTHLIMIT (256 * 1024)
struct group *
getgrent()
{
if (!_gr_fp && !start_gr()) {
return NULL;
}
#ifdef YP
if (_gr_stepping_yp) {
if (_nextypgroup(&_gr_group))
return(&_gr_group);
}
tryagain:
#endif
if (!grscan(0, 0, NULL))
return(NULL);
#ifdef YP
if(_gr_group.gr_name[0] == '+' && _gr_group.gr_name[1]) {
_getypgroup(&_gr_group, &_gr_group.gr_name[1],
"group.byname");
} else if(_gr_group.gr_name[0] == '+') {
if (!_nextypgroup(&_gr_group))
goto tryagain;
else
return(&_gr_group);
}
#endif
return(&_gr_group);
}
struct group *
getgrnam(name)
const char *name;
{
int rval;
if (!start_gr())
return(NULL);
#ifdef YP
tryagain:
#endif
rval = grscan(1, 0, name);
#ifdef YP
if(rval == -1 && (_gr_yp_enabled < 0 || (_gr_yp_enabled &&
_gr_group.gr_name[0] == '+'))) {
if (!(rval = _getypgroup(&_gr_group, name, "group.byname")))
goto tryagain;
}
#endif
if (!_gr_stayopen)
endgrent();
return(rval ? &_gr_group : NULL);
}
struct group *
#ifdef __STDC__
getgrgid(gid_t gid)
#else
getgrgid(gid)
gid_t gid;
#endif
{
int rval;
if (!start_gr())
return(NULL);
#ifdef YP
tryagain:
#endif
rval = grscan(1, gid, NULL);
#ifdef YP
if(rval == -1 && _gr_yp_enabled) {
char buf[16];
snprintf(buf, sizeof buf, "%d", (unsigned)gid);
if (!(rval = _getypgroup(&_gr_group, buf, "group.bygid")))
goto tryagain;
}
#endif
if (!_gr_stayopen)
endgrent();
return(rval ? &_gr_group : NULL);
}
static int
start_gr()
{
if (_gr_fp) {
rewind(_gr_fp);
return(1);
}
_gr_fp = fopen(_PATH_GROUP, "r");
if(!_gr_fp) return 0;
#ifdef YP
/*
* This is a disgusting hack, used to determine when YP is enabled.
* This would be easier if we had a group database to go along with
* the password database.
*/
{
char *line;
size_t linelen;
_gr_yp_enabled = 0;
while((line = fgetln(_gr_fp, &linelen)) != NULL) {
if(line[0] == '+') {
if(line[1] && line[1] != ':' && !_gr_yp_enabled) {
_gr_yp_enabled = 1;
} else {
_gr_yp_enabled = -1;
break;
}
}
}
rewind(_gr_fp);
}
#endif
if (maxlinelength == 0) {
if ((line = (char *)malloc(sizeof(char) *
MAXLINELENGTH)) == NULL)
return(0);
maxlinelength += MAXLINELENGTH;
}
if (maxgrp == 0) {
if ((members = (char **)malloc(sizeof(char **) *
MAXGRP)) == NULL)
return(0);
maxgrp += MAXGRP;
}
return 1;
}
int
setgrent()
{
return(setgroupent(0));
}
int
setgroupent(stayopen)
int stayopen;
{
if (!start_gr())
return(0);
_gr_stayopen = stayopen;
#ifdef YP
_gr_stepping_yp = 0;
#endif
return(1);
}
void
endgrent()
{
#ifdef YP
_gr_stepping_yp = 0;
#endif
if (_gr_fp) {
(void)fclose(_gr_fp);
_gr_fp = NULL;
}
}
static int
grscan(search, gid, name)
register int search, gid;
register char *name;
{
register char *cp, **m;
char *bp;
#ifdef YP
int _ypfound;
#endif
for (;;) {
#ifdef YP
_ypfound = 0;
#endif
if (fgets(line, maxlinelength, _gr_fp) == NULL)
return(0);
if (!index(line, '\n')) {
do {
if (feof(_gr_fp))
return(0);
/* don't allocate infinite memory */
if (MAXLINELENGTHLIMIT > 0 &&
maxlinelength >= MAXLINELENGTHLIMIT)
return(0);
if ((line = (char *)realloc(line,
sizeof(char) *
(maxlinelength + MAXLINELENGTH))) == NULL)
return(0);
if (fgets(line + maxlinelength - 1,
MAXLINELENGTH + 1, _gr_fp) == NULL)
return(0);
maxlinelength += MAXLINELENGTH;
} while (!index(line + maxlinelength -
MAXLINELENGTH - 1, '\n'));
}
#ifdef GROUP_IGNORE_COMMENTS
/*
* Ignore comments: ^[ \t]*#
*/
for (cp = line; *cp != '\0'; cp++)
if (*cp != ' ' && *cp != '\t')
break;
if (*cp == '#')
continue;
#endif
bp = line;
if ((_gr_group.gr_name = strsep(&bp, ":\n")) == NULL)
break;
#ifdef YP
/*
* XXX We need to be careful to avoid proceeding
* past this point under certain circumstances or
* we risk dereferencing null pointers down below.
*/
if (_gr_group.gr_name[0] == '+') {
if (strlen(_gr_group.gr_name) == 1) {
switch(search) {
case 0:
return(1);
case 1:
return(-1);
default:
return(0);
}
} else {
cp = &_gr_group.gr_name[1];
if (search && name != NULL)
if (strcmp(cp, name))
continue;
if (!_getypgroup(&_gr_group, cp,
"group.byname"))
continue;
if (search && name == NULL)
if (gid != _gr_group.gr_gid)
continue;
/* We're going to override -- tell the world. */
_ypfound++;
}
}
#else
if (_gr_group.gr_name[0] == '+')
continue;
#endif /* YP */
if (search && name) {
if(strcmp(_gr_group.gr_name, name)) {
continue;
}
}
#ifdef YP
if ((cp = strsep(&bp, ":\n")) == NULL)
if (_ypfound)
return(1);
else
break;
if (strlen(cp) || !_ypfound)
_gr_group.gr_passwd = cp;
#else
if ((_gr_group.gr_passwd = strsep(&bp, ":\n")) == NULL)
break;
#endif
if (!(cp = strsep(&bp, ":\n")))
#ifdef YP
if (_ypfound)
return(1);
else
#endif
continue;
#ifdef YP
/*
* Hurm. Should we be doing this? We allow UIDs to
* be overridden -- what about GIDs?
*/
if (!_ypfound)
#endif
_gr_group.gr_gid = atoi(cp);
if (search && name == NULL && _gr_group.gr_gid != gid)
continue;
cp = NULL;
if (bp == NULL) /* !!! Must check for this! */
break;
#ifdef YP
if ((cp = strsep(&bp, ":\n")) == NULL)
break;
if (!strlen(cp) && _ypfound)
return(1);
else
members[0] = NULL;
bp = cp;
cp = NULL;
#endif
for (m = members; ; bp++) {
if (m == (members + maxgrp - 1)) {
if ((members = (char **)
realloc(members,
sizeof(char **) *
(maxgrp + MAXGRP))) == NULL)
return(0);
m = members + maxgrp - 1;
maxgrp += MAXGRP;
}
if (*bp == ',') {
if (cp) {
*bp = '\0';
*m++ = cp;
cp = NULL;
}
} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
if (cp) {
*bp = '\0';
*m++ = cp;
}
break;
} else if (cp == NULL)
cp = bp;
}
_gr_group.gr_mem = members;
*m = NULL;
return(1);
}
/* NOTREACHED */
return (0);
}
#ifdef YP
static int
_gr_breakout_yp(struct group *gr, char *result)
{
char *s, *cp;
char **m;
/*
* XXX If 's' ends up being a NULL pointer, punt on this group.
* It means the NIS group entry is badly formatted and should
* be skipped.
*/
if ((s = strsep(&result, ":")) == NULL) return 0; /* name */
gr->gr_name = s;
if ((s = strsep(&result, ":")) == NULL) return 0; /* password */
gr->gr_passwd = s;
if ((s = strsep(&result, ":")) == NULL) return 0; /* gid */
gr->gr_gid = atoi(s);
if ((s = result) == NULL) return 0;
cp = 0;
for (m = members; ; s++) {
if (m == members + maxgrp - 1) {
if ((members = (char **)realloc(members,
sizeof(char **) * (maxgrp + MAXGRP))) == NULL)
return(0);
m = members + maxgrp - 1;
maxgrp += MAXGRP;
}
if (*s == ',') {
if (cp) {
*s = '\0';
*m++ = cp;
cp = NULL;
}
} else if (*s == '\0' || *s == '\n' || *s == ' ') {
if (cp) {
*s = '\0';
*m++ = cp;
}
break;
} else if (cp == NULL) {
cp = s;
}
}
_gr_group.gr_mem = members;
*m = NULL;
return 1;
}
static char *_gr_yp_domain;
static int
_getypgroup(struct group *gr, const char *name, char *map)
{
char *result, *s;
static char resultbuf[1024];
int resultlen;
if(!_gr_yp_domain) {
if(yp_get_default_domain(&_gr_yp_domain))
return 0;
}
if(yp_match(_gr_yp_domain, map, name, strlen(name),
&result, &resultlen))
return 0;
s = strchr(result, '\n');
if(s) *s = '\0';
if(resultlen >= sizeof resultbuf) return 0;
strncpy(resultbuf, result, resultlen);
free(result);
return(_gr_breakout_yp(gr, resultbuf));
}
static int
_nextypgroup(struct group *gr)
{
static char *key;
static int keylen;
char *lastkey, *result;
static char resultbuf[1024];
int resultlen;
int rv;
if(!_gr_yp_domain) {
if(yp_get_default_domain(&_gr_yp_domain))
return 0;
}
if(!_gr_stepping_yp) {
if(key) free(key);
rv = yp_first(_gr_yp_domain, "group.byname",
&key, &keylen, &result, &resultlen);
if(rv) {
return 0;
}
_gr_stepping_yp = 1;
goto unpack;
} else {
tryagain:
lastkey = key;
rv = yp_next(_gr_yp_domain, "group.byname", key, keylen,
&key, &keylen, &result, &resultlen);
free(lastkey);
unpack:
if(rv) {
_gr_stepping_yp = 0;
return 0;
}
if(resultlen > sizeof(resultbuf)) {
free(result);
goto tryagain;
}
strcpy(resultbuf, result);
free(result);
if((result = strchr(resultbuf, '\n')) != NULL)
*result = '\0';
if (_gr_breakout_yp(gr, resultbuf))
return(1);
else
goto tryagain;
}
}
#endif /* YP */