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: releng/10.3/usr.sbin/chkgrp/chkgrp.c 284994 2015-07-01 07:31:39Z des $"); 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 43243084Seadlerstatic void __dead2 4436332Sdesusage(void) 4536332Sdes{ 46284994Sdes 47284994Sdes fprintf(stderr, "usage: chkgrp [-q] [groupfile]\n"); 48284994Sdes exit(EX_USAGE); 4936332Sdes} 5036332Sdes 5136332Sdesint 5236332Sdesmain(int argc, char *argv[]) 5336332Sdes{ 54284994Sdes FILE *gf; 55284994Sdes unsigned long gid; 56284994Sdes unsigned int i; 57284994Sdes size_t len; 58284994Sdes int opt, quiet; 59284994Sdes int n = 0, k, e = 0; 60284994Sdes const char *cp, *f[4], *gfn, *p; 61284994Sdes char *line; 6236332Sdes 63284994Sdes quiet = 0; 64284994Sdes while ((opt = getopt(argc, argv, "q")) != -1) { 65284994Sdes switch (opt) { 66243084Seadler case 'q': 67243084Seadler quiet = 1; 68243084Seadler break; 69243084Seadler default: 70243084Seadler usage(); 71284994Sdes } 72284994Sdes } 7336332Sdes 74284994Sdes argc -= optind; 75284994Sdes argv += optind; 76243084Seadler 77284994Sdes if (argc == 0) 78284994Sdes gfn = "/etc/group"; 79284994Sdes else if (argc == 1) 80284994Sdes gfn = argv[0]; 81284994Sdes else 82284994Sdes usage(); 8336332Sdes 84284994Sdes /* open group file */ 85284994Sdes if ((gf = fopen(gfn, "r")) == NULL) 86284994Sdes err(EX_NOINPUT, "%s", gfn); 8736332Sdes 88284994Sdes /* check line by line */ 89284994Sdes while (++n) { 90284994Sdes if ((line = fgetln(gf, &len)) == NULL) 91284994Sdes break; 92284994Sdes if (len > 0 && line[len - 1] != '\n') { 93284994Sdes warnx("%s: line %d: no newline character", gfn, n); 94284994Sdes e = 1; 95284994Sdes } 96284994Sdes while (len && isspace(line[len-1])) 97284994Sdes len--; 98146641Sroberto 99284994Sdes /* ignore blank lines and comments */ 100284994Sdes for (p = line; p < line + len; p++) 101284994Sdes if (!isspace(*p)) break; 102284994Sdes if (!len || *p == '#') 103284994Sdes continue; 104149463Scperciva 105284994Sdes /* 106284994Sdes * Hack: special case for + line 107284994Sdes */ 108284994Sdes if (strncmp(line, "+:::", len) == 0) 109284994Sdes continue; 110146641Sroberto 111284994Sdes /* 112284994Sdes * A correct group entry has four colon-separated fields, 113284994Sdes * the third of which must be entirely numeric and the 114284994Sdes * fourth of which may be empty. 115284994Sdes */ 116284994Sdes for (i = k = 0; k < 4; k++) { 117284994Sdes for (f[k] = line + i; i < len && line[i] != ':'; i++) 118284994Sdes /* nothing */ ; 119284994Sdes if (k < 3 && line[i] != ':') 120284994Sdes break; 121284994Sdes line[i++] = 0; 122284994Sdes } 123146641Sroberto 124284994Sdes if (k < 4) { 125284994Sdes warnx("%s: line %d: missing field(s)", gfn, n); 126284994Sdes while (k < 4) 127284994Sdes f[k++] = ""; 128284994Sdes e = 1; 129284994Sdes } 130284994Sdes 131284994Sdes for (cp = f[0] ; *cp ; cp++) { 132284994Sdes if (!isalnum(*cp) && *cp != '.' && *cp != '_' && 133284994Sdes *cp != '-' && (cp > f[0] || *cp != '+')) { 134284994Sdes warnx("%s: line %d: '%c' invalid character", 135284994Sdes gfn, n, *cp); 136284994Sdes e = 1; 137284994Sdes } 138284994Sdes } 139284994Sdes 140284994Sdes for (cp = f[3] ; *cp ; cp++) { 141284994Sdes if (!isalnum(*cp) && *cp != '.' && *cp != '_' && 142284994Sdes *cp != '-' && *cp != ',') { 143284994Sdes warnx("%s: line %d: '%c' invalid character", 144284994Sdes gfn, n, *cp); 145284994Sdes e = 1; 146284994Sdes } 147284994Sdes } 148284994Sdes 149284994Sdes /* check if fourth field ended with a colon */ 150284994Sdes if (i < len) { 151284994Sdes warnx("%s: line %d: too many fields", gfn, n); 152284994Sdes e = 1; 153284994Sdes } 15436332Sdes 155284994Sdes /* check that none of the fields contain whitespace */ 156284994Sdes for (k = 0; k < 4; k++) { 157284994Sdes if (strcspn(f[k], " \t") != strlen(f[k])) { 158284994Sdes warnx("%s: line %d: field %d contains whitespace", 159284994Sdes gfn, n, k+1); 160284994Sdes e = 1; 161284994Sdes } 162284994Sdes } 16336332Sdes 164284994Sdes /* check that the GID is numeric */ 165284994Sdes if (strspn(f[2], "0123456789") != strlen(f[2])) { 166284994Sdes warnx("%s: line %d: group id is not numeric", gfn, n); 167284994Sdes e = 1; 168284994Sdes } 169243076Seadler 170284994Sdes /* check the range of the group id */ 171284994Sdes errno = 0; 172284994Sdes gid = strtoul(f[2], NULL, 10); 173284994Sdes if (errno != 0) { 174284994Sdes warnx("%s: line %d: strtoul failed", gfn, n); 175284994Sdes } else if (gid > GID_MAX) { 176284994Sdes warnx("%s: line %d: group id is too large (%ju > %ju)", 177284994Sdes gfn, n, (uintmax_t)gid, (uintmax_t)GID_MAX); 178284994Sdes e = 1; 179284994Sdes } 180243076Seadler } 18136332Sdes 182284994Sdes /* check what broke the loop */ 183284994Sdes if (ferror(gf)) 184284994Sdes err(EX_IOERR, "%s: line %d", gfn, n); 18536332Sdes 186284994Sdes /* done */ 187284994Sdes fclose(gf); 188284994Sdes if (e == 0 && quiet == 0) 189284994Sdes printf("%s is fine\n", gfn); 190284994Sdes exit(e ? EX_DATAERR : EX_OK); 19136332Sdes} 192