426 lines
10 KiB
C
426 lines
10 KiB
C
/*
|
|
* 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)
|
|
*/
|
|
|
|
/*
|
|
* Sun Oct 15 13:18:34 1995 Martin Schulze <joey@finlandia.infodrom.north.de>
|
|
*
|
|
* I have completely rewritten the whole argument handling (what?)
|
|
* to support two things. First I wanted "passwd $user $pw" to
|
|
|
|
(a very bad idea; command lines are visible to people doing ps
|
|
or running a background job that just collects all command lines)
|
|
|
|
* work and second I wanted simplicity checks to be done for
|
|
* root, too. Only root can turn this off 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.)
|
|
*/
|
|
|
|
/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
|
|
* - added Native Language Support
|
|
* Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
|
|
* - fixed strerr(errno) in gettext calls
|
|
*/
|
|
|
|
/*
|
|
* 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>
|
|
#include <stdlib.h>
|
|
#include "my_crypt.h"
|
|
#include "setpwnam.h"
|
|
#include "islocal.h"
|
|
#include "xstrncpy.h"
|
|
#include "nls.h"
|
|
#include "env.h"
|
|
|
|
#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 */
|
|
|
|
#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. :-)
|
|
*/
|
|
static 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;
|
|
}
|
|
|
|
static 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"
|
|
"classes: upper and lower case letters, digits and "
|
|
"non alphanumeric\n"
|
|
"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 */
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
usage(void) {
|
|
printf (_("Usage: passwd [username [password]]\n"));
|
|
printf(_("Only root may use the one and two argument forms.\n"));
|
|
}
|
|
#endif
|
|
|
|
int
|
|
main(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;
|
|
int 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}
|
|
};
|
|
|
|
sanitize_env();
|
|
setlocale(LC_ALL, "");
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
textdomain(PACKAGE);
|
|
|
|
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", util_linux_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, errsv;
|
|
|
|
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);
|
|
errsv = errno;
|
|
fprintf(stderr, _("Can't exec %s: %s\n"), args[0], strerror(errsv));
|
|
exit(1);
|
|
}
|
|
|
|
switch (argc - optind) {
|
|
case 0:
|
|
/* Why use getlogin()? Some systems allow having several
|
|
usernames with the same uid, especially several root accounts.
|
|
One changes the password for the username, not the uid. */
|
|
if ( !(user = getlogin()) || !*user ) {
|
|
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;
|
|
|
|
xstrncpy(pwdstr1, pwdstr, sizeof(pwdstr1));
|
|
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);
|
|
}
|