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