* Copyright (c) 1988 The Regents of the University of California.
* 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.
"@(#) Copyright (c) 1988 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)chpass.c 5.13 (Berkeley) %G%";
#include <sys/resource.h>
int p_change(), p_class(), p_expire(), p_gecos(), p_gid(), p_hdir();
int p_login(), p_passwd(), p_shell(), p_uid();
{ "Login", p_login
, 1, 5, e1
, },
{ "Password", p_passwd
, 1, 8, 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
, },
{ "Full Name", p_gecos
, 0, 9, e2
, },
{ "Office Phone", p_gecos
, 0, 12, e2
, },
{ "Home Phone", p_gecos
, 0, 10, e2
, },
{ "Location", p_gecos
, 0, 8, e2
, },
{ "Home directory", p_hdir
, 1, 14, e1
, },
{ "Shell", p_shell
, 0, 5, e1
, },
extern int errno
, optind
;
char *fend
, *newsh
, *passwd
, *temp
, *tend
;
char from
[MAXPATHLEN
], to
[MAXPATHLEN
];
while ((ch
= getopt(argc
, argv
, "a:s:")) != EOF
)
loadpw(optarg
, pw
= &lpw
);
/* protect p_field -- it thinks NULL is /bin/sh */
if (!(pw
= getpwuid(uid
))) {
"chpass: unknown user: uid %u\n", uid
);
if (!(pw
= getpwnam(*argv
))) {
"chpass: unknown user %s.\n", *argv
);
if (uid
&& uid
!= pw
->pw_uid
)
(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
);
if ((fd
= open(temp
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600)) < 0) {
"chpass: password file busy -- try again later.\n");
(void)fprintf(stderr
, "chpass: %s: %s; ",
if (!(temp_fp
= fdopen(fd
, "w"))) {
(void)fprintf(stderr
, "chpass: can't write %s; ", temp
);
if (p_shell(newsh
, pw
, (struct entry
*)NULL
))
else if (!aflag
&& !info(pw
))
/* root should have a 0 uid and a reasonable shell */
if (!strcmp(pw
->pw_name
, "root")) {
(void)fprintf(stderr
, "chpass: root uid should be 0.");
if (!(p
= getusershell())) {
"chpass: warning, unknown root shell.");
else if (!strcmp(pw
->pw_shell
, p
))
passwd
= _PATH_MASTERPASSWD
;
if (!freopen(passwd
, "r", stdin
)) {
(void)fprintf(stderr
, "chpass: can't read %s; ", passwd
);
(void)fprintf(stderr
, "chpass: can't fork; ");
(void)fprintf(stderr
, "chpass: mkpasswd failed; ");
bad
: (void)fprintf(stderr
, "%s unchanged.\n", _PATH_MASTERPASSWD
);
* 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.
(void)setpriority(PRIO_PROCESS
, 0, -20);
fend
= strcpy(from
, temp
) + strlen(temp
);
tend
= strcpy(to
, _PATH_PASSWD
) + strlen(_PATH_PASSWD
);
if ((fd
= open(from
, O_RDONLY
, 0)) >= 0)
(void)flock(fd
, LOCK_EX
);
(void)rename(from
, _PATH_PASSWD
);
(void)rename(temp
, passwd
);
if ((fd
= mkstemp(tfile
)) == -1 || !(fp
= fdopen(fd
, "w+"))) {
(void)fprintf(stderr
, "chpass: no temporary file");
* if print doesn't print out a shell field, make it restricted.
* Not particularly pretty, but print is the routine that checks
* to see if the user can change their shell.
list
[E_SHELL
].restricted
= 1;
* give the file to the real user; setuid permissions
* are discarded in edit()
(void)fchown(fd
, getuid(), getgid());
(void)fprintf(stderr
, "chpass: edit failed; ");
if (begin
.st_mtime
== end
.st_mtime
) {
(void)fprintf(stderr
, "chpass: no changes made; ");
register struct entry
*ep
;
while (fgets(buf
, sizeof(buf
), fp
)) {
if (!buf
[0] || buf
[0] == '#')
if (!(p
= index(buf
, '\n'))) {
(void)fprintf(stderr
, "chpass: line too long.\n");
"chpass: unrecognized field.\n");
if (!strncasecmp(buf
, ep
->prompt
, ep
->len
)) {
if (ep
->restricted
&& uid
) {
"chpass: you may not change the %s field.\n",
if (!(p
= index(buf
, ':'))) {
"chpass: line corrupted.\n");
if (ep
->except
&& strpbrk(p
, ep
->except
)) {
"chpass: illegal character in the \"%s\" field.\n",
if ((ep
->func
)(p
, pw
, ep
))
* 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...''
if (strlen(list
[E_NAME
].save
) + strlen(list
[E_BPHONE
].save
) +
strlen(list
[E_HPHONE
].save
) + strlen(list
[E_LOCATE
].save
)
(void)fprintf(stderr
, "chpass: gecos field too large.\n");
(void)sprintf(pw
->pw_gecos
= buf
, "%s,%s,%s,%s",
list
[E_NAME
].save
, list
[E_LOCATE
].save
, list
[E_BPHONE
].save
,
for (done
= 0; fgets(buf
, sizeof(buf
), stdin
);) {
/* skip lines that are too big */
(void)fprintf(stderr
, "chpass: line too long; ");
(void)fprintf(fp
, "%s", buf
);
if (!(p
= index(buf
, ':'))) {
(void)fprintf(stderr
, "chpass: corrupted entry; ");
if (strcmp(buf
, pw
->pw_name
)) {
(void)fprintf(fp
, "%s", buf
);
(void)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
);
(void)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
);
execl(_PATH_MKPASSWD
, "mkpasswd", "-p", file
, NULL
);
while ((w
= wait(&status
)) != pid
&& w
!= -1);
return(w
== -1 || status
);
char *p
, *editor
, *getenv();
if (editor
= getenv("EDITOR")) {
if (p
= rindex(editor
, '/'))
execlp(editor
, p
, file
, NULL
);
while ((w
= wait(&status
)) != pid
&& w
!= -1);
return(w
== -1 || status
);
register struct passwd
*pw
;
pw
->pw_name
= strsep(arg
, ":");
pw
->pw_passwd
= strsep((char *)NULL
, ":");
if (!(cp
= strsep((char *)NULL
, ":")))
if (!(cp
= strsep((char *)NULL
, ":")))
pw
->pw_class
= strsep((char *)NULL
, ":");
if (!(cp
= strsep((char *)NULL
, ":")))
pw
->pw_change
= atol(cp
);
if (!(cp
= strsep((char *)NULL
, ":")))
pw
->pw_expire
= atol(cp
);
pw
->pw_gecos
= strsep((char *)NULL
, ":");
pw
->pw_dir
= strsep((char *)NULL
, ":");
pw
->pw_shell
= strsep((char *)NULL
, ":");
if (!pw
->pw_shell
|| strsep((char *)NULL
, ":")) {
bad
: (void)fprintf(stderr
, "chpass: bad password list.\n");
(void)printf("re-edit the password file? [y]: ");
if (c
!= EOF
&& c
!= (int)'\n')
while (getchar() != (int)'\n');
(void)fprintf(stderr
, "chpass: %s\n", strerror(EACCES
));
(void)fprintf(stderr
, "usage: chpass [-a list] [-s shell] [user]\n");