skeylogin.c revision 1.13 1 1.13 is /* $NetBSD: skeylogin.c,v 1.13 2000/04/13 13:25:56 is Exp $ */
2 1.6 thorpej
3 1.1 deraadt /* S/KEY v1.1b (skeylogin.c)
4 1.1 deraadt *
5 1.1 deraadt * Authors:
6 1.1 deraadt * Neil M. Haller <nmh (at) thumper.bellcore.com>
7 1.1 deraadt * Philip R. Karn <karn (at) chicago.qualcomm.com>
8 1.1 deraadt * John S. Walden <jsw (at) thumper.bellcore.com>
9 1.1 deraadt * Scott Chasin <chasin (at) crimelab.com>
10 1.1 deraadt *
11 1.1 deraadt * S/KEY verification check, lookups, and authentication.
12 1.1 deraadt */
13 1.1 deraadt
14 1.1 deraadt #include <sys/param.h>
15 1.1 deraadt #ifdef QUOTA
16 1.1 deraadt #include <sys/quota.h>
17 1.1 deraadt #endif
18 1.1 deraadt #include <sys/stat.h>
19 1.1 deraadt #include <sys/time.h>
20 1.1 deraadt #include <sys/timeb.h>
21 1.1 deraadt #include <sys/resource.h>
22 1.1 deraadt
23 1.5 pk
24 1.1 deraadt #include <stdio.h>
25 1.5 pk #include <stdlib.h>
26 1.1 deraadt #include <string.h>
27 1.1 deraadt #include <sys/types.h>
28 1.1 deraadt #include <sys/stat.h>
29 1.1 deraadt #include <time.h>
30 1.1 deraadt #include <errno.h>
31 1.5 pk
32 1.1 deraadt #include "skey.h"
33 1.1 deraadt
34 1.5 pk #define _PATH_KEYFILE "/etc/skeykeys"
35 1.1 deraadt
36 1.1 deraadt /* Issue a skey challenge for user 'name'. If successful,
37 1.1 deraadt * fill in the caller's skey structure and return 0. If unsuccessful
38 1.1 deraadt * (e.g., if name is unknown) return -1.
39 1.1 deraadt *
40 1.1 deraadt * The file read/write pointer is left at the start of the
41 1.1 deraadt * record.
42 1.11 mrg *
43 1.11 mrg * XXX this function should be changed to have a length param for the
44 1.11 mrg * prompt!
45 1.1 deraadt */
46 1.1 deraadt int
47 1.1 deraadt getskeyprompt(mp,name,prompt)
48 1.5 pk struct skey *mp;
49 1.5 pk char *name;
50 1.5 pk char *prompt;
51 1.1 deraadt {
52 1.1 deraadt int rval;
53 1.1 deraadt
54 1.1 deraadt sevenbit(name);
55 1.1 deraadt rval = skeylookup(mp,name);
56 1.7 mrg #if 0
57 1.7 mrg strcpy(prompt, "s/key 55 latour1\n");
58 1.7 mrg #endif
59 1.5 pk switch (rval) {
60 1.1 deraadt case -1: /* File error */
61 1.1 deraadt return -1;
62 1.1 deraadt case 0: /* Lookup succeeded, return challenge */
63 1.11 mrg sprintf(prompt,"s/key %d %s\n",mp->n - 1,mp->seed);
64 1.1 deraadt return 0;
65 1.1 deraadt case 1: /* User not found */
66 1.1 deraadt fclose(mp->keyfile);
67 1.1 deraadt return -1;
68 1.1 deraadt }
69 1.1 deraadt return -1; /* Can't happen */
70 1.5 pk }
71 1.5 pk
72 1.1 deraadt /* Return a skey challenge string for user 'name'. If successful,
73 1.1 deraadt * fill in the caller's skey structure and return 0. If unsuccessful
74 1.1 deraadt * (e.g., if name is unknown) return -1.
75 1.1 deraadt *
76 1.1 deraadt * The file read/write pointer is left at the start of the
77 1.1 deraadt * record.
78 1.1 deraadt */
79 1.1 deraadt int
80 1.7 mrg skeychallenge(mp,name, ss, sslen)
81 1.5 pk struct skey *mp;
82 1.9 mycroft const char *name;
83 1.5 pk char *ss;
84 1.7 mrg int sslen;
85 1.1 deraadt {
86 1.1 deraadt int rval;
87 1.1 deraadt
88 1.1 deraadt rval = skeylookup(mp,name);
89 1.1 deraadt switch(rval){
90 1.1 deraadt case -1: /* File error */
91 1.1 deraadt return -1;
92 1.1 deraadt case 0: /* Lookup succeeded, issue challenge */
93 1.12 mycroft (void)snprintf(ss, (size_t)sslen, "s/key %d %s", mp->n - 1,
94 1.12 mycroft mp->seed);
95 1.1 deraadt return 0;
96 1.1 deraadt case 1: /* User not found */
97 1.1 deraadt fclose(mp->keyfile);
98 1.1 deraadt return -1;
99 1.1 deraadt }
100 1.1 deraadt return -1; /* Can't happen */
101 1.10 simonb }
102 1.1 deraadt
103 1.1 deraadt /* Find an entry in the One-time Password database.
104 1.1 deraadt * Return codes:
105 1.1 deraadt * -1: error in opening database
106 1.1 deraadt * 0: entry found, file R/W pointer positioned at beginning of record
107 1.1 deraadt * 1: entry not found, file R/W pointer positioned at EOF
108 1.1 deraadt */
109 1.1 deraadt int
110 1.1 deraadt skeylookup(mp,name)
111 1.5 pk struct skey *mp;
112 1.9 mycroft const char *name;
113 1.1 deraadt {
114 1.1 deraadt int found;
115 1.12 mycroft size_t len;
116 1.8 christos long recstart = 0;
117 1.1 deraadt char *cp;
118 1.1 deraadt struct stat statbuf;
119 1.1 deraadt
120 1.5 pk /* See if the _PATH_KEYFILE exists, and create it if not */
121 1.2 deraadt
122 1.5 pk if (stat(_PATH_KEYFILE,&statbuf) == -1 && errno == ENOENT) {
123 1.5 pk mp->keyfile = fopen(_PATH_KEYFILE,"w+");
124 1.5 pk if (mp->keyfile)
125 1.5 pk chmod(_PATH_KEYFILE, 0644);
126 1.1 deraadt } else {
127 1.1 deraadt /* Otherwise open normally for update */
128 1.5 pk mp->keyfile = fopen(_PATH_KEYFILE,"r+");
129 1.1 deraadt }
130 1.5 pk if (mp->keyfile == NULL)
131 1.1 deraadt return -1;
132 1.1 deraadt
133 1.1 deraadt /* Look up user name in database */
134 1.1 deraadt len = strlen(name);
135 1.13 is if (len > 8)
136 1.13 is len = 8; /* Added 8/2/91 - nmh */
137 1.1 deraadt found = 0;
138 1.5 pk while (!feof(mp->keyfile)) {
139 1.1 deraadt recstart = ftell(mp->keyfile);
140 1.1 deraadt mp->recstart = recstart;
141 1.5 pk if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf) {
142 1.1 deraadt break;
143 1.1 deraadt }
144 1.1 deraadt rip(mp->buf);
145 1.5 pk if (mp->buf[0] == '#')
146 1.1 deraadt continue; /* Comment */
147 1.5 pk if ((mp->logname = strtok(mp->buf," \t")) == NULL)
148 1.1 deraadt continue;
149 1.5 pk if ((cp = strtok(NULL," \t")) == NULL)
150 1.1 deraadt continue;
151 1.1 deraadt mp->n = atoi(cp);
152 1.5 pk if ((mp->seed = strtok(NULL," \t")) == NULL)
153 1.1 deraadt continue;
154 1.5 pk if ((mp->val = strtok(NULL," \t")) == NULL)
155 1.1 deraadt continue;
156 1.5 pk if (strlen(mp->logname) == len
157 1.1 deraadt && strncmp(mp->logname,name,len) == 0){
158 1.1 deraadt found = 1;
159 1.1 deraadt break;
160 1.1 deraadt }
161 1.1 deraadt }
162 1.5 pk if (found) {
163 1.1 deraadt fseek(mp->keyfile,recstart,0);
164 1.1 deraadt return 0;
165 1.1 deraadt } else
166 1.1 deraadt return 1;
167 1.1 deraadt }
168 1.1 deraadt /* Verify response to a s/key challenge.
169 1.1 deraadt *
170 1.1 deraadt * Return codes:
171 1.1 deraadt * -1: Error of some sort; database unchanged
172 1.1 deraadt * 0: Verify successful, database updated
173 1.1 deraadt * 1: Verify failed, database unchanged
174 1.1 deraadt *
175 1.1 deraadt * The database file is always closed by this call.
176 1.1 deraadt */
177 1.1 deraadt int
178 1.1 deraadt skeyverify(mp,response)
179 1.5 pk struct skey *mp;
180 1.5 pk char *response;
181 1.1 deraadt {
182 1.1 deraadt char key[8];
183 1.1 deraadt char fkey[8];
184 1.1 deraadt char filekey[8];
185 1.1 deraadt time_t now;
186 1.13 is int prevprio;
187 1.1 deraadt struct tm *tm;
188 1.5 pk char tbuf[27];
189 1.1 deraadt
190 1.1 deraadt time(&now);
191 1.1 deraadt tm = localtime(&now);
192 1.1 deraadt strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm);
193 1.1 deraadt
194 1.5 pk if (response == NULL) {
195 1.1 deraadt fclose(mp->keyfile);
196 1.1 deraadt return -1;
197 1.1 deraadt }
198 1.5 pk rip(response);
199 1.1 deraadt
200 1.1 deraadt /* Convert response to binary */
201 1.5 pk if (etob(key, response) != 1 && atob8(key, response) != 0) {
202 1.1 deraadt /* Neither english words or ascii hex */
203 1.1 deraadt fclose(mp->keyfile);
204 1.1 deraadt return -1;
205 1.1 deraadt }
206 1.1 deraadt
207 1.1 deraadt /* Compute fkey = f(key) */
208 1.1 deraadt memcpy(fkey,key,sizeof(key));
209 1.1 deraadt fflush (stdout);
210 1.1 deraadt
211 1.1 deraadt f(fkey);
212 1.5 pk /*
213 1.5 pk * in order to make the window of update as short as possible
214 1.5 pk * we must do the comparison here and if OK write it back
215 1.5 pk * other wise the same password can be used twice to get in
216 1.5 pk * to the system
217 1.5 pk */
218 1.1 deraadt
219 1.13 is /* Save the priority for later use. */
220 1.13 is errno = 0;
221 1.13 is prevprio = getpriority(PRIO_PROCESS, 0);
222 1.13 is if (errno) {
223 1.13 is /* Don't report the error; just don't use it later. */
224 1.13 is prevprio = PRIO_MAX + 1;
225 1.13 is } else {
226 1.13 is setpriority(PRIO_PROCESS, 0, -4);
227 1.13 is }
228 1.1 deraadt
229 1.1 deraadt /* reread the file record NOW*/
230 1.1 deraadt
231 1.1 deraadt fseek(mp->keyfile,mp->recstart,0);
232 1.5 pk if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
233 1.13 is if (prevprio != PRIO_MAX + 1) {
234 1.13 is setpriority(PRIO_PROCESS, 0, prevprio);
235 1.13 is }
236 1.1 deraadt fclose(mp->keyfile);
237 1.1 deraadt return -1;
238 1.1 deraadt }
239 1.1 deraadt rip(mp->buf);
240 1.1 deraadt mp->logname = strtok(mp->buf," \t");
241 1.12 mycroft (void) strtok(NULL," \t"); /* ignore counter */
242 1.1 deraadt mp->seed = strtok(NULL," \t");
243 1.1 deraadt mp->val = strtok(NULL," \t");
244 1.1 deraadt /* And convert file value to hex for comparison */
245 1.1 deraadt atob8(filekey,mp->val);
246 1.1 deraadt
247 1.1 deraadt /* Do actual comparison */
248 1.1 deraadt fflush (stdout);
249 1.1 deraadt
250 1.5 pk if (memcmp(filekey,fkey,8) != 0){
251 1.1 deraadt /* Wrong response */
252 1.13 is if (prevprio != PRIO_MAX + 1) {
253 1.13 is setpriority(PRIO_PROCESS, 0, prevprio);
254 1.13 is }
255 1.1 deraadt fclose(mp->keyfile);
256 1.1 deraadt return 1;
257 1.1 deraadt }
258 1.1 deraadt
259 1.5 pk /*
260 1.5 pk * Update key in database by overwriting entire record. Note
261 1.1 deraadt * that we must write exactly the same number of bytes as in
262 1.1 deraadt * the original record (note fixed width field for N)
263 1.1 deraadt */
264 1.1 deraadt btoa8(mp->val,key);
265 1.1 deraadt mp->n--;
266 1.1 deraadt fseek(mp->keyfile,mp->recstart,0);
267 1.5 pk fprintf(mp->keyfile, "%s %04d %-16s %s %-21s\n",
268 1.5 pk mp->logname,mp->n,mp->seed, mp->val, tbuf);
269 1.1 deraadt
270 1.1 deraadt fclose(mp->keyfile);
271 1.10 simonb
272 1.13 is if (prevprio != PRIO_MAX + 1) {
273 1.13 is setpriority(PRIO_PROCESS, 0, prevprio);
274 1.13 is }
275 1.1 deraadt return 0;
276 1.1 deraadt }
277 1.1 deraadt
278 1.1 deraadt
279 1.1 deraadt /*
280 1.1 deraadt * skey_haskey ()
281 1.1 deraadt *
282 1.1 deraadt * Returns: 1 user doesnt exist, -1 fle error, 0 user exists.
283 1.1 deraadt *
284 1.1 deraadt */
285 1.10 simonb
286 1.5 pk int
287 1.1 deraadt skey_haskey (username)
288 1.9 mycroft const char *username;
289 1.1 deraadt {
290 1.5 pk struct skey skey;
291 1.10 simonb
292 1.5 pk return (skeylookup (&skey, username));
293 1.1 deraadt }
294 1.10 simonb
295 1.1 deraadt /*
296 1.1 deraadt * skey_keyinfo ()
297 1.1 deraadt *
298 1.1 deraadt * Returns the current sequence number and
299 1.1 deraadt * seed for the passed user.
300 1.1 deraadt *
301 1.1 deraadt */
302 1.5 pk char *
303 1.5 pk skey_keyinfo (username)
304 1.9 mycroft const char *username;
305 1.1 deraadt {
306 1.5 pk int i;
307 1.5 pk static char str [50];
308 1.5 pk struct skey skey;
309 1.5 pk
310 1.7 mrg i = skeychallenge (&skey, username, str, sizeof str);
311 1.5 pk if (i == -1)
312 1.5 pk return 0;
313 1.1 deraadt
314 1.5 pk return str;
315 1.1 deraadt }
316 1.10 simonb
317 1.1 deraadt /*
318 1.1 deraadt * skey_passcheck ()
319 1.1 deraadt *
320 1.1 deraadt * Check to see if answer is the correct one to the current
321 1.1 deraadt * challenge.
322 1.1 deraadt *
323 1.1 deraadt * Returns: 0 success, -1 failure
324 1.1 deraadt *
325 1.1 deraadt */
326 1.10 simonb
327 1.5 pk int
328 1.1 deraadt skey_passcheck (username, passwd)
329 1.9 mycroft const char *username;
330 1.9 mycroft char *passwd;
331 1.1 deraadt {
332 1.5 pk int i;
333 1.5 pk struct skey skey;
334 1.5 pk
335 1.5 pk i = skeylookup (&skey, username);
336 1.5 pk if (i == -1 || i == 1)
337 1.5 pk return -1;
338 1.5 pk
339 1.5 pk if (skeyverify (&skey, passwd) == 0)
340 1.5 pk return skey.n;
341 1.5 pk
342 1.5 pk return -1;
343 1.1 deraadt }
344 1.1 deraadt
345 1.1 deraadt /*
346 1.1 deraadt * skey_authenticate ()
347 1.1 deraadt *
348 1.1 deraadt * Used when calling program will allow input of the user's
349 1.1 deraadt * response to the challenge.
350 1.1 deraadt *
351 1.1 deraadt * Returns: 0 success, -1 failure
352 1.1 deraadt *
353 1.1 deraadt */
354 1.10 simonb
355 1.5 pk int
356 1.1 deraadt skey_authenticate (username)
357 1.9 mycroft const char *username;
358 1.1 deraadt {
359 1.5 pk int i;
360 1.5 pk char pbuf[256], skeyprompt[50];
361 1.5 pk struct skey skey;
362 1.5 pk
363 1.5 pk /* Attempt a S/Key challenge */
364 1.7 mrg i = skeychallenge (&skey, username, skeyprompt, sizeof skeyprompt);
365 1.5 pk
366 1.5 pk if (i == -2)
367 1.5 pk return 0;
368 1.5 pk
369 1.5 pk printf("[%s]\n", skeyprompt);
370 1.5 pk fflush(stdout);
371 1.5 pk
372 1.5 pk printf("Response: ");
373 1.5 pk readskey(pbuf, sizeof (pbuf));
374 1.5 pk rip(pbuf);
375 1.5 pk
376 1.5 pk /* Is it a valid response? */
377 1.5 pk if (i == 0 && skeyverify (&skey, pbuf) == 0) {
378 1.5 pk if (skey.n < 5) {
379 1.5 pk printf ("\nWarning! Key initialization needed soon. ");
380 1.5 pk printf ("(%d logins left)\n", skey.n);
381 1.5 pk }
382 1.5 pk return 0;
383 1.5 pk }
384 1.5 pk return -1;
385 1.1 deraadt }
386