1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer 12 * in this position and unchanged. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <err.h> 35 #include <errno.h> 36 #include <ctype.h> 37 #include <inttypes.h> 38 #include <limits.h> 39 #include <stdint.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include <sysexits.h> 45 46 static void __dead2 47 usage(void) 48 { 49 50 fprintf(stderr, "usage: chkgrp [-q] [groupfile]\n"); 51 exit(EX_USAGE); 52 } 53 54 int 55 main(int argc, char *argv[]) 56 { 57 FILE *gf; 58 unsigned long gid; 59 unsigned int i; 60 size_t len; 61 int opt, quiet; 62 int n = 0, k, e = 0; 63 const char *cp, *f[4], *gfn, *p; 64 char *line; 65 66 quiet = 0; 67 while ((opt = getopt(argc, argv, "q")) != -1) { 68 switch (opt) { 69 case 'q': 70 quiet = 1; 71 break; 72 default: 73 usage(); 74 } 75 } 76 77 argc -= optind; 78 argv += optind; 79 80 if (argc == 0) 81 gfn = "/etc/group"; 82 else if (argc == 1) 83 gfn = argv[0]; 84 else 85 usage(); 86 87 /* open group file */ 88 if ((gf = fopen(gfn, "r")) == NULL) 89 err(EX_NOINPUT, "%s", gfn); 90 91 /* check line by line */ 92 while (++n) { 93 if ((line = fgetln(gf, &len)) == NULL) 94 break; 95 if (len > 0 && line[len - 1] != '\n') { 96 warnx("%s: line %d: no newline character", gfn, n); 97 e = 1; 98 } 99 while (len && isspace(line[len-1])) 100 len--; 101 102 /* ignore blank lines and comments */ 103 for (p = line; p < line + len; p++) 104 if (!isspace(*p)) break; 105 if (!len || *p == '#') 106 continue; 107 108 /* 109 * Hack: special case for + line 110 */ 111 if (strncmp(line, "+:::", len) == 0 || 112 strncmp(line, "+:*::", len) == 0) 113 continue; 114 115 /* 116 * A correct group entry has four colon-separated fields, 117 * the third of which must be entirely numeric and the 118 * fourth of which may be empty. 119 */ 120 for (i = k = 0; k < 4; k++) { 121 for (f[k] = line + i; i < len && line[i] != ':'; i++) 122 /* nothing */ ; 123 if (k < 3 && line[i] != ':') 124 break; 125 line[i++] = 0; 126 } 127 128 if (k < 4) { 129 warnx("%s: line %d: missing field(s)", gfn, n); 130 while (k < 4) 131 f[k++] = ""; 132 e = 1; 133 } 134 135 for (cp = f[0] ; *cp ; cp++) { 136 if (!isalnum(*cp) && *cp != '.' && *cp != '_' && 137 *cp != '-' && (cp > f[0] || *cp != '+')) { 138 warnx("%s: line %d: '%c' invalid character", 139 gfn, n, *cp); 140 e = 1; 141 } 142 } 143 144 for (cp = f[3] ; *cp ; cp++) { 145 if (!isalnum(*cp) && *cp != '.' && *cp != '_' && 146 *cp != '-' && *cp != ',') { 147 warnx("%s: line %d: '%c' invalid character", 148 gfn, n, *cp); 149 e = 1; 150 } 151 } 152 153 /* check if fourth field ended with a colon */ 154 if (i < len) { 155 warnx("%s: line %d: too many fields", gfn, n); 156 e = 1; 157 } 158 159 /* check that none of the fields contain whitespace */ 160 for (k = 0; k < 4; k++) { 161 if (strcspn(f[k], " \t") != strlen(f[k])) { 162 warnx("%s: line %d: field %d contains whitespace", 163 gfn, n, k+1); 164 e = 1; 165 } 166 } 167 168 /* check that the GID is numeric */ 169 if (strspn(f[2], "0123456789") != strlen(f[2])) { 170 warnx("%s: line %d: group id is not numeric", gfn, n); 171 e = 1; 172 } 173 174 /* check the range of the group id */ 175 errno = 0; 176 gid = strtoul(f[2], NULL, 10); 177 if (errno != 0) { 178 warnx("%s: line %d: strtoul failed", gfn, n); 179 } else if (gid > GID_MAX) { 180 warnx("%s: line %d: group id is too large (%ju > %ju)", 181 gfn, n, (uintmax_t)gid, (uintmax_t)GID_MAX); 182 e = 1; 183 } 184 } 185 186 /* check what broke the loop */ 187 if (ferror(gf)) 188 err(EX_IOERR, "%s: line %d", gfn, n); 189 190 /* done */ 191 fclose(gf); 192 if (e == 0 && quiet == 0) 193 printf("%s is fine\n", gfn); 194 exit(e ? EX_DATAERR : EX_OK); 195 } 196