1 /*- 2 * Copyright (c) 1991, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Portions Copyright(C) 1994, Jason Downs. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by the University of 17 * California, Berkeley and its contributors. 18 * 4. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 __COPYRIGHT("@(#) Copyright (c) 1991, 1993, 1994\n\ 38 The Regents of the University of California. All rights reserved.\n"); 39 #endif /* not lint */ 40 41 #ifndef lint 42 #if 0 43 static char sccsid[] = "from: @(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94"; 44 #else 45 __RCSID("$NetBSD: pwd_mkdb.c,v 1.10 1997/10/17 12:18:22 lukem Exp $"); 46 #endif 47 #endif /* not lint */ 48 49 #include <sys/param.h> 50 #include <sys/stat.h> 51 52 #include <db.h> 53 #include <err.h> 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <limits.h> 57 #include <pwd.h> 58 #include <signal.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <unistd.h> 63 #include <util.h> 64 65 #define INSECURE 1 66 #define SECURE 2 67 #define PERM_INSECURE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 68 #define PERM_SECURE (S_IRUSR|S_IWUSR) 69 70 /* pull this out of the C library. */ 71 extern const char __yp_token[]; 72 73 HASHINFO openinfo = { 74 4096, /* bsize */ 75 32, /* ffactor */ 76 256, /* nelem */ 77 2048 * 1024, /* cachesize */ 78 NULL, /* hash() */ 79 0 /* lorder */ 80 }; 81 82 static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean; 83 static struct passwd pwd; /* password structure */ 84 static char *pname; /* password file name */ 85 static char prefix[MAXPATHLEN]; 86 87 void cleanup __P((void)); 88 void error __P((char *)); 89 int main __P((int, char **)); 90 void mv __P((char *, char *)); 91 int scan __P((FILE *, struct passwd *, int *)); 92 void usage __P((void)); 93 94 int 95 main(argc, argv) 96 int argc; 97 char *argv[]; 98 { 99 DB *dp, *edp; 100 DBT data, key; 101 FILE *fp, *oldfp; 102 sigset_t set; 103 int ch, cnt, len, makeold, tfd, flags; 104 char *p, *t; 105 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], buf2[MAXPATHLEN], tbuf[1024]; 106 int hasyp = 0; 107 DBT ypdata, ypkey; 108 109 oldfp = NULL; 110 strcpy(prefix, "/"); 111 makeold = 0; 112 while ((ch = getopt(argc, argv, "d:pv")) != -1) 113 switch(ch) { 114 case 'd': 115 strncpy(prefix, optarg, sizeof(prefix)); 116 prefix[sizeof(prefix)-1] = '\0'; 117 break; 118 case 'p': /* create V7 "file.orig" */ 119 makeold = 1; 120 break; 121 case 'v': /* backward compatible */ 122 break; 123 case '?': 124 default: 125 usage(); 126 } 127 argc -= optind; 128 argv += optind; 129 130 if (argc != 1) 131 usage(); 132 133 /* 134 * This could be changed to allow the user to interrupt. 135 * Probably not worth the effort. 136 */ 137 sigemptyset(&set); 138 sigaddset(&set, SIGTSTP); 139 sigaddset(&set, SIGHUP); 140 sigaddset(&set, SIGINT); 141 sigaddset(&set, SIGQUIT); 142 sigaddset(&set, SIGTERM); 143 (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL); 144 145 /* We don't care what the user wants. */ 146 (void)umask(0); 147 148 pname = *argv; 149 /* Open the original password file */ 150 if (!(fp = fopen(pname, "r"))) 151 error(pname); 152 153 /* Open the temporary insecure password database. */ 154 (void)snprintf(buf, sizeof(buf), "%s%s.tmp", prefix, _PATH_MP_DB); 155 dp = dbopen(buf, 156 O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo); 157 if (dp == NULL) 158 error(buf); 159 clean = FILE_INSECURE; 160 161 /* 162 * Open file for old password file. Minor trickiness -- don't want to 163 * chance the file already existing, since someone (stupidly) might 164 * still be using this for permission checking. So, open it first and 165 * fdopen the resulting fd. The resulting file should be readable by 166 * everyone. 167 */ 168 if (makeold) { 169 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 170 if ((tfd = open(buf, 171 O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0) 172 error(buf); 173 if ((oldfp = fdopen(tfd, "w")) == NULL) 174 error(buf); 175 clean = FILE_ORIG; 176 } 177 178 /* 179 * The databases actually contain three copies of the original data. 180 * Each password file entry is converted into a rough approximation 181 * of a ``struct passwd'', with the strings placed inline. This 182 * object is then stored as the data for three separate keys. The 183 * first key * is the pw_name field prepended by the _PW_KEYBYNAME 184 * character. The second key is the pw_uid field prepended by the 185 * _PW_KEYBYUID character. The third key is the line number in the 186 * original file prepended by the _PW_KEYBYNUM character. (The special 187 * characters are prepended to ensure that the keys do not collide.) 188 * 189 * If we see something go by that looks like YP, we save a special 190 * pointer record, which if YP is enabled in the C lib, will speed 191 * things up. 192 */ 193 data.data = (u_char *)buf; 194 key.data = (u_char *)tbuf; 195 for (cnt = 1; scan(fp, &pwd, &flags); ++cnt) { 196 #define COMPACT(e) t = e; while ((*p++ = *t++)); 197 198 /* look like YP? */ 199 if((pwd.pw_name[0] == '+') || (pwd.pw_name[0] == '-')) 200 hasyp++; 201 202 /* 203 * Warn about potentially unsafe uid/gid overrides. 204 */ 205 if (pwd.pw_name[0] == '+') { 206 if ((flags & _PASSWORD_NOUID) == 0 && pwd.pw_uid == 0) 207 warnx("line %d: superuser override in YP inclusion", cnt); 208 if ((flags & _PASSWORD_NOGID) == 0 && pwd.pw_gid == 0) 209 warnx("line %d: wheel override in YP inclusion", cnt); 210 } 211 212 /* Create insecure data. */ 213 p = buf; 214 COMPACT(pwd.pw_name); 215 COMPACT("*"); 216 memmove(p, &pwd.pw_uid, sizeof(int)); 217 p += sizeof(int); 218 memmove(p, &pwd.pw_gid, sizeof(int)); 219 p += sizeof(int); 220 memmove(p, &pwd.pw_change, sizeof(time_t)); 221 p += sizeof(time_t); 222 COMPACT(pwd.pw_class); 223 COMPACT(pwd.pw_gecos); 224 COMPACT(pwd.pw_dir); 225 COMPACT(pwd.pw_shell); 226 memmove(p, &pwd.pw_expire, sizeof(time_t)); 227 p += sizeof(time_t); 228 memmove(p, &flags, sizeof(int)); 229 p += sizeof(int); 230 data.size = p - buf; 231 232 /* Store insecure by name. */ 233 tbuf[0] = _PW_KEYBYNAME; 234 len = strlen(pwd.pw_name); 235 memmove(tbuf + 1, pwd.pw_name, len); 236 key.size = len + 1; 237 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1) 238 error("put"); 239 240 /* Store insecure by number. */ 241 tbuf[0] = _PW_KEYBYNUM; 242 memmove(tbuf + 1, &cnt, sizeof(cnt)); 243 key.size = sizeof(cnt) + 1; 244 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1) 245 error("put"); 246 247 /* Store insecure by uid. */ 248 tbuf[0] = _PW_KEYBYUID; 249 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid)); 250 key.size = sizeof(pwd.pw_uid) + 1; 251 if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1) 252 error("put"); 253 254 /* Create original format password file entry */ 255 if (makeold) 256 (void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n", 257 pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos, 258 pwd.pw_dir, pwd.pw_shell); 259 } 260 261 /* Store YP token, if needed. */ 262 if(hasyp) { 263 ypkey.data = (u_char *)__yp_token; 264 ypkey.size = strlen(__yp_token); 265 ypdata.data = (u_char *)NULL; 266 ypdata.size = 0; 267 268 if ((dp->put)(dp, &ypkey, &ypdata, R_NOOVERWRITE) == -1) 269 error("put"); 270 } 271 272 (void)(dp->close)(dp); 273 if (makeold) { 274 (void)fflush(oldfp); 275 (void)fclose(oldfp); 276 } 277 278 /* Open the temporary encrypted password database. */ 279 (void)snprintf(buf, sizeof(buf), "%s%s.tmp", prefix, _PATH_SMP_DB); 280 edp = dbopen(buf, 281 O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo); 282 if (!edp) 283 error(buf); 284 clean = FILE_SECURE; 285 286 rewind(fp); 287 for (cnt = 1; scan(fp, &pwd, &flags); ++cnt) { 288 289 /* Create secure data. */ 290 p = buf; 291 COMPACT(pwd.pw_name); 292 COMPACT(pwd.pw_passwd); 293 memmove(p, &pwd.pw_uid, sizeof(int)); 294 p += sizeof(int); 295 memmove(p, &pwd.pw_gid, sizeof(int)); 296 p += sizeof(int); 297 memmove(p, &pwd.pw_change, sizeof(time_t)); 298 p += sizeof(time_t); 299 COMPACT(pwd.pw_class); 300 COMPACT(pwd.pw_gecos); 301 COMPACT(pwd.pw_dir); 302 COMPACT(pwd.pw_shell); 303 memmove(p, &pwd.pw_expire, sizeof(time_t)); 304 p += sizeof(time_t); 305 memmove(p, &flags, sizeof(int)); 306 p += sizeof(int); 307 data.size = p - buf; 308 309 /* Store secure by name. */ 310 tbuf[0] = _PW_KEYBYNAME; 311 len = strlen(pwd.pw_name); 312 memmove(tbuf + 1, pwd.pw_name, len); 313 key.size = len + 1; 314 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1) 315 error("put"); 316 317 /* Store secure by number. */ 318 tbuf[0] = _PW_KEYBYNUM; 319 memmove(tbuf + 1, &cnt, sizeof(cnt)); 320 key.size = sizeof(cnt) + 1; 321 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1) 322 error("put"); 323 324 /* Store secure by uid. */ 325 tbuf[0] = _PW_KEYBYUID; 326 memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid)); 327 key.size = sizeof(pwd.pw_uid) + 1; 328 if ((dp->put)(edp, &key, &data, R_NOOVERWRITE) == -1) 329 error("put"); 330 } 331 332 /* Store YP token, if needed. */ 333 if(hasyp) { 334 ypkey.data = (u_char *)__yp_token; 335 ypkey.size = strlen(__yp_token); 336 ypdata.data = (u_char *)NULL; 337 ypdata.size = 0; 338 339 if((dp->put)(edp, &ypkey, &ypdata, R_NOOVERWRITE) == -1) 340 error("put"); 341 } 342 343 (void)(edp->close)(edp); 344 345 /* Set master.passwd permissions, in case caller forgot. */ 346 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR); 347 (void)fclose(fp); 348 349 /* Install as the real password files. */ 350 (void)snprintf(buf, sizeof(buf), "%s%s.tmp", prefix, _PATH_MP_DB); 351 (void)snprintf(buf2, sizeof(buf2), "%s%s", prefix, _PATH_MP_DB); 352 mv(buf, buf2); 353 (void)snprintf(buf, sizeof(buf), "%s%s.tmp", prefix, _PATH_SMP_DB); 354 (void)snprintf(buf2, sizeof(buf2), "%s%s", prefix, _PATH_SMP_DB); 355 mv(buf, buf2); 356 if (makeold) { 357 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 358 (void)snprintf(buf2, sizeof(buf2), "%s%s", prefix, 359 _PATH_PASSWD); 360 mv(buf, buf2); 361 } 362 /* 363 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8) 364 * all use flock(2) on it to block other incarnations of themselves. 365 * The rename means that everything is unlocked, as the original file 366 * can no longer be accessed. 367 */ 368 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, _PATH_MASTERPASSWD); 369 mv(pname, buf); 370 exit(0); 371 } 372 373 int 374 scan(fp, pw, flags) 375 FILE *fp; 376 struct passwd *pw; 377 int *flags; 378 { 379 static int lcnt; 380 static char line[LINE_MAX]; 381 char *p; 382 383 if (!fgets(line, sizeof(line), fp)) 384 return (0); 385 ++lcnt; 386 /* 387 * ``... if I swallow anything evil, put your fingers down my 388 * throat...'' 389 * -- The Who 390 */ 391 if (!(p = strchr(line, '\n'))) { 392 warnx("line too long"); 393 goto fmt; 394 395 } 396 *p = '\0'; 397 if (!pw_scan(line, pw, flags)) { 398 warnx("at line #%d", lcnt); 399 fmt: errno = EFTYPE; /* XXX */ 400 error(pname); 401 } 402 403 return (1); 404 } 405 406 void 407 mv(from, to) 408 char *from, *to; 409 { 410 char buf[MAXPATHLEN]; 411 412 if (rename(from, to)) { 413 int sverrno = errno; 414 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); 415 errno = sverrno; 416 error(buf); 417 } 418 } 419 420 void 421 error(name) 422 char *name; 423 { 424 425 warn(name); 426 cleanup(); 427 exit(1); 428 } 429 430 void 431 cleanup() 432 { 433 char buf[MAXPATHLEN]; 434 435 switch(clean) { 436 case FILE_ORIG: 437 (void)snprintf(buf, sizeof(buf), "%s.orig", pname); 438 (void)unlink(buf); 439 /* FALLTHROUGH */ 440 case FILE_SECURE: 441 (void)snprintf(buf, sizeof(buf), "%s%s.tmp", prefix, 442 _PATH_SMP_DB); 443 (void)unlink(buf); 444 /* FALLTHROUGH */ 445 case FILE_INSECURE: 446 (void)snprintf(buf, sizeof(buf), "%s%s.tmp", prefix, 447 _PATH_MP_DB); 448 (void)unlink(buf); 449 } 450 } 451 452 void 453 usage() 454 { 455 456 (void)fprintf(stderr, "usage: pwd_mkdb [-p] [-d directory] file\n"); 457 exit(1); 458 } 459