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 "opie.h"
53
54static 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
61static 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
130ret:
131  close(s);
132  return -1;
133};
134#endif /* OPIEAUTO */
135
136int 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
237l0:
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
287l1:
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
388l2:
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};
398