| /* |
| * passwd.c - change password on an account |
| * |
| * Initially written for Linux by Peter Orbaek <poe@daimi.aau.dk> |
| * Currently maintained at ftp://ftp.daimi.aau.dk/pub/linux/poe/ |
| |
| Hacked by Alvaro Martinez Echevarria, alvaro@enano.etsit.upm.es, |
| to allow peaceful coexistence with yp. Nov 94. |
| |
| Hacked to allow root to set passwd from command line. |
| by Arpad Magossanyi (mag@tas.vein.hu) |
| |
| Hacked by Peter Breitenlohner, peb@mppmu.mpg.de, |
| moved Alvaro's changes to setpwnam.c (so they get used |
| by chsh and chfn as well). Oct 5, 96. |
| |
| */ |
| |
| /* |
| * Sun Oct 15 13:18:34 1995 Martin Schulze <joey@finlandia.infodrom.north.de> |
| * |
| * I have completely rewritten the whole argument handlig (what?) |
| * to support two things. First I wanted "passwd $user $pw" to |
| * work and second I wanted simplicity checks to be done for |
| * root, too. Only root can turn this of using the -f |
| * switch. Okay, I started with this to support -V version |
| * information, but one thing comes to the next. *sigh* |
| * In a later step perhaps we'll be able to support shadow |
| * passwords. (?) |
| * |
| * I have also included a DEBUG mode (-DDEBUG) to test the |
| * argument handling _without_ any write attempt to |
| * /etc/passwd. |
| * |
| * If you're paranoid about security on your system, you may want |
| * to add -DLOGALL to CFLAGS. This will turn on additional syslog |
| * logging of every password change. (user changes are logged as |
| * auth.notice, but changing root's password is logged as |
| * auth.warning. (Of course, the password itself is not logged.) |
| */ |
| |
| /* |
| * Usage: passwd [username [password]] |
| * Only root may use the one and two argument forms. |
| */ |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdarg.h> |
| #include <termios.h> |
| #include <getopt.h> |
| #include <malloc.h> |
| #include <fcntl.h> |
| #include <pwd.h> |
| #include <ctype.h> |
| #include <time.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <sys/resource.h> |
| |
| #if defined (__GNU_LIBRARY__) && __GNU_LIBRARY__ > 1 |
| #include <crypt.h> |
| #endif |
| |
| #if 0 |
| # include "../version.h" |
| #else |
| char version[] = "admutil 1.18, 15-Oct-95"; |
| #endif |
| |
| #ifndef _PATH_CHFN |
| # define _PATH_CHFN "/usr/bin/chfn" |
| # define _PATH_CHSH "/usr/bin/chsh" |
| #endif |
| |
| #define LOGALL |
| |
| #ifdef LOGALL |
| #include <syslog.h> |
| #endif /* LOGALL */ |
| |
| extern int is_local(char *); /* islocal.c */ |
| extern int setpwnam(struct passwd *); /* setpwnam.c */ |
| |
| #define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.') |
| #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') |
| |
| static void |
| pexit(char *str, ...) |
| { |
| va_list vlst; |
| |
| va_start(vlst, str); |
| vfprintf(stderr, str, vlst); |
| fprintf(stderr, ": "); |
| perror(""); |
| va_end(vlst); |
| exit(1); |
| } |
| |
| /* |
| * Do various checks for stupid passwords here... |
| * |
| * This would probably be the best place for checking against |
| * dictionaries. :-) |
| */ |
| |
| int check_passwd_string(char *passwd, char *string) |
| { |
| int r; |
| char *p, *q; |
| |
| r = 0; |
| /* test for string at the beginning of passwd */ |
| for (p = passwd, q = string; *q && *p; q++, p++) { |
| if(tolower(*p) != tolower(*q)) { |
| r++; |
| break; |
| } |
| } |
| |
| /* test for reverse string at the beginning of passwd */ |
| for (p = passwd, q = string + strlen(string)-1; |
| *p && q >= string; p++, q--) { |
| if(tolower(*p) != tolower(*q)) { |
| r++; |
| break; |
| } |
| } |
| |
| /* test for string at the end of passwd */ |
| for (p = passwd + strlen(passwd)-1, q = string + strlen(string)-1; |
| q >= string && p >= passwd; q--, p--) { |
| if(tolower(*p) != tolower(*q)) { |
| r++; |
| break; |
| } |
| } |
| |
| /* test for reverse string at the beginning of passwd */ |
| for (p = passwd + strlen(passwd)-1, q = string; |
| p >= passwd && *q; p--, q++) { |
| if(tolower(*p) != tolower(*q)) { |
| r++; |
| break; |
| } |
| } |
| |
| if (r != 4) { |
| return 0; |
| } |
| return 1; |
| } |
| |
| int check_passwd(char *passwd, char *oldpasswd, char *user, char *gecos) |
| { |
| int ucase, lcase, digit, other; |
| char *c, *g, *p; |
| |
| if ( (strlen(passwd) < 6) ) { |
| printf("The password must have at least 6 characters, try again.\n"); |
| return 0; |
| } |
| |
| other = digit = ucase = lcase = 0; |
| for (p = passwd; *p; p++) { |
| ucase = ucase || isupper(*p); |
| lcase = lcase || islower(*p); |
| digit = digit || isdigit(*p); |
| other = other || !isalnum(*p); |
| } |
| |
| if ( (other + digit + ucase + lcase) < 2) { |
| printf("The password must contain characters out of two of the following\n"); |
| printf("classes: upper and lower case letters, digits and non alphanumeric\n"); |
| printf("characters. See passwd(1) for more information.\n"); |
| return 0; |
| } |
| |
| if ( oldpasswd[0] && !strncmp(oldpasswd, crypt(passwd, oldpasswd), 13) ) { |
| printf("You cannot reuse the old password.\n"); |
| return 0; |
| } |
| |
| if ( !check_passwd_string(passwd, user) ) { |
| printf("Please don't use something like your username as password!\n"); |
| return 0; |
| } |
| |
| /* check against realname */ |
| if ( (c = index(gecos, ',')) ) { |
| if ( c-gecos && (g = (char *)malloc (c-gecos+1)) ) { |
| strncpy (g, gecos, c-gecos); |
| g[c-gecos] = 0; |
| while ( (c=rindex(g, ' ')) ) { |
| if ( !check_passwd_string(passwd, c+1) ) { |
| printf("Please don't use something like your realname as password!\n"); |
| free (g); |
| return 0; |
| } |
| *c = '\0'; |
| } /* while */ |
| if ( !check_passwd_string(passwd, g) ) { |
| printf("Please don't use something like your realname as password!\n"); |
| free (g); |
| return 0; |
| } |
| free (g); |
| } /* if malloc */ |
| } |
| |
| /* |
| * if ( !check_password_dict(passwd) ) ... |
| */ |
| |
| return 1; /* fine */ |
| } |
| |
| void usage() |
| { |
| printf ("Usage: passwd [username [password]]\n"); |
| printf("Only root may use the one and two argument forms.\n"); |
| } |
| |
| int |
| main(argc, argv) |
| int argc; |
| char *argv[]; |
| { |
| struct passwd *pe; |
| uid_t gotuid = getuid(); |
| char *pwdstr = NULL, *cryptstr, *oldstr; |
| char pwdstr1[10]; |
| char *user; |
| time_t tm; |
| char salt[2]; |
| int force_passwd = 0; |
| int silent = 0; |
| char c; |
| int opt_index; |
| int fullname = 0, shell = 0; |
| static const struct option long_options[] = |
| { |
| {"fullname", no_argument, 0, 'f'}, |
| {"shell", no_argument, 0, 's'}, |
| {"force", no_argument, 0, 'o'}, |
| {"quiet", no_argument, 0, 'q'}, |
| {"silent", no_argument, 0, 'q'}, |
| {"version", no_argument, 0, 'v'}, |
| {0, 0, 0, 0} |
| }; |
| |
| optind = 0; |
| while ( (c = getopt_long(argc, argv, "foqsvV", long_options, &opt_index)) != -1 ) { |
| switch (c) { |
| case 'f': |
| fullname = 1; |
| break; |
| case 's': |
| shell = 1; |
| break; |
| case 'o': |
| force_passwd = 1; |
| break; |
| case 'q': |
| silent = 1; |
| break; |
| case 'V': |
| case 'v': |
| printf("%s\n", version); |
| exit(0); |
| default: |
| fprintf(stderr, "Usage: passwd [-foqsvV] [user [password]]\n"); |
| exit(1); |
| } /* switch (c) */ |
| } /* while */ |
| |
| if (fullname || shell) { |
| char *args[100]; |
| int i, j; |
| |
| setuid(getuid()); /* drop special privs. */ |
| if (fullname) |
| args[0] = _PATH_CHFN; |
| else |
| args[0] = _PATH_CHSH; |
| |
| for (i = optind, j = 1; (i < argc) && (j < 99); i++, j++) |
| args[j] = argv[i]; |
| |
| args[j] = NULL; |
| execv(args[0], args); |
| fprintf(stderr, "Can't exec %s: %s\n", args[0], strerror(errno)); |
| exit(1); |
| } |
| |
| switch (argc - optind) { |
| case 0: |
| if ( !(user = getlogin()) ) { |
| if ( !(pe = getpwuid( getuid() )) ) { |
| pexit("Cannot find login name"); |
| } else |
| user = pe->pw_name; |
| } |
| break; |
| case 1: |
| if(gotuid) { |
| printf("Only root can change the password for others.\n"); |
| exit (1); |
| } else |
| user = argv[optind]; |
| break; |
| case 2: |
| if(gotuid) { |
| printf("Only root can change the password for others.\n"); |
| exit(1); |
| } else { |
| user = argv[optind]; |
| pwdstr = argv[optind+1]; |
| } |
| break; |
| default: |
| printf("Too many arguments.\n"); |
| exit (1); |
| } /* switch */ |
| |
| if(!(pe = getpwnam(user))) { |
| pexit("Can't find username anywhere. Is `%s' really a user?", user); |
| } |
| |
| if (!(is_local(user))) { |
| puts("Sorry, I can only change local passwords. Use yppasswd instead."); |
| exit(1); |
| } |
| |
| /* if somebody got into changing utmp... */ |
| if(gotuid && gotuid != pe->pw_uid) { |
| puts("UID and username does not match, imposter!"); |
| exit(1); |
| } |
| |
| if ( !silent ) |
| printf( "Changing password for %s\n", user ); |
| |
| if ( (gotuid && pe->pw_passwd && pe->pw_passwd[0]) |
| || (!gotuid && !strcmp(user,"root")) ) { |
| oldstr = getpass("Enter old password: "); |
| if(strncmp(pe->pw_passwd, crypt(oldstr, pe->pw_passwd), 13)) { |
| puts("Illegal password, imposter."); |
| exit(1); |
| } |
| } |
| |
| if ( pwdstr ) { /* already set on command line */ |
| if ( !force_passwd && !check_passwd(pwdstr, pe->pw_passwd, user, pe->pw_gecos) ) |
| exit (1); |
| } else { |
| /* password not set on command line by root, ask for it ... */ |
| |
| redo_it: |
| pwdstr = getpass("Enter new password: "); |
| if (pwdstr[0] == '\0') { |
| puts("Password not changed."); |
| exit(1); |
| } |
| |
| if ( (gotuid || (!gotuid && !force_passwd)) |
| && !check_passwd(pwdstr, pe->pw_passwd, user, pe->pw_gecos) ) |
| goto redo_it; |
| |
| strncpy(pwdstr1, pwdstr, 9); |
| pwdstr1[9] = 0; |
| pwdstr = getpass("Re-type new password: "); |
| |
| if(strncmp(pwdstr, pwdstr1, 8)) { |
| puts("You misspelled it. Password not changed."); |
| exit(1); |
| } |
| } /* pwdstr i.e. password set on command line */ |
| |
| time(&tm); tm ^= getpid(); |
| salt[0] = bin_to_ascii(tm & 0x3f); |
| salt[1] = bin_to_ascii((tm >> 6) & 0x3f); |
| cryptstr = crypt(pwdstr, salt); |
| |
| if (pwdstr[0] == 0) cryptstr = ""; |
| |
| #ifdef LOGALL |
| openlog("passwd", 0, LOG_AUTH); |
| if (gotuid) |
| syslog(LOG_NOTICE,"password changed, user %s",user); |
| else { |
| if ( !strcmp(user, "root") ) |
| syslog(LOG_WARNING,"ROOT PASSWORD CHANGED"); |
| else |
| syslog(LOG_NOTICE,"password changed by root, user %s",user); |
| } |
| closelog(); |
| #endif /* LOGALL */ |
| |
| pe->pw_passwd = cryptstr; |
| #ifdef DEBUG |
| printf ("calling setpwnam to set password.\n"); |
| #else |
| if (setpwnam( pe ) < 0) { |
| perror( "setpwnam" ); |
| printf( "Password *NOT* changed. Try again later.\n" ); |
| exit( 1 ); |
| } |
| #endif |
| |
| if ( !silent ) |
| printf("Password changed.\n"); |
| exit(0); |
| } |