login_skey.c revision 1.10
1/*	$OpenBSD: login_skey.c,v 1.10 2002/06/28 01:14:37 deraadt Exp $	*/
2
3/*-
4 * Copyright (c) 1995 Berkeley Software Design, Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *      This product includes software developed by Berkeley Software Design,
17 *      Inc.
18 * 4. The name of Berkeley Software Design, Inc.  may not be used to endorse
19 *    or promote products derived from this software without specific prior
20 *    written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 *	BSDI $From: login_skey.c,v 1.3 1996/09/04 05:24:56 prb Exp $
35 */
36#include <sys/types.h>
37#include <sys/param.h>
38#include <sys/stat.h>
39#include <sys/time.h>
40#include <sys/resource.h>
41
42#include <ctype.h>
43#include <errno.h>
44#include <fcntl.h>
45#include <paths.h>
46#include <pwd.h>
47#include <readpassphrase.h>
48#include <signal.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <syslog.h>
53#include <unistd.h>
54
55#include <login_cap.h>
56#include <bsd_auth.h>
57#include <sha1.h>
58#include <skey.h>
59
60void quit(int);
61void suspend(int);
62
63volatile sig_atomic_t resumed;
64struct skey skey;
65
66int
67main(argc, argv)
68	int argc;
69	char **argv;
70{
71	FILE *back = NULL;
72	char *class = 0;
73	char *username = 0;
74	char skeyprompt[SKEY_MAX_CHALLENGE+17];
75	char passbuf[SKEY_MAX_PW_LEN+1];
76	int c, haskey;
77	int mode = 0;
78
79	skeyprompt[0] = '\0';
80
81	(void)signal(SIGINT, quit);
82	(void)signal(SIGQUIT, quit);
83	(void)signal(SIGALRM, quit);
84	(void)signal(SIGTSTP, suspend);
85	(void)setpriority(PRIO_PROCESS, 0, 0);
86
87	openlog(NULL, LOG_ODELAY, LOG_AUTH);
88
89	while ((c = getopt(argc, argv, "ds:v:")) != -1)
90		switch (c) {
91		case 'd':	/* to remain undocumented */
92			back = stdout;
93			break;
94		case 'v':
95			break;
96		case 's':	/* service */
97			if (strcmp(optarg, "login") == 0)
98				mode = 0;
99			else if (strcmp(optarg, "challenge") == 0)
100				mode = 1;
101			else if (strcmp(optarg, "response") == 0)
102				mode = 2;
103			else {
104				syslog(LOG_ERR, "%s: invalid service", optarg);
105				exit(1);
106			}
107			break;
108		default:
109			syslog(LOG_ERR, "usage error");
110			exit(1);
111		}
112
113	switch (argc - optind) {
114	case 2:
115		class = argv[optind + 1];
116	case 1:
117		username = argv[optind];
118		break;
119	default:
120		syslog(LOG_ERR, "usage error");
121		exit(1);
122	}
123
124
125	if (back == NULL && (back = fdopen(3, "r+")) == NULL)  {
126		syslog(LOG_ERR, "reopening back channel: %m");
127		exit(1);
128	}
129
130	if (mode == 2) {
131		mode = 0;
132		c = -1;
133		/* XXX - redo these loops! */
134		while (++c < sizeof(skeyprompt) &&
135		    read(3, &skeyprompt[c], 1) == 1) {
136			if (skeyprompt[c] == '\0') {
137				mode++;
138				break;
139			}
140		}
141		if (mode == 1) {
142			c = -1;
143			while (++c < sizeof(passbuf) &&
144			    read(3, &passbuf[c], 1) == 1) {
145				if (passbuf[c] == '\0') {
146					mode++;
147					break;
148				}
149			}
150		}
151		if (mode < 2) {
152			syslog(LOG_ERR, "protocol error on back channel");
153			exit(1);
154		}
155		/*
156		 * Sigh.  S/Key really is a stateful protocol.
157		 * We must assume that a user will only try to
158		 * authenticate one at a time and that this call to
159		 * skeychallenge will produce the same results as
160		 * the call to skeychallenge when mode was 1.
161		 *
162		 * Furthermore, RFC2289 requires that an entry be
163		 * locked against a partial guess race which is
164		 * simply not possible if the calling program queries
165		 * the user for the passphrase itself.  Oh well.
166		 */
167		haskey = (skeychallenge(&skey, username, skeyprompt) == 0);
168	} else {
169		/*
170		 * Attempt an S/Key challenge.
171		 * The OpenBSD skeychallenge() will always fill in a
172		 * challenge, even if it has to cons one up.
173		 */
174		haskey = (skeychallenge(&skey, username, skeyprompt) == 0);
175		strlcat(skeyprompt, "\nS/Key Password: ", sizeof skeyprompt);
176		if (mode == 1) {
177			fprintf(back, BI_VALUE " challenge %s\n",
178			    auth_mkvalue(skeyprompt));
179			fprintf(back, BI_CHALLENGE "\n");
180			exit(0);
181		}
182
183		/* Time out getting passphrase after 2 minutes to avoid a DoS */
184		if (haskey)
185			alarm(120);
186		resumed = 0;
187		readpassphrase(skeyprompt, passbuf, sizeof(passbuf), 0);
188		if (passbuf[0] == '\0')
189			readpassphrase("S/Key Password [echo on]: ",
190			    passbuf, sizeof(passbuf), RPP_ECHO_ON);
191		alarm(0);
192		if (resumed) {
193			/*
194			 * We were suspended by the user.  Our lock is
195			 * no longer valid so we must regain it so
196			 * an attacker cannot do a partial guess of
197			 * an S/Key response already in progress.
198			 */
199			haskey = (skeylookup(&skey, username) == 0);
200		}
201	}
202
203	/*
204	 * Ignore keyboard interupt/suspend during database update.
205	 */
206	signal(SIGINT, SIG_IGN);
207	signal(SIGQUIT, SIG_IGN);
208	signal(SIGTSTP, SIG_IGN);
209
210	if (haskey && skeyverify(&skey, passbuf) == 0) {
211		if (mode == 0) {
212			if (skey.n <= 1)
213				printf("Warning! You MUST change your "
214				    "S/Key password now!\n");
215			else if (skey.n < 5)
216				printf("Warning! Change S/Key password soon\n");
217		}
218		fprintf(back, BI_AUTH "\n");
219		fprintf(back, BI_SECURE "\n");
220		exit(0);
221	}
222	fprintf(back, BI_REJECT "\n");
223	exit(1);
224}
225
226void
227quit(signo)
228	int signo;
229{
230
231	_exit(1);
232}
233
234void
235suspend(signo)
236	int signo;
237{
238	sigset_t nset;
239	int save_errno = errno;
240
241	/*
242	 * Unlock the skey record so we don't sleep holding the lock.
243	 * Unblock SIGTSTP, set it to the default action and then
244	 * resend it so we are suspended properly.
245	 * When we resume, reblock SIGTSTP, reset the signal handler,
246	 * set a flag and restore errno.
247	 */
248	alarm(0);
249	skey_unlock(&skey);
250	(void)signal(signo, SIG_DFL);
251	(void)sigemptyset(&nset);
252	(void)sigaddset(&nset, signo);
253	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
254	(void)kill(getpid(), signo);
255	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
256	(void)signal(signo, suspend);
257	resumed = 1;
258	errno = save_errno;
259}
260