1 #include <stdlib.h>
2 #include <string.h>
3 #include <stdint.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <time.h>
8
9 #include <winsock2.h>
10 #include <windows.h>
11 #include <iphlpapi.h>
12
13 #include "inc.h"
14 #include "tcpcrypt_divert.h"
15 #include "tcpcryptd.h"
16 #include "tcpcrypt.h"
17 #include "checksum.h"
18 #include "util.h"
19
20 #include <windivert.h>
21
22 #define MAC_SIZE 14
23
24 static int _s;
25 static divert_cb _cb;
26
27 struct packet {
28 unsigned char p_buf[2048];
29 int p_len;
30 struct packet *p_next;
31 } _outbound;
32
33 enum {
34 STATE_NONE = 0,
35 STATE_REDIRECT,
36 STATE_HANDSHAKE,
37 STATE_CONNECTED
38 };
39
40 #define CONN_TIMEOUT 15
41
42 static struct conmap {
43 struct sockaddr_in src;
44 struct sockaddr_in dst;
45 int state;
46 time_t dead;
47 struct conmap *next;
48 } _cons;
49
50 static struct in_addr _local_ip;
51
52 extern int do_divert_open(void);
53 extern int do_divert_read(int s, void *buf, int len);
54 extern int do_divert_write(int s, void *buf, int len);
55 extern void do_divert_close(int s);
56
divert_open(int port,divert_cb cb)57 static int divert_open(int port, divert_cb cb)
58 {
59 int s;
60 struct sockaddr_in s_in;
61 socklen_t len = sizeof(s_in);
62
63 _s = do_divert_open();
64 _cb = cb;
65
66 /* figure out local IP */
67 if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
68 err(1, "socket()");
69
70 memset(&s_in, 0, sizeof(&s_in));
71 s_in.sin_family = PF_INET;
72 s_in.sin_addr.s_addr = inet_addr("8.8.8.8");
73 s_in.sin_port = htons(666);
74
75 if (connect(s, (struct sockaddr*) &s_in, sizeof(s_in)) == -1)
76 err(1, "connect()");
77
78 if (getsockname(s, (struct sockaddr*) &s_in, &len) == -1)
79 err(1, "getsockname()");
80
81 _local_ip.s_addr = s_in.sin_addr.s_addr;
82
83 xprintf(XP_ALWAYS, "Local IP is %s\n", inet_ntoa(_local_ip));
84
85 close(s);
86
87 return _s;
88 }
89
divert_close(void)90 static void divert_close(void)
91 {
92 do_divert_close(_s);
93 }
94
do_redirect(struct conmap * c,struct ip * ip,struct tcphdr * tcp,struct in_addr * fromip,uint32_t toip,uint16_t * fromport,uint16_t toport)95 static void do_redirect(struct conmap *c,
96 struct ip *ip, struct tcphdr *tcp,
97 struct in_addr *fromip, uint32_t toip,
98 uint16_t *fromport, uint16_t toport)
99 {
100 #if 0
101 xprintf(XP_NOISY, "rdr %s:%d->",
102 inet_ntoa(*fromip), ntohs(*fromport));
103 #endif
104 fromip->s_addr = toip;
105 *fromport = htons(toport);
106
107 checksum_packet(NULL, ip, tcp);
108 #if 0
109 xprintf(XP_NOISY,"%s:%d\n",
110 inet_ntoa(*fromip), ntohs(*fromport));
111 #endif
112
113 if (tcp->th_flags & (TH_RST | TH_FIN)) {
114 c->dead = time(NULL);
115 }
116 }
117
find_conmap_prev(int sport)118 static struct conmap *find_conmap_prev(int sport)
119 {
120 struct conmap *c = &_cons;
121
122 while (c->next) {
123 if (c->next->src.sin_port == sport)
124 return c;
125
126 c = c->next;
127 }
128
129 return NULL;
130 }
131
find_conmap(int sport)132 static struct conmap *find_conmap(int sport)
133 {
134 struct conmap *c = find_conmap_prev(sport);
135
136 if (!c)
137 return NULL;
138
139 return c->next;
140 }
141
redirect(struct ip * ip,int len,int flags)142 static struct conmap *redirect(struct ip *ip, int len, int flags)
143 {
144 struct tcphdr *tcp = get_tcp(ip);
145
146 if (ntohs(tcp->th_dport) == 80) {
147 struct conmap *c = find_conmap(tcp->th_sport);
148
149 if (!c && (tcp->th_flags == TH_SYN)) {
150 c = xmalloc(sizeof(*c));
151 memset(c, 0, sizeof(*c));
152
153 c->src.sin_port = tcp->th_sport;
154 c->state = STATE_REDIRECT;
155
156 c->next = _cons.next;
157 _cons.next = c;
158 }
159
160 /* not redirecting */
161 if (!c)
162 return NULL;
163
164 /* don't redirect outbound handshake but divert it */
165 if (c->state == STATE_HANDSHAKE)
166 return c;
167
168 if (c->state != STATE_REDIRECT)
169 return NULL;
170
171 if (tcp->th_flags == TH_SYN)
172 c->dead = 0;
173
174 c->dst.sin_addr.s_addr = ip->ip_dst.s_addr;
175
176 do_redirect(c, ip, tcp,
177 &ip->ip_dst, _local_ip.s_addr,
178 &tcp->th_dport, REDIRECT_PORT);
179
180 } else if (ntohs(tcp->th_sport) == REDIRECT_PORT) {
181 struct conmap *c = find_conmap(tcp->th_dport);
182
183 if (!c || c->state != STATE_REDIRECT)
184 return NULL;
185
186 do_redirect(c, ip, tcp,
187 &ip->ip_src, c->dst.sin_addr.s_addr,
188 &tcp->th_sport, 80);
189 }
190
191 return NULL;
192 }
193
print_con(void)194 static void print_con(void)
195 {
196 struct conmap *c = &_cons;
197
198 printf("Dumping con\n");
199
200 while ((c = c->next)) {
201 printf("con %s:%d ",
202 inet_ntoa(c->src.sin_addr),
203 ntohs(c->src.sin_port));
204
205 printf("->%s:%d %d [dead %u]\n",
206 inet_ntoa(c->dst.sin_addr),
207 ntohs(c->dst.sin_port),
208 c->state,
209 c->dead);
210 }
211 }
212
kill_dead(void)213 static void kill_dead(void)
214 {
215 time_t now = time(NULL);
216 struct conmap *prev = &_cons, *cur;
217
218 while ((cur = prev->next)) {
219 if (cur->dead && (now - cur->dead) >= CONN_TIMEOUT) {
220 prev->next = cur->next;
221 free(cur);
222 continue;
223 }
224
225 prev = cur;
226 }
227 }
228
firewall_divert(struct ip * ip,int len,int flags)229 static int firewall_divert(struct ip *ip, int len, int flags)
230 {
231 struct tcphdr *tcp = get_tcp(ip);
232 struct conmap *c;
233
234 kill_dead();
235 // print_con();
236
237 c = redirect(ip, len, flags);
238
239 /* don't firewall our injections */
240 if (ip->ip_tos == INJECT_TOS) {
241 ip->ip_tos = 0;
242 checksum_ip(ip);
243 return 0;
244 }
245
246 /* stuff we didn't redirect, so it's going to the outside world */
247 if (c) {
248 /* divert syns */
249 if (tcp->th_flags == TH_SYN)
250 c->state = STATE_HANDSHAKE;
251
252 /* XXX assume it's ACK of 3 way handshake. Won't work with
253 * retransmits */
254 if (!(tcp->th_flags & TH_SYN)) {
255 if (c->state == STATE_HANDSHAKE) {
256 // c->state = STATE_CONNECTED;
257 return 1;
258 }
259 }
260 }
261
262 /* divert handshake */
263 if (tcp->th_flags & TH_SYN)
264 return 1;
265
266 return 0;
267 }
268
do_divert_next_packet(unsigned char * buf,int rc)269 static void do_divert_next_packet(unsigned char *buf, int rc)
270 {
271 int verdict = DIVERT_MODIFY;
272 int flags = 0;
273 struct ip *iph = (struct ip*) &buf[MAC_SIZE];
274 int len;
275 PDIVERT_ADDRESS addr = (PDIVERT_ADDRESS)buf;
276
277 if (rc < MAC_SIZE)
278 errx(1, "short read %d", rc);
279
280 if (addr->Direction == WINDIVERT_DIRECTION_INBOUND)
281 flags |= DF_IN;
282
283 // XXX ethernet padding on short packets? (46 byte minimum)
284 len = rc - MAC_SIZE;
285 if (len > ntohs(iph->ip_len)) {
286 xprintf(XP_ALWAYS, "Trimming from %d to %d\n",
287 len, ntohs(iph->ip_len));
288
289 len = ntohs(iph->ip_len);
290 }
291
292 if (firewall_divert(iph, len, flags))
293 verdict = _cb(iph, len, flags);
294
295 switch (verdict) {
296 case DIVERT_MODIFY:
297 rc = ntohs(iph->ip_len) + MAC_SIZE;
298 /* fallthrough */
299 case DIVERT_ACCEPT:
300 flags = do_divert_write(_s, buf, rc);
301 if (flags == -1)
302 err(1, "write()");
303
304 if (flags != rc)
305 errx(1, "wrote %d/%d", flags, rc);
306 break;
307
308 case DIVERT_DROP:
309 break;
310
311 default:
312 abort();
313 break;
314 }
315 }
316
divert_next_packet(int s)317 static void divert_next_packet(int s)
318 {
319 unsigned char buf[2048];
320 int rc;
321
322 rc = do_divert_read(_s, buf, sizeof(buf));
323 if (rc == -1)
324 err(1, "read()");
325
326 if (rc == 0)
327 errx(1, "EOF");
328
329 do_divert_next_packet(buf, rc);
330 }
331
divert_inject(void * data,int len)332 static void divert_inject(void *data, int len)
333 {
334 struct packet *p, *p2;
335 struct ip *ip;
336
337 p = malloc(sizeof(*p));
338 if (!p)
339 err(1, "malloc()");
340
341 memset(p, 0, sizeof(*p));
342
343 // XXX: for divert, we can just zero the ethhdr, which contains the
344 // DIVERT_ADDRESS. A zeroed address usually gives the desired
345 // result.
346
347 /* payload */
348 p->p_len = len + MAC_SIZE;
349
350 if (p->p_len > sizeof(p->p_buf))
351 errx(1, "too big (divert_inject)");
352
353 memcpy(&p->p_buf[MAC_SIZE], data, len);
354
355 /* Keep TOS signaling consistent */
356 ip = (struct ip*) &p->p_buf[MAC_SIZE];
357 ip->ip_tos = INJECT_TOS;
358 checksum_ip(ip);
359
360 /* add to list */
361 p2 = &_outbound;
362
363 if (p2->p_next)
364 p2 = p2->p_next;
365
366 p2->p_next = p;
367 }
368
divert_cycle(void)369 static void divert_cycle(void)
370 {
371 struct packet *p = _outbound.p_next;
372
373 while (p) {
374 struct packet *next = p->p_next;
375
376 do_divert_next_packet(p->p_buf, p->p_len);
377
378 free(p);
379
380 p = next;
381 }
382
383 _outbound.p_next = NULL;
384 }
385
divert_orig_dest(struct sockaddr_in * out,struct ip * ip,int * flags)386 static int divert_orig_dest(struct sockaddr_in *out, struct ip *ip, int *flags)
387 {
388 struct tcphdr *tcp = get_tcp(ip);
389 struct conmap *c = find_conmap(tcp->th_sport);
390
391 if (!c || c->state != STATE_REDIRECT)
392 return -1;
393
394 memset(out, 0, sizeof(*out));
395
396 out->sin_family = PF_INET;
397 out->sin_addr.s_addr = c->dst.sin_addr.s_addr;
398 out->sin_port = htons(80);
399
400 return 0;
401 }
402
win_dont_rdr(int s)403 void win_dont_rdr(int s)
404 {
405 struct sockaddr_in s_in;
406 struct conmap *c;
407 socklen_t len = sizeof(s_in);
408
409 if (getsockname(s, (struct sockaddr*) &s_in, &len) == -1)
410 err(1, "getsockname()");
411
412 c = find_conmap(s_in.sin_port);
413 if (c) {
414 printf("XXX TODO\n");
415 return;
416 }
417
418 c = xmalloc(sizeof(*c));
419 memset(c, 0, sizeof(*c));
420
421 c->src.sin_port = s_in.sin_port;
422 c->state = STATE_HANDSHAKE;
423
424 c->next = _cons.next;
425 _cons.next = c;
426
427 xprintf(XP_NOISY, "No RDR on %d\n", ntohs(c->src.sin_port));
428 }
429
win_handshake_complete(int s)430 void win_handshake_complete(int s)
431 {
432 struct sockaddr_in s_in;
433 struct conmap *c, *con;
434 socklen_t len = sizeof(s_in);
435
436 if (getsockname(s, (struct sockaddr*) &s_in, &len) == -1)
437 err(1, "getsockname()");
438
439 if (!(c = find_conmap_prev(s_in.sin_port))) {
440 printf("XXX TODO 222\n");
441 return;
442 }
443 con = c->next;
444
445 if (con->state != STATE_HANDSHAKE) {
446 printf("DDDD TODO\n");
447 return;
448 }
449
450 c->next = con->next;
451 free(con);
452 }
453
win_local_ip(void)454 uint32_t win_local_ip(void)
455 {
456 return _local_ip.s_addr;
457 }
458
divert_get(void)459 struct divert *divert_get(void)
460 {
461 static struct divert _divert_win = {
462 .open = divert_open,
463 .next_packet = divert_next_packet,
464 .close = divert_close,
465 .inject = divert_inject,
466 .cycle = divert_cycle,
467 .orig_dest = divert_orig_dest,
468 };
469
470 return &_divert_win;
471 }
472