Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libskey / skeylogin.c
1 /*   Login code for S/KEY Authentication.  S/KEY is a trademark
2  *   of Bellcore.
3  *
4  *   Mink is the former name of the S/KEY authentication system.
5  *   Many references for mink  may still be found in this program.
6  *
7  * $FreeBSD: src/lib/libskey/skeylogin.c,v 1.14.6.1 2000/07/18 11:38:24 sheldonh Exp $
8  */
9
10 #include <sys/param.h>
11 #include <sys/stat.h>
12 #include <sys/time.h>
13 #include <sys/resource.h>
14
15 #include <errno.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <time.h>
20 #include <utmp.h>
21
22 #include "skey.h"
23 #include "pathnames.h"
24
25 static char *skipspace __P((char *));
26
27 #define setpriority(x,y,z)      /* nothing */
28
29 static const char *month[12] = {
30         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
31         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
32 };
33
34 /* Look up skey info for user 'name'. If successful, fill in the caller's
35  * skey structure and return 0. If unsuccessful (e.g., if name is unknown)
36  * return -1. If an optional challenge string buffer is given, update it.
37  *
38  * The file read/write pointer is left at the start of the
39  * record.
40  */
41 int
42 skeyinfo(mp,name,ss)
43 struct skey *mp;
44 const char *name;
45 char *ss;
46 {
47         int rval;
48
49         rval = skeylookup(mp,name);
50         switch(rval){
51         case -1:        /* File error */
52                 return -1;
53         case 0:         /* Lookup succeeded */
54                 if (ss != 0) {
55                         sprintf(ss, "s/key %d %s",mp->n - 1,mp->seed);
56                         fclose(mp->keyfile);
57                 }
58                 return 0;
59         case 1:         /* User not found */
60                 fclose(mp->keyfile);
61                 return -1;
62         }
63         return -1;      /* Can't happen */
64 }
65
66 /* Return  a skey challenge string for user 'name'. If successful,
67  * fill in the caller's skey structure and return 0. If unsuccessful
68  * (e.g., if name is unknown) return -1.
69  *
70  * The file read/write pointer is left at the start of the
71  * record.
72  */
73 int
74 skeychallenge(mp,name, ss)
75 struct skey *mp;
76 const char *name;
77 char *ss;
78 {
79         int rval;
80
81         rval = skeylookup(mp,name);
82         switch(rval){
83         case -1:        /* File error */
84                 return -1;
85         case 0:         /* Lookup succeeded, issue challenge */
86                 sprintf(ss, "s/key %d %s",mp->n - 1,mp->seed);
87                 return 0;
88         case 1:         /* User not found */
89                 fclose(mp->keyfile);
90                 return -1;
91         }
92         return -1;      /* Can't happen */
93 }
94
95 /* Find an entry in the One-time Password database.
96  * Return codes:
97  * -1: error in opening database
98  *  0: entry found, file R/W pointer positioned at beginning of record
99  *  1: entry not found, file R/W pointer positioned at EOF
100  */
101 int
102 skeylookup(mp,name)
103 struct skey *mp;
104 const char *name;
105 {
106         int found;
107         size_t len;
108         long recstart = 0;
109         char *cp, *p;
110         struct stat statbuf;
111         mode_t oldmask;
112
113         /* See if the _PATH_SKEYFILE exists, and create it if not */
114         if(stat(_PATH_SKEYFILE,&statbuf) == -1 && errno == ENOENT){
115                 oldmask = umask(S_IRWXG|S_IRWXO);
116                 mp->keyfile = fopen(_PATH_SKEYFILE,"w+");
117                 (void)umask(oldmask);
118         } else {
119                 /* Otherwise open normally for update */
120                 mp->keyfile = fopen(_PATH_SKEYFILE,"r+");
121         }
122         if(mp->keyfile == NULL)
123                 return -1;
124
125         /* Look up user name in database */
126         len = strlen(name);
127         if(len > UT_NAMESIZE)
128                 len = UT_NAMESIZE;
129         found = 0;
130         while(!feof(mp->keyfile)){
131                 recstart = ftell(mp->keyfile);
132                 mp->recstart = recstart;
133                 if(fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
134                         break;
135                 }
136                 rip(mp->buf);
137                 if(mp->buf[0] == '#')
138                         continue;       /* Comment */
139                 p = mp->buf;
140                 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
141                         ;
142                 if((mp->logname = cp) == NULL)
143                         continue;
144                 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
145                         ;
146                 if(cp == NULL)
147                         continue;
148                 mp->n = atoi(cp);
149                 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
150                         ;
151                 if((mp->seed = cp) == NULL)
152                         continue;
153                 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
154                         ;
155                 if((mp->val = cp) == NULL)
156                         continue;
157                 if(strlen(mp->logname) == len
158                  && strncmp(mp->logname,name,len) == 0){
159                         found = 1;
160                         break;
161                 }
162         }
163         if(found){
164                 fseek(mp->keyfile,recstart,0);
165                 return 0;
166         } else
167                 return 1;
168 }
169 /* Verify response to a s/key challenge.
170  *
171  * Return codes:
172  * -1: Error of some sort; database unchanged
173  *  0:  Verify successful, database updated
174  *  1:  Verify failed, database unchanged
175  *
176  * The database file is always closed by this call.
177  */
178 int
179 skeyverify(mp,response)
180 struct skey *mp;
181 char *response;
182 {
183         char key[8];
184         char fkey[8];
185         char filekey[8];
186         time_t now;
187         struct tm *tm;
188         char tbuf[27], fbuf[20];
189         char *cp, *p;
190
191         time(&now);
192         tm = localtime(&now);
193 /* can't use %b here, because it can be in national form */
194         strftime(fbuf, sizeof(fbuf), "%d,%Y %T", tm);
195         snprintf(tbuf, sizeof(tbuf), " %s %s", month[tm->tm_mon], fbuf);
196
197         if(response == NULL){
198                 fclose(mp->keyfile);
199                 return -1;
200         }
201         rip(response);
202
203         /* Convert response to binary */
204         if(etob(key,response) != 1 && atob8(key,response) != 0){
205                 /* Neither english words or ascii hex */
206                 fclose(mp->keyfile);
207                 return -1;
208         }
209
210         /* Compute fkey = f(key) */
211         memcpy(fkey,key,sizeof(key));
212         f(fkey);
213         /* in order to make the window of update as short as possible
214            we must do the comparison here and if OK write it back
215            other wise the same password can be used twice to get in
216            to the system
217         */
218
219         setpriority(PRIO_PROCESS, 0, -4);
220
221         /* reread the file record NOW*/
222
223         fseek(mp->keyfile,mp->recstart,0);
224         if(fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
225                 setpriority(PRIO_PROCESS, 0, 0);
226                 fclose(mp->keyfile);
227                 return -1;
228         }
229         rip(mp->buf);
230         p = mp->buf;
231         while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
232                 ;
233         mp->logname = cp;
234         while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
235                 ;
236         while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
237                 ;
238         mp->seed = cp;
239         while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
240                 ;
241         mp->val = cp;
242         /* And convert file value to hex for comparison */
243         atob8(filekey,mp->val);
244
245         /* Do actual comparison */
246         if(memcmp(filekey,fkey,8) != 0){
247                 /* Wrong response */
248                 setpriority(PRIO_PROCESS, 0, 0);
249                 fclose(mp->keyfile);
250                 return 1;
251         }
252
253         /* Update key in database by overwriting entire record. Note
254          * that we must write exactly the same number of bytes as in
255          * the original record (note fixed width field for N)
256          */
257         btoa8(mp->val,key);
258         mp->n--;
259         fseek(mp->keyfile,mp->recstart,0);
260         fprintf(mp->keyfile,"%s %04d %-16s %s %-21s\n",mp->logname,mp->n,mp->seed,
261          mp->val, tbuf);
262
263         fclose(mp->keyfile);
264
265         setpriority(PRIO_PROCESS, 0, 0);
266         return 0;
267 }
268
269
270 /* Convert 8-byte hex-ascii string to binary array
271  * Returns 0 on success, -1 on error
272  */
273 int
274 atob8(out,in)
275 register char *out,*in;
276 {
277         register int i;
278         register int val;
279
280         if(in == NULL || out == NULL)
281                 return -1;
282
283         for(i=0;i<8;i++){
284                 if((in = skipspace(in)) == NULL)
285                         return -1;
286                 if((val = htoi(*in++)) == -1)
287                         return -1;
288                 *out = val << 4;
289
290                 if((in = skipspace(in)) == NULL)
291                         return -1;
292                 if((val = htoi(*in++)) == -1)
293                         return -1;
294                 *out++ |= val;
295         }
296         return 0;
297 }
298
299 static
300 char *
301 skipspace(cp)
302 register char *cp;
303 {
304         while(*cp == ' ' || *cp == '\t')
305                 cp++;
306
307         if(*cp == '\0')
308                 return NULL;
309         else
310                 return cp;
311 }
312
313 /* Convert 8-byte binary array to hex-ascii string */
314 int
315 btoa8(out,in)
316 register char *out,*in;
317 {
318         register int i;
319
320         if(in == NULL || out == NULL)
321                 return -1;
322
323         for(i=0;i<8;i++){
324                 sprintf(out,"%02x",*in++ & 0xff);
325                 out += 2;
326         }
327         return 0;
328 }
329
330
331 /* Convert hex digit to binary integer */
332 int
333 htoi(c)
334 register char c;
335 {
336         if('0' <= c && c <= '9')
337                 return c - '0';
338         if('a' <= c && c <= 'f')
339                 return 10 + c - 'a';
340         if('A' <= c && c <= 'F')
341                 return 10 + c - 'A';
342         return -1;
343 }