login_skey.c revision 1.12
1/*	$OpenBSD: login_skey.c,v 1.12 2004/03/10 21:30:27 millert 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(int argc, char *argv[])
68{
69	FILE *back = NULL;
70	char *class = 0;
71	char *username = 0;
72	char skeyprompt[SKEY_MAX_CHALLENGE+17];
73	char passbuf[SKEY_MAX_PW_LEN+1];
74	int c, haskey;
75	int mode = 0;
76
77	skeyprompt[0] = '\0';
78
79	(void)signal(SIGINT, quit);
80	(void)signal(SIGQUIT, quit);
81	(void)signal(SIGALRM, quit);
82	(void)signal(SIGTSTP, suspend);
83	(void)setpriority(PRIO_PROCESS, 0, 0);
84
85	openlog(NULL, LOG_ODELAY, LOG_AUTH);
86
87	while ((c = getopt(argc, argv, "ds:v:")) != -1)
88		switch (c) {
89		case 'd':	/* to remain undocumented */
90			back = stdout;
91			break;
92		case 'v':
93			break;
94		case 's':	/* service */
95			if (strcmp(optarg, "login") == 0)
96				mode = 0;
97			else if (strcmp(optarg, "challenge") == 0)
98				mode = 1;
99			else if (strcmp(optarg, "response") == 0)
100				mode = 2;
101			else {
102				syslog(LOG_ERR, "%s: invalid service", optarg);
103				exit(1);
104			}
105			break;
106		default:
107			syslog(LOG_ERR, "usage error");
108			exit(1);
109		}
110
111	switch (argc - optind) {
112	case 2:
113		class = argv[optind + 1];
114	case 1:
115		username = argv[optind];
116		break;
117	default:
118		syslog(LOG_ERR, "usage error");
119		exit(1);
120	}
121
122
123	if (back == NULL && (back = fdopen(3, "r+")) == NULL)  {
124		syslog(LOG_ERR, "reopening back channel: %m");
125		exit(1);
126	}
127
128	if (mode == 2) {
129		mode = 0;
130		c = -1;
131		/* XXX - redo these loops! */
132		while (++c < sizeof(skeyprompt) &&
133		    read(3, &skeyprompt[c], 1) == 1) {
134			if (skeyprompt[c] == '\0') {
135				mode++;
136				break;
137			}
138		}
139		if (mode == 1) {
140			c = -1;
141			while (++c < sizeof(passbuf) &&
142			    read(3, &passbuf[c], 1) == 1) {
143				if (passbuf[c] == '\0') {
144					mode++;
145					break;
146				}
147			}
148		}
149		if (mode < 2) {
150			syslog(LOG_ERR, "protocol error on back channel");
151			exit(1);
152		}
153		/*
154		 * Sigh.  S/Key really is a stateful protocol.
155		 * We must assume that a user will only try to
156		 * authenticate one at a time and that this call to
157		 * skeychallenge will produce the same results as
158		 * the call to skeychallenge when mode was 1.
159		 *
160		 * Furthermore, RFC2289 requires that an entry be
161		 * locked against a partial guess race which is
162		 * simply not possible if the calling program queries
163		 * the user for the passphrase itself.  Oh well.
164		 */
165		haskey = (skeychallenge(&skey, username, skeyprompt) == 0);
166	} else {
167		/*
168		 * Attempt an S/Key challenge.
169		 * The OpenBSD skeychallenge() will always fill in a
170		 * challenge, even if it has to cons one up.
171		 */
172		haskey = (skeychallenge(&skey, username, skeyprompt) == 0);
173		strlcat(skeyprompt, "\nS/Key Password: ", sizeof skeyprompt);
174		if (mode == 1) {
175			fprintf(back, BI_VALUE " challenge %s\n",
176			    auth_mkvalue(skeyprompt));
177			fprintf(back, BI_CHALLENGE "\n");
178			exit(0);
179		}
180
181		/* Time out getting passphrase after 2 minutes to avoid a DoS */
182		if (haskey)
183			alarm(120);
184		resumed = 0;
185		if (!readpassphrase(skeyprompt, passbuf, sizeof(passbuf), 0))
186			exit(1);
187		if (passbuf[0] == '\0')
188			readpassphrase("S/Key Password [echo on]: ",
189			    passbuf, sizeof(passbuf), RPP_ECHO_ON);
190		alarm(0);
191		if (resumed) {
192			/*
193			 * We were suspended by the user.  Our lock is
194			 * no longer valid so we must regain it so
195			 * an attacker cannot do a partial guess of
196			 * an S/Key response already in progress.
197			 */
198			haskey = (skeylookup(&skey, username) == 0);
199		}
200	}
201
202	/*
203	 * Ignore keyboard interupt/suspend during database update.
204	 */
205	signal(SIGINT, SIG_IGN);
206	signal(SIGQUIT, SIG_IGN);
207	signal(SIGTSTP, SIG_IGN);
208
209	if (haskey && skeyverify(&skey, passbuf) == 0) {
210		if (mode == 0) {
211			if (skey.n <= 1)
212				printf("Warning! You MUST change your "
213				    "S/Key password now!\n");
214			else if (skey.n < 5)
215				printf("Warning! Change S/Key password soon\n");
216		}
217		fprintf(back, BI_AUTH "\n");
218		fprintf(back, BI_SECURE "\n");
219		exit(0);
220	}
221	fprintf(back, BI_REJECT "\n");
222	exit(1);
223}
224
225void
226quit(int signo)
227{
228
229	_exit(1);
230}
231
232void
233suspend(int signo)
234{
235	sigset_t nset;
236	int save_errno = errno;
237
238	/*
239	 * Unlock the skey record so we don't sleep holding the lock.
240	 * Unblock SIGTSTP, set it to the default action and then
241	 * resend it so we are suspended properly.
242	 * When we resume, reblock SIGTSTP, reset the signal handler,
243	 * set a flag and restore errno.
244	 */
245	alarm(0);
246	skey_unlock(&skey);
247	(void)signal(signo, SIG_DFL);
248	(void)sigemptyset(&nset);
249	(void)sigaddset(&nset, signo);
250	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
251	(void)kill(getpid(), signo);
252	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
253	(void)signal(signo, suspend);
254	resumed = 1;
255	errno = save_errno;
256}
257