More minor IPI work.
[dragonfly/vkernel-mp.git] / lib / libskey / skeylogin.c
blobe9bd51996df3f4cbcba5eb27d4bfb01284b8db5a
1 /* Login code for S/KEY Authentication. S/KEY is a trademark
2 * of Bellcore.
4 * Mink is the former name of the S/KEY authentication system.
5 * Many references for mink may still be found in this program.
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.4 2004/10/25 21:38:45 drhodus Exp $
9 */
11 #include <sys/param.h>
12 #include <sys/stat.h>
13 #include <sys/time.h>
14 #include <sys/resource.h>
16 #include <errno.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <time.h>
21 #include <utmp.h>
23 #include "skey.h"
24 #include "pathnames.h"
26 static char *skipspace (char *);
28 #define setpriority(x,y,z) /* nothing */
30 static const char *month[12] = {
31 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
32 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
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.
39 * The file read/write pointer is left at the start of the
40 * record.
42 int
43 skeyinfo(mp,name,ss)
44 struct skey *mp;
45 const char *name;
46 char *ss;
48 int rval;
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);
59 return 0;
60 case 1: /* User not found */
61 fclose(mp->keyfile);
62 return -1;
64 return -1; /* Can't happen */
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.
71 * The file read/write pointer is left at the start of the
72 * record.
74 int
75 skeychallenge(mp,name, ss)
76 struct skey *mp;
77 const char *name;
78 char *ss;
80 int rval;
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;
93 return -1; /* Can't happen */
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
103 skeylookup(mp,name)
104 struct skey *mp;
105 const char *name;
107 int found;
108 size_t len;
109 long recstart = 0;
110 char *cp, *p;
111 struct stat statbuf;
112 mode_t oldmask;
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+");
123 if(mp->keyfile == NULL)
124 return -1;
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;
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')
143 if((mp->logname = cp) == NULL)
144 continue;
145 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
147 if(cp == NULL)
148 continue;
149 mp->n = atoi(cp);
150 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
152 if((mp->seed = cp) == NULL)
153 continue;
154 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
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;
164 if(found){
165 fseek(mp->keyfile,recstart,0);
166 return 0;
167 } else
168 return 1;
170 /* Verify response to a s/key challenge.
172 * Return codes:
173 * -1: Error of some sort; database unchanged
174 * 0: Verify successful, database updated
175 * 1: Verify failed, database unchanged
177 * The database file is always closed by this call.
180 skeyverify(mp,response)
181 struct skey *mp;
182 char *response;
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;
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);
198 if(response == NULL){
199 fclose(mp->keyfile);
200 return -1;
202 rip(response);
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;
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
220 setpriority(PRIO_PROCESS, 0, -4);
222 /* reread the file record NOW*/
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;
230 rip(mp->buf);
231 p = mp->buf;
232 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
234 mp->logname = cp;
235 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
237 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
239 mp->seed = cp;
240 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
242 mp->val = cp;
243 /* And convert file value to hex for comparison */
244 atob8(filekey,mp->val);
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;
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)
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);
264 fclose(mp->keyfile);
266 setpriority(PRIO_PROCESS, 0, 0);
267 return 0;
271 /* Convert 8-byte hex-ascii string to binary array
272 * Returns 0 on success, -1 on error
275 atob8(out,in)
276 char *out,*in;
278 int i;
279 int val;
281 if(in == NULL || out == NULL)
282 return -1;
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;
291 if((in = skipspace(in)) == NULL)
292 return -1;
293 if((val = htoi(*in++)) == -1)
294 return -1;
295 *out++ |= val;
297 return 0;
300 static
301 char *
302 skipspace(cp)
303 char *cp;
305 while(*cp == ' ' || *cp == '\t')
306 cp++;
308 if(*cp == '\0')
309 return NULL;
310 else
311 return cp;
314 /* Convert 8-byte binary array to hex-ascii string */
316 btoa8(out,in)
317 char *out,*in;
319 int i;
321 if(in == NULL || out == NULL)
322 return -1;
324 for(i=0;i<8;i++){
325 sprintf(out,"%02x",*in++ & 0xff);
326 out += 2;
328 return 0;
332 /* Convert hex digit to binary integer */
334 htoi(c)
335 char c;
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;