skeylogin.c revision 1.9.2.1 1 /* $NetBSD: skeylogin.c,v 1.9.2.1 2000/04/30 12:30:55 he 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)
132 len = 8; /* Added 8/2/91 - nmh */
133 found = 0;
134 while (!feof(mp->keyfile)) {
135 recstart = ftell(mp->keyfile);
136 mp->recstart = recstart;
137 if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf) {
138 break;
139 }
140 rip(mp->buf);
141 if (mp->buf[0] == '#')
142 continue; /* Comment */
143 if ((mp->logname = strtok(mp->buf," \t")) == NULL)
144 continue;
145 if ((cp = strtok(NULL," \t")) == NULL)
146 continue;
147 mp->n = atoi(cp);
148 if ((mp->seed = strtok(NULL," \t")) == NULL)
149 continue;
150 if ((mp->val = strtok(NULL," \t")) == NULL)
151 continue;
152 if (strlen(mp->logname) == len
153 && strncmp(mp->logname,name,len) == 0){
154 found = 1;
155 break;
156 }
157 }
158 if (found) {
159 fseek(mp->keyfile,recstart,0);
160 return 0;
161 } else
162 return 1;
163 }
164 /* Verify response to a s/key challenge.
165 *
166 * Return codes:
167 * -1: Error of some sort; database unchanged
168 * 0: Verify successful, database updated
169 * 1: Verify failed, database unchanged
170 *
171 * The database file is always closed by this call.
172 */
173 int
174 skeyverify(mp,response)
175 struct skey *mp;
176 char *response;
177 {
178 char key[8];
179 char fkey[8];
180 char filekey[8];
181 time_t now;
182 int prevprio;
183 struct tm *tm;
184 char tbuf[27];
185 char *cp;
186
187 time(&now);
188 tm = localtime(&now);
189 strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm);
190
191 if (response == NULL) {
192 fclose(mp->keyfile);
193 return -1;
194 }
195 rip(response);
196
197 /* Convert response to binary */
198 if (etob(key, response) != 1 && atob8(key, response) != 0) {
199 /* Neither english words or ascii hex */
200 fclose(mp->keyfile);
201 return -1;
202 }
203
204 /* Compute fkey = f(key) */
205 memcpy(fkey,key,sizeof(key));
206 fflush (stdout);
207
208 f(fkey);
209 /*
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 /* Save the priority for later use. */
217 errno = 0;
218 prevprio = getpriority(PRIO_PROCESS, 0);
219 if (errno) {
220 /* Don't report the error; just don't use it later. */
221 prevprio = PRIO_MAX + 1;
222 } else {
223 setpriority(PRIO_PROCESS, 0, -4);
224 }
225
226 /* reread the file record NOW*/
227
228 fseek(mp->keyfile,mp->recstart,0);
229 if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
230 if (prevprio != PRIO_MAX + 1) {
231 setpriority(PRIO_PROCESS, 0, prevprio);
232 }
233 fclose(mp->keyfile);
234 return -1;
235 }
236 rip(mp->buf);
237 mp->logname = strtok(mp->buf," \t");
238 cp = strtok(NULL," \t") ;
239 mp->seed = strtok(NULL," \t");
240 mp->val = strtok(NULL," \t");
241 /* And convert file value to hex for comparison */
242 atob8(filekey,mp->val);
243
244 /* Do actual comparison */
245 fflush (stdout);
246
247 if (memcmp(filekey,fkey,8) != 0){
248 /* Wrong response */
249 if (prevprio != PRIO_MAX + 1) {
250 setpriority(PRIO_PROCESS, 0, prevprio);
251 }
252 fclose(mp->keyfile);
253 return 1;
254 }
255
256 /*
257 * Update key in database by overwriting entire record. Note
258 * that we must write exactly the same number of bytes as in
259 * the original record (note fixed width field for N)
260 */
261 btoa8(mp->val,key);
262 mp->n--;
263 fseek(mp->keyfile,mp->recstart,0);
264 fprintf(mp->keyfile, "%s %04d %-16s %s %-21s\n",
265 mp->logname,mp->n,mp->seed, mp->val, tbuf);
266
267 fclose(mp->keyfile);
268
269 if (prevprio != PRIO_MAX + 1) {
270 setpriority(PRIO_PROCESS, 0, prevprio);
271 }
272 return 0;
273 }
274
275
276 /*
277 * skey_haskey ()
278 *
279 * Returns: 1 user doesnt exist, -1 fle error, 0 user exists.
280 *
281 */
282
283 int
284 skey_haskey (username)
285 const char *username;
286 {
287 struct skey skey;
288
289 return (skeylookup (&skey, username));
290 }
291
292 /*
293 * skey_keyinfo ()
294 *
295 * Returns the current sequence number and
296 * seed for the passed user.
297 *
298 */
299 char *
300 skey_keyinfo (username)
301 const char *username;
302 {
303 int i;
304 static char str [50];
305 struct skey skey;
306
307 i = skeychallenge (&skey, username, str, sizeof str);
308 if (i == -1)
309 return 0;
310
311 return str;
312 }
313
314 /*
315 * skey_passcheck ()
316 *
317 * Check to see if answer is the correct one to the current
318 * challenge.
319 *
320 * Returns: 0 success, -1 failure
321 *
322 */
323
324 int
325 skey_passcheck (username, passwd)
326 const char *username;
327 char *passwd;
328 {
329 int i;
330 struct skey skey;
331
332 i = skeylookup (&skey, username);
333 if (i == -1 || i == 1)
334 return -1;
335
336 if (skeyverify (&skey, passwd) == 0)
337 return skey.n;
338
339 return -1;
340 }
341
342 /*
343 * skey_authenticate ()
344 *
345 * Used when calling program will allow input of the user's
346 * response to the challenge.
347 *
348 * Returns: 0 success, -1 failure
349 *
350 */
351
352 int
353 skey_authenticate (username)
354 const char *username;
355 {
356 int i;
357 char pbuf[256], skeyprompt[50];
358 struct skey skey;
359
360 /* Attempt a S/Key challenge */
361 i = skeychallenge (&skey, username, skeyprompt, sizeof skeyprompt);
362
363 if (i == -2)
364 return 0;
365
366 printf("[%s]\n", skeyprompt);
367 fflush(stdout);
368
369 printf("Response: ");
370 readskey(pbuf, sizeof (pbuf));
371 rip(pbuf);
372
373 /* Is it a valid response? */
374 if (i == 0 && skeyverify (&skey, pbuf) == 0) {
375 if (skey.n < 5) {
376 printf ("\nWarning! Key initialization needed soon. ");
377 printf ("(%d logins left)\n", skey.n);
378 }
379 return 0;
380 }
381 return -1;
382 }
383