/* * 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 * . * * 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 #include #include #include #include #include #include #ifdef USE_SYSLOG #include #ifndef LOG_AUTHPRIV #define LOG_AUTHPRIV LOG_AUTH #endif #endif #ifdef HAVE_GETSPNAM #include #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 * 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