136332Sdes/*- 2228976Suqs * Copyright (c) 1998 Dag-Erling Co��dan Sm��rgrav 336332Sdes * All rights reserved. 436332Sdes * 536332Sdes * Redistribution and use in source and binary forms, with or without 636332Sdes * modification, are permitted provided that the following conditions 736332Sdes * are met: 836332Sdes * 1. Redistributions of source code must retain the above copyright 936332Sdes * notice, this list of conditions and the following disclaimer 1036332Sdes * in this position and unchanged. 1136332Sdes * 2. Redistributions in binary form must reproduce the above copyright 1236332Sdes * notice, this list of conditions and the following disclaimer in the 1336332Sdes * documentation and/or other materials provided with the distribution. 1436332Sdes * 3. The name of the author may not be used to endorse or promote products 1536416Sdes * derived from this software without specific prior written permission 1636332Sdes * 1736332Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1836332Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1936332Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2036332Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2136332Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2236332Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2336332Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2436332Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2536332Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2636332Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2736332Sdes */ 2836332Sdes 29114601Sobrien#include <sys/cdefs.h> 30114601Sobrien__FBSDID("$FreeBSD$"); 3136332Sdes 3236332Sdes#include <err.h> 33243076Seadler#include <errno.h> 3436332Sdes#include <ctype.h> 35243076Seadler#include <limits.h> 36243076Seadler#include <stdint.h> 3736332Sdes#include <stdio.h> 3878700Sdes#include <stdlib.h> 3936332Sdes#include <string.h> 40243084Seadler#include <unistd.h> 4136332Sdes#include <sysexits.h> 4236332Sdes 43149463Scpercivastatic char empty[] = { 0 }; 44149463Scperciva 45243084Seadlerstatic void __dead2 4636332Sdesusage(void) 4736332Sdes{ 4836416Sdes fprintf(stderr, "usage: chkgrp [groupfile]\n"); 4936332Sdes exit(EX_USAGE); 5036332Sdes} 5136332Sdes 5236332Sdesint 5336332Sdesmain(int argc, char *argv[]) 5436332Sdes{ 5579160Smjacob unsigned int i; 5679160Smjacob size_t len; 57243084Seadler int quiet; 58243084Seadler int ch; 5978700Sdes int n = 0, k, e = 0; 6078700Sdes char *line, *f[4], *p; 61146641Sroberto const char *cp, *gfn; 6236332Sdes FILE *gf; 6336332Sdes 64243084Seadler quiet = 0; 65243084Seadler while ((ch = getopt(argc, argv, "q")) != -1) { 66243084Seadler switch (ch) { 67243084Seadler case 'q': 68243084Seadler quiet = 1; 69243084Seadler break; 70243084Seadler case '?': 71243084Seadler default: 72243084Seadler usage(); 73243084Seadler } 7436332Sdes } 7536332Sdes 76243084Seadler if (optind == argc) 77243084Seadler gfn = "/etc/group"; 78243084Seadler else if (optind == argc - 1) 79243084Seadler gfn = argv[optind]; 80243084Seadler else 81243084Seadler usage(); 82243084Seadler 8336332Sdes /* open group file */ 8436332Sdes if ((gf = fopen(gfn, "r")) == NULL) 85243077Seadler err(EX_NOINPUT, "%s", gfn); 8636332Sdes 8736332Sdes /* check line by line */ 8836332Sdes while (++n) { 8936332Sdes if ((line = fgetln(gf, &len)) == NULL) 9036332Sdes break; 91115891Sroam if (len > 0 && line[len - 1] != '\n') { 92115891Sroam warnx("%s: line %d: no newline character", gfn, n); 93243083Seadler e = 1; 94115891Sroam } 9536332Sdes while (len && isspace(line[len-1])) 9636332Sdes len--; 9736332Sdes 9836332Sdes /* ignore blank lines and comments */ 9936332Sdes for (p = line; p < (line + len); p++) 10036332Sdes if (!isspace(*p)) break; 10136332Sdes if (!len || (*p == '#')) { 10236332Sdes#if 0 10336332Sdes /* entry is correct, so print it */ 10436332Sdes printf("%*.*s\n", len, len, line); 10536332Sdes#endif 10636332Sdes continue; 10736332Sdes } 10836332Sdes 10936332Sdes /* 11036332Sdes * A correct group entry has four colon-separated fields, the third 11136332Sdes * of which must be entirely numeric and the fourth of which may 11236332Sdes * be empty. 11336332Sdes */ 11436332Sdes for (i = k = 0; k < 4; k++) { 11536332Sdes for (f[k] = line+i; (i < len) && (line[i] != ':'); i++) 11636332Sdes /* nothing */ ; 11736332Sdes if ((k < 3) && (line[i] != ':')) 11836332Sdes break; 11936332Sdes line[i++] = 0; 12036332Sdes } 121146641Sroberto 122149463Scperciva if (k < 4) { 123149463Scperciva warnx("%s: line %d: missing field(s)", gfn, n); 124149463Scperciva for ( ; k < 4; k++) 125149463Scperciva f[k] = empty; 126243083Seadler e = 1; 127149463Scperciva } 128149463Scperciva 129146641Sroberto for (cp = f[0] ; *cp ; cp++) { 130148695Sroberto if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' && 131148695Sroberto (cp > f[0] || *cp != '+')) { 132146641Sroberto warnx("%s: line %d: '%c' invalid character", gfn, n, *cp); 133243083Seadler e = 1; 134146641Sroberto } 135146641Sroberto } 136146641Sroberto 137146641Sroberto for (cp = f[3] ; *cp ; cp++) { 138146641Sroberto if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' && 139146641Sroberto *cp != ',') { 140146641Sroberto warnx("%s: line %d: '%c' invalid character", gfn, n, *cp); 141243083Seadler e = 1; 142146641Sroberto } 143146641Sroberto } 144146641Sroberto 14536332Sdes /* check if fourth field ended with a colon */ 14636332Sdes if (i < len) { 14736332Sdes warnx("%s: line %d: too many fields", gfn, n); 148243083Seadler e = 1; 14936332Sdes } 15036332Sdes 15136332Sdes /* check that none of the fields contain whitespace */ 152146641Sroberto for (k = 0; k < 4; k++) { 153146641Sroberto if (strcspn(f[k], " \t") != strlen(f[k])) { 15436332Sdes warnx("%s: line %d: field %d contains whitespace", 15536332Sdes gfn, n, k+1); 156243083Seadler e = 1; 157146641Sroberto } 158146641Sroberto } 15936332Sdes 16036332Sdes /* check that the GID is numeric */ 16136332Sdes if (strspn(f[2], "0123456789") != strlen(f[2])) { 16236332Sdes warnx("%s: line %d: GID is not numeric", gfn, n); 163243083Seadler e = 1; 16436332Sdes } 165243076Seadler 166243076Seadler /* check the range of the group id */ 167243076Seadler errno = 0; 168243076Seadler unsigned long groupid = strtoul(f[2], NULL, 10); 169243076Seadler if (errno != 0) { 170243076Seadler warnx("%s: line %d: strtoul failed", gfn, n); 171243076Seadler } 172243076Seadler else if (groupid > GID_MAX) { 173243076Seadler warnx("%s: line %d: group id is too large (> %ju)", 174243076Seadler gfn, n, (uintmax_t)GID_MAX); 175243083Seadler e = 1; 176243076Seadler } 17736332Sdes 17836332Sdes#if 0 17936332Sdes /* entry is correct, so print it */ 18036332Sdes printf("%s:%s:%s:%s\n", f[0], f[1], f[2], f[3]); 18136332Sdes#endif 18236332Sdes } 18336332Sdes 18436332Sdes /* check what broke the loop */ 18536332Sdes if (ferror(gf)) 18636332Sdes err(EX_IOERR, "%s: line %d", gfn, n); 18736332Sdes 18836332Sdes /* done */ 18936332Sdes fclose(gf); 190243084Seadler if (e == 0 && quiet == 0) 191146641Sroberto printf("%s is fine\n", gfn); 19236332Sdes exit(e ? EX_DATAERR : EX_OK); 19336332Sdes} 194