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