summaryrefslogtreecommitdiffstats
path: root/contrib/pwdauth.c
blob: ca15495930810efc4dad4f3e5b2794fe9c72dfb3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
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