skeylogin.c revision 1.1 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.1 1994/05/21 05:46:16 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 if(stat(KEYFILE,&statbuf) == -1 && errno == ENOENT){
116 mp->keyfile = fopen(KEYFILE,"w+");
117 } else {
118 /* Otherwise open normally for update */
119 mp->keyfile = fopen(KEYFILE,"r+");
120 }
121 if(mp->keyfile == NULL)
122 return -1;
123
124 /* Look up user name in database */
125 len = strlen(name);
126 if( len > 8 ) len = 8; /* Added 8/2/91 - nmh */
127 found = 0;
128 while(!feof(mp->keyfile)){
129 recstart = ftell(mp->keyfile);
130 mp->recstart = recstart;
131 if(fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
132 break;
133 }
134 rip(mp->buf);
135 if(mp->buf[0] == '#')
136 continue; /* Comment */
137 if((mp->logname = strtok(mp->buf," \t")) == NULL)
138 continue;
139 if((cp = strtok(NULL," \t")) == NULL)
140 continue;
141 mp->n = atoi(cp);
142 if((mp->seed = strtok(NULL," \t")) == NULL)
143 continue;
144 if((mp->val = strtok(NULL," \t")) == NULL)
145 continue;
146 if(strlen(mp->logname) == len
147 && strncmp(mp->logname,name,len) == 0){
148 found = 1;
149 break;
150 }
151 }
152 if(found){
153 fseek(mp->keyfile,recstart,0);
154 return 0;
155 } else
156 return 1;
157 }
158 /* Verify response to a s/key challenge.
159 *
160 * Return codes:
161 * -1: Error of some sort; database unchanged
162 * 0: Verify successful, database updated
163 * 1: Verify failed, database unchanged
164 *
165 * The database file is always closed by this call.
166 */
167 int
168 skeyverify(mp,response)
169 struct skey *mp;
170 char *response;
171 {
172 struct timeval startval;
173 struct timeval endval;
174 long microsec;
175 char key[8];
176 char fkey[8];
177 char filekey[8];
178 time_t now;
179 struct tm *tm;
180 char tbuf[27],buf[60];
181 char me[80];
182 int rval;
183 char *cp;
184
185 time(&now);
186 tm = localtime(&now);
187 strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm);
188
189 if(response == NULL){
190 fclose(mp->keyfile);
191 return -1;
192 }
193 rip (response);
194
195 /* Convert response to binary */
196 if(etob(key, response) != 1 && atob8(key, response) != 0){
197 /* Neither english words or ascii hex */
198 fclose(mp->keyfile);
199 return -1;
200 }
201
202 /* Compute fkey = f(key) */
203 memcpy(fkey,key,sizeof(key));
204 fflush (stdout);
205
206 f(fkey);
207 /* in order to make the window of update as short as possible
208 we must do the comparison here and if OK write it back
209 other wise the same password can be used twice to get in
210 to the system
211 */
212
213 setpriority(PRIO_PROCESS, 0, -4);
214
215 /* reread the file record NOW*/
216
217 fseek(mp->keyfile,mp->recstart,0);
218 if(fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
219 setpriority(PRIO_PROCESS, 0, 0);
220 fclose(mp->keyfile);
221 return -1;
222 }
223 rip(mp->buf);
224 mp->logname = strtok(mp->buf," \t");
225 cp = strtok(NULL," \t") ;
226 mp->seed = strtok(NULL," \t");
227 mp->val = strtok(NULL," \t");
228 /* And convert file value to hex for comparison */
229 atob8(filekey,mp->val);
230
231 /* Do actual comparison */
232 fflush (stdout);
233
234 if(memcmp(filekey,fkey,8) != 0){
235 /* Wrong response */
236 setpriority(PRIO_PROCESS, 0, 0);
237 fclose(mp->keyfile);
238 return 1;
239 }
240
241 /* Update key in database by overwriting entire record. Note
242 * that we must write exactly the same number of bytes as in
243 * the original record (note fixed width field for N)
244 */
245 btoa8(mp->val,key);
246 mp->n--;
247 fseek(mp->keyfile,mp->recstart,0);
248 fprintf(mp->keyfile,"%s %04d %-16s %s %-21s\n",mp->logname,mp->n,mp->seed,
249 mp->val, tbuf);
250
251 fclose(mp->keyfile);
252
253 setpriority(PRIO_PROCESS, 0, 0);
254 return 0;
255 }
256
257
258 /* Convert 8-byte hex-ascii string to binary array
259 * Returns 0 on success, -1 on error
260 */
261 atob8(out,in)
262 register char *out,*in;
263 {
264 register int i;
265 register int val;
266
267 if (in == NULL || out == NULL)
268 return -1;
269
270 for(i=0;i<8;i++){
271 if((in = skipspace(in)) == NULL)
272 return -1;
273 if((val = htoi(*in++)) == -1)
274 return -1;
275 *out = val << 4;
276
277 if((in = skipspace(in)) == NULL)
278 return -1;
279 if((val = htoi(*in++)) == -1)
280 return -1;
281 *out++ |= val;
282 }
283 return 0;
284 }
285
286 char *
287 skipspace(cp)
288 register char *cp;
289 {
290 while(*cp == ' ' || *cp == '\t')
291 cp++;
292
293 if(*cp == '\0')
294 return NULL;
295 else
296 return cp;
297 }
298
299 /* Convert 8-byte binary array to hex-ascii string */
300 int
301 btoa8(out,in)
302 register char *out,*in;
303 {
304 register int i;
305
306 if(in == NULL || out == NULL)
307 return -1;
308
309 for(i=0;i<8;i++){
310 sprintf(out,"%02x",*in++ & 0xff);
311 out += 2;
312 }
313 return 0;
314 }
315
316
317 /* Convert hex digit to binary integer */
318 int
319 htoi(c)
320 register char c;
321 {
322 if('0' <= c && c <= '9')
323 return c - '0';
324 if('a' <= c && c <= 'f')
325 return 10 + c - 'a';
326 if('A' <= c && c <= 'F')
327 return 10 + c - 'A';
328 return -1;
329 }
330
331 /*
332 * skey_haskey ()
333 *
334 * Returns: 1 user doesnt exist, -1 fle error, 0 user exists.
335 *
336 */
337
338 skey_haskey (username)
339 char *username;
340 {
341 int i;
342 struct skey skey;
343
344 return (skeylookup (&skey, username));
345 }
346
347 /*
348 * skey_keyinfo ()
349 *
350 * Returns the current sequence number and
351 * seed for the passed user.
352 *
353 */
354 char *skey_keyinfo (username)
355 char *username;
356 {
357 int i;
358 static char str [50];
359
360 struct skey skey;
361
362 i = skeychallenge (&skey, username, str);
363 if (i == -1)
364 return 0;
365
366 return str;
367 }
368
369 /*
370 * skey_passcheck ()
371 *
372 * Check to see if answer is the correct one to the current
373 * challenge.
374 *
375 * Returns: 0 success, -1 failure
376 *
377 */
378
379 skey_passcheck (username, passwd)
380 char *username, *passwd;
381 {
382 int i;
383 struct skey skey;
384
385 i = skeylookup (&skey, username);
386
387 if (i == -1 || i == 1)
388 return -1;
389
390 if (skeyverify (&skey, passwd) == 0)
391 return skey.n;
392
393 return -1;
394 }
395
396 /*
397 * skey_authenticate ()
398 *
399 * Used when calling program will allow input of the user's
400 * response to the challenge.
401 *
402 * Returns: 0 success, -1 failure
403 *
404 */
405
406 skey_authenticate (username)
407 char *username;
408 {
409 int i;
410 char pbuf [256], skeyprompt [50];
411 struct skey skey;
412
413 /* Attempt a S/Key challenge */
414 i = skeychallenge (&skey, username, skeyprompt);
415
416 if (i == -2)
417 return 0;
418
419 printf ("[%s]\n", skeyprompt);
420 fflush (stdout);
421
422 printf ("Response: ");
423 readpass (pbuf, sizeof (pbuf));
424 rip (pbuf);
425
426 /* Is it a valid response? */
427 if (i == 0 && skeyverify (&skey, pbuf) == 0)
428 {
429 if (skey.n < 5)
430 {
431 printf ("\nWarning! Key initialization needed soon. ");
432 printf ("(%d logins left)\n", skey.n);
433 }
434 return 0;
435 }
436 return -1;
437 }
438
439