/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1998 Dag-Erling Smørgrav4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer11* in this position and unchanged.12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15* 3. The name of the author may not be used to endorse or promote products16* derived from this software without specific prior written permission17*18* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR19* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES20* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.21* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,22* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT23* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,24* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY25* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT26* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF27* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.28*/2930#include <sys/cdefs.h>31#include <err.h>32#include <errno.h>33#include <ctype.h>34#include <inttypes.h>35#include <limits.h>36#include <stdint.h>37#include <stdio.h>38#include <stdlib.h>39#include <string.h>40#include <unistd.h>41#include <sysexits.h>4243static void __dead244usage(void)45{4647fprintf(stderr, "usage: chkgrp [-q] [groupfile]\n");48exit(EX_USAGE);49}5051int52main(int argc, char *argv[])53{54FILE *gf;55unsigned long gid;56unsigned int i;57size_t len;58int opt, quiet;59int n = 0, k, e = 0;60const char *cp, *f[4], *gfn, *p;61char *line;6263quiet = 0;64while ((opt = getopt(argc, argv, "q")) != -1) {65switch (opt) {66case 'q':67quiet = 1;68break;69default:70usage();71}72}7374argc -= optind;75argv += optind;7677if (argc == 0)78gfn = "/etc/group";79else if (argc == 1)80gfn = argv[0];81else82usage();8384/* open group file */85if ((gf = fopen(gfn, "r")) == NULL)86err(EX_NOINPUT, "%s", gfn);8788/* check line by line */89while (++n) {90if ((line = fgetln(gf, &len)) == NULL)91break;92if (len > 0 && line[len - 1] != '\n') {93warnx("%s: line %d: no newline character", gfn, n);94e = 1;95}96while (len && isspace(line[len-1]))97len--;9899/* ignore blank lines and comments */100for (p = line; p < line + len; p++)101if (!isspace(*p)) break;102if (!len || *p == '#')103continue;104105/*106* Hack: special case for + line107*/108if (strncmp(line, "+:::", len) == 0 ||109strncmp(line, "+:*::", len) == 0)110continue;111112/*113* A correct group entry has four colon-separated fields,114* the third of which must be entirely numeric and the115* fourth of which may be empty.116*/117for (i = k = 0; k < 4; k++) {118for (f[k] = line + i; i < len && line[i] != ':'; i++)119/* nothing */ ;120if (k < 3 && line[i] != ':')121break;122line[i++] = 0;123}124125if (k < 4) {126warnx("%s: line %d: missing field(s)", gfn, n);127while (k < 4)128f[k++] = "";129e = 1;130}131132for (cp = f[0] ; *cp ; cp++) {133if (!isalnum(*cp) && *cp != '.' && *cp != '_' &&134*cp != '-' && (cp > f[0] || *cp != '+')) {135warnx("%s: line %d: '%c' invalid character",136gfn, n, *cp);137e = 1;138}139}140141for (cp = f[3] ; *cp ; cp++) {142if (!isalnum(*cp) && *cp != '.' && *cp != '_' &&143*cp != '-' && *cp != ',') {144warnx("%s: line %d: '%c' invalid character",145gfn, n, *cp);146e = 1;147}148}149150/* check if fourth field ended with a colon */151if (i < len) {152warnx("%s: line %d: too many fields", gfn, n);153e = 1;154}155156/* check that none of the fields contain whitespace */157for (k = 0; k < 4; k++) {158if (strcspn(f[k], " \t") != strlen(f[k])) {159warnx("%s: line %d: field %d contains whitespace",160gfn, n, k+1);161e = 1;162}163}164165/* check that the GID is numeric */166if (strspn(f[2], "0123456789") != strlen(f[2])) {167warnx("%s: line %d: group id is not numeric", gfn, n);168e = 1;169}170171/* check the range of the group id */172errno = 0;173gid = strtoul(f[2], NULL, 10);174if (errno != 0) {175warnx("%s: line %d: strtoul failed", gfn, n);176} else if (gid > GID_MAX) {177warnx("%s: line %d: group id is too large (%ju > %ju)",178gfn, n, (uintmax_t)gid, (uintmax_t)GID_MAX);179e = 1;180}181}182183/* check what broke the loop */184if (ferror(gf))185err(EX_IOERR, "%s: line %d", gfn, n);186187/* done */188fclose(gf);189if (e == 0 && quiet == 0)190printf("%s is fine\n", gfn);191exit(e ? EX_DATAERR : EX_OK);192}193194195