1 /* $NetBSD: mknetid.c,v 1.17 2009/10/20 00:51:14 snj Exp $ */ 2 3 /* 4 * Copyright (c) 1996 Mats O Jansson <moj@stacken.kth.se> 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 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 #ifndef lint 31 __RCSID("$NetBSD: mknetid.c,v 1.17 2009/10/20 00:51:14 snj Exp $"); 32 #endif 33 34 /* 35 * Originally written by Mats O Jansson <moj@stacken.kth.se> 36 * Simplified a bit by Jason R. Thorpe <thorpej@NetBSD.org> 37 */ 38 39 #include <sys/param.h> 40 #include <sys/queue.h> 41 #include <ctype.h> 42 #include <err.h> 43 #include <grp.h> 44 #include <limits.h> 45 #include <netdb.h> 46 #include <pwd.h> 47 #include <stdio.h> 48 #include <string.h> 49 #include <stdlib.h> 50 #include <unistd.h> 51 52 #include <rpcsvc/ypclnt.h> 53 54 #include "protos.h" 55 56 struct user { 57 char *usr_name; /* user name */ 58 int usr_uid; /* user uid */ 59 int usr_gid; /* user gid */ 60 int gid_count; /* number of gids */ 61 int gid[NGROUPS]; /* additional gids */ 62 TAILQ_ENTRY(user) read; /* links in read order */ 63 TAILQ_ENTRY(user) hash; /* links in hash order */ 64 }; 65 66 #define HASHMAX 55 67 68 void add_group(const char *, const char *); 69 void add_user(const char *, const char *, const char *); 70 int hashidx(char); 71 int isgsep(char); 72 int main(int, char *[]); 73 void print_hosts(const char *, const char *); 74 void print_netid(const char *); 75 void print_passwd_group(int, const char *); 76 void read_group(const char *); 77 void read_passwd(const char *); 78 void usage(void); 79 80 TAILQ_HEAD(user_list, user); 81 struct user_list root; 82 struct user_list hroot[HASHMAX]; 83 84 int 85 main(int argc, char *argv[]) 86 { 87 const char *HostFile = _PATH_HOSTS; 88 const char *PasswdFile = _PATH_PASSWD; 89 const char *GroupFile = _PATH_GROUP; 90 const char *NetidFile = "/etc/netid"; 91 92 int qflag, ch; 93 char *domain; 94 95 TAILQ_INIT(&root); 96 for (ch = 0; ch < HASHMAX; ch++) 97 TAILQ_INIT((&hroot[ch])); 98 99 qflag = 0; 100 domain = NULL; 101 102 while ((ch = getopt(argc, argv, "d:g:h:m:p:q")) != -1) { 103 switch (ch) { 104 case 'd': 105 domain = optarg; 106 break; 107 108 case 'g': 109 GroupFile = optarg; 110 break; 111 112 case 'h': 113 HostFile = optarg; 114 break; 115 116 case 'm': 117 NetidFile = optarg; 118 break; 119 120 case 'p': 121 PasswdFile = optarg; 122 break; 123 124 case 'q': 125 qflag++; 126 break; 127 128 default: 129 usage(); 130 } 131 } 132 if (argc != optind) 133 usage(); 134 135 if (domain == NULL) 136 if (yp_get_default_domain(&domain)) 137 errx(1, "Can't get YP domain name"); 138 139 read_passwd(PasswdFile); 140 read_group(GroupFile); 141 142 print_passwd_group(qflag, domain); 143 print_hosts(HostFile, domain); 144 print_netid(NetidFile); 145 146 exit (0); 147 } 148 149 int 150 hashidx(char key) 151 { 152 if (key < 'A') 153 return(0); 154 155 if (key <= 'Z') 156 return(1 + key - 'A'); 157 158 if (key < 'a') 159 return(27); 160 161 if (key <= 'z') 162 return(28 + key - 'a'); 163 164 return(54); 165 } 166 167 void 168 add_user(const char *username, const char *uid, const char *gid) 169 { 170 struct user *u; 171 int idx; 172 173 idx = hashidx(username[0]); 174 175 u = (struct user *)malloc(sizeof(struct user)); 176 if (u == NULL) 177 err(1, "can't allocate user"); 178 memset(u, 0, sizeof(struct user)); 179 180 u->usr_name = strdup(username); 181 if (u->usr_name == NULL) 182 err(1, "can't allocate user name"); 183 184 u->usr_uid = atoi(uid); 185 u->usr_gid = atoi(gid); 186 u->gid_count = -1; 187 188 TAILQ_INSERT_TAIL(&root, u, read); 189 TAILQ_INSERT_TAIL((&hroot[idx]), u, hash); 190 } 191 192 void 193 add_group(const char *username, const char *gid) 194 { 195 struct user *u; 196 int g, idx; 197 198 g = atoi(gid); 199 idx = hashidx(username[0]); 200 201 for (u = hroot[idx].tqh_first; 202 u != NULL; u = u->hash.tqe_next) { 203 if (strcmp(username, u->usr_name) == 0) { 204 if (g != u->usr_gid) { 205 u->gid_count++; 206 if (u->gid_count < NGROUPS) 207 u->gid[u->gid_count] = g; 208 } 209 return; 210 } 211 } 212 } 213 214 void 215 read_passwd(const char *fname) 216 { 217 FILE *pfile; 218 size_t line_no; 219 int colon; 220 size_t len; 221 char *line, *p, *k, *u, *g; 222 223 if ((pfile = fopen(fname, "r")) == NULL) 224 err(1, "%s", fname); 225 226 line_no = 0; 227 for (; 228 (line = fparseln(pfile, &len, &line_no, NULL, FPARSELN_UNESCALL)); 229 free(line)) { 230 if (len == 0) { 231 warnx("%s line %lu: empty line", fname, 232 (unsigned long)line_no); 233 continue; 234 } 235 236 p = line; 237 for (k = p, colon = 0; *k != '\0'; k++) 238 if (*k == ':') 239 colon++; 240 241 if (colon != 6) { 242 warnx("%s line %lu: incorrect number of fields", 243 fname, (unsigned long)line_no); 244 continue; 245 } 246 247 k = p; 248 p = strchr(p, ':'); 249 *p++ = '\0'; 250 251 /* If it's a YP entry, skip it. */ 252 if (*k == '+' || *k == '-') 253 continue; 254 255 /* terminate password */ 256 p = strchr(p, ':'); 257 *p++ = '\0'; 258 259 /* terminate uid */ 260 u = p; 261 p = strchr(p, ':'); 262 *p++ = '\0'; 263 264 /* terminate gid */ 265 g = p; 266 p = strchr(p, ':'); 267 *p++ = '\0'; 268 269 add_user(k, u, g); 270 } 271 (void)fclose(pfile); 272 } 273 274 int 275 isgsep(char ch) 276 { 277 278 switch (ch) { 279 case ',': 280 case ' ': 281 case '\t': 282 case '\0': 283 return (1); 284 } 285 286 return (0); 287 } 288 289 void 290 read_group(const char *fname) 291 { 292 FILE *gfile; 293 size_t line_no; 294 int colon; 295 size_t len; 296 char *line, *p, *k, *u, *g; 297 298 if ((gfile = fopen(fname, "r")) == NULL) 299 err(1, "%s", fname); 300 301 line_no = 0; 302 for (; 303 (line = fparseln(gfile, &len, &line_no, NULL, FPARSELN_UNESCALL)); 304 free(line)) { 305 if (len == 0) { 306 warnx("%s line %lu: empty line", fname, 307 (unsigned long)line_no); 308 continue; 309 } 310 311 p = line; 312 for (k = p, colon = 0; *k != '\0'; k++) 313 if (*k == ':') 314 colon++; 315 316 if (colon != 3) { 317 warnx("%s line %lu: incorrect number of fields", 318 fname, (unsigned long)line_no); 319 continue; 320 } 321 322 /* terminate key */ 323 k = p; 324 p = strchr(p, ':'); 325 *p++ = '\0'; 326 327 if (*k == '+' || *k == '-') 328 continue; 329 330 /* terminate password */ 331 p = strchr(p, ':'); 332 *p++ = '\0'; 333 334 /* terminate gid */ 335 g = p; 336 p = strchr(p, ':'); 337 *p++ = '\0'; 338 339 /* get the group list */ 340 for (u = p; *u != '\0'; u = p) { 341 /* find separator */ 342 for (; isgsep(*p) == 0; p++) 343 ; 344 345 if (*p != '\0') { 346 *p = '\0'; 347 if (u != p) 348 add_group(u, g); 349 p++; 350 } else if (u != p) 351 add_group(u, g); 352 } 353 } 354 (void)fclose(gfile); 355 } 356 357 void 358 print_passwd_group(int qflag, const char *domain) 359 { 360 struct user *u, *p; 361 int i; 362 363 for (u = root.tqh_first; u != NULL; u = u->read.tqe_next) { 364 for (p = root.tqh_first; p->usr_uid != u->usr_uid; 365 p = p->read.tqe_next) 366 /* empty */ ; 367 if (p != u) { 368 if (!qflag) { 369 warnx("unix.%d@%s %s", u->usr_uid, domain, 370 "multiply defined, ignoring duplicate"); 371 } 372 } else { 373 printf("unix.%d@%s %d:%d", u->usr_uid, domain, 374 u->usr_uid, u->usr_gid); 375 if (u->gid_count >= 0) 376 for (i = 0; i <= u->gid_count; i++) 377 printf(",%d", u->gid[i]); 378 printf("\n"); 379 } 380 } 381 } 382 383 void 384 print_hosts(const char *fname, const char *domain) 385 { 386 FILE *hfile; 387 size_t len; 388 char *line, *p, *k, *u; 389 390 if ((hfile = fopen(fname, "r")) == NULL) 391 err(1, "%s", fname); 392 393 for (; 394 (line = fparseln(hfile, &len, NULL, NULL, FPARSELN_UNESCALL)); 395 free(line)) { 396 if (len == 0) 397 continue; 398 399 p = line; 400 /* Find the key, replace trailing whitespace will <NUL> */ 401 for (k = p; *p && isspace((unsigned char)*p) == 0; p++) 402 ; 403 while (*p && isspace((unsigned char)*p)) 404 *p++ = '\0'; 405 406 /* Get first hostname. */ 407 for (u = p; *p && !isspace((unsigned char)*p); p++) 408 ; 409 *p = '\0'; 410 411 printf("unix.%s@%s 0:%s\n", u, domain, u); 412 } 413 (void) fclose(hfile); 414 } 415 416 void 417 print_netid(const char *fname) 418 { 419 FILE *mfile; 420 size_t len; 421 char *line, *p, *k, *u; 422 423 mfile = fopen(fname, "r"); 424 if (mfile == NULL) 425 return; 426 427 for (; 428 (line = fparseln(mfile, &len, NULL, NULL, FPARSELN_UNESCALL)); 429 free(line)) { 430 if (len == 0) 431 continue; 432 433 p = line; 434 /* Find the key, replace trailing whitespace will <NUL> */ 435 for (k = p; *p && !isspace((unsigned char)*p); p++) 436 ; 437 while (*p && isspace((unsigned char)*p)) 438 *p++ = '\0'; 439 440 /* Get netid entry. */ 441 for (u = p; *p && !isspace((unsigned char)*p); p++) 442 ; 443 *p = '\0'; 444 445 printf("%s %s\n", k, u); 446 } 447 } 448 449 void 450 usage(void) 451 { 452 453 fprintf(stderr, "usage: %s %s\n", getprogname(), 454 "[-d domain] [-q] [-p passwdfile] [-g groupfile]"); 455 fprintf(stderr, " %s %s", getprogname(), 456 "[-g groupfile] [-h hostfile] [-m netidfile]"); 457 exit(1); 458 } 459