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