skeylogin.c revision 1.5 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.5 1995/06/05 19:48:38 pk 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 <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 char *skipspace __ARGS((char *));
37 int skeylookup __ARGS((struct skey *, char *));
38
39 /* Issue a skey challenge for user 'name'. If successful,
40 * fill in the caller's skey structure and return 0. If unsuccessful
41 * (e.g., if name is unknown) return -1.
42 *
43 * The file read/write pointer is left at the start of the
44 * record.
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 strcpy(prompt,"s/key 55 latour1\n");
57 switch (rval) {
58 case -1: /* File error */
59 return -1;
60 case 0: /* Lookup succeeded, return challenge */
61 sprintf(prompt,"s/key %d %s\n",mp->n - 1,mp->seed);
62 return 0;
63 case 1: /* User not found */
64 fclose(mp->keyfile);
65 return -1;
66 }
67 return -1; /* Can't happen */
68 }
69
70 /* Return a skey challenge string for user 'name'. If successful,
71 * fill in the caller's skey structure and return 0. If unsuccessful
72 * (e.g., if name is unknown) return -1.
73 *
74 * The file read/write pointer is left at the start of the
75 * record.
76 */
77 int
78 skeychallenge(mp,name, ss)
79 struct skey *mp;
80 char *name;
81 char *ss;
82 {
83 int rval;
84
85 rval = skeylookup(mp,name);
86 switch(rval){
87 case -1: /* File error */
88 return -1;
89 case 0: /* Lookup succeeded, issue challenge */
90 sprintf(ss, "s/key %d %s",mp->n - 1,mp->seed);
91 return 0;
92 case 1: /* User not found */
93 fclose(mp->keyfile);
94 return -1;
95 }
96 return -1; /* Can't happen */
97 }
98
99 /* Find an entry in the One-time Password database.
100 * Return codes:
101 * -1: error in opening database
102 * 0: entry found, file R/W pointer positioned at beginning of record
103 * 1: entry not found, file R/W pointer positioned at EOF
104 */
105 int
106 skeylookup(mp,name)
107 struct skey *mp;
108 char *name;
109 {
110 int found;
111 int len;
112 long recstart;
113 char *cp;
114 struct stat statbuf;
115
116 /* See if the _PATH_KEYFILE exists, and create it if not */
117
118 if (stat(_PATH_KEYFILE,&statbuf) == -1 && errno == ENOENT) {
119 mp->keyfile = fopen(_PATH_KEYFILE,"w+");
120 if (mp->keyfile)
121 chmod(_PATH_KEYFILE, 0644);
122 } else {
123 /* Otherwise open normally for update */
124 mp->keyfile = fopen(_PATH_KEYFILE,"r+");
125 }
126 if (mp->keyfile == NULL)
127 return -1;
128
129 /* Look up user name in database */
130 len = strlen(name);
131 if( len > 8 ) len = 8; /* Added 8/2/91 - nmh */
132 found = 0;
133 while (!feof(mp->keyfile)) {
134 recstart = ftell(mp->keyfile);
135 mp->recstart = recstart;
136 if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf) {
137 break;
138 }
139 rip(mp->buf);
140 if (mp->buf[0] == '#')
141 continue; /* Comment */
142 if ((mp->logname = strtok(mp->buf," \t")) == NULL)
143 continue;
144 if ((cp = strtok(NULL," \t")) == NULL)
145 continue;
146 mp->n = atoi(cp);
147 if ((mp->seed = strtok(NULL," \t")) == NULL)
148 continue;
149 if ((mp->val = strtok(NULL," \t")) == NULL)
150 continue;
151 if (strlen(mp->logname) == len
152 && strncmp(mp->logname,name,len) == 0){
153 found = 1;
154 break;
155 }
156 }
157 if (found) {
158 fseek(mp->keyfile,recstart,0);
159 return 0;
160 } else
161 return 1;
162 }
163 /* Verify response to a s/key challenge.
164 *
165 * Return codes:
166 * -1: Error of some sort; database unchanged
167 * 0: Verify successful, database updated
168 * 1: Verify failed, database unchanged
169 *
170 * The database file is always closed by this call.
171 */
172 int
173 skeyverify(mp,response)
174 struct skey *mp;
175 char *response;
176 {
177 char key[8];
178 char fkey[8];
179 char filekey[8];
180 time_t now;
181 struct tm *tm;
182 char tbuf[27];
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 /*
208 * in order to make the window of update as short as possible
209 * we must do the comparison here and if OK write it back
210 * other wise the same password can be used twice to get in
211 * to the system
212 */
213
214 setpriority(PRIO_PROCESS, 0, -4);
215
216 /* reread the file record NOW*/
217
218 fseek(mp->keyfile,mp->recstart,0);
219 if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
220 setpriority(PRIO_PROCESS, 0, 0);
221 fclose(mp->keyfile);
222 return -1;
223 }
224 rip(mp->buf);
225 mp->logname = strtok(mp->buf," \t");
226 cp = strtok(NULL," \t") ;
227 mp->seed = strtok(NULL," \t");
228 mp->val = strtok(NULL," \t");
229 /* And convert file value to hex for comparison */
230 atob8(filekey,mp->val);
231
232 /* Do actual comparison */
233 fflush (stdout);
234
235 if (memcmp(filekey,fkey,8) != 0){
236 /* Wrong response */
237 setpriority(PRIO_PROCESS, 0, 0);
238 fclose(mp->keyfile);
239 return 1;
240 }
241
242 /*
243 * Update key in database by overwriting entire record. Note
244 * that we must write exactly the same number of bytes as in
245 * the original record (note fixed width field for N)
246 */
247 btoa8(mp->val,key);
248 mp->n--;
249 fseek(mp->keyfile,mp->recstart,0);
250 fprintf(mp->keyfile, "%s %04d %-16s %s %-21s\n",
251 mp->logname,mp->n,mp->seed, mp->val, tbuf);
252
253 fclose(mp->keyfile);
254
255 setpriority(PRIO_PROCESS, 0, 0);
256 return 0;
257 }
258
259
260 /*
261 * skey_haskey ()
262 *
263 * Returns: 1 user doesnt exist, -1 fle error, 0 user exists.
264 *
265 */
266
267 int
268 skey_haskey (username)
269 char *username;
270 {
271 int i;
272 struct skey skey;
273
274 return (skeylookup (&skey, username));
275 }
276
277 /*
278 * skey_keyinfo ()
279 *
280 * Returns the current sequence number and
281 * seed for the passed user.
282 *
283 */
284 char *
285 skey_keyinfo (username)
286 char *username;
287 {
288 int i;
289 static char str [50];
290 struct skey skey;
291
292 i = skeychallenge (&skey, username, str);
293 if (i == -1)
294 return 0;
295
296 return str;
297 }
298
299 /*
300 * skey_passcheck ()
301 *
302 * Check to see if answer is the correct one to the current
303 * challenge.
304 *
305 * Returns: 0 success, -1 failure
306 *
307 */
308
309 int
310 skey_passcheck (username, passwd)
311 char *username, *passwd;
312 {
313 int i;
314 struct skey skey;
315
316 i = skeylookup (&skey, username);
317 if (i == -1 || i == 1)
318 return -1;
319
320 if (skeyverify (&skey, passwd) == 0)
321 return skey.n;
322
323 return -1;
324 }
325
326 /*
327 * skey_authenticate ()
328 *
329 * Used when calling program will allow input of the user's
330 * response to the challenge.
331 *
332 * Returns: 0 success, -1 failure
333 *
334 */
335
336 int
337 skey_authenticate (username)
338 char *username;
339 {
340 int i;
341 char pbuf[256], skeyprompt[50];
342 struct skey skey;
343
344 /* Attempt a S/Key challenge */
345 i = skeychallenge (&skey, username, skeyprompt);
346
347 if (i == -2)
348 return 0;
349
350 printf("[%s]\n", skeyprompt);
351 fflush(stdout);
352
353 printf("Response: ");
354 readskey(pbuf, sizeof (pbuf));
355 rip(pbuf);
356
357 /* Is it a valid response? */
358 if (i == 0 && skeyverify (&skey, pbuf) == 0) {
359 if (skey.n < 5) {
360 printf ("\nWarning! Key initialization needed soon. ");
361 printf ("(%d logins left)\n", skey.n);
362 }
363 return 0;
364 }
365 return -1;
366 }
367