Merge branch 'master' of ssh://crater.dragonflybsd.org/repository/git/dragonfly into...
[dragonfly.git] / lib / libskey / skeylogin.c
blobab2c198e4179d81c60d6116a578d11510422dfda
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.5 2008/09/30 16:57:06 swildner 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(struct skey *mp, const char *name, char *ss)
45 int rval;
47 rval = skeylookup(mp,name);
48 switch(rval){
49 case -1: /* File error */
50 return -1;
51 case 0: /* Lookup succeeded */
52 if (ss != 0) {
53 sprintf(ss, "s/key %d %s",mp->n - 1,mp->seed);
54 fclose(mp->keyfile);
56 return 0;
57 case 1: /* User not found */
58 fclose(mp->keyfile);
59 return -1;
61 return -1; /* Can't happen */
64 /* Return a skey challenge string for user 'name'. If successful,
65 * fill in the caller's skey structure and return 0. If unsuccessful
66 * (e.g., if name is unknown) return -1.
68 * The file read/write pointer is left at the start of the
69 * record.
71 int
72 skeychallenge(struct skey *mp, const char *name, char *ss)
74 int rval;
76 rval = skeylookup(mp,name);
77 switch(rval){
78 case -1: /* File error */
79 return -1;
80 case 0: /* Lookup succeeded, issue challenge */
81 sprintf(ss, "s/key %d %s",mp->n - 1,mp->seed);
82 return 0;
83 case 1: /* User not found */
84 fclose(mp->keyfile);
85 return -1;
87 return -1; /* Can't happen */
90 /* Find an entry in the One-time Password database.
91 * Return codes:
92 * -1: error in opening database
93 * 0: entry found, file R/W pointer positioned at beginning of record
94 * 1: entry not found, file R/W pointer positioned at EOF
96 int
97 skeylookup(struct skey *mp, const char *name)
99 int found;
100 size_t len;
101 long recstart = 0;
102 char *cp, *p;
103 struct stat statbuf;
104 mode_t oldmask;
106 /* See if the _PATH_SKEYFILE exists, and create it if not */
107 if(stat(_PATH_SKEYFILE,&statbuf) == -1 && errno == ENOENT){
108 oldmask = umask(S_IRWXG|S_IRWXO);
109 mp->keyfile = fopen(_PATH_SKEYFILE,"w+");
110 (void)umask(oldmask);
111 } else {
112 /* Otherwise open normally for update */
113 mp->keyfile = fopen(_PATH_SKEYFILE,"r+");
115 if(mp->keyfile == NULL)
116 return -1;
118 /* Look up user name in database */
119 len = strlen(name);
120 if(len > UT_NAMESIZE)
121 len = UT_NAMESIZE;
122 found = 0;
123 while(!feof(mp->keyfile)){
124 recstart = ftell(mp->keyfile);
125 mp->recstart = recstart;
126 if(fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
127 break;
129 rip(mp->buf);
130 if(mp->buf[0] == '#')
131 continue; /* Comment */
132 p = mp->buf;
133 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
135 if((mp->logname = cp) == NULL)
136 continue;
137 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
139 if(cp == NULL)
140 continue;
141 mp->n = atoi(cp);
142 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
144 if((mp->seed = cp) == NULL)
145 continue;
146 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
148 if((mp->val = cp) == NULL)
149 continue;
150 if(strlen(mp->logname) == len
151 && strncmp(mp->logname,name,len) == 0){
152 found = 1;
153 break;
156 if(found){
157 fseek(mp->keyfile,recstart,0);
158 return 0;
159 } else
160 return 1;
162 /* Verify response to a s/key challenge.
164 * Return codes:
165 * -1: Error of some sort; database unchanged
166 * 0: Verify successful, database updated
167 * 1: Verify failed, database unchanged
169 * The database file is always closed by this call.
172 skeyverify(struct skey *mp, char *response)
174 char key[8];
175 char fkey[8];
176 char filekey[8];
177 time_t now;
178 struct tm *tm;
179 char tbuf[27], fbuf[20];
180 char *cp, *p;
182 time(&now);
183 tm = localtime(&now);
184 /* can't use %b here, because it can be in national form */
185 strftime(fbuf, sizeof(fbuf), "%d,%Y %T", tm);
186 snprintf(tbuf, sizeof(tbuf), " %s %s", month[tm->tm_mon], fbuf);
188 if(response == NULL){
189 fclose(mp->keyfile);
190 return -1;
192 rip(response);
194 /* Convert response to binary */
195 if(etob(key,response) != 1 && atob8(key,response) != 0){
196 /* Neither english words or ascii hex */
197 fclose(mp->keyfile);
198 return -1;
201 /* Compute fkey = f(key) */
202 memcpy(fkey,key,sizeof(key));
203 f(fkey);
204 /* in order to make the window of update as short as possible
205 we must do the comparison here and if OK write it back
206 other wise the same password can be used twice to get in
207 to the system
210 setpriority(PRIO_PROCESS, 0, -4);
212 /* reread the file record NOW*/
214 fseek(mp->keyfile,mp->recstart,0);
215 if(fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
216 setpriority(PRIO_PROCESS, 0, 0);
217 fclose(mp->keyfile);
218 return -1;
220 rip(mp->buf);
221 p = mp->buf;
222 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
224 mp->logname = cp;
225 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
227 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
229 mp->seed = cp;
230 while ((cp = strsep(&p, " \t")) != NULL && *cp == '\0')
232 mp->val = cp;
233 /* And convert file value to hex for comparison */
234 atob8(filekey,mp->val);
236 /* Do actual comparison */
237 if(memcmp(filekey,fkey,8) != 0){
238 /* Wrong response */
239 setpriority(PRIO_PROCESS, 0, 0);
240 fclose(mp->keyfile);
241 return 1;
244 /* Update key in database by overwriting entire record. Note
245 * that we must write exactly the same number of bytes as in
246 * the original record (note fixed width field for N)
248 btoa8(mp->val,key);
249 mp->n--;
250 fseek(mp->keyfile,mp->recstart,0);
251 fprintf(mp->keyfile,"%s %04d %-16s %s %-21s\n",mp->logname,mp->n,mp->seed,
252 mp->val, tbuf);
254 fclose(mp->keyfile);
256 setpriority(PRIO_PROCESS, 0, 0);
257 return 0;
261 /* Convert 8-byte hex-ascii string to binary array
262 * Returns 0 on success, -1 on error
265 atob8(char *out, char *in)
267 int i;
268 int val;
270 if(in == NULL || out == NULL)
271 return -1;
273 for(i=0;i<8;i++){
274 if((in = skipspace(in)) == NULL)
275 return -1;
276 if((val = htoi(*in++)) == -1)
277 return -1;
278 *out = val << 4;
280 if((in = skipspace(in)) == NULL)
281 return -1;
282 if((val = htoi(*in++)) == -1)
283 return -1;
284 *out++ |= val;
286 return 0;
289 static
290 char *
291 skipspace(char *cp)
293 while(*cp == ' ' || *cp == '\t')
294 cp++;
296 if(*cp == '\0')
297 return NULL;
298 else
299 return cp;
302 /* Convert 8-byte binary array to hex-ascii string */
304 btoa8(char *out, char *in)
306 int i;
308 if(in == NULL || out == NULL)
309 return -1;
311 for(i=0;i<8;i++){
312 sprintf(out,"%02x",*in++ & 0xff);
313 out += 2;
315 return 0;
319 /* Convert hex digit to binary integer */
321 htoi(char c)
323 if('0' <= c && c <= '9')
324 return c - '0';
325 if('a' <= c && c <= 'f')
326 return 10 + c - 'a';
327 if('A' <= c && c <= 'F')
328 return 10 + c - 'A';
329 return -1;