grpck.c revision f48205be61a214698b763ff550ab9e657525104c
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/param.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <pwd.h>
#include <errno.h>
#include <locale.h>
#include <limits.h>
#define BADLINE "Too many/few fields"
#define TOOLONG "Line too long"
#define NONAME "No group name"
#define BADNAME "Bad character(s) in group name"
#define BADGID "Invalid GID"
#define NULLNAME "Null login name"
#define NOTFOUND "Logname not found in password file"
#define DUPNAME "Duplicate logname entry"
#define DUPNAME2 "Duplicate logname entry (gid first occurs in passwd entry)"
#define NOMEM "Out of memory"
#define NGROUPS "Maximum groups exceeded for logname "
#define BLANKLINE "Blank line detected. Please remove line"
#define LONGNAME "Group name too long"
int eflag, badchar, baddigit, badlognam, colons, len;
static int longnam = 0;
int code;
#define MYBUFSIZE (LINE_MAX) /* max line length including newline and null */
#define NUM_COLONS 3
char *buf;
char *nptr;
char *cptr;
FILE *fptr;
gid_t gid;
void error(char *msg);
struct group {
struct group *nxt;
int cnt;
gid_t grp;
};
struct node {
struct node *next;
int ngroups;
struct group *groups;
char user[1];
};
void *
emalloc(size_t size)
{
void *vp;
vp = malloc(size);
if (vp == NULL) {
fprintf(stderr, "%s\n", gettext(NOMEM));
exit(1);
}
return (vp);
}
int
main(int argc, char *argv[])
{
struct passwd *pwp;
struct node *root = NULL;
struct node *t;
struct group *gp;
int ngroups_max;
int ngroups = 0;
int listlen;
int i;
int lineno = 0;
char *buf_off, *tmpbuf;
int delim[NUM_COLONS + 1], buf_len, bufsize;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
code = 0;
ngroups_max = sysconf(_SC_NGROUPS_MAX);
if (argc == 1)
argv[1] = "/etc/group";
else if (argc != 2) {
fprintf(stderr, gettext("usage: %s filename\n"), *argv);
exit(1);
}
if ((fptr = fopen(argv[1], "r")) == NULL) {
fprintf(stderr, gettext("cannot open file %s: %s\n"), argv[1],
strerror(errno));
exit(1);
}
#ifdef ORIG_SVR4
while ((pwp = getpwent()) != NULL) {
t = (struct node *)emalloc(sizeof (*t) + strlen(pwp->pw_name));
t->next = root;
root = t;
strcpy(t->user, pwp->pw_name);
t->ngroups = 1;
if (!ngroups_max)
t->groups = NULL;
else {
t->groups = (struct group *)
emalloc(sizeof (struct group));
t->groups->grp = pwp->pw_gid;
t->groups->cnt = 1;
t->groups->nxt = NULL;
}
}
#endif
bufsize = MYBUFSIZE;
if ((buf = malloc(bufsize)) == NULL) {
(void) fprintf(stderr, gettext(NOMEM));
exit(1);
}
while (!feof(fptr) && !ferror(fptr)) {
buf_len = 0;
buf_off = buf;
while (fgets(buf_off, (bufsize - buf_len), fptr) != NULL) {
buf_len += strlen(buf_off);
if (buf[buf_len - 1] == '\n' || feof(fptr))
break;
tmpbuf = realloc(buf, (bufsize + MYBUFSIZE));
if (tmpbuf == NULL) {
(void) fprintf(stderr, gettext(NOMEM));
exit(1);
}
bufsize += MYBUFSIZE;
buf = tmpbuf;
buf_off = buf + buf_len;
}
if (buf_len == 0)
continue;
/* Report error to be consistent with libc */
if ((buf_len + 1) > LINE_MAX)
error(TOOLONG);
lineno++;
if (buf[0] == '\n') /* blank lines are ignored */
{
code = 1; /* exit with error code = 1 */
eflag = 0; /* force print of "blank" line */
fprintf(stderr, "\n%s %d\n", gettext(BLANKLINE),
lineno);
continue;
}
if (buf[buf_len - 1] == '\n') {
if ((tmpbuf = strdup(buf)) == NULL) {
(void) fprintf(stderr, gettext(NOMEM));
exit(1);
}
tmpbuf[buf_len - 1] = ',';
} else {
if ((tmpbuf = malloc(buf_len + 2)) == NULL) {
(void) fprintf(stderr, gettext(NOMEM));
exit(1);
}
(void) strcpy(tmpbuf, buf);
tmpbuf[buf_len++] = ',';
tmpbuf[buf_len] = '\0';
}
colons = 0;
eflag = 0;
badchar = 0;
baddigit = 0;
badlognam = 0;
gid = 0;
ngroups++; /* Increment number of groups found */
/* Check that entry is not a nameservice redirection */
if (buf[0] == '+' || buf[0] == '-') {
/*
* Should set flag here to allow special case checking
* in the rest of the code,
* but for now, we'll just ignore this entry.
*/
free(tmpbuf);
continue;
}
/* Check number of fields */
for (i = 0; buf[i] != NULL; i++) {
if (buf[i] == ':') {
delim[colons] = i;
if (++colons > NUM_COLONS)
break;
}
}
if (colons != NUM_COLONS) {
error(BADLINE);
free(tmpbuf);
continue;
}
/* check to see that group name is at least 1 character */
/* and that all characters are lowrcase or digits. */
if (buf[0] == ':')
error(NONAME);
else {
for (i = 0; buf[i] != ':'; i++) {
if (i >= LOGNAME_MAX)
longnam++;
if (!(islower(buf[i]) || isdigit(buf[i])))
badchar++;
}
if (longnam > 0)
error(LONGNAME);
if (badchar > 0)
error(BADNAME);
}
/* check that GID is numeric and <= 31 bits */
len = (delim[2] - delim[1]) - 1;
if (len > 10 || len < 1)
error(BADGID);
else {
for (i = (delim[1]+1); i < delim[2]; i++) {
if (! (isdigit(buf[i])))
baddigit++;
else if (baddigit == 0)
gid = gid * 10 + (gid_t)(buf[i] - '0');
/* converts ascii GID to decimal */
}
if (baddigit > 0)
error(BADGID);
else if (gid > (gid_t)MAXUID)
error(BADGID);
}
/* check that logname appears in the passwd file */
nptr = &tmpbuf[delim[2]];
nptr++;
listlen = strlen(nptr) - 1;
while ((cptr = strchr(nptr, ',')) != NULL) {
*cptr = NULL;
if (*nptr == NULL) {
if (listlen)
error(NULLNAME);
nptr++;
continue;
}
for (t = root; t != NULL; t = t->next) {
if (strcmp(t->user, nptr) == 0)
break;
}
if (t == NULL) {
#ifndef ORIG_SVR4
/*
* User entry not found, so check if in
* password file
*/
struct passwd *pwp;
if ((pwp = getpwnam(nptr)) == NULL) {
#endif
badlognam++;
error(NOTFOUND);
goto getnext;
#ifndef ORIG_SVR4
}
/* Usrname found, so add entry to user-list */
t = (struct node *)
emalloc(sizeof (*t) + strlen(nptr));
t->next = root;
root = t;
strcpy(t->user, nptr);
t->ngroups = 1;
if (!ngroups_max)
t->groups = NULL;
else {
t->groups = (struct group *)
emalloc(sizeof (struct group));
t->groups->grp = pwp->pw_gid;
t->groups->cnt = 1;
t->groups->nxt = NULL;
}
}
#endif
if (!ngroups_max)
goto getnext;
t->ngroups++;
/*
* check for duplicate logname in group
*/
for (gp = t->groups; gp != NULL; gp = gp->nxt) {
if (gid == gp->grp) {
if (gp->cnt++ == 1) {
badlognam++;
if (gp->nxt == NULL)
error(DUPNAME2);
else
error(DUPNAME);
}
goto getnext;
}
}
gp = (struct group *)emalloc(sizeof (struct group));
gp->grp = gid;
gp->cnt = 1;
gp->nxt = t->groups;
t->groups = gp;
getnext:
nptr = ++cptr;
}
free(tmpbuf);
}
if (ngroups == 0) {
fprintf(stderr, gettext("Group file '%s' is empty\n"), argv[1]);
code = 1;
}
if (ngroups_max) {
for (t = root; t != NULL; t = t->next) {
if (t->ngroups > ngroups_max) {
fprintf(stderr, "\n\n%s%s (%d)\n",
NGROUPS, t->user, t->ngroups);
code = 1;
}
}
}
return (code);
}
/* Error printing routine */
void
error(char *msg)
{
code = 1;
if (eflag == 0) {
fprintf(stderr, "\n\n%s", buf);
eflag = 1;
}
if (longnam != 0) {
fprintf(stderr, "\t%s\n", gettext(msg));
longnam = 0;
return;
}
if (badchar != 0) {
fprintf(stderr, "\t%d %s\n", badchar, gettext(msg));
badchar = 0;
return;
} else if (baddigit != 0) {
fprintf(stderr, "\t%s\n", gettext(msg));
baddigit = 0;
return;
} else if (badlognam != 0) {
fprintf(stderr, "\t%s - %s\n", nptr, gettext(msg));
badlognam = 0;
return;
} else {
fprintf(stderr, "\t%s\n", gettext(msg));
return;
}
}