1116742Ssam/* generator.c: The opiegenerator() library function.
2116904Ssam
3139530Ssam%%% portions-copyright-cmetz-96
4116742SsamPortions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5116742SsamReserved. The Inner Net License Version 2 applies to these portions of
6116742Ssamthe software.
7116742SsamYou should have received a copy of the license with this software. If
8116742Ssamyou didn't get a copy, you may request one from <license@inner.net>.
9116742Ssam
10116904Ssam        History:
11116904Ssam
12116904Ssam	Modified by cmetz for OPIE 2.4. Added opieauto code based on
13116904Ssam	        previously released test code. Renamed buffer to challenge.
14116904Ssam		Use struct opie_otpkey for keys.
15116904Ssam	Modified by cmetz for OPIE 2.32. If secret=NULL, always return
16116742Ssam		as if opieauto returned "get the secret". Renamed
17116742Ssam		_opieparsechallenge() to __opieparsechallenge(). Check
18116742Ssam		challenge for extended response support and don't send
19116742Ssam		an init-hex response if extended response support isn't
20116742Ssam		indicated in the challenge.
21116904Ssam	Modified by cmetz for OPIE 2.31. Renamed "init" to "init-hex".
22116904Ssam		Removed active attack protection support. Fixed fairly
23116904Ssam		bug in how init response was computed (i.e., dead wrong).
24116904Ssam	Modified by cmetz for OPIE 2.3. Use _opieparsechallenge(). ifdef
25116904Ssam		around string.h. Output hex responses by default, output
26116904Ssam		OTP re-init extended responses (same secret) if sequence
27116904Ssam		number falls below 10.
28116904Ssam	Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al.
29116904Ssam		Bug fixes.
30116904Ssam	Created at NRL for OPIE 2.2.
31116742Ssam
32116742Ssam$FreeBSD$
33116742Ssam*/
34116742Ssam
35116742Ssam#include "opie_cfg.h"
36116742Ssam#if HAVE_STRING_H
37116742Ssam#include <string.h>
38116742Ssam#endif /* HAVE_STRING_H */
39116742Ssam#if OPIEAUTO
40116742Ssam#include <errno.h>
41116742Ssam#if HAVE_STDLIB_H
42116742Ssam#include <stdlib.h>
43138568Ssam#endif /* HAVE_STDLIB_H */
44116742Ssam#include <sys/stat.h>
45138568Ssam
46116742Ssam#include <sys/socket.h>
47116742Ssam#include <sys/un.h>
48116742Ssam#endif /* OPIEAUTO */
49116742Ssam#if DEBUG
50138568Ssam#include <syslog.h>
51116742Ssam#endif /* DEBUG */
52116742Ssam#include "opie.h"
53116742Ssam
54138568Ssamstatic char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
55138568Ssam
56138568Ssam#if OPIEAUTO
57116742Ssam#ifndef max
58116742Ssam#define max(x, y) (((x) > (y)) ? (x) : (y))
59116742Ssam#endif /* max */
60116742Ssam
61116742Ssamstatic int opieauto_connect FUNCTION_NOARGS
62116742Ssam{
63116742Ssam  int s;
64116742Ssam  struct sockaddr_un sun;
65116742Ssam  char buffer[1024];
66138568Ssam  char *c, *c2 ="/.opieauto";
67138568Ssam  uid_t myuid = getuid(), myeuid = geteuid();
68138568Ssam
69138568Ssam  if (!myuid || !myeuid || (myuid != myeuid)) {
70138568Ssam#if DEBUG
71138568Ssam    syslog(LOG_DEBUG, "opieauto_connect: superuser and/or setuid not allowed");
72167283Ssam#endif /* DEBUG */
73167283Ssam    return -1;
74167283Ssam  };
75167283Ssam
76167283Ssam  memset(&sun, 0, sizeof(struct sockaddr_un));
77167283Ssam  sun.sun_family = AF_UNIX;
78167283Ssam
79167283Ssam  if (!(c = getenv("HOME"))) {
80167283Ssam#if DEBUG
81167283Ssam    syslog(LOG_DEBUG, "opieauto_connect: no HOME variable?");
82117811Ssam#endif /* DEBUG */
83117811Ssam    return -1;
84117811Ssam  };
85117811Ssam
86117811Ssam  if (strlen(c) > (sizeof(sun.sun_path) - strlen(c2) - 1)) {
87117811Ssam#if DEBUG
88117811Ssam    syslog(LOG_DEBUG, "opieauto_connect: HOME is too long: %s", c);
89138568Ssam#endif /* DEBUG */
90138568Ssam    return -1;
91138568Ssam  };
92138568Ssam
93138568Ssam  strcpy(sun.sun_path, c);
94138568Ssam  strcat(sun.sun_path, c2);
95138568Ssam
96116742Ssam  if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
97117811Ssam#if DEBUG
98117811Ssam    syslog(LOG_DEBUG, "opieauto_connect: socket: %s(%d)", strerror(errno), errno);
99116742Ssam#endif /* DEBUG */
100138568Ssam    return -1;
101116742Ssam  };
102138568Ssam
103116742Ssam  {
104138568Ssam    struct stat st;
105138568Ssam
106116742Ssam    if (stat(sun.sun_path, &st) < 0) {
107116742Ssam#if DEBUG
108148291Ssam      syslog(LOG_DEBUG, "opieauto_connect: stat: %s(%d)\n", strerror(errno), errno);
109148290Ssam#endif /* DEBUG */
110153349Ssam      goto ret;
111154736Ssam    };
112153346Ssam
113127648Ssam    if (connect(s, (struct sockaddr *)&sun, sizeof(struct sockaddr_un))) {
114138568Ssam#if DEBUG
115116742Ssam      syslog(LOG_DEBUG, "opieauto_connect: connect: %s(%d)\n", strerror(errno), errno);
116138568Ssam#endif /* DEBUG */
117138568Ssam      goto ret;
118138568Ssam    };
119121816Sbrooks
120116742Ssam    if ((st.st_uid != myuid) || (!S_ISSOCK(st.st_mode)) || ((st.st_mode & 07777) != 0600)) {
121117811Ssam#if DEBUG
122117811Ssam      syslog(LOG_DEBUG, "opieauto_connect: something's fishy about the socket\n");
123117811Ssam#endif /* DEBUG */
124116742Ssam      goto ret;
125116742Ssam    };
126116742Ssam  };
127160690Ssam
128116742Ssam  return s;
129116742Ssam
130116742Ssamret:
131138568Ssam  close(s);
132116742Ssam  return -1;
133116742Ssam};
134138568Ssam#endif /* OPIEAUTO */
135138568Ssam
136138568Ssamint opiegenerator FUNCTION((challenge, secret, response), char *challenge AND char *secret AND char *response)
137138568Ssam{
138138568Ssam  int algorithm;
139138568Ssam  int sequence;
140138568Ssam  char *seed;
141138568Ssam  struct opie_otpkey key;
142165894Ssam  int i;
143116742Ssam  int exts;
144138568Ssam#if OPIEAUTO
145138568Ssam  int s;
146138568Ssam  int window;
147138568Ssam  char cmd[1+1+1+1+4+1+OPIE_SEED_MAX+1+4+1+4+1+4+1+4+1];
148138568Ssam  char *c;
149138568Ssam#endif /* OPIEAUTO */
150116742Ssam
151116742Ssam  if (!(challenge = strstr(challenge, "otp-")))
152138568Ssam    return 1;
153138568Ssam
154138568Ssam  challenge += 4;
155138568Ssam
156138568Ssam  if (__opieparsechallenge(challenge, &algorithm, &sequence, &seed, &exts))
157138568Ssam    return 1;
158138568Ssam
159138568Ssam  if ((sequence < 2) || (sequence > 9999))
160138568Ssam    return 1;
161138568Ssam
162138568Ssam  if (*secret) {
163138568Ssam    if (opiepasscheck(secret))
164138568Ssam      return -2;
165138568Ssam
166138568Ssam    if (i = opiekeycrunch(algorithm, &key, seed, secret))
167138568Ssam      return i;
168138568Ssam
169138568Ssam    if (sequence <= OPIE_SEQUENCE_RESTRICT) {
170138568Ssam      if (!(exts & 1))
171138568Ssam	return 1;
172138568Ssam
173138568Ssam      {
174138568Ssam	char newseed[OPIE_SEED_MAX + 1];
175138568Ssam	struct opie_otpkey newkey;
176138568Ssam	char *c;
177138568Ssam	char buf[OPIE_SEED_MAX + 48 + 1];
178138568Ssam
179138568Ssam	while (sequence-- != 0)
180138568Ssam	  opiehash(&key, algorithm);
181138568Ssam
182138568Ssam	if (opienewseed(strcpy(newseed, seed)) < 0)
183138568Ssam	  return -1;
184138568Ssam
185138568Ssam	if (opiekeycrunch(algorithm, &newkey, newseed, secret))
186138568Ssam	  return -1;
187138568Ssam
188138568Ssam	for (i = 0; i < 499; i++)
189138568Ssam	  opiehash(&newkey, algorithm);
190138568Ssam
191138568Ssam	strcpy(response, "init-hex:");
192138568Ssam	strcat(response, opiebtoh(buf, &key));
193138568Ssam	if (snprintf(buf, sizeof(buf), ":%s 499 %s:", algids[algorithm],
194138568Ssam	    newseed) >= sizeof(buf)) {
195138568Ssam#ifdef DEBUG
196138568Ssam	  syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at init-hex");
197138568Ssam#endif /* DEBUG */
198116742Ssam	  return -1;
199138568Ssam	}
200138568Ssam	strcat(response, buf);
201116742Ssam	strcat(response, opiebtoh(buf, &newkey));
202138568Ssam      };
203138568Ssam    };
204138568Ssam  };
205138568Ssam
206138568Ssam#if OPIEAUTO
207138568Ssam  if ((s = opieauto_connect()) >= 0) {
208138568Ssam    if ((i = read(s, cmd, sizeof(cmd)-1)) < 0) {
209138568Ssam#if DEBUG
210138568Ssam      syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno);
211138568Ssam#endif /* DEBUG */
212138568Ssam      close(s);
213138568Ssam      s = -1;
214138568Ssam      goto l0;
215138568Ssam    };
216138568Ssam    cmd[i] = 0;
217138568Ssam    if ((cmd[0] != 'C') || (cmd[1] != '+') || (cmd[2] != ' ')) {
218138568Ssam#if DEBUG
219138568Ssam      syslog(LOG_DEBUG, "opiegenerator: got invalid/failing C+ response: %s\n", cmd);
220138568Ssam#endif /* DEBUG */
221138568Ssam      close(s);
222138568Ssam      s = -1;
223138568Ssam      goto l0;
224138568Ssam    };
225138568Ssam
226138568Ssam    window = strtoul(&cmd[3], &c, 10);
227138568Ssam    if (!window || (window >= (OPIE_SEQUENCE_MAX - OPIE_SEQUENCE_RESTRICT)) || !isspace(*c)) {
228138568Ssam#if DEBUG
229138568Ssam      syslog(LOG_DEBUG, "opiegenerator: got bogus option response: %s\n", cmd);
230138568Ssam#endif /* DEBUG */
231138568Ssam      close(s);
232138568Ssam      s = -1;
233138568Ssam      goto l0;
234138568Ssam    };
235138568Ssam  };
236138568Ssam
237138568Ssaml0:
238138568Ssam  if (*secret) {
239138568Ssam    int j;
240138568Ssam
241138568Ssam    if (s < 0) {
242138568Ssam      j = 0;
243138568Ssam      goto l1;
244138568Ssam    };
245138568Ssam
246138568Ssam    j = max(sequence - window + 1, OPIE_SEQUENCE_RESTRICT);
247138568Ssam
248138568Ssam    for (i = j; i > 0; i--)
249116742Ssam      opiehash(&key, algorithm);
250116742Ssam
251116742Ssam    {
252116742Ssam      char buf[16+1];
253116742Ssam
254116742Ssam      opiebtoa8(buf, &key);
255116742Ssam
256116742Ssam      if (snprintf(cmd, sizeof(cmd), "S= %d %d %s %s\n", algorithm, sequence,
257116742Ssam          seed, buf) >= sizeof(cmd)) {
258116742Ssam#if DEBUG
259116742Ssam        syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at S=\n");
260116742Ssam#endif /* DEBUG */
261116742Ssam	goto l1;
262116742Ssam      }
263116742Ssam    }
264116742Ssam
265116742Ssam    if (write(s, cmd, i = strlen(cmd)) != i) {
266116742Ssam#if DEBUG
267116742Ssam      syslog(LOG_DEBUG, "opiegenerator: write: %s(%d)\n", strerror(errno), errno);
268116742Ssam#endif /* DEBUG */
269116742Ssam      goto l1;
270116742Ssam    };
271138568Ssam
272116742Ssam    if ((i = read(s, cmd, sizeof(cmd))) < 0) {
273138568Ssam#if DEBUG
274116742Ssam      syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno);
275116742Ssam#endif /* DEBUG */
276138568Ssam    };
277116742Ssam    close(s);
278116742Ssam
279116742Ssam    cmd[i] = 0;
280116742Ssam    i = strlen(seed);
281116742Ssam    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')) {
282116742Ssam#if DEBUG
283116742Ssam      syslog(LOG_DEBUG, "opiegenerator: got invalid/failing S+ response: %s\n", cmd);
284116742Ssam#endif /* DEBUG */
285116742Ssam    };
286116742Ssam
287116742Ssaml1:
288116742Ssam    for (i = sequence - j; i > 0; i--)
289116742Ssam      opiehash(&key, algorithm);
290116742Ssam
291116742Ssam    opiebtoh(response, &key);
292116742Ssam  } else {
293116742Ssam    if (s < 0)
294138568Ssam      goto l2;
295116742Ssam
296116742Ssam    if ((snprintf(cmd, sizeof(cmd), "s= %d %d %s\n", algorithm, sequence,
297116742Ssam        seed) >= sizeof(cmd))) {
298116742Ssam#if DEBUG
299116742Ssam      syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at s=\n");
300116742Ssam#endif /* DEBUG */
301116742Ssam      goto l2;
302116742Ssam    }
303116742Ssam
304116742Ssam    if (write(s, cmd, i = strlen(cmd)) != i) {
305116742Ssam#if DEBUG
306116742Ssam      syslog(LOG_DEBUG, "opiegenerator: write: %s(%d)\n", strerror(errno), errno);
307116742Ssam#endif /* DEBUG */
308116742Ssam      goto l2;
309116742Ssam    };
310116742Ssam
311116742Ssam    if ((i = read(s, cmd, sizeof(cmd))) < 0) {
312116742Ssam#if DEBUG
313138568Ssam      syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno);
314138568Ssam#endif /* DEBUG */
315138568Ssam      goto l2;
316138568Ssam    };
317138568Ssam    close(s);
318138568Ssam
319138568Ssam    i = strlen(seed);
320116742Ssam
321116742Ssam    if ((cmd[0] != 's') || (cmd[2] != ' ') || (strtoul(&cmd[3], &c, 10) != algorithm) || (strtoul(c + 1, &c, 10) != sequence) || strncmp(++c, seed, i)) {
322116742Ssam#if DEBUG
323116742Ssam      if (c)
324116742Ssam	*c = 0;
325116742Ssam      else
326116742Ssam	cmd[3] = 0;
327116742Ssam
328116742Ssam      syslog(LOG_DEBUG, "opiegenerator: got bogus/invalid s response: %s\n", cmd);
329116742Ssam#endif /* DEBUG */
330116742Ssam      goto l2;
331116742Ssam    };
332116742Ssam
333116742Ssam    c += i;
334116742Ssam
335165887Ssam    if (cmd[1] == '-') {
336165887Ssam#if DEBUG
337165887Ssam      if (*c != '\n') {
338165887Ssam	*c = 0;
339165887Ssam	syslog(LOG_DEBUG, "opiegenerator: got invalid s- response: %s\n", cmd);
340165887Ssam      };
341165887Ssam#endif /* DEBUG */
342165887Ssam      goto l2;
343165887Ssam    };
344165887Ssam
345165887Ssam    if (cmd[1] != '+') {
346116742Ssam#if DEBUG
347148299Ssam      *c = 0;
348116742Ssam      syslog(LOG_DEBUG, "opiegenerator: got invalid s response: %s\n", cmd);
349116742Ssam#endif /* DEBUG */
350148299Ssam      goto l2;
351165887Ssam    };
352138568Ssam
353165569Ssam    {
354165569Ssam      char *c2;
355116742Ssam
356116742Ssam      if (!(c2 = strchr(++c, '\n'))) {
357138568Ssam#if DEBUG
358138568Ssam	*c = 0;
359138568Ssam	syslog(LOG_DEBUG, "opiegenerator: got invalid s+ response: %s\n", cmd);
360138568Ssam#endif /* DEBUG */
361148290Ssam	goto l2;
362148290Ssam      };
363138568Ssam
364116742Ssam      *c2++ = 0;
365138568Ssam    };
366165569Ssam
367116742Ssam    if (!opieatob8(&key, c))
368120482Ssam      goto l2;
369116742Ssam
370116742Ssam    opiebtoh(response, &key);
371116742Ssam  };
372116742Ssam
373116742Ssam  if (s >= 0)
374116742Ssam    close(s);
375116742Ssam#else /* OPIEAUTO */
376116742Ssam  if (*secret) {
377116742Ssam    while (sequence-- != 0)
378116742Ssam      opiehash(&key, algorithm);
379116742Ssam
380116742Ssam    opiebtoh(response, &key);
381116742Ssam  } else
382116742Ssam    return -2;
383116742Ssam#endif /* OPIEAUTO */
384116742Ssam
385138568Ssam  return 0;
386116742Ssam
387138568Ssam#if OPIEAUTO
388138568Ssaml2:
389116742Ssam#if DEBUG
390165887Ssam  syslog(LOG_DEBUG, "opiegenerator: no opieauto response available.\n");
391165887Ssam#endif /* DEBUG */
392165887Ssam  if (s >= 0)
393165887Ssam    close(s);
394116742Ssam
395165887Ssam  return -2;
396120482Ssam#endif /* OPIEAUTO */
397120482Ssam};
398120482Ssam