309 lines
7.3 KiB
C
309 lines
7.3 KiB
C
|
/*
|
||
|
* pwdauth.c - program to verify a given username/password pair.
|
||
|
*
|
||
|
* Run it with username in argv[1] (may be omitted - default is the
|
||
|
* current user), and send it the password over a pipe on stdin.
|
||
|
* Exit status: 0 - correct password, 1 - wrong password, >1 - other
|
||
|
* errors. For use with shadow passwords, this program should be
|
||
|
* installed setuid root.
|
||
|
*
|
||
|
* This can be used, for example, by xlock - you don't have to install
|
||
|
* this large and complex (== possibly insecure) program setuid root,
|
||
|
* just modify it to run this simple program to do the authentication.
|
||
|
*
|
||
|
* Recent versions (xlockmore-3.9) are cleaner, and drop privileges as
|
||
|
* soon as possible after getting the user's encrypted password.
|
||
|
* Using this program probably doesn't make it more secure, and has one
|
||
|
* disadvantage: since we don't get the encrypted user's password at
|
||
|
* startup (but at the time the user is authenticated), it is not clear
|
||
|
* how we should handle errors (like getpwnam() returning NULL).
|
||
|
* - fail the authentication? Problem: no way to unlock (other than kill
|
||
|
* the process from somewhere else) if the NIS server stops responding.
|
||
|
* - succeed and unlock? Problem: it's too easy to unlock by unplugging
|
||
|
* the box from the network and waiting until NIS times out...
|
||
|
*
|
||
|
* This program is Copyright (C) 1996 Marek Michalkiewicz
|
||
|
* <marekm@i17linuxb.ists.pwr.wroc.pl>.
|
||
|
*
|
||
|
* It may be used and distributed freely for any purposes. There is no
|
||
|
* warranty - use at your own risk. I am not liable for any damages etc.
|
||
|
* If you improve it, please send me your changes.
|
||
|
*/
|
||
|
|
||
|
static char rcsid[] = "$Id: pwdauth.c,v 1.2 1997/12/07 23:26:45 marekm Exp $";
|
||
|
|
||
|
/*
|
||
|
* Define USE_SYSLOG to use syslog() to log successful and failed
|
||
|
* authentication. This should be safe even if your system has
|
||
|
* the infamous syslog buffer overrun security problem...
|
||
|
*/
|
||
|
#define USE_SYSLOG
|
||
|
|
||
|
/*
|
||
|
* Define HAVE_GETSPNAM to get shadow passwords using getspnam().
|
||
|
* Some systems don't have getspnam(), but getpwnam() returns
|
||
|
* encrypted passwords only if running as root.
|
||
|
*
|
||
|
* According to the xlock source (not tested, except Linux) -
|
||
|
* define: Linux, Solaris 2.x, SVR4, ...
|
||
|
* undef: HP-UX with Secured Passwords, FreeBSD, NetBSD, QNX.
|
||
|
* Known not supported (yet): Ultrix, OSF/1, SCO.
|
||
|
*/
|
||
|
#define HAVE_GETSPNAM
|
||
|
|
||
|
/*
|
||
|
* Define HAVE_PW_ENCRYPT to use pw_encrypt() instead of crypt().
|
||
|
* pw_encrypt() is like the standard crypt(), except that it may
|
||
|
* support better password hashing algorithms.
|
||
|
*
|
||
|
* Define if linking with libshadow.a from the shadow password
|
||
|
* suite (Linux, SunOS 4.x?).
|
||
|
*/
|
||
|
#undef HAVE_PW_ENCRYPT
|
||
|
|
||
|
/*
|
||
|
* Define HAVE_AUTH_METHODS to support the shadow suite specific
|
||
|
* extension: the encrypted password field contains a list of
|
||
|
* administrator defined authentication methods, separated by
|
||
|
* semicolons. This program only supports the standard password
|
||
|
* authentication method (a string that doesn't start with '@').
|
||
|
*/
|
||
|
#undef HAVE_AUTH_METHODS
|
||
|
|
||
|
/*
|
||
|
* FAIL_DELAY - number of seconds to sleep before exiting if the
|
||
|
* password was wrong, to slow down password guessing attempts.
|
||
|
*/
|
||
|
#define FAIL_DELAY 2
|
||
|
|
||
|
/* No user-serviceable parts below :-). */
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/wait.h>
|
||
|
#include <unistd.h>
|
||
|
#include <pwd.h>
|
||
|
|
||
|
#ifdef USE_SYSLOG
|
||
|
#include <syslog.h>
|
||
|
#ifndef LOG_AUTHPRIV
|
||
|
#define LOG_AUTHPRIV LOG_AUTH
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_GETSPNAM
|
||
|
#include <shadow.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_PW_ENCRYPT
|
||
|
extern char *pw_encrypt();
|
||
|
#define crypt pw_encrypt
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Read the password (one line) from fp. We don't turn off echo
|
||
|
* because we expect input from a pipe.
|
||
|
*/
|
||
|
static char *
|
||
|
get_line(fp)
|
||
|
FILE *fp;
|
||
|
{
|
||
|
static char buf[128];
|
||
|
char *cp;
|
||
|
int ch;
|
||
|
|
||
|
cp = buf;
|
||
|
while ((ch = getc(fp)) != EOF && ch != '\0' && ch != '\n') {
|
||
|
if (cp >= buf + sizeof buf - 1)
|
||
|
break;
|
||
|
*cp++ = ch;
|
||
|
}
|
||
|
*cp = '\0';
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get the password file entry for the current user. If the name
|
||
|
* returned by getlogin() is correct (matches the current real uid),
|
||
|
* return the entry for that user. Otherwise, return the entry (if
|
||
|
* any) matching the current real uid. Return NULL on failure.
|
||
|
*/
|
||
|
static struct passwd *
|
||
|
get_my_pwent()
|
||
|
{
|
||
|
uid_t uid = getuid();
|
||
|
char *name = getlogin();
|
||
|
|
||
|
if (name && *name) {
|
||
|
struct passwd *pw = getpwnam(name);
|
||
|
|
||
|
if (pw && pw->pw_uid == uid)
|
||
|
return pw;
|
||
|
}
|
||
|
return getpwuid(uid);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Verify the password. The system-dependent shadow support is here.
|
||
|
*/
|
||
|
static int
|
||
|
password_auth_ok(pw, pass)
|
||
|
const struct passwd *pw;
|
||
|
const char *pass;
|
||
|
{
|
||
|
int result;
|
||
|
char *cp;
|
||
|
#ifdef HAVE_AUTH_METHODS
|
||
|
char *buf;
|
||
|
#endif
|
||
|
#ifdef HAVE_GETSPNAM
|
||
|
struct spwd *sp;
|
||
|
#endif
|
||
|
|
||
|
if (pw) {
|
||
|
#ifdef HAVE_GETSPNAM
|
||
|
sp = getspnam(pw->pw_name);
|
||
|
if (sp)
|
||
|
cp = sp->sp_pwdp;
|
||
|
else
|
||
|
#endif
|
||
|
cp = pw->pw_passwd;
|
||
|
} else
|
||
|
cp = "xx";
|
||
|
|
||
|
#ifdef HAVE_AUTH_METHODS
|
||
|
buf = strdup(cp); /* will be modified by strtok() */
|
||
|
if (!buf) {
|
||
|
fprintf(stderr, "Out of memory.\n");
|
||
|
exit(13);
|
||
|
}
|
||
|
cp = strtok(buf, ";");
|
||
|
while (cp && *cp == '@')
|
||
|
cp = strtok(NULL, ";");
|
||
|
|
||
|
/* fail if no password authentication for this user */
|
||
|
if (!cp)
|
||
|
cp = "xx";
|
||
|
#endif
|
||
|
|
||
|
if (*pass || *cp)
|
||
|
result = (strcmp(crypt(pass, cp), cp) == 0);
|
||
|
else
|
||
|
result = 1; /* user with no password */
|
||
|
|
||
|
#ifdef HAVE_AUTH_METHODS
|
||
|
free(buf);
|
||
|
#endif
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Main program.
|
||
|
*/
|
||
|
int
|
||
|
main(argc, argv)
|
||
|
int argc;
|
||
|
char **argv;
|
||
|
{
|
||
|
struct passwd *pw;
|
||
|
char *pass, *name;
|
||
|
char myname[32];
|
||
|
|
||
|
#ifdef USE_SYSLOG
|
||
|
openlog("pwdauth", LOG_PID | LOG_CONS, LOG_AUTHPRIV);
|
||
|
#endif
|
||
|
pw = get_my_pwent();
|
||
|
if (!pw) {
|
||
|
#ifdef USE_SYSLOG
|
||
|
syslog(LOG_ERR, "can't get login name for uid %d.\n",
|
||
|
(int) getuid());
|
||
|
#endif
|
||
|
fprintf(stderr, "Who are you?\n");
|
||
|
exit(2);
|
||
|
}
|
||
|
strncpy(myname, pw->pw_name, sizeof myname - 1);
|
||
|
myname[sizeof myname - 1] = '\0';
|
||
|
name = myname;
|
||
|
|
||
|
if (argc > 1) {
|
||
|
name = argv[1];
|
||
|
pw = getpwnam(name);
|
||
|
}
|
||
|
|
||
|
pass = get_line(stdin);
|
||
|
if (password_auth_ok(pw, pass)) {
|
||
|
#ifdef USE_SYSLOG
|
||
|
syslog(pw->pw_uid ? LOG_INFO : LOG_NOTICE,
|
||
|
"user `%s' entered correct password for `%.32s'.\n",
|
||
|
myname, name);
|
||
|
#endif
|
||
|
exit(0);
|
||
|
}
|
||
|
#ifdef USE_SYSLOG
|
||
|
/* be careful not to overrun the syslog buffer */
|
||
|
syslog((!pw || pw->pw_uid) ? LOG_NOTICE : LOG_WARNING,
|
||
|
"user `%s' entered incorrect password for `%.32s'.\n",
|
||
|
myname, name);
|
||
|
#endif
|
||
|
#ifdef FAIL_DELAY
|
||
|
sleep(FAIL_DELAY);
|
||
|
#endif
|
||
|
fprintf(stderr, "Wrong password.\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
/*
|
||
|
* You can use code similar to the following to run this program.
|
||
|
* Return values: >=0 - program exit status (use the <sys/wait.h>
|
||
|
* macros to get the exit code, it is shifted left by 8 bits),
|
||
|
* -1 - check errno.
|
||
|
*/
|
||
|
int
|
||
|
verify_password(const char *username, const char *password)
|
||
|
{
|
||
|
int pipe_fd[2];
|
||
|
int pid, wpid, status;
|
||
|
|
||
|
if (pipe(pipe_fd))
|
||
|
return -1;
|
||
|
|
||
|
if ((pid = fork()) == 0) {
|
||
|
char *arg[3];
|
||
|
char *env[1];
|
||
|
|
||
|
/* child */
|
||
|
close(pipe_fd[1]);
|
||
|
if (pipe_fd[0] != 0) {
|
||
|
if (dup2(pipe_fd[0], 0) != 0)
|
||
|
_exit(127);
|
||
|
close(pipe_fd[0]);
|
||
|
}
|
||
|
arg[0] = "/usr/bin/pwdauth";
|
||
|
arg[1] = username;
|
||
|
arg[2] = NULL;
|
||
|
env[0] = NULL;
|
||
|
execve(arg[0], arg, env);
|
||
|
_exit(127);
|
||
|
} else if (pid == -1) {
|
||
|
/* error */
|
||
|
close(pipe_fd[0]);
|
||
|
close(pipe_fd[1]);
|
||
|
return -1;
|
||
|
}
|
||
|
/* parent */
|
||
|
close(pipe_fd[0]);
|
||
|
write(pipe_fd[1], password, strlen(password));
|
||
|
write(pipe_fd[1], "\n", 1);
|
||
|
close(pipe_fd[1]);
|
||
|
|
||
|
while ((wpid = wait(&status)) != pid) {
|
||
|
if (wpid == -1)
|
||
|
return -1;
|
||
|
}
|
||
|
return status;
|
||
|
}
|
||
|
#endif
|