diff options
Diffstat (limited to '')
-rw-r--r-- | contrib/pwdauth.c | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/contrib/pwdauth.c b/contrib/pwdauth.c new file mode 100644 index 0000000..ca15495 --- /dev/null +++ b/contrib/pwdauth.c @@ -0,0 +1,308 @@ +/* + * 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$"; + +/* + * 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 |