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: stable/12/contrib/opie/libopie/generator.c 257264 2013-10-28 18:24:31Z sbruno $
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 
55 static 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 
62 static 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 
131 ret:
132   close(s);
133   return -1;
134 };
135 #endif /* OPIEAUTO */
136 
137 int 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 
238 l0:
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 
288 l1:
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
389 l2:
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