* 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.6 (Berkeley) %G%";
#include <sys/resource.h>
#include </usr/src/include/pwd.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
, },
char *fend
, *passwd
, *temp
, *tend
, buf
[1024];
char from
[MAXPATHLEN
], to
[MAXPATHLEN
];
if (!(pw
= getpwuid(uid
))) {
fprintf(stderr
, "chpass: unknown user: uid %u\n", uid
);
if (!(pw
= getpwnam(argv
[1]))) {
fprintf(stderr
, "chpass: unknown user %s.\n", argv
[1]);
if (uid
&& uid
!= pw
->pw_uid
) {
fprintf(stderr
, "chpass: %s\n", strerror(EACCES
));
fprintf(stderr
, "usage: chpass [user]\n");
(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");
fprintf(stderr
, "chpass: %s: %s", temp
, strerror(errno
));
if (!(temp_fp
= fdopen(fd
, "w"))) {
fprintf(stderr
, "chpass: can't write %s", temp
);
* 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
)
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
,
/* root should have a 0 uid and a reasonable shell */
if (!strcmp(pw
->pw_name
, "root")) {
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
)) {
fprintf(stderr
, "chpass: can't read %s", passwd
);
fprintf(stderr
, "chpass: can't fork");
fprintf(stderr
, "chpass: mkpasswd failed");
bad
: fprintf(stderr
, "; information unchanged.\n");
* 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
, passwd
) + strlen(passwd
);
if ((fd
= open(from
, O_RDONLY
, 0)) >= 0)
(void)flock(fd
, LOCK_EX
);
(void)rename(from
, _PATH_PASSWD
);
(void)rename(temp
, passwd
);
register struct entry
*ep
;
char *tfile
, buf
[1024], *getenv();
tfile
= "/tmp/passwd.XXXXXX";
if ((fd
= mkstemp(tfile
)) == -1 || !(fp
= fdopen(fd
, "w+"))) {
fprintf(stderr
, "chpass: no temporary file");
* give the file to the real user; setuid permissions
* are discarded in edit()
(void)fchown(fd
, getuid(), getgid());
fprintf(stderr
, "chpass: edit failed");
if (begin
.st_mtime
== end
.st_mtime
) {
fprintf(stderr
, "chpass: no changes made");
while (fgets(buf
, sizeof(buf
), fp
)) {
if (!(p
= index(buf
, '\n'))) {
fprintf(stderr
, "chpass: line too long");
for (ep
= list
; ep
->prompt
; ++ep
)
if (!strncasecmp(buf
, ep
->prompt
, ep
->len
)) {
if (ep
->restricted
&& euid
)
if (!(p
= index(buf
, ':'))) {
"chpass: line corrupted");
if (ep
->except
&& strpbrk(p
, ep
->except
)) {
"chpass: illegal character in the \"%s\" field",
if ((ep
->func
)(p
, pw
, ep
))
for (done
= 0; fgets(buf
, sizeof(buf
), stdin
);) {
/* skip lines that are too big */
fprintf(stderr
, "chpass: line too long");
if (!(p
= index(buf
, ':'))) {
fprintf(stderr
, "chpass: corrupted entry");
if (strcmp(buf
, pw
->pw_name
)) {
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
);
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
);