2 * pwdauth.c - program to verify a given username/password pair.
4 * Run it with username in argv[1] (may be omitted - default is the
5 * current user), and send it the password over a pipe on stdin.
6 * Exit status: 0 - correct password, 1 - wrong password, >1 - other
7 * errors. For use with shadow passwords, this program should be
8 * installed setuid root.
10 * This can be used, for example, by xlock - you don't have to install
11 * this large and complex (== possibly insecure) program setuid root,
12 * just modify it to run this simple program to do the authentication.
14 * Recent versions (xlockmore-3.9) are cleaner, and drop privileges as
15 * soon as possible after getting the user's encrypted password.
16 * Using this program probably doesn't make it more secure, and has one
17 * disadvantage: since we don't get the encrypted user's password at
18 * startup (but at the time the user is authenticated), it is not clear
19 * how we should handle errors (like getpwnam() returning NULL).
20 * - fail the authentication? Problem: no way to unlock (other than kill
21 * the process from somewhere else) if the NIS server stops responding.
22 * - succeed and unlock? Problem: it's too easy to unlock by unplugging
23 * the box from the network and waiting until NIS times out...
25 * This program is Copyright (C) 1996 Marek Michalkiewicz
26 * <marekm@i17linuxb.ists.pwr.wroc.pl>.
28 * It may be used and distributed freely for any purposes. There is no
29 * warranty - use at your own risk. I am not liable for any damages etc.
30 * If you improve it, please send me your changes.
33 static char rcsid[] = "$Id: pwdauth.c 1342 2007-11-10 23:46:11Z nekral-guest $";
36 * Define USE_SYSLOG to use syslog() to log successful and failed
37 * authentication. This should be safe even if your system has
38 * the infamous syslog buffer overrun security problem...
43 * Define HAVE_GETSPNAM to get shadow passwords using getspnam().
44 * Some systems don't have getspnam(), but getpwnam() returns
45 * encrypted passwords only if running as root.
47 * According to the xlock source (not tested, except Linux) -
48 * define: Linux, Solaris 2.x, SVR4, ...
49 * undef: HP-UX with Secured Passwords, FreeBSD, NetBSD, QNX.
50 * Known not supported (yet): Ultrix, OSF/1, SCO.
55 * Define HAVE_PW_ENCRYPT to use pw_encrypt() instead of crypt().
56 * pw_encrypt() is like the standard crypt(), except that it may
57 * support better password hashing algorithms.
59 * Define if linking with libshadow.a from the shadow password
60 * suite (Linux, SunOS 4.x?).
62 #undef HAVE_PW_ENCRYPT
65 * Define HAVE_AUTH_METHODS to support the shadow suite specific
66 * extension: the encrypted password field contains a list of
67 * administrator defined authentication methods, separated by
68 * semicolons. This program only supports the standard password
69 * authentication method (a string that doesn't start with '@').
71 #undef HAVE_AUTH_METHODS
74 * FAIL_DELAY - number of seconds to sleep before exiting if the
75 * password was wrong, to slow down password guessing attempts.
79 /* No user-serviceable parts below :-). */
84 #include <sys/types.h>
92 #define LOG_AUTHPRIV LOG_AUTH
100 #ifdef HAVE_PW_ENCRYPT
101 extern char *pw_encrypt();
102 #define crypt pw_encrypt
106 * Read the password (one line) from fp. We don't turn off echo
107 * because we expect input from a pipe.
113 static char buf[128];
118 while ((ch = getc(fp)) != EOF && ch != '\0' && ch != '\n') {
119 if (cp >= buf + sizeof buf - 1)
128 * Get the password file entry for the current user. If the name
129 * returned by getlogin() is correct (matches the current real uid),
130 * return the entry for that user. Otherwise, return the entry (if
131 * any) matching the current real uid. Return NULL on failure.
133 static struct passwd *
136 uid_t uid = getuid();
137 char *name = getlogin();
140 struct passwd *pw = getpwnam(name);
142 if (pw && pw->pw_uid == uid)
145 return getpwuid(uid);
149 * Verify the password. The system-dependent shadow support is here.
152 password_auth_ok(pw, pass)
153 const struct passwd *pw;
158 #ifdef HAVE_AUTH_METHODS
167 sp = getspnam(pw->pw_name);
176 #ifdef HAVE_AUTH_METHODS
177 buf = strdup(cp); /* will be modified by strtok() */
179 fprintf(stderr, "Out of memory.\n");
182 cp = strtok(buf, ";");
183 while (cp && *cp == '@')
184 cp = strtok(NULL, ";");
186 /* fail if no password authentication for this user */
192 result = (strcmp(crypt(pass, cp), cp) == 0);
194 result = 1; /* user with no password */
196 #ifdef HAVE_AUTH_METHODS
215 openlog("pwdauth", LOG_PID | LOG_CONS, LOG_AUTHPRIV);
220 syslog(LOG_ERR, "can't get login name for uid %d.\n",
223 fprintf(stderr, "Who are you?\n");
226 strncpy(myname, pw->pw_name, sizeof myname - 1);
227 myname[sizeof myname - 1] = '\0';
235 pass = get_line(stdin);
236 if (password_auth_ok(pw, pass)) {
238 syslog(pw->pw_uid ? LOG_INFO : LOG_NOTICE,
239 "user `%s' entered correct password for `%.32s'.\n",
245 /* be careful not to overrun the syslog buffer */
246 syslog((!pw || pw->pw_uid) ? LOG_NOTICE : LOG_WARNING,
247 "user `%s' entered incorrect password for `%.32s'.\n",
253 fprintf(stderr, "Wrong password.\n");
259 * You can use code similar to the following to run this program.
260 * Return values: >=0 - program exit status (use the <sys/wait.h>
261 * macros to get the exit code, it is shifted left by 8 bits),
265 verify_password(const char *username, const char *password)
268 int pid, wpid, status;
273 if ((pid = fork()) == 0) {
279 if (pipe_fd[0] != 0) {
280 if (dup2(pipe_fd[0], 0) != 0)
284 arg[0] = "/usr/bin/pwdauth";
288 execve(arg[0], arg, env);
290 } else if (pid == -1) {
298 write(pipe_fd[1], password, strlen(password));
299 write(pipe_fd[1], "\n", 1);
302 while ((wpid = wait(&status)) != pid) {