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