Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / opie / libopie / generator.c
1 /* generator.c: The opiegenerator() library function.
2
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
6 the software.
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
9
10         History:
11
12         Modified by cmetz for OPIE 2.4. Added opieauto code based on
13                 previously released test code. Renamed buffer to challenge.
14                 Use struct opie_otpkey for keys.
15         Modified by cmetz for OPIE 2.32. If secret=NULL, always return
16                 as if opieauto returned "get the secret". Renamed
17                 _opieparsechallenge() to __opieparsechallenge(). Check
18                 challenge for extended response support and don't send
19                 an init-hex response if extended response support isn't
20                 indicated in the challenge.
21         Modified by cmetz for OPIE 2.31. Renamed "init" to "init-hex".
22                 Removed active attack protection support. Fixed fairly
23                 bug in how init response was computed (i.e., dead wrong).
24         Modified by cmetz for OPIE 2.3. Use _opieparsechallenge(). ifdef
25                 around string.h. Output hex responses by default, output
26                 OTP re-init extended responses (same secret) if sequence
27                 number falls below 10.
28         Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al.
29                 Bug fixes.
30         Created at NRL for OPIE 2.2.
31
32 $FreeBSD: src/contrib/opie/libopie/generator.c,v 1.3.6.2 2002/07/15 14:48:47 des Exp $
33 */
34
35 #include "opie_cfg.h"
36 #if HAVE_STRING_H
37 #include <string.h>
38 #endif /* HAVE_STRING_H */
39 #if OPIEAUTO
40 #include <errno.h>
41 #if HAVE_STDLIB_H
42 #include <stdlib.h>
43 #endif /* HAVE_STDLIB_H */
44 #include <sys/stat.h>
45
46 #include <sys/socket.h>
47 #include <sys/un.h>
48 #endif /* OPIEAUTO */
49 #if DEBUG
50 #include <syslog.h>
51 #endif /* DEBUG */
52 #include "opie.h"
53
54 static char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
55
56 #if OPIEAUTO
57 #ifndef max
58 #define max(x, y) (((x) > (y)) ? (x) : (y))
59 #endif /* max */
60
61 static int opieauto_connect FUNCTION_NOARGS
62 {
63   int s;
64   struct sockaddr_un sun;
65   char buffer[1024];
66   char *c, *c2 ="/.opieauto";
67   uid_t myuid = getuid(), myeuid = geteuid();
68
69   if (!myuid || !myeuid || (myuid != myeuid)) {
70 #if DEBUG
71     syslog(LOG_DEBUG, "opieauto_connect: superuser and/or setuid not allowed");
72 #endif /* DEBUG */
73     return -1;
74   };
75
76   memset(&sun, 0, sizeof(struct sockaddr_un));
77   sun.sun_family = AF_UNIX;
78
79   if (!(c = getenv("HOME"))) {
80 #if DEBUG
81     syslog(LOG_DEBUG, "opieauto_connect: no HOME variable?");
82 #endif /* DEBUG */
83     return -1;
84   };
85
86   if (strlen(c) > (sizeof(sun.sun_path) - strlen(c2) - 1)) {
87 #if DEBUG
88     syslog(LOG_DEBUG, "opieauto_connect: HOME is too long: %s", c);
89 #endif /* DEBUG */
90     return -1;
91   };
92
93   strcpy(sun.sun_path, c);
94   strcat(sun.sun_path, c2);
95
96   if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
97 #if DEBUG
98     syslog(LOG_DEBUG, "opieauto_connect: socket: %s(%d)", strerror(errno), errno);
99 #endif /* DEBUG */
100     return -1;
101   };
102
103   {
104     struct stat st;
105
106     if (stat(sun.sun_path, &st) < 0) {
107 #if DEBUG
108       syslog(LOG_DEBUG, "opieauto_connect: stat: %s(%d)\n", strerror(errno), errno);
109 #endif /* DEBUG */
110       goto ret;
111     };
112
113     if (connect(s, (struct sockaddr *)&sun, sizeof(struct sockaddr_un))) {
114 #if DEBUG
115       syslog(LOG_DEBUG, "opieauto_connect: connect: %s(%d)\n", strerror(errno), errno);
116 #endif /* DEBUG */
117       goto ret;
118     };
119
120     if ((st.st_uid != myuid) || (!S_ISSOCK(st.st_mode)) || ((st.st_mode & 07777) != 0600)) {
121 #if DEBUG
122       syslog(LOG_DEBUG, "opieauto_connect: something's fishy about the socket\n");
123 #endif /* DEBUG */
124       goto ret;
125     };
126   };
127
128   return s;
129
130 ret:
131   close(s);
132   return -1;
133 };
134 #endif /* OPIEAUTO */
135
136 int opiegenerator FUNCTION((challenge, secret, response), char *challenge AND char *secret AND char *response)
137 {
138   int algorithm;
139   int sequence;
140   char *seed;
141   struct opie_otpkey key;
142   int i;
143   int exts;
144 #if OPIEAUTO
145   int s;
146   int window;
147   char cmd[1+1+1+1+4+1+OPIE_SEED_MAX+1+4+1+4+1+4+1+4+1];
148   char *c;
149 #endif /* OPIEAUTO */
150
151   if (!(challenge = strstr(challenge, "otp-")))
152     return 1;
153
154   challenge += 4;
155
156   if (__opieparsechallenge(challenge, &algorithm, &sequence, &seed, &exts))
157     return 1;
158
159   if ((sequence < 2) || (sequence > 9999))
160     return 1;
161
162   if (*secret) {
163     if (opiepasscheck(secret))
164       return -2;
165
166     if (i = opiekeycrunch(algorithm, &key, seed, secret))
167       return i;
168
169     if (sequence <= OPIE_SEQUENCE_RESTRICT) {
170       if (!(exts & 1))
171         return 1;
172
173       {
174         char newseed[OPIE_SEED_MAX + 1];
175         struct opie_otpkey newkey;
176         char *c;
177         char buf[OPIE_SEED_MAX + 48 + 1];
178
179         while (sequence-- != 0)
180           opiehash(&key, algorithm);
181
182         if (opienewseed(strcpy(newseed, seed)) < 0)
183           return -1;
184
185         if (opiekeycrunch(algorithm, &newkey, newseed, secret))
186           return -1;
187
188         for (i = 0; i < 499; i++)
189           opiehash(&newkey, algorithm);
190
191         strcpy(response, "init-hex:");
192         strcat(response, opiebtoh(buf, &key));
193         if (snprintf(buf, sizeof(buf), ":%s 499 %s:", algids[algorithm],
194             newseed) >= sizeof(buf)) {
195 #ifdef DEBUG
196           syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at init-hex");
197 #endif /* DEBUG */
198           return -1;
199         }
200         strcat(response, buf);
201         strcat(response, opiebtoh(buf, &newkey));
202       };
203     };
204   };
205
206 #if OPIEAUTO
207   if ((s = opieauto_connect()) >= 0) {
208     if ((i = read(s, cmd, sizeof(cmd)-1)) < 0) {
209 #if DEBUG
210       syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno);
211 #endif /* DEBUG */
212       close(s);
213       s = -1;
214       goto l0;
215     };
216     cmd[i] = 0;
217     if ((cmd[0] != 'C') || (cmd[1] != '+') || (cmd[2] != ' ')) {
218 #if DEBUG
219       syslog(LOG_DEBUG, "opiegenerator: got invalid/failing C+ response: %s\n", cmd);
220 #endif /* DEBUG */
221       close(s);
222       s = -1;
223       goto l0;
224     };
225
226     window = strtoul(&cmd[3], &c, 10);
227     if (!window || (window >= (OPIE_SEQUENCE_MAX - OPIE_SEQUENCE_RESTRICT)) || !isspace(*c)) {
228 #if DEBUG
229       syslog(LOG_DEBUG, "opiegenerator: got bogus option response: %s\n", cmd);
230 #endif /* DEBUG */
231       close(s);
232       s = -1;
233       goto l0;
234     };
235   };
236
237 l0:
238   if (*secret) {
239     int j;
240
241     if (s < 0) {
242       j = 0;
243       goto l1;
244     };
245
246     j = max(sequence - window + 1, OPIE_SEQUENCE_RESTRICT);
247
248     for (i = j; i > 0; i--)
249       opiehash(&key, algorithm);
250
251     {
252       char buf[16+1];
253
254       opiebtoa8(buf, &key);
255
256       if (snprintf(cmd, sizeof(cmd), "S= %d %d %s %s\n", algorithm, sequence,
257           seed, buf) >= sizeof(cmd)) {
258 #if DEBUG
259         syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at S=\n");
260 #endif /* DEBUG */
261         goto l1;
262       }
263     }
264
265     if (write(s, cmd, i = strlen(cmd)) != i) {
266 #if DEBUG
267       syslog(LOG_DEBUG, "opiegenerator: write: %s(%d)\n", strerror(errno), errno);
268 #endif /* DEBUG */
269       goto l1;
270     };
271
272     if ((i = read(s, cmd, sizeof(cmd))) < 0) {
273 #if DEBUG
274       syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno);
275 #endif /* DEBUG */
276     };
277     close(s);
278
279     cmd[i] = 0;
280     i = strlen(seed);
281     if ((cmd[0] != 'S') || (cmd[1] != '+') || (cmd[2] != ' ') || (strtoul(&cmd[3], &c, 10) != algorithm) || (strtoul(c + 1, &c, 10) != sequence) || strncmp(++c, seed, i) || (*(c + i) != '\n')) {
282 #if DEBUG
283       syslog(LOG_DEBUG, "opiegenerator: got invalid/failing S+ response: %s\n", cmd);
284 #endif /* DEBUG */
285     };
286
287 l1:
288     for (i = sequence - j; i > 0; i--)
289       opiehash(&key, algorithm);
290
291     opiebtoh(response, &key);
292   } else {
293     if (s < 0)
294       goto l2;
295
296     if ((snprintf(cmd, sizeof(cmd), "s= %d %d %s\n", algorithm, sequence,
297         seed) >= sizeof(cmd))) {
298 #if DEBUG
299       syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at s=\n");
300 #endif /* DEBUG */
301       goto l2;
302     }
303
304     if (write(s, cmd, i = strlen(cmd)) != i) {
305 #if DEBUG
306       syslog(LOG_DEBUG, "opiegenerator: write: %s(%d)\n", strerror(errno), errno);
307 #endif /* DEBUG */
308       goto l2;
309     };
310
311     if ((i = read(s, cmd, sizeof(cmd))) < 0) {
312 #if DEBUG
313       syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno);
314 #endif /* DEBUG */
315       goto l2;
316     };
317     close(s);
318
319     i = strlen(seed);
320
321     if ((cmd[0] != 's') || (cmd[2] != ' ') || (strtoul(&cmd[3], &c, 10) != algorithm) || (strtoul(c + 1, &c, 10) != sequence) || strncmp(++c, seed, i)) {
322 #if DEBUG
323       if (c)
324         *c = 0;
325       else
326         cmd[3] = 0;
327       
328       syslog(LOG_DEBUG, "opiegenerator: got bogus/invalid s response: %s\n", cmd);
329 #endif /* DEBUG */
330       goto l2;
331     };
332
333     c += i;
334
335     if (cmd[1] == '-') {
336 #if DEBUG
337       if (*c != '\n') {
338         *c = 0;
339         syslog(LOG_DEBUG, "opiegenerator: got invalid s- response: %s\n", cmd);
340       };
341 #endif /* DEBUG */
342       goto l2;
343     };
344
345     if (cmd[1] != '+') {
346 #if DEBUG
347       *c = 0;
348       syslog(LOG_DEBUG, "opiegenerator: got invalid s response: %s\n", cmd);
349 #endif /* DEBUG */
350       goto l2;
351     };
352
353     {
354       char *c2;
355
356       if (!(c2 = strchr(++c, '\n'))) {
357 #if DEBUG
358         *c = 0;
359         syslog(LOG_DEBUG, "opiegenerator: got invalid s+ response: %s\n", cmd);
360 #endif /* DEBUG */
361         goto l2;
362       };
363
364       *c2++ = 0;
365     };
366
367     if (!opieatob8(&key, c))
368       goto l2;
369
370     opiebtoh(response, &key);
371   };
372
373   if (s >= 0)
374     close(s);
375 #else /* OPIEAUTO */
376   if (*secret) {
377     while (sequence-- != 0)
378       opiehash(&key, algorithm);
379
380     opiebtoh(response, &key);
381   } else
382     return -2;
383 #endif /* OPIEAUTO */
384
385   return 0;
386
387 #if OPIEAUTO
388 l2:
389 #if DEBUG
390   syslog(LOG_DEBUG, "opiegenerator: no opieauto response available.\n");
391 #endif /* DEBUG */
392   if (s >= 0)
393     close(s);
394
395   return -2;
396 #endif /* OPIEAUTO */
397 };