diff options
Diffstat (limited to 'login-utils/passwd.c')
-rw-r--r-- | login-utils/passwd.c | 466 |
1 files changed, 313 insertions, 153 deletions
diff --git a/login-utils/passwd.c b/login-utils/passwd.c index 1c786e94e..f707c0521 100644 --- a/login-utils/passwd.c +++ b/login-utils/passwd.c @@ -1,12 +1,43 @@ -/* passwd.c - change password on an account +/* + * 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. + */ -/* 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) */ +/* + * 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]] @@ -17,7 +48,10 @@ #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> @@ -26,21 +60,170 @@ #include <errno.h> #include <sys/resource.h> -extern int is_local(char *); +#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)+'.') -#define MAX_LENGTH 1024 - static void -pexit(str) - char *str; +pexit(char *str, ...) { - perror(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; @@ -50,56 +233,101 @@ main(argc, argv) uid_t gotuid = getuid(); char *pwdstr = NULL, *cryptstr, *oldstr; char pwdstr1[10]; - int ucase, lcase, other; - char *p, *q, *user; + char *user; time_t tm; char salt[2]; - FILE *fd_in, *fd_out; - char line[MAX_LENGTH]; - char colonuser[16]; - int error=0; - int r; - int ptmp; -#ifndef USE_SETPWNAM - struct rlimit rlim; -#endif + 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} + }; - if(argc > 3) { - puts("Too many arguments"); - exit(1); - } else if(argc >= 2) { - if(gotuid) { - puts("Only root can change the password for others"); + 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); - } - user = argv[1]; - - if (argc == 3) pwdstr = argv[2]; - - } else { - if (!(user = getlogin())) { - if (!(pe = getpwuid( getuid() ))) { + } /* 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; + 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 */ -#ifndef USE_SETPWNAM - umask(022); - - rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; - setrlimit(RLIMIT_CPU, &rlim); - setrlimit(RLIMIT_FSIZE, &rlim); - setrlimit(RLIMIT_STACK, &rlim); - setrlimit(RLIMIT_DATA, &rlim); - setrlimit(RLIMIT_RSS, &rlim); - rlim.rlim_cur = rlim.rlim_max = 0; - setrlimit(RLIMIT_CORE, &rlim); -#endif - if(!(pe = getpwnam(user))) { - pexit("Can't find username anywhere. Are you really a user?"); + pexit("Can't find username anywhere. Is `%s' really a user?", user); } if (!(is_local(user))) { @@ -113,9 +341,11 @@ main(argc, argv) exit(1); } - printf( "Changing password for %s\n", user ); + if ( !silent ) + printf( "Changing password for %s\n", user ); - if(gotuid && pe->pw_passwd && pe->pw_passwd[0]) { + 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."); @@ -123,7 +353,10 @@ main(argc, argv) } } - if (!pwdstr) { + 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: @@ -132,126 +365,53 @@ main(argc, argv) puts("Password not changed."); exit(1); } - - if((strlen(pwdstr) < 6) && gotuid) { - puts("The password must have at least 6 characters, try again."); - goto redo_it; - } - - other = ucase = lcase = 0; - for(p = pwdstr; *p; p++) { - ucase = ucase || isupper(*p); - lcase = lcase || islower(*p); - other = other || !isalpha(*p); - } - - if((!ucase || !lcase) && !other && gotuid) { - puts("The password must have both upper- and lowercase"); - puts("letters, or non-letters; try again."); - goto redo_it; - } - - if (pe->pw_passwd[0] - && !strncmp(pe->pw_passwd, crypt(pwdstr, pe->pw_passwd), 13) - && gotuid) { - puts("You cannot reuse the old password."); - goto redo_it; - } - - r = 0; - for(p = pwdstr, q = pe->pw_name; *q && *p; q++, p++) { - if(tolower(*p) != tolower(*q)) { - r = 1; - break; - } - } - - for(p = pwdstr + strlen(pwdstr)-1, q = pe->pw_name; - *q && p >= pwdstr; q++, p--) { - if(tolower(*p) != tolower(*q)) { - r += 2; - break; - } - } - - if(gotuid && r != 3) { - puts("Please don't use something like your username as password!"); + + if ( (gotuid || (!gotuid && !force_passwd)) + && !check_passwd(pwdstr, pe->pw_passwd, user, pe->pw_gecos) ) goto redo_it; - } - - /* do various other checks for stupid passwords here... */ 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 != argv[2] i.e. password set on command line */ + } /* pwdstr i.e. password set on command line */ - time(&tm); + 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 USE_SETPWNAM +#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 ); } -#else - if ((ptmp = open("/etc/ptmp", O_CREAT|O_EXCL|O_WRONLY, 0600)) < 0) { - pexit("Can't exclusively open /etc/ptmp, can't update password"); - } - fd_out = fdopen(ptmp, "w"); - - if(!(fd_in = fopen("/etc/passwd", "r"))) { - pexit("Can't read /etc/passwd, can't update password"); - } - - strcpy(colonuser, user); - strcat(colonuser, ":"); - while(fgets(line, sizeof(line), fd_in)) { - if(!strncmp(line,colonuser,strlen(colonuser))) { - pe->pw_passwd = cryptstr; - if(putpwent(pe, fd_out) < 0) { - error = 1; - } - } else { - if(fputs(line,fd_out) < 0) { - error = 1; - } - } - if(error) { - puts("Error while writing new password file, password not changed."); - fclose(fd_out); - endpwent(); - unlink("/etc/ptmp"); - exit(1); - } - } - fclose(fd_in); - fclose(fd_out); - - unlink("/etc/passwd.OLD"); /* passwd.OLD not required */ - if (link("/etc/passwd", "/etc/passwd.OLD")) - pexit("link(/etc/passwd, /etc/passwd.OLD) failed: no change"); - if (unlink("/etc/passwd") < 0) - pexit("unlink(/etc/passwd) failed: no change"); - if (link("/etc/ptmp", "/etc/passwd") < 0) - pexit("link(/etc/ptmp, /etc/passwd) failed: PASSWD file DROPPED!!"); - if (unlink("/etc/ptmp") < 0) - pexit("unlink(/etc/ptmp) failed: /etc/ptmp still exists"); - - chmod("/etc/passwd", 0644); - chown("/etc/passwd", 0, 0); #endif - - puts("Password changed."); + + if ( !silent ) + printf("Password changed.\n"); exit(0); } |