GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: libexec/login_skey/login_skey.c Lines: 0 118 0.0 %
Date: 2017-11-07 Branches: 0 74 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: login_skey.c,v 1.26 2017/07/20 15:47:29 bluhm Exp $	*/
2
3
/*
4
 * Copyright (c) 2000, 2001, 2004 Todd C. Miller <Todd.Miller@courtesan.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/socket.h>
20
#include <sys/stat.h>
21
#include <sys/time.h>
22
#include <sys/resource.h>
23
24
#include <ctype.h>
25
#include <errno.h>
26
#include <fcntl.h>
27
#include <paths.h>
28
#include <pwd.h>
29
#include <readpassphrase.h>
30
#include <signal.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
#include <syslog.h>
35
#include <unistd.h>
36
#include <limits.h>
37
#include <err.h>
38
39
#include <login_cap.h>
40
#include <bsd_auth.h>
41
#include <skey.h>
42
43
#define	MODE_LOGIN	0
44
#define	MODE_CHALLENGE	1
45
#define	MODE_RESPONSE	2
46
47
void quit(int);
48
void send_fd(int);
49
void suspend(int);
50
51
volatile sig_atomic_t resumed;
52
struct skey skey;
53
54
int
55
main(int argc, char *argv[])
56
{
57
	FILE *back = NULL;
58
	char *user = NULL, *cp, *ep;
59
	char challenge[SKEY_MAX_CHALLENGE+17], response[SKEY_MAX_PW_LEN+1];
60
	const char *errstr;
61
	int ch, fd = -1, haskey = 0, mode = MODE_LOGIN;
62
63
	(void)signal(SIGINT, quit);
64
	(void)signal(SIGQUIT, quit);
65
	(void)signal(SIGALRM, quit);
66
	(void)signal(SIGTSTP, suspend);
67
	(void)setpriority(PRIO_PROCESS, 0, 0);
68
69
	if (pledge("stdio rpath wpath flock sendfd proc tty", NULL) == -1) {
70
		syslog(LOG_AUTH|LOG_ERR, "pledge: %m");
71
		exit(1);
72
	}
73
74
	openlog(NULL, LOG_ODELAY, LOG_AUTH);
75
76
	while ((ch = getopt(argc, argv, "ds:v:")) != -1) {
77
		switch (ch) {
78
		case 'd':
79
			back = stdout;
80
			break;
81
		case 's':	/* service */
82
			if (strcmp(optarg, "login") == 0)
83
				mode = MODE_LOGIN;
84
			else if (strcmp(optarg, "challenge") == 0)
85
				mode = MODE_CHALLENGE;
86
			else if (strcmp(optarg, "response") == 0)
87
				mode = MODE_RESPONSE;
88
			else {
89
				syslog(LOG_ERR, "%s: invalid service", optarg);
90
				exit(1);
91
			}
92
			break;
93
		case 'v':
94
			if (strncmp(optarg, "fd=", 3) == 0) {
95
				fd = strtonum(optarg + 3, 0, INT_MAX, &errstr);
96
				if (errstr != NULL) {
97
					syslog(LOG_ERR, "fd is %s: %s",
98
					    errstr, optarg + 3);
99
					fd = -1;
100
				}
101
			}
102
			/* silently ignore unsupported variables */
103
			break;
104
		default:
105
			syslog(LOG_ERR, "usage error");
106
			exit(1);
107
		}
108
	}
109
	argc -= optind;
110
	argv += optind;
111
112
	switch (argc) {
113
	case 2:	/* silently ignore class */
114
	case 1:
115
		user = *argv;
116
		break;
117
	default:
118
		syslog(LOG_ERR, "usage error");
119
		exit(1);
120
	}
121
122
	if (back == NULL && (back = fdopen(3, "r+")) == NULL)  {
123
		syslog(LOG_ERR, "reopening back channel: %m");
124
		exit(1);
125
	}
126
127
	/*
128
	 * Note: our skeychallenge2() will always fill in the challenge,
129
	 *       even if it has to create a fake one.
130
	 */
131
	switch (mode) {
132
	case MODE_LOGIN:
133
		haskey = (skeychallenge2(fd, &skey, user, challenge) == 0);
134
		strlcat(challenge, "\nS/Key Password:", sizeof(challenge));
135
136
		/* time out getting passphrase after 2 minutes to avoid a DoS */
137
		if (haskey)
138
			alarm(120);
139
		resumed = 0;
140
		if (!readpassphrase(challenge, response, sizeof(response), 0))
141
			exit(1);
142
		if (response[0] == '\0')
143
			readpassphrase("S/Key Password [echo on]: ",
144
			    response, sizeof(response), RPP_ECHO_ON);
145
		alarm(0);
146
		if (resumed) {
147
			/*
148
			 * We were suspended by the user.  Our lock is
149
			 * no longer valid so we must regain it so
150
			 * an attacker cannot do a partial guess of
151
			 * an S/Key response already in progress.
152
			 */
153
			haskey = (skeylookup(&skey, user) == 0);
154
		}
155
		break;
156
157
	case MODE_CHALLENGE:
158
		haskey = (skeychallenge2(fd, &skey, user, challenge) == 0);
159
		strlcat(challenge, "\nS/Key Password:", sizeof(challenge));
160
		fprintf(back, BI_VALUE " challenge %s\n",
161
		    auth_mkvalue(challenge));
162
		fprintf(back, BI_CHALLENGE "\n");
163
		if (haskey) {
164
			fprintf(back, BI_FDPASS "\n");
165
			fflush(back);
166
			send_fd(fileno(back));
167
		}
168
		exit(0);
169
170
	case MODE_RESPONSE:
171
		/* read challenge */
172
		mode = -1;
173
		cp = challenge;
174
		ep = challenge + sizeof(challenge);
175
		while (cp < ep && read(fileno(back), cp, 1) == 1) {
176
			if (*cp++ == '\0') {
177
				mode = MODE_CHALLENGE;
178
				break;
179
			}
180
		}
181
		if (mode != MODE_CHALLENGE) {
182
			syslog(LOG_ERR,
183
			    "protocol error: bad/missing challenge");
184
			exit(1);
185
		}
186
187
		/* read response */
188
		cp = response;
189
		ep = response + sizeof(response);
190
		while (cp < ep && read(fileno(back), cp, 1) == 1) {
191
			if (*cp++ == '\0') {
192
				mode = MODE_RESPONSE;
193
				break;
194
			}
195
		}
196
		if (mode != MODE_RESPONSE) {
197
			syslog(LOG_ERR,
198
			    "protocol error: bad/missing response");
199
			exit(1);
200
		}
201
202
		/*
203
		 * Since the entry is locked we do not need to compare
204
		 * the passed in challenge to the S/Key database but
205
		 * maybe we should anyway?
206
		 */
207
		haskey = (skeychallenge2(fd, &skey, user, challenge) == 0);
208
		break;
209
	}
210
211
	/*
212
	 * Ignore keyboard interrupt/suspend during database update.
213
	 */
214
	signal(SIGINT, SIG_IGN);
215
	signal(SIGQUIT, SIG_IGN);
216
	signal(SIGTSTP, SIG_IGN);
217
218
	if (haskey && skeyverify(&skey, response) == 0) {
219
		if (mode == MODE_LOGIN) {
220
			if (skey.n <= 1)
221
				printf("Warning! You MUST change your "
222
				    "S/Key password now!\n");
223
			else if (skey.n < 5)
224
				printf("Warning! Change S/Key password soon\n");
225
		}
226
		fprintf(back, BI_AUTH "\n");
227
		fprintf(back, BI_SECURE "\n");
228
		exit(0);
229
	}
230
	fprintf(back, BI_REJECT "\n");
231
	exit(1);
232
}
233
234
/* ARGSUSED */
235
void
236
quit(int signo)
237
{
238
239
	_exit(1);
240
}
241
242
/* ARGSUSED */
243
void
244
suspend(int signo)
245
{
246
	sigset_t nset;
247
	int save_errno = errno;
248
249
	/*
250
	 * Unlock the skey record so we don't sleep holding the lock.
251
	 * Unblock SIGTSTP, set it to the default action and then
252
	 * resend it so we are suspended properly.
253
	 * When we resume, reblock SIGTSTP, reset the signal handler,
254
	 * set a flag and restore errno.
255
	 */
256
	alarm(0);
257
	skey_unlock(&skey);
258
	(void)signal(signo, SIG_DFL);
259
	(void)sigemptyset(&nset);
260
	(void)sigaddset(&nset, signo);
261
	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
262
	(void)kill(getpid(), signo);
263
	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
264
	(void)signal(signo, suspend);
265
	resumed = 1;
266
	errno = save_errno;
267
}
268
269
void
270
send_fd(int sock)
271
{
272
	struct msghdr msg;
273
	struct cmsghdr *cmp;
274
	union {
275
		struct cmsghdr hdr;
276
		char buf[CMSG_SPACE(sizeof(int))];
277
	} cmsgbuf;
278
279
	memset(&msg, 0, sizeof(msg));
280
	msg.msg_control = &cmsgbuf.buf;
281
	msg.msg_controllen = sizeof(cmsgbuf.buf);
282
283
	cmp = CMSG_FIRSTHDR(&msg);
284
	cmp->cmsg_len = CMSG_LEN(sizeof(int));
285
	cmp->cmsg_level = SOL_SOCKET;
286
	cmp->cmsg_type = SCM_RIGHTS;
287
288
	*(int *)CMSG_DATA(cmp) = fileno(skey.keyfile);
289
290
	if (sendmsg(sock, &msg, 0) < 0)
291
		syslog(LOG_ERR, "sendmsg: %m");
292
}