/* passwd-helper.c --- verifying typed passwords with external helper program * written by Olaf Kirch * xscreensaver, Copyright (c) 1993-2005 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* The idea here is to be able to run xscreensaver without any setuid bits. * Password verification happens through an external program that you feed * your password to on stdin. The external command is invoked with a user * name argument. * * The external helper does whatever authentication is necessary. Currently, * SuSE uses "unix2_chkpwd", which is a variation of "unix_chkpwd" from the * PAM distribution. * * Normally, the password helper should just authenticate the calling user * (i.e. based on the caller's real uid). This is in order to prevent * brute-forcing passwords in a shadow environment. A less restrictive * approach would be to allow verifying other passwords as well, but always * with a 2 second delay or so. (Not sure what SuSE's "unix2_chkpwd" * currently does.) * -- Olaf Kirch , 16-Dec-2003 */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifndef NO_LOCKING /* whole file */ #include /* not used for much... */ /* This file doesn't need the Xt headers, so stub these types out... */ #undef XtPointer #define XtAppContext void* #define XrmDatabase void* #define XtIntervalId void* #define XtPointer void* #define Widget void* #include "xscreensaver.h" #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include static int ext_run (const char *user, const char *typed_passwd, int verbose_p) { int pfd[2], status; pid_t pid; if (pipe(pfd) < 0) return 0; if (verbose_p) fprintf (stderr, "%s: ext_run (%s, %s)\n", blurb(), PASSWD_HELPER_PROGRAM, user); block_sigchld(); if ((pid = fork()) < 0) { close(pfd[0]); close(pfd[1]); return 0; } if (pid == 0) { close(pfd[1]); if (pfd[0] != 0) dup2(pfd[0], 0); /* Helper is invoked as helper service-name [user] */ execlp(PASSWD_HELPER_PROGRAM, PASSWD_HELPER_PROGRAM, "xscreensaver", user, NULL); if (verbose_p) fprintf(stderr, "%s: %s\n", PASSWD_HELPER_PROGRAM, strerror(errno)); exit(1); } close(pfd[0]); /* Write out password to helper process */ if (!typed_passwd) typed_passwd = ""; write(pfd[1], typed_passwd, strlen(typed_passwd)); close(pfd[1]); while (waitpid(pid, &status, 0) < 0) { if (errno == EINTR) continue; if (verbose_p) fprintf(stderr, "%s: ext_run: waitpid failed: %s\n", blurb(), strerror(errno)); unblock_sigchld(); return 0; } unblock_sigchld(); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) return 0; return 1; } /* This can be called at any time, and says whether the typed password belongs to either the logged in user (real uid, not effective); or to root. */ int ext_passwd_valid_p (const char *typed_passwd, int verbose_p) { struct passwd *pw; int res = 0; if ((pw = getpwuid(getuid())) != NULL) res = ext_run (pw->pw_name, typed_passwd, verbose_p); endpwent(); #ifdef ALLOW_ROOT_PASSWD if (!res) res = ext_run ("root", typed_passwd, verbose_p); #endif /* ALLOW_ROOT_PASSWD */ return res; } int ext_priv_init (int argc, char **argv, int verbose_p) { /* Make sure the passwd helper exists */ if (access(PASSWD_HELPER_PROGRAM, X_OK) < 0) { fprintf(stderr, "%s: warning: %s does not exist.\n" "%s: password authentication via " "external helper will not work.\n", blurb(), PASSWD_HELPER_PROGRAM, blurb()); return 0; } return 1; } #endif /* NO_LOCKING -- whole file */