skeylogin.c revision 1.9.2.1 1 1.9.2.1 he /* $NetBSD: skeylogin.c,v 1.9.2.1 2000/04/30 12:30:55 he Exp $ */
2 1.6 thorpej
3 1.1 deraadt /* S/KEY v1.1b (skeylogin.c)
4 1.1 deraadt *
5 1.1 deraadt * Authors:
6 1.1 deraadt * Neil M. Haller <nmh (at) thumper.bellcore.com>
7 1.1 deraadt * Philip R. Karn <karn (at) chicago.qualcomm.com>
8 1.1 deraadt * John S. Walden <jsw (at) thumper.bellcore.com>
9 1.1 deraadt * Scott Chasin <chasin (at) crimelab.com>
10 1.1 deraadt *
11 1.1 deraadt * S/KEY verification check, lookups, and authentication.
12 1.1 deraadt */
13 1.1 deraadt
14 1.1 deraadt #include <sys/param.h>
15 1.1 deraadt #ifdef QUOTA
16 1.1 deraadt #include <sys/quota.h>
17 1.1 deraadt #endif
18 1.1 deraadt #include <sys/stat.h>
19 1.1 deraadt #include <sys/time.h>
20 1.1 deraadt #include <sys/timeb.h>
21 1.1 deraadt #include <sys/resource.h>
22 1.1 deraadt
23 1.5 pk
24 1.1 deraadt #include <stdio.h>
25 1.5 pk #include <stdlib.h>
26 1.1 deraadt #include <string.h>
27 1.1 deraadt #include <sys/types.h>
28 1.1 deraadt #include <sys/stat.h>
29 1.1 deraadt #include <time.h>
30 1.1 deraadt #include <errno.h>
31 1.5 pk
32 1.1 deraadt #include "skey.h"
33 1.1 deraadt
34 1.5 pk #define _PATH_KEYFILE "/etc/skeykeys"
35 1.1 deraadt
36 1.1 deraadt /* Issue a skey challenge for user 'name'. If successful,
37 1.1 deraadt * fill in the caller's skey structure and return 0. If unsuccessful
38 1.1 deraadt * (e.g., if name is unknown) return -1.
39 1.1 deraadt *
40 1.1 deraadt * The file read/write pointer is left at the start of the
41 1.1 deraadt * record.
42 1.1 deraadt */
43 1.1 deraadt int
44 1.1 deraadt getskeyprompt(mp,name,prompt)
45 1.5 pk struct skey *mp;
46 1.5 pk char *name;
47 1.5 pk char *prompt;
48 1.1 deraadt {
49 1.1 deraadt int rval;
50 1.1 deraadt
51 1.1 deraadt sevenbit(name);
52 1.1 deraadt rval = skeylookup(mp,name);
53 1.7 mrg #if 0
54 1.7 mrg strcpy(prompt, "s/key 55 latour1\n");
55 1.7 mrg #endif
56 1.5 pk switch (rval) {
57 1.1 deraadt case -1: /* File error */
58 1.1 deraadt return -1;
59 1.1 deraadt case 0: /* Lookup succeeded, return challenge */
60 1.7 mrg sprintf(prompt,"s/key %d %s\n",mp->n - 1,mp->seed); /* XXX: sprintf (getskeyprompt()) appears unused */
61 1.1 deraadt return 0;
62 1.1 deraadt case 1: /* User not found */
63 1.1 deraadt fclose(mp->keyfile);
64 1.1 deraadt return -1;
65 1.1 deraadt }
66 1.1 deraadt return -1; /* Can't happen */
67 1.5 pk }
68 1.5 pk
69 1.1 deraadt /* Return a skey challenge string for user 'name'. If successful,
70 1.1 deraadt * fill in the caller's skey structure and return 0. If unsuccessful
71 1.1 deraadt * (e.g., if name is unknown) return -1.
72 1.1 deraadt *
73 1.1 deraadt * The file read/write pointer is left at the start of the
74 1.1 deraadt * record.
75 1.1 deraadt */
76 1.1 deraadt int
77 1.7 mrg skeychallenge(mp,name, ss, sslen)
78 1.5 pk struct skey *mp;
79 1.9 mycroft const char *name;
80 1.5 pk char *ss;
81 1.7 mrg int sslen;
82 1.1 deraadt {
83 1.1 deraadt int rval;
84 1.1 deraadt
85 1.1 deraadt rval = skeylookup(mp,name);
86 1.1 deraadt switch(rval){
87 1.1 deraadt case -1: /* File error */
88 1.1 deraadt return -1;
89 1.1 deraadt case 0: /* Lookup succeeded, issue challenge */
90 1.7 mrg (void)snprintf(ss, sslen, "s/key %d %s",mp->n - 1,mp->seed);
91 1.1 deraadt return 0;
92 1.1 deraadt case 1: /* User not found */
93 1.1 deraadt fclose(mp->keyfile);
94 1.1 deraadt return -1;
95 1.1 deraadt }
96 1.1 deraadt return -1; /* Can't happen */
97 1.1 deraadt }
98 1.1 deraadt
99 1.1 deraadt /* Find an entry in the One-time Password database.
100 1.1 deraadt * Return codes:
101 1.1 deraadt * -1: error in opening database
102 1.1 deraadt * 0: entry found, file R/W pointer positioned at beginning of record
103 1.1 deraadt * 1: entry not found, file R/W pointer positioned at EOF
104 1.1 deraadt */
105 1.1 deraadt int
106 1.1 deraadt skeylookup(mp,name)
107 1.5 pk struct skey *mp;
108 1.9 mycroft const char *name;
109 1.1 deraadt {
110 1.1 deraadt int found;
111 1.1 deraadt int len;
112 1.8 christos long recstart = 0;
113 1.1 deraadt char *cp;
114 1.1 deraadt struct stat statbuf;
115 1.1 deraadt
116 1.5 pk /* See if the _PATH_KEYFILE exists, and create it if not */
117 1.2 deraadt
118 1.5 pk if (stat(_PATH_KEYFILE,&statbuf) == -1 && errno == ENOENT) {
119 1.5 pk mp->keyfile = fopen(_PATH_KEYFILE,"w+");
120 1.5 pk if (mp->keyfile)
121 1.5 pk chmod(_PATH_KEYFILE, 0644);
122 1.1 deraadt } else {
123 1.1 deraadt /* Otherwise open normally for update */
124 1.5 pk mp->keyfile = fopen(_PATH_KEYFILE,"r+");
125 1.1 deraadt }
126 1.5 pk if (mp->keyfile == NULL)
127 1.1 deraadt return -1;
128 1.1 deraadt
129 1.1 deraadt /* Look up user name in database */
130 1.1 deraadt len = strlen(name);
131 1.9.2.1 he if (len > 8)
132 1.9.2.1 he len = 8; /* Added 8/2/91 - nmh */
133 1.1 deraadt found = 0;
134 1.5 pk while (!feof(mp->keyfile)) {
135 1.1 deraadt recstart = ftell(mp->keyfile);
136 1.1 deraadt mp->recstart = recstart;
137 1.5 pk if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf) {
138 1.1 deraadt break;
139 1.1 deraadt }
140 1.1 deraadt rip(mp->buf);
141 1.5 pk if (mp->buf[0] == '#')
142 1.1 deraadt continue; /* Comment */
143 1.5 pk if ((mp->logname = strtok(mp->buf," \t")) == NULL)
144 1.1 deraadt continue;
145 1.5 pk if ((cp = strtok(NULL," \t")) == NULL)
146 1.1 deraadt continue;
147 1.1 deraadt mp->n = atoi(cp);
148 1.5 pk if ((mp->seed = strtok(NULL," \t")) == NULL)
149 1.1 deraadt continue;
150 1.5 pk if ((mp->val = strtok(NULL," \t")) == NULL)
151 1.1 deraadt continue;
152 1.5 pk if (strlen(mp->logname) == len
153 1.1 deraadt && strncmp(mp->logname,name,len) == 0){
154 1.1 deraadt found = 1;
155 1.1 deraadt break;
156 1.1 deraadt }
157 1.1 deraadt }
158 1.5 pk if (found) {
159 1.1 deraadt fseek(mp->keyfile,recstart,0);
160 1.1 deraadt return 0;
161 1.1 deraadt } else
162 1.1 deraadt return 1;
163 1.1 deraadt }
164 1.1 deraadt /* Verify response to a s/key challenge.
165 1.1 deraadt *
166 1.1 deraadt * Return codes:
167 1.1 deraadt * -1: Error of some sort; database unchanged
168 1.1 deraadt * 0: Verify successful, database updated
169 1.1 deraadt * 1: Verify failed, database unchanged
170 1.1 deraadt *
171 1.1 deraadt * The database file is always closed by this call.
172 1.1 deraadt */
173 1.1 deraadt int
174 1.1 deraadt skeyverify(mp,response)
175 1.5 pk struct skey *mp;
176 1.5 pk char *response;
177 1.1 deraadt {
178 1.1 deraadt char key[8];
179 1.1 deraadt char fkey[8];
180 1.1 deraadt char filekey[8];
181 1.1 deraadt time_t now;
182 1.9.2.1 he int prevprio;
183 1.1 deraadt struct tm *tm;
184 1.5 pk char tbuf[27];
185 1.1 deraadt char *cp;
186 1.1 deraadt
187 1.1 deraadt time(&now);
188 1.1 deraadt tm = localtime(&now);
189 1.1 deraadt strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm);
190 1.1 deraadt
191 1.5 pk if (response == NULL) {
192 1.1 deraadt fclose(mp->keyfile);
193 1.1 deraadt return -1;
194 1.1 deraadt }
195 1.5 pk rip(response);
196 1.1 deraadt
197 1.1 deraadt /* Convert response to binary */
198 1.5 pk if (etob(key, response) != 1 && atob8(key, response) != 0) {
199 1.1 deraadt /* Neither english words or ascii hex */
200 1.1 deraadt fclose(mp->keyfile);
201 1.1 deraadt return -1;
202 1.1 deraadt }
203 1.1 deraadt
204 1.1 deraadt /* Compute fkey = f(key) */
205 1.1 deraadt memcpy(fkey,key,sizeof(key));
206 1.1 deraadt fflush (stdout);
207 1.1 deraadt
208 1.1 deraadt f(fkey);
209 1.5 pk /*
210 1.5 pk * in order to make the window of update as short as possible
211 1.5 pk * we must do the comparison here and if OK write it back
212 1.5 pk * other wise the same password can be used twice to get in
213 1.5 pk * to the system
214 1.5 pk */
215 1.1 deraadt
216 1.9.2.1 he /* Save the priority for later use. */
217 1.9.2.1 he errno = 0;
218 1.9.2.1 he prevprio = getpriority(PRIO_PROCESS, 0);
219 1.9.2.1 he if (errno) {
220 1.9.2.1 he /* Don't report the error; just don't use it later. */
221 1.9.2.1 he prevprio = PRIO_MAX + 1;
222 1.9.2.1 he } else {
223 1.9.2.1 he setpriority(PRIO_PROCESS, 0, -4);
224 1.9.2.1 he }
225 1.1 deraadt
226 1.1 deraadt /* reread the file record NOW*/
227 1.1 deraadt
228 1.1 deraadt fseek(mp->keyfile,mp->recstart,0);
229 1.5 pk if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
230 1.9.2.1 he if (prevprio != PRIO_MAX + 1) {
231 1.9.2.1 he setpriority(PRIO_PROCESS, 0, prevprio);
232 1.9.2.1 he }
233 1.1 deraadt fclose(mp->keyfile);
234 1.1 deraadt return -1;
235 1.1 deraadt }
236 1.1 deraadt rip(mp->buf);
237 1.1 deraadt mp->logname = strtok(mp->buf," \t");
238 1.1 deraadt cp = strtok(NULL," \t") ;
239 1.1 deraadt mp->seed = strtok(NULL," \t");
240 1.1 deraadt mp->val = strtok(NULL," \t");
241 1.1 deraadt /* And convert file value to hex for comparison */
242 1.1 deraadt atob8(filekey,mp->val);
243 1.1 deraadt
244 1.1 deraadt /* Do actual comparison */
245 1.1 deraadt fflush (stdout);
246 1.1 deraadt
247 1.5 pk if (memcmp(filekey,fkey,8) != 0){
248 1.1 deraadt /* Wrong response */
249 1.9.2.1 he if (prevprio != PRIO_MAX + 1) {
250 1.9.2.1 he setpriority(PRIO_PROCESS, 0, prevprio);
251 1.9.2.1 he }
252 1.1 deraadt fclose(mp->keyfile);
253 1.1 deraadt return 1;
254 1.1 deraadt }
255 1.1 deraadt
256 1.5 pk /*
257 1.5 pk * Update key in database by overwriting entire record. Note
258 1.1 deraadt * that we must write exactly the same number of bytes as in
259 1.1 deraadt * the original record (note fixed width field for N)
260 1.1 deraadt */
261 1.1 deraadt btoa8(mp->val,key);
262 1.1 deraadt mp->n--;
263 1.1 deraadt fseek(mp->keyfile,mp->recstart,0);
264 1.5 pk fprintf(mp->keyfile, "%s %04d %-16s %s %-21s\n",
265 1.5 pk mp->logname,mp->n,mp->seed, mp->val, tbuf);
266 1.1 deraadt
267 1.1 deraadt fclose(mp->keyfile);
268 1.1 deraadt
269 1.9.2.1 he if (prevprio != PRIO_MAX + 1) {
270 1.9.2.1 he setpriority(PRIO_PROCESS, 0, prevprio);
271 1.9.2.1 he }
272 1.1 deraadt return 0;
273 1.1 deraadt }
274 1.1 deraadt
275 1.1 deraadt
276 1.1 deraadt /*
277 1.1 deraadt * skey_haskey ()
278 1.1 deraadt *
279 1.1 deraadt * Returns: 1 user doesnt exist, -1 fle error, 0 user exists.
280 1.1 deraadt *
281 1.1 deraadt */
282 1.1 deraadt
283 1.5 pk int
284 1.1 deraadt skey_haskey (username)
285 1.9 mycroft const char *username;
286 1.1 deraadt {
287 1.5 pk struct skey skey;
288 1.1 deraadt
289 1.5 pk return (skeylookup (&skey, username));
290 1.1 deraadt }
291 1.1 deraadt
292 1.1 deraadt /*
293 1.1 deraadt * skey_keyinfo ()
294 1.1 deraadt *
295 1.1 deraadt * Returns the current sequence number and
296 1.1 deraadt * seed for the passed user.
297 1.1 deraadt *
298 1.1 deraadt */
299 1.5 pk char *
300 1.5 pk skey_keyinfo (username)
301 1.9 mycroft const char *username;
302 1.1 deraadt {
303 1.5 pk int i;
304 1.5 pk static char str [50];
305 1.5 pk struct skey skey;
306 1.5 pk
307 1.7 mrg i = skeychallenge (&skey, username, str, sizeof str);
308 1.5 pk if (i == -1)
309 1.5 pk return 0;
310 1.1 deraadt
311 1.5 pk return str;
312 1.1 deraadt }
313 1.1 deraadt
314 1.1 deraadt /*
315 1.1 deraadt * skey_passcheck ()
316 1.1 deraadt *
317 1.1 deraadt * Check to see if answer is the correct one to the current
318 1.1 deraadt * challenge.
319 1.1 deraadt *
320 1.1 deraadt * Returns: 0 success, -1 failure
321 1.1 deraadt *
322 1.1 deraadt */
323 1.1 deraadt
324 1.5 pk int
325 1.1 deraadt skey_passcheck (username, passwd)
326 1.9 mycroft const char *username;
327 1.9 mycroft char *passwd;
328 1.1 deraadt {
329 1.5 pk int i;
330 1.5 pk struct skey skey;
331 1.5 pk
332 1.5 pk i = skeylookup (&skey, username);
333 1.5 pk if (i == -1 || i == 1)
334 1.5 pk return -1;
335 1.5 pk
336 1.5 pk if (skeyverify (&skey, passwd) == 0)
337 1.5 pk return skey.n;
338 1.5 pk
339 1.5 pk return -1;
340 1.1 deraadt }
341 1.1 deraadt
342 1.1 deraadt /*
343 1.1 deraadt * skey_authenticate ()
344 1.1 deraadt *
345 1.1 deraadt * Used when calling program will allow input of the user's
346 1.1 deraadt * response to the challenge.
347 1.1 deraadt *
348 1.1 deraadt * Returns: 0 success, -1 failure
349 1.1 deraadt *
350 1.1 deraadt */
351 1.1 deraadt
352 1.5 pk int
353 1.1 deraadt skey_authenticate (username)
354 1.9 mycroft const char *username;
355 1.1 deraadt {
356 1.5 pk int i;
357 1.5 pk char pbuf[256], skeyprompt[50];
358 1.5 pk struct skey skey;
359 1.5 pk
360 1.5 pk /* Attempt a S/Key challenge */
361 1.7 mrg i = skeychallenge (&skey, username, skeyprompt, sizeof skeyprompt);
362 1.5 pk
363 1.5 pk if (i == -2)
364 1.5 pk return 0;
365 1.5 pk
366 1.5 pk printf("[%s]\n", skeyprompt);
367 1.5 pk fflush(stdout);
368 1.5 pk
369 1.5 pk printf("Response: ");
370 1.5 pk readskey(pbuf, sizeof (pbuf));
371 1.5 pk rip(pbuf);
372 1.5 pk
373 1.5 pk /* Is it a valid response? */
374 1.5 pk if (i == 0 && skeyverify (&skey, pbuf) == 0) {
375 1.5 pk if (skey.n < 5) {
376 1.5 pk printf ("\nWarning! Key initialization needed soon. ");
377 1.5 pk printf ("(%d logins left)\n", skey.n);
378 1.5 pk }
379 1.5 pk return 0;
380 1.5 pk }
381 1.5 pk return -1;
382 1.1 deraadt }
383