1 /* Login code for S/KEY Authentication. S/KEY is a trademark
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 $
11 #include <sys/param.h>
14 #include <sys/resource.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
43 skeyinfo(struct skey
*mp
, const char *name
, char *ss
)
47 rval
= skeylookup(mp
,name
);
49 case -1: /* File error */
51 case 0: /* Lookup succeeded */
53 sprintf(ss
, "s/key %d %s",mp
->n
- 1,mp
->seed
);
57 case 1: /* User not found */
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
72 skeychallenge(struct skey
*mp
, const char *name
, char *ss
)
76 rval
= skeylookup(mp
,name
);
78 case -1: /* File error */
80 case 0: /* Lookup succeeded, issue challenge */
81 sprintf(ss
, "s/key %d %s",mp
->n
- 1,mp
->seed
);
83 case 1: /* User not found */
87 return -1; /* Can't happen */
90 /* Find an entry in the One-time Password database.
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
97 skeylookup(struct skey
*mp
, const char *name
)
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
);
112 /* Otherwise open normally for update */
113 mp
->keyfile
= fopen(_PATH_SKEYFILE
,"r+");
115 if(mp
->keyfile
== NULL
)
118 /* Look up user name in database */
120 if(len
> UT_NAMESIZE
)
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
){
130 if(mp
->buf
[0] == '#')
131 continue; /* Comment */
133 while ((cp
= strsep(&p
, " \t")) != NULL
&& *cp
== '\0')
135 if((mp
->logname
= cp
) == NULL
)
137 while ((cp
= strsep(&p
, " \t")) != NULL
&& *cp
== '\0')
142 while ((cp
= strsep(&p
, " \t")) != NULL
&& *cp
== '\0')
144 if((mp
->seed
= cp
) == NULL
)
146 while ((cp
= strsep(&p
, " \t")) != NULL
&& *cp
== '\0')
148 if((mp
->val
= cp
) == NULL
)
150 if(strlen(mp
->logname
) == len
151 && strncmp(mp
->logname
,name
,len
) == 0){
157 fseek(mp
->keyfile
,recstart
,0);
162 /* Verify response to a s/key challenge.
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
)
179 char tbuf
[27], fbuf
[20];
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
){
194 /* Convert response to binary */
195 if(etob(key
,response
) != 1 && atob8(key
,response
) != 0){
196 /* Neither english words or ascii hex */
201 /* Compute fkey = f(key) */
202 memcpy(fkey
,key
,sizeof(key
));
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
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);
222 while ((cp
= strsep(&p
, " \t")) != NULL
&& *cp
== '\0')
225 while ((cp
= strsep(&p
, " \t")) != NULL
&& *cp
== '\0')
227 while ((cp
= strsep(&p
, " \t")) != NULL
&& *cp
== '\0')
230 while ((cp
= strsep(&p
, " \t")) != NULL
&& *cp
== '\0')
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){
239 setpriority(PRIO_PROCESS
, 0, 0);
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)
250 fseek(mp
->keyfile
,mp
->recstart
,0);
251 fprintf(mp
->keyfile
,"%s %04d %-16s %s %-21s\n",mp
->logname
,mp
->n
,mp
->seed
,
256 setpriority(PRIO_PROCESS
, 0, 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
)
270 if(in
== NULL
|| out
== NULL
)
274 if((in
= skipspace(in
)) == NULL
)
276 if((val
= htoi(*in
++)) == -1)
280 if((in
= skipspace(in
)) == NULL
)
282 if((val
= htoi(*in
++)) == -1)
293 while(*cp
== ' ' || *cp
== '\t')
302 /* Convert 8-byte binary array to hex-ascii string */
304 btoa8(char *out
, char *in
)
308 if(in
== NULL
|| out
== NULL
)
312 sprintf(out
,"%02x",*in
++ & 0xff);
319 /* Convert hex digit to binary integer */
323 if('0' <= c
&& c
<= '9')
325 if('a' <= c
&& c
<= 'f')
327 if('A' <= c
&& c
<= 'F')