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