date and time created 83/01/01 13:24:54 by sam
[unix-history] / usr / src / usr.bin / passwd / passwd.c
#ifndef lint
static char sccsid[] = "@(#)passwd.c 4.1 (Berkeley) %G%";
#endif
/*
* enter a password in the password file
* this program should be suid with owner
* with an owner with write permission on /etc/passwd
*/
#include <sys/file.h>
#include <stdio.h>
#include <signal.h>
#include <pwd.h>
#include <errno.h>
char passwd[] = "/etc/passwd";
char temp[] = "/etc/ptmp";
struct passwd *pwd;
struct passwd *getpwent();
int endpwent();
char *strcpy();
char *crypt();
char *getpass();
char *getlogin();
char *pw;
char pwbuf[10];
extern int errno;
main(argc, argv)
char *argv[];
{
char *p;
int i;
char saltc[2];
long salt;
int u;
int insist;
int ok, flags;
int c, pwlen, fd;
FILE *tf;
char *uname;
insist = 0;
if (argc < 2) {
if ((uname = getlogin()) == NULL) {
printf ("Usage: passwd user\n");
exit(1);
}
printf("Changing password for %s\n", uname);
} else
uname = argv[1];
while (((pwd = getpwent()) != NULL) && strcmp(pwd->pw_name, uname))
;
u = getuid();
if (pwd == NULL || (u != 0 && u != pwd->pw_uid)) {
printf("Permission denied.\n");
exit(1);
}
endpwent();
if (pwd->pw_passwd[0] && u != 0) {
strcpy(pwbuf, getpass("Old password:"));
pw = crypt(pwbuf, pwd->pw_passwd);
if (strcmp(pw, pwd->pw_passwd) != 0) {
printf("Sorry.\n");
exit(1);
}
}
tryagain:
strcpy(pwbuf, getpass("New password:"));
pwlen = strlen(pwbuf);
if (pwlen == 0) {
printf("Password unchanged.\n");
exit(1);
}
/*
* Insure password is of reasonable length and
* composition. If we really wanted to make things
* sticky, we could check the dictionary for common
* words, but then things would really be slow.
*/
ok = 0;
flags = 0;
p = pwbuf;
while (c = *p++) {
if (c >= 'a' && c <= 'z')
flags |= 2;
else if (c >= 'A' && c <= 'Z')
flags |= 4;
else if (c >= '0' && c <= '9')
flags |= 1;
else
flags |= 8;
}
if (flags >= 7 && pwlen >= 4)
ok = 1;
if ((flags == 2 || flags == 4) && pwlen >= 6)
ok = 1;
if ((flags == 3 || flags == 5 || flags == 6) && pwlen >= 5)
ok = 1;
if (!ok && insist < 2) {
if (flags == 1)
printf("Please use %s.\n", flags == 1 ?
"at least one non-numeric character" :
"a longer password");
insist++;
goto tryagain;
}
if (strcmp(pwbuf, getpass("Retype new password:")) != 0) {
printf("Mismatch - password unchanged.\n");
exit(1);
}
time(&salt);
salt = 9 * getpid();
saltc[0] = salt & 077;
saltc[1] = (salt>>6) & 077;
for (i = 0; i < 2; i++) {
c = saltc[i] + '.';
if (c > '9')
c += 7;
if (c > 'Z')
c += 6;
saltc[i] = c;
}
pw = crypt(pwbuf, saltc);
signal(SIGHUP, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
/*
* The mode here could be 644 except then old versions
* of passwd that don't honor the advisory locks might
* sneak in and mess things up. If we could believe the
* locking were honored, then we could also eliminate the
* chmod below after the rename.
*/
fd = open(temp, FWRONLY|FCREATE|FEXLOCK|FNBLOCK, 0600);
if (fd < 0) {
fprintf(stderr, "passwd: ");
if (errno == EBUSY)
fprintf(stderr, "password file busy - try again.\n");
else
perror(temp);
exit(1);
}
signal(SIGTSTP, SIG_IGN);
if ((tf = fdopen(fd, "w")) == NULL) {
fprintf(stderr, "passwd: fdopen failed?\n");
exit(1);
}
/*
* Copy passwd to temp, replacing matching lines
* with new password.
*/
while ((pwd = getpwent()) != NULL) {
if (strcmp(pwd->pw_name,uname) == 0) {
u = getuid();
if (u && u != pwd->pw_uid) {
fprintf(stderr, "passwd: permission denied.\n");
unlink(temp);
exit(1);
}
pwd->pw_passwd = pw;
if (pwd->pw_gecos[0] == '*')
pwd->pw_gecos++;
}
fprintf(tf,"%s:%s:%d:%d:%s:%s:%s\n",
pwd->pw_name,
pwd->pw_passwd,
pwd->pw_uid,
pwd->pw_gid,
pwd->pw_gecos,
pwd->pw_dir,
pwd->pw_shell);
}
endpwent();
if (rename(temp, passwd) < 0) {
fprintf(stderr, "passwd: "); perror("rename");
unlink(temp);
exit(1);
}
chmod(passwd, 0644);
fclose(tf);
}