-/*
+/*-
* Copyright (c) 1988 The Regents of the University of California.
* All rights reserved.
*
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
*/
#ifndef lint
#endif /* not lint */
#ifndef lint
-static char sccsid[] = "@(#)chpass.c 5.2 (Berkeley) %G%";
+static char sccsid[] = "@(#)chpass.c 5.17 (Berkeley) 3/3/91";
#endif /* not lint */
#include <sys/param.h>
-#include <sys/file.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <sys/time.h>
#include <sys/resource.h>
-#include </usr/src/include/pwd.h>
+#include <fcntl.h>
+#include <pwd.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
-#include <chpass.h>
-#include <strings.h>
-
-char e1[] = ": ";
-char e2[] = ":,";
-
-int p_login(), p_uid(), p_gid(), p_hdir(), p_shell(), p_change(), p_class();
-int p_expire(), p_save();
-
-struct entry list[] = {
- { "Login", p_login, 1, 5, e1, },
- { "Uid", p_uid, 1, 3, e1, },
- { "Gid", p_gid, 1, 3, e1, },
- { "Class", p_class, 1, 5, e1, },
- { "Change", p_change, 1, 6, NULL, },
- { "Expire", p_expire, 1, 6, NULL, },
-#define E_NAME 6
- { "Full Name", p_save, 0, 9, e2, },
-#define E_BPHONE 7
- { "Office Phone", p_save, 0, 12, e2, },
-#define E_HPHONE 8
- { "Home Phone", p_save, 0, 10, e2, },
-#define E_LOCATE 9
- { "Location", p_save, 0, 8, e2, },
- { "Home directory", p_hdir, 1, 14, e1, },
- { "Shell", p_shell, 0, 5, e1, },
- { NULL, 0, },
-};
+#include <string.h>
+#include "chpass.h"
+#include "pathnames.h"
-uid_t euid, uid;
+char *progname = "chpass";
+char *tempname;
+uid_t uid;
main(argc, argv)
int argc;
char **argv;
{
- extern int errno;
- register char *p;
- struct passwd *pw;
- struct rlimit rlim;
- FILE *temp_fp;
- int fd;
- char *fend, *passwd, *temp, *tend, buf[1024];
- char from[MAXPATHLEN], to[MAXPATHLEN];
- char *getusershell();
-
- euid = geteuid();
- uid = getuid();
- switch(--argc) {
- case 0:
- if (!(pw = getpwuid(uid))) {
- fprintf(stderr, "chpass: unknown user: uid %u\n", uid);
- exit(1);
+ extern int optind;
+ extern char *optarg;
+ register enum { NEWSH, LOADENTRY, EDITENTRY } op;
+ register struct passwd *pw;
+ struct passwd lpw;
+ int ch, pfd, tfd;
+ char *arg;
+
+ op = EDITENTRY;
+ while ((ch = getopt(argc, argv, "a:s:")) != EOF)
+ switch(ch) {
+ case 'a':
+ op = LOADENTRY;
+ arg = optarg;
+ break;
+ case 's':
+ op = NEWSH;
+ arg = optarg;
+ break;
+ case '?':
+ default:
+ usage();
}
- break;
- case 1:
- if (!(pw = getpwnam(argv[1]))) {
- fprintf(stderr, "chpass: unknown user %s.\n", argv[1]);
- exit(1);
- }
- if (uid && uid != pw->pw_uid) {
- fprintf(stderr, "chpass: %s\n", strerror(EACCES));
- exit(1);
- }
- break;
- default:
- fprintf(stderr, "usage: chpass [user]\n");
- exit(1);
- }
+ argc -= optind;
+ argv += optind;
- (void)signal(SIGHUP, SIG_IGN);
- (void)signal(SIGINT, SIG_IGN);
- (void)signal(SIGQUIT, SIG_IGN);
- (void)signal(SIGTSTP, SIG_IGN);
-
- rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
- (void)setrlimit(RLIMIT_CPU, &rlim);
- (void)setrlimit(RLIMIT_FSIZE, &rlim);
-
- (void)umask(0);
+ uid = getuid();
- temp = _PATH_PTMP;
- if ((fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
- if (errno == EEXIST) {
- fprintf(stderr,
- "chpass: password file busy -- try again later.\n");
- exit(1);
+ if (op == EDITENTRY || op == NEWSH)
+ switch(argc) {
+ case 0:
+ if (!(pw = getpwuid(uid))) {
+ (void)fprintf(stderr,
+ "chpass: unknown user: uid %u\n", uid);
+ exit(1);
+ }
+ break;
+ case 1:
+ if (!(pw = getpwnam(*argv))) {
+ (void)fprintf(stderr,
+ "chpass: unknown user %s.\n", *argv);
+ exit(1);
+ }
+ if (uid && uid != pw->pw_uid)
+ baduser();
+ break;
+ default:
+ usage();
}
- fprintf(stderr, "chpass: %s: %s", temp, strerror(errno));
- goto bad;
- }
- if (!(temp_fp = fdopen(fd, "w"))) {
- fprintf(stderr, "chpass: can't write %s", temp);
- goto bad;
- }
-
- if (!info(pw))
- goto bad;
- /*
- * special checks...
- *
- * there has to be a limit on the size of the gecos fields,
- * otherwise getpwent(3) can choke.
- * ``if I swallow anything evil, put your fingers down my throat...''
- * -- The Who
- */
- if (strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) +
- strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save)
- > 512) {
- fprintf(stderr, "chpass: gecos field too large.\n");
- exit(1);
+ if (op == NEWSH) {
+ /* protect p_shell -- it thinks NULL is /bin/sh */
+ if (!arg[0])
+ usage();
+ if (p_shell(arg, pw, (ENTRY *)NULL))
+ pw_error((char *)NULL, 0, 1);
}
- (void)sprintf(pw->pw_gecos = buf, "%s,%s,%s,%s",
- list[E_NAME].save, list[E_LOCATE].save, list[E_BPHONE].save,
- list[E_HPHONE].save);
- /* root should have a 0 uid and a reasonable shell */
- if (!strcmp(pw->pw_name, "root")) {
- if (pw->pw_uid) {
- fprintf(stderr, "chpass: root uid should be 0.");
+ if (op == LOADENTRY) {
+ if (uid)
+ baduser();
+ pw = &lpw;
+ if (!pw_scan(arg, pw))
exit(1);
- }
- setusershell();
- for (;;)
- if (!(p = getusershell())) {
- fprintf(stderr,
- "chpass: warning, unknown root shell.");
- break;
- }
- else if (!strcmp(pw->pw_shell, p))
- break;
- }
-
- passwd = _PATH_MASTERPASSWD;
- if (!freopen(passwd, "r", stdin)) {
- fprintf(stderr, "chpass: can't read %s", passwd);
- goto bad;
- }
- if (!copy(pw, temp_fp))
- goto bad;
-
- (void)fclose(temp_fp);
- (void)fclose(stdin);
-
- switch(fork()) {
- case 0:
- break;
- case -1:
- fprintf(stderr, "chpass: can't fork");
- goto bad;
- /* NOTREACHED */
- default:
- exit(0);
- /* NOTREACHED */
- }
-
- if (makedb(temp)) {
- fprintf(stderr, "chpass: mkpasswd failed");
-bad: fprintf(stderr, "; information unchanged.\n");
- (void)unlink(temp);
- exit(1);
}
/*
- * possible race; have to rename four files, and someone could slip
- * in between them. LOCK_EX and rename the ``passwd.dir'' file first
- * so that getpwent(3) can't slip in; the lock should never fail and
- * it's unclear what to do if it does. Rename ``ptmp'' last so that
- * passwd/vipw/chpass can't slip in.
+ * The temporary file/file descriptor usage is a little tricky here.
+ * 1: We start off with two fd's, one for the master password
+ * file (used to lock everything), and one for a temporary file.
+ * 2: Display() gets an fp for the temporary file, and copies the
+ * user's information into it. It then gives the temporary file
+ * to the user and closes the fp, closing the underlying fd.
+ * 3: The user edits the temporary file some number of times.
+ * 4: Verify() gets an fp for the temporary file, and verifies the
+ * contents. It can't use an fp derived from the step #2 fd,
+ * because the user's editor may have created a new instance of
+ * the file. Once the file is verified, its contents are stored
+ * in a password structure. The verify routine closes the fp,
+ * closing the underlying fd.
+ * 5: Delete the temporary file.
+ * 6: Get a new temporary file/fd. Pw_copy() gets an fp for it
+ * file and copies the master password file into it, replacing
+ * the user record with a new one. We can't use the first
+ * temporary file for this because it was owned by the user.
+ * Pw_copy() closes its fp, flushing the data and closing the
+ * underlying file descriptor. We can't close the master
+ * password fp, or we'd lose the lock.
+ * 7: Call pw_mkdb() (which renames the temporary file) and exit.
+ * The exit closes the master passwd fp/fd.
*/
- (void)setpriority(PRIO_PROCESS, 0, -20);
- fend = strcpy(from, temp) + strlen(temp);
- tend = strcpy(to, passwd) + strlen(passwd);
- bcopy(".dir", fend, 5);
- bcopy(".dir", tend, 5);
- if ((fd = open(from, O_RDONLY, 0)) >= 0)
- (void)flock(fd, LOCK_EX);
- /* here we go... */
- (void)rename(from, to);
- bcopy(".pag", fend, 5);
- bcopy(".pag", tend, 5);
- (void)rename(from, to);
- bcopy(".orig", fend, 6);
- (void)rename(from, _PATH_PASSWD);
- (void)rename(temp, passwd);
- /* done! */
- exit(0);
-}
-
-info(pw)
- struct passwd *pw;
-{
- register struct entry *ep;
- register char *p;
- struct stat begin, end;
- FILE *fp;
- int fd, rval;
- char *tfile, buf[1024], *getenv();
+ pw_init();
+ pfd = pw_lock();
+ tfd = pw_tmp();
- tfile = "/tmp/passwd.XXXXXX";
- if ((fd = mkstemp(tfile)) == -1 || !(fp = fdopen(fd, "w+"))) {
- fprintf(stderr, "chpass: no temporary file");
- return(0);
+ if (op == EDITENTRY) {
+ display(tfd, pw);
+ edit(pw);
+ (void)unlink(tempname);
+ tfd = pw_tmp();
}
+
+ pw_copy(pfd, tfd, pw);
- print(fp, pw);
- (void)fflush(fp);
-
- (void)fstat(fd, &begin);
- rval = edit(tfile);
- (void)unlink(tfile);
-
- if (rval) {
- fprintf(stderr, "chpass: edit failed");
- return(0);
- }
- (void)fstat(fd, &end);
- if (begin.st_mtime == end.st_mtime) {
- fprintf(stderr, "chpass: no changes made");
- return(0);
- }
- (void)rewind(fp);
- while (fgets(buf, sizeof(buf), fp)) {
- if (!buf[0])
- continue;
- if (!(p = index(buf, '\n'))) {
- fprintf(stderr, "chpass: line too long");
- return(0);
- }
- *p = '\0';
- for (ep = list; ep->prompt; ++ep)
- if (!strncasecmp(buf, ep->prompt, ep->len)) {
- if (ep->restricted && euid)
- continue;
- if (!(p = index(buf, ':'))) {
- fprintf(stderr,
- "chpass: line corrupted");
- return(0);
- }
- while (isspace(*++p));
- if (ep->except && strpbrk(p, ep->except)) {
- fprintf(stderr,
- "chpass: illegal character in the \"%s\" field",
- ep->prompt);
- return(0);
- }
- if ((ep->func)(p, pw, ep))
- return(0);
- break;
- }
- }
- (void)fclose(fp);
- return(1);
-}
-
-copy(pw, fp)
- struct passwd *pw;
- FILE *fp;
-{
- register int done;
- register char *p;
- char buf[1024];
-
- for (done = 0; fgets(buf, sizeof(buf), stdin);) {
- /* skip lines that are too big */
- if (!index(buf, '\n')) {
- fprintf(stderr, "chpass: line too long");
- return(0);
- }
- if (done) {
- fprintf(fp, "%s", buf);
- continue;
- }
- if (!(p = index(buf, ':'))) {
- fprintf(stderr, "chpass: corrupted entry");
- return(0);
- }
- *p = '\0';
- if (strcmp(buf, pw->pw_name)) {
- *p = ':';
- fprintf(fp, "%s", buf);
- continue;
- }
- fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
- pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
- pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
- pw->pw_dir, pw->pw_shell);
- done = 1;
- }
- if (!done)
- fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
- pw->pw_name, "NOLOGIN", pw->pw_uid, pw->pw_gid,
- pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
- pw->pw_dir, pw->pw_shell);
- return(1);
+ if (!pw_mkdb())
+ pw_error((char *)NULL, 0, 1);
+ exit(0);
}
-makedb(file)
- char *file;
+baduser()
{
- int status, pid, w;
-
- if (!(pid = vfork())) {
- execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL);
- _exit(127);
- }
- while ((w = wait(&status)) != pid && w != -1);
- return(w == -1 || status);
+ (void)fprintf(stderr, "chpass: %s\n", strerror(EACCES));
+ exit(1);
}
-edit(file)
- char *file;
+usage()
{
- int status, pid, w;
- char *p, *editor, *getenv();
-
- if (editor = getenv("EDITOR")) {
- if (p = rindex(editor, '/'))
- ++p;
- else
- p = editor;
- }
- else
- p = editor = "vi";
- if (!(pid = vfork())) {
- execlp(editor, p, file, NULL);
- _exit(127);
- }
- while ((w = wait(&status)) != pid && w != -1);
- return(w == -1 || status);
+ (void)fprintf(stderr, "usage: chpass [-a list] [-s shell] [user]\n");
+ exit(1);
}