1 |
|
|
/* OpenBSD S/Key (skeysubr.c) |
2 |
|
|
* |
3 |
|
|
* Authors: |
4 |
|
|
* Neil M. Haller <nmh@thumper.bellcore.com> |
5 |
|
|
* Philip R. Karn <karn@chicago.qualcomm.com> |
6 |
|
|
* John S. Walden <jsw@thumper.bellcore.com> |
7 |
|
|
* Scott Chasin <chasin@crimelab.com> |
8 |
|
|
* Todd C. Miller <Todd.Miller@courtesan.com> |
9 |
|
|
* |
10 |
|
|
* S/Key misc routines. |
11 |
|
|
* |
12 |
|
|
* $OpenBSD: skeysubr.c,v 1.34 2015/10/06 15:07:45 tim Exp $ |
13 |
|
|
*/ |
14 |
|
|
|
15 |
|
|
#include <stdio.h> |
16 |
|
|
#include <stdlib.h> |
17 |
|
|
#include <string.h> |
18 |
|
|
#include <ctype.h> |
19 |
|
|
#include <signal.h> |
20 |
|
|
#include <termios.h> |
21 |
|
|
#include <unistd.h> |
22 |
|
|
#include <md5.h> |
23 |
|
|
#include <sha1.h> |
24 |
|
|
#include <rmd160.h> |
25 |
|
|
|
26 |
|
|
#include "skey.h" |
27 |
|
|
|
28 |
|
|
/* Default hash function to use (index into skey_algorithm_table array) */ |
29 |
|
|
#ifndef SKEY_HASH_DEFAULT |
30 |
|
|
#define SKEY_HASH_DEFAULT 0 /* md5 */ |
31 |
|
|
#endif |
32 |
|
|
|
33 |
|
|
static void keycrunch_md5(char *, char *, size_t); |
34 |
|
|
static void keycrunch_sha1(char *, char *, size_t); |
35 |
|
|
static void keycrunch_rmd160(char *, char *, size_t); |
36 |
|
|
static void skey_echo(int); |
37 |
|
|
static void trapped(int); |
38 |
|
|
|
39 |
|
|
/* Current hash type (index into skey_algorithm_table array) */ |
40 |
|
|
static int skey_hash_type = SKEY_HASH_DEFAULT; |
41 |
|
|
|
42 |
|
|
/* |
43 |
|
|
* Hash types we support. |
44 |
|
|
* Each has an associated keycrunch() and f() function. |
45 |
|
|
*/ |
46 |
|
|
struct skey_algorithm_table { |
47 |
|
|
const char *name; |
48 |
|
|
void (*keycrunch)(char *, char *, size_t); |
49 |
|
|
}; |
50 |
|
|
static struct skey_algorithm_table skey_algorithm_table[] = { |
51 |
|
|
{ "md5", keycrunch_md5 }, |
52 |
|
|
{ "sha1", keycrunch_sha1 }, |
53 |
|
|
{ "rmd160", keycrunch_rmd160 }, |
54 |
|
|
{ NULL } |
55 |
|
|
}; |
56 |
|
|
|
57 |
|
|
|
58 |
|
|
/* |
59 |
|
|
* Crunch a key: |
60 |
|
|
* Concatenate the seed and the password, run through hash function and |
61 |
|
|
* collapse to 64 bits. This is defined as the user's starting key. |
62 |
|
|
* The result pointer must have at least SKEY_BINKEY_SIZE bytes of storage. |
63 |
|
|
* The seed and password may be of any length. |
64 |
|
|
*/ |
65 |
|
|
int |
66 |
|
|
keycrunch(char *result, char *seed, char *passwd) |
67 |
|
|
{ |
68 |
|
|
char *buf, *p; |
69 |
|
|
size_t buflen; |
70 |
|
|
|
71 |
|
72 |
buflen = strlen(seed) + strlen(passwd); |
72 |
✗✓ |
36 |
if ((buf = malloc(buflen + 1)) == NULL) |
73 |
|
|
return(-1); |
74 |
|
|
|
75 |
|
36 |
(void)strlcpy(buf, seed, buflen + 1); |
76 |
✓✓ |
480 |
for (p = buf; *p; p++) |
77 |
|
204 |
*p = (char)tolower((unsigned char)*p); |
78 |
|
|
|
79 |
|
36 |
(void)strlcat(buf, passwd, buflen + 1); |
80 |
|
36 |
sevenbit(buf); |
81 |
|
|
|
82 |
|
36 |
skey_algorithm_table[skey_hash_type].keycrunch(result, buf, buflen); |
83 |
|
|
|
84 |
|
36 |
(void)free(buf); |
85 |
|
36 |
return(0); |
86 |
|
36 |
} |
87 |
|
|
|
88 |
|
|
static void |
89 |
|
|
keycrunch_md5(char *result, char *buf, size_t buflen) |
90 |
|
|
{ |
91 |
|
2400 |
MD5_CTX md; |
92 |
|
1200 |
u_int32_t results[4]; |
93 |
|
|
|
94 |
|
|
/* Crunch the key through MD5 */ |
95 |
|
1200 |
MD5Init(&md); |
96 |
|
1200 |
MD5Update(&md, (unsigned char *)buf, buflen); |
97 |
|
1200 |
MD5Final((unsigned char *)results, &md); |
98 |
|
|
|
99 |
|
|
/* Fold result from 128 to 64 bits */ |
100 |
|
1200 |
results[0] ^= results[2]; |
101 |
|
1200 |
results[1] ^= results[3]; |
102 |
|
|
|
103 |
|
1200 |
(void)memcpy((void *)result, (void *)results, SKEY_BINKEY_SIZE); |
104 |
|
1200 |
} |
105 |
|
|
|
106 |
|
|
static void |
107 |
|
|
keycrunch_sha1(char *result, char *buf, size_t buflen) |
108 |
|
|
{ |
109 |
|
2400 |
SHA1_CTX sha; |
110 |
|
|
int i, j; |
111 |
|
|
|
112 |
|
|
/* Crunch the key through SHA1 */ |
113 |
|
1200 |
SHA1Init(&sha); |
114 |
|
1200 |
SHA1Update(&sha, (unsigned char *)buf, buflen); |
115 |
|
1200 |
SHA1Pad(&sha); |
116 |
|
|
|
117 |
|
|
/* Fold 160 to 64 bits */ |
118 |
|
1200 |
sha.state[0] ^= sha.state[2]; |
119 |
|
1200 |
sha.state[1] ^= sha.state[3]; |
120 |
|
1200 |
sha.state[0] ^= sha.state[4]; |
121 |
|
|
|
122 |
|
|
/* |
123 |
|
|
* SHA1 is a big endian algorithm but RFC2289 mandates that |
124 |
|
|
* the result be in little endian form, so we copy to the |
125 |
|
|
* result buffer manually. |
126 |
|
|
*/ |
127 |
✓✓ |
7200 |
for (i = 0, j = 0; j < 8; i++, j += 4) { |
128 |
|
2400 |
result[j] = (u_char)(sha.state[i] & 0xff); |
129 |
|
2400 |
result[j+1] = (u_char)((sha.state[i] >> 8) & 0xff); |
130 |
|
2400 |
result[j+2] = (u_char)((sha.state[i] >> 16) & 0xff); |
131 |
|
2400 |
result[j+3] = (u_char)((sha.state[i] >> 24) & 0xff); |
132 |
|
|
} |
133 |
|
1200 |
} |
134 |
|
|
|
135 |
|
|
static void |
136 |
|
|
keycrunch_rmd160(char *result, char *buf, size_t buflen) |
137 |
|
|
{ |
138 |
|
2400 |
RMD160_CTX rmd; |
139 |
|
1200 |
u_int32_t results[5]; |
140 |
|
|
|
141 |
|
|
/* Crunch the key through RMD-160 */ |
142 |
|
1200 |
RMD160Init(&rmd); |
143 |
|
1200 |
RMD160Update(&rmd, (unsigned char *)buf, buflen); |
144 |
|
1200 |
RMD160Final((unsigned char *)results, &rmd); |
145 |
|
|
|
146 |
|
|
/* Fold 160 to 64 bits */ |
147 |
|
1200 |
results[0] ^= results[2]; |
148 |
|
1200 |
results[1] ^= results[3]; |
149 |
|
1200 |
results[0] ^= results[4]; |
150 |
|
|
|
151 |
|
1200 |
(void)memcpy((void *)result, (void *)results, SKEY_BINKEY_SIZE); |
152 |
|
1200 |
} |
153 |
|
|
|
154 |
|
|
/* |
155 |
|
|
* The one-way hash function f(). |
156 |
|
|
* Takes SKEY_BINKEY_SIZE bytes and returns SKEY_BINKEY_SIZE bytes in place. |
157 |
|
|
*/ |
158 |
|
|
void |
159 |
|
|
f(char *x) |
160 |
|
|
{ |
161 |
|
7128 |
skey_algorithm_table[skey_hash_type].keycrunch(x, x, SKEY_BINKEY_SIZE); |
162 |
|
3564 |
} |
163 |
|
|
|
164 |
|
|
/* Strip trailing cr/lf from a line of text */ |
165 |
|
|
void |
166 |
|
|
rip(char *buf) |
167 |
|
|
{ |
168 |
|
|
buf += strcspn(buf, "\r\n"); |
169 |
|
|
|
170 |
|
|
if (*buf) |
171 |
|
|
*buf = '\0'; |
172 |
|
|
} |
173 |
|
|
|
174 |
|
|
/* Read in secret password (turns off echo) */ |
175 |
|
|
char * |
176 |
|
|
readpass(char *buf, int n) |
177 |
|
|
{ |
178 |
|
|
void (*old_handler)(int); |
179 |
|
|
|
180 |
|
|
/* Turn off echoing */ |
181 |
|
|
skey_echo(0); |
182 |
|
|
|
183 |
|
|
/* Catch SIGINT and save old signal handler */ |
184 |
|
|
old_handler = signal(SIGINT, trapped); |
185 |
|
|
|
186 |
|
|
if (fgets(buf, n, stdin) == NULL) |
187 |
|
|
buf[0] = '\0'; |
188 |
|
|
rip(buf); |
189 |
|
|
|
190 |
|
|
(void)putc('\n', stderr); |
191 |
|
|
(void)fflush(stderr); |
192 |
|
|
|
193 |
|
|
/* Restore signal handler and turn echo back on */ |
194 |
|
|
if (old_handler != SIG_ERR) |
195 |
|
|
(void)signal(SIGINT, old_handler); |
196 |
|
|
skey_echo(1); |
197 |
|
|
|
198 |
|
|
sevenbit(buf); |
199 |
|
|
|
200 |
|
|
return(buf); |
201 |
|
|
} |
202 |
|
|
|
203 |
|
|
/* Read in an s/key OTP (does not turn off echo) */ |
204 |
|
|
char * |
205 |
|
|
readskey(char *buf, int n) |
206 |
|
|
{ |
207 |
|
|
if (fgets(buf, n, stdin) == NULL) |
208 |
|
|
buf[0] = '\0'; |
209 |
|
|
rip(buf); |
210 |
|
|
|
211 |
|
|
sevenbit(buf); |
212 |
|
|
|
213 |
|
|
return(buf); |
214 |
|
|
} |
215 |
|
|
|
216 |
|
|
/* Signal handler for trapping ^C */ |
217 |
|
|
/*ARGSUSED*/ |
218 |
|
|
static void |
219 |
|
|
trapped(int sig) |
220 |
|
|
{ |
221 |
|
|
write(STDERR_FILENO, "^C\n", 3); |
222 |
|
|
|
223 |
|
|
/* Turn on echo if necessary */ |
224 |
|
|
skey_echo(1); |
225 |
|
|
|
226 |
|
|
_exit(1); |
227 |
|
|
} |
228 |
|
|
|
229 |
|
|
/* |
230 |
|
|
* Convert 16-byte hex-ascii string to 8-byte binary array |
231 |
|
|
* Returns 0 on success, -1 on error |
232 |
|
|
*/ |
233 |
|
|
int |
234 |
|
|
atob8(char *out, char *in) |
235 |
|
|
{ |
236 |
|
|
int i; |
237 |
|
|
int val; |
238 |
|
|
|
239 |
|
|
if (in == NULL || out == NULL) |
240 |
|
|
return(-1); |
241 |
|
|
|
242 |
|
|
for (i=0; i < 8; i++) { |
243 |
|
|
if ((in = skipspace(in)) == NULL) |
244 |
|
|
return(-1); |
245 |
|
|
if ((val = htoi(*in++)) == -1) |
246 |
|
|
return(-1); |
247 |
|
|
*out = val << 4; |
248 |
|
|
|
249 |
|
|
if ((in = skipspace(in)) == NULL) |
250 |
|
|
return(-1); |
251 |
|
|
if ((val = htoi(*in++)) == -1) |
252 |
|
|
return(-1); |
253 |
|
|
*out++ |= val; |
254 |
|
|
} |
255 |
|
|
return(0); |
256 |
|
|
} |
257 |
|
|
|
258 |
|
|
/* Convert 8-byte binary array to 16-byte hex-ascii string */ |
259 |
|
|
int |
260 |
|
|
btoa8(char *out, char *in) |
261 |
|
|
{ |
262 |
✗✓ |
216 |
if (in == NULL || out == NULL) |
263 |
|
|
return(-1); |
264 |
|
|
|
265 |
|
108 |
(void)snprintf(out, 17, "%02x%02x%02x%02x%02x%02x%02x%02x", |
266 |
|
108 |
in[0] & 0xff, in[1] & 0xff, in[2] & 0xff, in[3] & 0xff, |
267 |
|
108 |
in[4] & 0xff, in[5] & 0xff, in[6] & 0xff, in[7] & 0xff); |
268 |
|
|
|
269 |
|
108 |
return(0); |
270 |
|
108 |
} |
271 |
|
|
|
272 |
|
|
/* Convert hex digit to binary integer */ |
273 |
|
|
int |
274 |
|
|
htoi(int c) |
275 |
|
|
{ |
276 |
|
|
if ('0' <= c && c <= '9') |
277 |
|
|
return(c - '0'); |
278 |
|
|
if ('a' <= c && c <= 'f') |
279 |
|
|
return(10 + c - 'a'); |
280 |
|
|
if ('A' <= c && c <= 'F') |
281 |
|
|
return(10 + c - 'A'); |
282 |
|
|
return(-1); |
283 |
|
|
} |
284 |
|
|
|
285 |
|
|
/* Skip leading spaces from the string */ |
286 |
|
|
char * |
287 |
|
|
skipspace(char *cp) |
288 |
|
|
{ |
289 |
|
|
while (*cp == ' ' || *cp == '\t') |
290 |
|
|
cp++; |
291 |
|
|
|
292 |
|
|
if (*cp == '\0') |
293 |
|
|
return(NULL); |
294 |
|
|
else |
295 |
|
|
return(cp); |
296 |
|
|
} |
297 |
|
|
|
298 |
|
|
/* Remove backspaced over characters from the string */ |
299 |
|
|
void |
300 |
|
|
backspace(char *buf) |
301 |
|
|
{ |
302 |
|
|
char bs = 0x8; |
303 |
|
|
char *cp = buf; |
304 |
|
|
char *out = buf; |
305 |
|
|
|
306 |
|
|
while (*cp) { |
307 |
|
|
if (*cp == bs) { |
308 |
|
|
if (out == buf) { |
309 |
|
|
cp++; |
310 |
|
|
continue; |
311 |
|
|
} else { |
312 |
|
|
cp++; |
313 |
|
|
out--; |
314 |
|
|
} |
315 |
|
|
} else { |
316 |
|
|
*out++ = *cp++; |
317 |
|
|
} |
318 |
|
|
|
319 |
|
|
} |
320 |
|
|
*out = '\0'; |
321 |
|
|
} |
322 |
|
|
|
323 |
|
|
/* Make sure line is all seven bits */ |
324 |
|
|
void |
325 |
|
|
sevenbit(char *s) |
326 |
|
|
{ |
327 |
✓✓ |
1476 |
while (*s) |
328 |
|
684 |
*s++ &= 0x7f; |
329 |
|
36 |
} |
330 |
|
|
|
331 |
|
|
/* Set hash algorithm type */ |
332 |
|
|
char * |
333 |
|
|
skey_set_algorithm(char *new) |
334 |
|
|
{ |
335 |
|
|
int i; |
336 |
|
|
|
337 |
✓✓ |
216 |
for (i = 0; skey_algorithm_table[i].name; i++) { |
338 |
✓✓ |
84 |
if (strcmp(new, skey_algorithm_table[i].name) == 0) { |
339 |
|
36 |
skey_hash_type = i; |
340 |
|
36 |
return(new); |
341 |
|
|
} |
342 |
|
|
} |
343 |
|
|
|
344 |
|
4 |
return(NULL); |
345 |
|
40 |
} |
346 |
|
|
|
347 |
|
|
/* Get current hash type */ |
348 |
|
|
const char * |
349 |
|
|
skey_get_algorithm(void) |
350 |
|
|
{ |
351 |
|
80 |
return(skey_algorithm_table[skey_hash_type].name); |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
/* Turn echo on/off */ |
355 |
|
|
static void |
356 |
|
|
skey_echo(int action) |
357 |
|
|
{ |
358 |
|
|
static struct termios term; |
359 |
|
|
static int echo = 0; |
360 |
|
|
|
361 |
|
|
if (action == 0) { |
362 |
|
|
/* Turn echo off */ |
363 |
|
|
(void) tcgetattr(fileno(stdin), &term); |
364 |
|
|
if ((echo = (term.c_lflag & ECHO))) { |
365 |
|
|
term.c_lflag &= ~ECHO; |
366 |
|
|
(void) tcsetattr(fileno(stdin), TCSAFLUSH|TCSASOFT, &term); |
367 |
|
|
} |
368 |
|
|
} else if (action && echo) { |
369 |
|
|
/* Turn echo on */ |
370 |
|
|
term.c_lflag |= ECHO; |
371 |
|
|
(void) tcsetattr(fileno(stdin), TCSAFLUSH|TCSASOFT, &term); |
372 |
|
|
echo = 0; |
373 |
|
|
} |
374 |
|
|
} |