xref: /openbsd/libexec/login_skey/login_skey.c (revision 89c6b15d)
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 
60 void quit(int);
61 void suspend(int);
62 
63 volatile sig_atomic_t resumed;
64 struct skey skey;
65 
66 int
67 main(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 
225 void
226 quit(int signo)
227 {
228 
229 	_exit(1);
230 }
231 
232 void
233 suspend(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