]>
git.saurik.com Git - apple/system_cmds.git/blob - pwd_mkdb.tproj/pwd_mkdb.c
1 /* $OpenBSD: pwd_mkdb.c,v 1.36 2003/06/08 21:14:55 millert Exp $ */
4 * Copyright (c) 1991, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 * Portions Copyright (c) 1994, Jason Downs. All rights reserved.
7 * Portions Copyright (c) 1998, Todd C. Miller. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include <sys/cdefs.h>
36 __unused
static const char copyright
[] =
37 "@(#) Copyright (c) 1991, 1993, 1994\n\
38 The Regents of the University of California. All rights reserved.\n";
43 static const char sccsid
[] = "from: @(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94";
45 __unused
static const char rcsid
[] = "$OpenBSD: pwd_mkdb.c,v 1.36 2003/06/08 21:14:55 millert Exp $";
49 #include <sys/param.h>
65 #include <sys/param.h>
70 #define PERM_INSECURE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
71 #define PERM_SECURE (S_IRUSR|S_IWUSR)
73 #define FILE_SECURE 0x01
74 #define FILE_INSECURE 0x02
75 #define FILE_ORIG 0x04
77 #define SHADOW_GROUP "wheel"
83 .cachesize
= 2048 * 1024,
88 static char *pname
; /* password file name */
89 static char *basedir
; /* dir holding master.passwd */
90 static int clean
; /* what to remove on cleanup */
91 static int hasyp
; /* are we running YP? */
96 void cp(char *, char *, mode_t
);
97 void mv(char *, char *);
98 int scan(FILE *, struct passwd
*, int *);
100 char *changedir(char *path
, char *dir
);
101 void db_store(FILE *, FILE *, DB
*, DB
*,struct passwd
*, int, char *, uid_t
);
104 main(int argc
, char **argv
)
108 FILE *fp
, *oldfp
= NULL
;
113 uid_t olduid
= UID_MAX
;
115 int ch
, tfd
, makeold
, secureonly
, flags
, checkonly
;
116 char *username
, buf
[MAX(MAXPATHLEN
, LINE_MAX
* 2)];
118 flags
= checkonly
= makeold
= secureonly
= 0;
120 while ((ch
= getopt(argc
, argv
, "cd:psu:v")) != -1)
122 case 'c': /* verify only */
127 if (strlen(basedir
) > MAXPATHLEN
- 40)
128 errx(1, "basedir too long");
130 case 'p': /* create V7 "file.orig" */
133 case 's': /* only update spwd.db */
136 case 'u': /* only update this record */
138 if (strlen(username
) > _PW_NAME_LEN
)
139 errx(1, "username too long");
141 case 'v': /* backward compatible */
150 if (argc
!= 1 || (makeold
&& secureonly
) ||
151 (username
&& (*username
== '+' || *username
== '-')))
154 if ((grp
= getgrnam(SHADOW_GROUP
)) == NULL
)
155 errx(1, "cannot find `%s' in the group database, aborting",
157 shadow
= grp
->gr_gid
;
160 * This could be changed to allow the user to interrupt.
161 * Probably not worth the effort.
164 sigaddset(&set
, SIGTSTP
);
165 sigaddset(&set
, SIGHUP
);
166 sigaddset(&set
, SIGINT
);
167 sigaddset(&set
, SIGQUIT
);
168 sigaddset(&set
, SIGTERM
);
169 (void)sigprocmask(SIG_BLOCK
, &set
, (sigset_t
*)NULL
);
171 /* We don't care what the user wants. */
174 if (**argv
!= '/' && basedir
== NULL
)
175 errx(1, "%s must be specified as an absolute path", *argv
);
177 if ((pname
= strdup(changedir(*argv
, basedir
))) == NULL
)
179 /* Open the original password file */
180 if (!(fp
= fopen(pname
, "r")))
183 /* Check only if password database is valid */
187 for (cnt
= 1; scan(fp
, &pwd
, &flags
); ++cnt
)
192 if (fstat(fileno(fp
), &st
) == -1)
195 /* Tweak openinfo values for large passwd files. */
196 if (st
.st_size
> (off_t
)100*1024)
197 openinfo
.cachesize
= (u_int
)MIN(st
.st_size
* 20, (off_t
)12*1024*1024);
198 if (st
.st_size
/ 128 > openinfo
.nelem
)
199 openinfo
.nelem
= (u_int
)(st
.st_size
/ 128);
201 /* If only updating a single record, stash the old uid */
203 dp
= dbopen(_PATH_MP_DB
, O_RDONLY
, 0, DB_HASH
, NULL
);
206 buf
[0] = _PW_KEYBYNAME
;
207 strlcpy(buf
+ 1, username
, sizeof(buf
) - 1);
208 key
.data
= (u_char
*)buf
;
209 key
.size
= strlen(buf
+ 1) + 1;
210 if ((dp
->get
)(dp
, &key
, &data
, 0) == 0) {
211 char *p
= (char *)data
.data
;
212 /* Skip to uid field */
217 memcpy(&olduid
, p
, sizeof(olduid
));
223 /* Open the temporary encrypted password database. */
224 (void)snprintf(buf
, sizeof(buf
), "%s.tmp",
225 changedir(_PATH_SMP_DB
, basedir
));
227 cp(changedir(_PATH_SMP_DB
, basedir
), buf
, PERM_SECURE
);
229 O_RDWR
, PERM_SECURE
, DB_HASH
, &openinfo
);
232 O_RDWR
|O_CREAT
|O_EXCL
, PERM_SECURE
, DB_HASH
, &openinfo
);
236 if (fchown(edp
->fd(edp
), (uid_t
)-1, shadow
) != 0)
237 warn("%s: unable to set group to %s", _PATH_SMP_DB
,
239 else if (fchmod(edp
->fd(edp
), PERM_SECURE
|S_IRGRP
) != 0)
240 warn("%s: unable to make group readable", _PATH_SMP_DB
);
241 clean
|= FILE_SECURE
;
243 /* Open the temporary insecure password database. */
245 (void)snprintf(buf
, sizeof(buf
), "%s.tmp",
246 changedir(_PATH_MP_DB
, basedir
));
248 cp(changedir(_PATH_MP_DB
, basedir
), buf
, PERM_INSECURE
);
249 dp
= dbopen(buf
, O_RDWR
, PERM_INSECURE
, DB_HASH
,
252 dp
= dbopen(buf
, O_RDWR
|O_CREAT
|O_EXCL
, PERM_INSECURE
,
257 clean
|= FILE_INSECURE
;
262 * Open file for old password file. Minor trickiness -- don't want to
263 * chance the file already existing, since someone (stupidly) might
264 * still be using this for permission checking. So, open it first and
265 * fdopen the resulting fd. The resulting file should be readable by
269 (void)snprintf(buf
, sizeof(buf
), "%s.orig", pname
);
271 O_WRONLY
|O_CREAT
|O_EXCL
, PERM_INSECURE
)) < 0)
273 if ((oldfp
= fdopen(tfd
, "w")) == NULL
)
279 * The databases actually contain three copies of the original data.
280 * Each password file entry is converted into a rough approximation
281 * of a ``struct passwd'', with the strings placed inline. This
282 * object is then stored as the data for three separate keys. The
283 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
284 * character. The second key is the pw_uid field prepended by the
285 * _PW_KEYBYUID character. The third key is the line number in the
286 * original file prepended by the _PW_KEYBYNUM character. (The special
287 * characters are prepended to ensure that the keys do not collide.)
289 * If we see something go by that looks like YP, we save a special
290 * pointer record, which if YP is enabled in the C lib, will speed
295 * Write the .db files.
296 * We do this three times, one per key type (for getpw{nam,uid,ent}).
297 * The first time through we also check for YP, issue warnings
298 * and save the V7 format passwd file if necessary.
300 db_store(fp
, oldfp
, edp
, dp
, &pwd
, _PW_KEYBYNAME
, username
, olduid
);
301 db_store(fp
, oldfp
, edp
, dp
, &pwd
, _PW_KEYBYUID
, username
, olduid
);
302 db_store(fp
, oldfp
, edp
, dp
, &pwd
, _PW_KEYBYNUM
, username
, olduid
);
304 /* Store YP token, if needed. */
305 if (hasyp
&& !username
) {
306 key
.data
= (u_char
*)_PW_YPTOKEN
;
307 key
.size
= strlen(_PW_YPTOKEN
);
308 data
.data
= (u_char
*)NULL
;
311 if ((edp
->put
)(edp
, &key
, &data
, R_NOOVERWRITE
) == -1)
314 if (dp
&& (dp
->put
)(dp
, &key
, &data
, R_NOOVERWRITE
) == -1)
318 if ((edp
->close
)(edp
))
320 if (dp
&& (dp
->close
)(dp
))
323 if (fclose(oldfp
) == EOF
)
327 /* Set master.passwd permissions, in case caller forgot. */
328 (void)fchmod(fileno(fp
), S_IRUSR
|S_IWUSR
);
332 /* Install as the real password files. */
334 (void)snprintf(buf
, sizeof(buf
), "%s.tmp",
335 changedir(_PATH_MP_DB
, basedir
));
336 mv(buf
, changedir(_PATH_MP_DB
, basedir
));
338 (void)snprintf(buf
, sizeof(buf
), "%s.tmp",
339 changedir(_PATH_SMP_DB
, basedir
));
340 mv(buf
, changedir(_PATH_SMP_DB
, basedir
));
342 (void)snprintf(buf
, sizeof(buf
), "%s.orig", pname
);
343 mv(buf
, changedir(_PATH_PASSWD
, basedir
));
347 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
348 * all use flock(2) on it to block other incarnations of themselves.
349 * The rename means that everything is unlocked, as the original file
350 * can no longer be accessed.
352 mv(pname
, changedir(_PATH_MASTERPASSWD
, basedir
));
357 scan(FILE *fp
, struct passwd
*pw
, int *flags
)
360 static char line
[LINE_MAX
];
363 if (fgets(line
, sizeof(line
), fp
) == NULL
)
367 * ``... if I swallow anything evil, put your fingers down my
372 if (*p
!= '\0' && *(p
+= strlen(line
) - 1) != '\n') {
373 warnx("line too long");
378 if (!pw_scan(line
, pw
, flags
)) {
379 warnx("at line #%d", lcnt
);
380 fmt
: errno
= EFTYPE
; /* XXX */
388 cp(char *from
, char *to
, mode_t mode
)
390 static char buf
[MAXBSIZE
];
392 ssize_t rcount
, wcount
;
394 if ((from_fd
= open(from
, O_RDONLY
, 0)) < 0)
396 if ((to_fd
= open(to
, O_WRONLY
|O_CREAT
|O_EXCL
, mode
)) < 0)
398 while ((rcount
= read(from_fd
, buf
, MAXBSIZE
)) > 0) {
399 wcount
= write(to_fd
, buf
, rcount
);
400 if (rcount
!= wcount
|| wcount
== -1) {
403 (void)snprintf(buf
, sizeof(buf
), "%s to %s", from
, to
);
411 (void)snprintf(buf
, sizeof(buf
), "%s to %s", from
, to
);
418 mv(char *from
, char *to
)
420 char buf
[MAXPATHLEN
* 2];
422 if (rename(from
, to
)) {
425 (void)snprintf(buf
, sizeof(buf
), "%s to %s", from
, to
);
450 char buf
[MAXPATHLEN
];
452 if (clean
& FILE_ORIG
) {
453 (void)snprintf(buf
, sizeof(buf
), "%s.orig", pname
);
456 if (clean
& FILE_SECURE
) {
457 (void)snprintf(buf
, sizeof(buf
), "%s.tmp",
458 changedir(_PATH_SMP_DB
, basedir
));
461 if (clean
& FILE_INSECURE
) {
462 (void)snprintf(buf
, sizeof(buf
), "%s.tmp",
463 changedir(_PATH_MP_DB
, basedir
));
471 (void)fprintf(stderr
,
472 "usage: pwd_mkdb [-c] [-p | -s] [-d basedir] [-u username] file\n");
477 changedir(char *path
, char *dir
)
479 static char fixed
[MAXPATHLEN
];
485 if ((p
= strrchr(path
, '/')) != NULL
)
487 snprintf(fixed
, sizeof(fixed
), "%s/%s", dir
, path
);
492 db_store(FILE *fp
, FILE *oldfp
, DB
*edp
, DB
*dp
, struct passwd
*pw
,
493 int keytype
, char *username
, uid_t olduid
)
496 int dbmode
, found
= 0;
498 char *p
, *t
, buf
[LINE_MAX
* 2], tbuf
[1024];
501 static int firsttime
= 1;
503 /* If given a username just add that record to the existing db. */
504 dbmode
= username
? 0 : R_NOOVERWRITE
;
507 data
.data
= (u_char
*)buf
;
508 key
.data
= (u_char
*)tbuf
;
509 for (cnt
= 1; scan(fp
, pw
, &flags
); ++cnt
) {
512 if (pw
->pw_name
== NULL
)
518 if ((pw
->pw_name
[0] == '+') || (pw
->pw_name
[0] == '-'))
521 /* Warn about potentially unsafe uid/gid overrides. */
522 if (pw
->pw_name
[0] == '+') {
523 if (!(flags
& _PASSWORD_NOUID
) && !pw
->pw_uid
)
524 warnx("line %d: superuser override in "
525 "YP inclusion", cnt
);
526 if (!(flags
& _PASSWORD_NOGID
) && !pw
->pw_gid
)
527 warnx("line %d: wheel override in "
528 "YP inclusion", cnt
);
531 /* Create V7 format password file entry. */
533 if (fprintf(oldfp
, "%s:*:%u:%u:%s:%s:%s\n",
534 pw
->pw_name
, pw
->pw_uid
, pw
->pw_gid
,
535 pw
->pw_gecos
, pw
->pw_dir
, pw
->pw_shell
)
540 /* Are we updating a specific record? */
542 if (strcmp(username
, pw
->pw_name
) != 0)
545 /* If the uid changed, remove the old record by uid. */
546 if (olduid
!= UID_MAX
&& olduid
!= pw
->pw_uid
) {
547 tbuf
[0] = _PW_KEYBYUID
;
548 memcpy(tbuf
+ 1, &olduid
, sizeof(olduid
));
549 key
.size
= sizeof(olduid
) + 1;
550 (edp
->del
)(edp
, &key
, 0);
552 (dp
->del
)(dp
, &key
, 0);
554 /* XXX - should check to see if line number changed. */
561 memmove(tbuf
+ 1, &cnt
, sizeof(cnt
));
562 key
.size
= sizeof(cnt
) + 1;
566 len
= strlen(pw
->pw_name
);
567 memmove(tbuf
+ 1, pw
->pw_name
, len
);
572 memmove(tbuf
+ 1, &pw
->pw_uid
, sizeof(pw
->pw_uid
));
573 key
.size
= sizeof(pw
->pw_uid
) + 1;
577 #define COMPACT(e) t = e; while ((*p++ = *t++));
578 /* Create the secure record. */
580 COMPACT(pw
->pw_name
);
581 COMPACT(pw
->pw_passwd
);
582 memmove(p
, &pw
->pw_uid
, sizeof(uid_t
));
584 memmove(p
, &pw
->pw_gid
, sizeof(gid_t
));
586 memmove(p
, &pw
->pw_change
, sizeof(time_t));
588 COMPACT(pw
->pw_class
);
589 COMPACT(pw
->pw_gecos
);
591 COMPACT(pw
->pw_shell
);
592 memmove(p
, &pw
->pw_expire
, sizeof(time_t));
594 memmove(p
, &flags
, sizeof(int));
598 /* Write the secure record. */
599 if ((edp
->put
)(edp
, &key
, &data
, dbmode
) == -1)
605 /* Star out password to make insecure record. */
606 p
= buf
+ strlen(pw
->pw_name
) + 1; /* skip pw_name */
607 len
= strlen(pw
->pw_passwd
);
608 memset(p
, 0, len
); /* zero pw_passwd */
609 t
= p
+ len
+ 1; /* skip pw_passwd */
613 memmove(p
, t
, data
.size
- (t
- buf
));
614 data
.size
-= len
- 1;
616 /* Write the insecure record. */
617 if ((dp
->put
)(dp
, &key
, &data
, dbmode
) == -1)
622 if (username
&& !found
&& olduid
!= UID_MAX
)
623 errorx("can't find user in master.passwd");