skeylogin.c revision 1.10 1 /* $NetBSD: skeylogin.c,v 1.10 1999/07/02 15:45:23 simonb 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 int
44 getskeyprompt(mp,name,prompt)
45 struct skey *mp;
46 char *name;
47 char *prompt;
48 {
49 int rval;
50
51 sevenbit(name);
52 rval = skeylookup(mp,name);
53 #if 0
54 strcpy(prompt, "s/key 55 latour1\n");
55 #endif
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); /* XXX: sprintf (getskeyprompt()) appears unused */
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
69 /* Return a skey challenge string for user 'name'. If successful,
70 * fill in the caller's skey structure and return 0. If unsuccessful
71 * (e.g., if name is unknown) return -1.
72 *
73 * The file read/write pointer is left at the start of the
74 * record.
75 */
76 int
77 skeychallenge(mp,name, ss, sslen)
78 struct skey *mp;
79 const char *name;
80 char *ss;
81 int sslen;
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 (void)snprintf(ss, sslen, "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 const char *name;
109 {
110 int found;
111 int len;
112 long recstart = 0;
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 const char *username;
270 {
271 struct skey skey;
272
273 return (skeylookup (&skey, username));
274 }
275
276 /*
277 * skey_keyinfo ()
278 *
279 * Returns the current sequence number and
280 * seed for the passed user.
281 *
282 */
283 char *
284 skey_keyinfo (username)
285 const char *username;
286 {
287 int i;
288 static char str [50];
289 struct skey skey;
290
291 i = skeychallenge (&skey, username, str, sizeof str);
292 if (i == -1)
293 return 0;
294
295 return str;
296 }
297
298 /*
299 * skey_passcheck ()
300 *
301 * Check to see if answer is the correct one to the current
302 * challenge.
303 *
304 * Returns: 0 success, -1 failure
305 *
306 */
307
308 int
309 skey_passcheck (username, passwd)
310 const char *username;
311 char *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 const 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, sizeof 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