#ifndef lint static char sccsid[] = "@(#)passwd.c 4.2 (Berkeley) 2/9/83"; #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) { 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); }