X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/de29b7596f69e6f1e9cb329d415554fe9d64c32f..refs/tags/BSD-4_3_Net_2:/usr/src/usr.bin/chpass/chpass.c diff --git a/usr/src/usr.bin/chpass/chpass.c b/usr/src/usr.bin/chpass/chpass.c index ed7223cf63..4cf13e2032 100644 --- a/usr/src/usr.bin/chpass/chpass.c +++ b/usr/src/usr.bin/chpass/chpass.c @@ -1,18 +1,34 @@ -/* +/*- * 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 @@ -22,347 +38,148 @@ char copyright[] = #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 -#include #include #include #include #include -#include +#include +#include #include #include #include -#include -#include - -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 +#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); }