1 #include <stdint.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include <assert.h>
7 #include <errno.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10
11 #include <tcpcrypt/tcpcrypt.h>
12 #include "shared/socket_address.h"
13 #include "tcpcrypt_ctl.h"
14 #include "config.h"
15
16 #define MAX_LEN 1200
17
18 #define TCP_CRYPT 15
19
20 #ifndef SOL_TCP
21 #define SOL_TCP IPPROTO_TCP
22 #endif
23
24 enum {
25 IMP_UNKNOWN = 0,
26 IMP_USER,
27 IMP_KERNEL,
28 };
29
30 struct conf {
31 char *cf_ctl;
32 int cf_s;
33 uint32_t cf_seq;
34 struct socket_address cf_sa;
35 int cf_imp;
36 };
37
38 union sockaddr_u {
39 struct sockaddr addr;
40 struct sockaddr_in in;
41 struct sockaddr_in6 in6;
42 struct sockaddr_storage storage;
43 };
44
45 static struct conf _conf = {
46 .cf_s = -1,
47 .cf_ctl = TCPCRYPTD_CONTROL_SOCKET,
48 .cf_sa = SOCKET_ADDRESS_NULL
49 };
50
ensure_control_addr_resolved()51 static void ensure_control_addr_resolved()
52 {
53 struct socket_address sa;
54 int r;
55 static const int error_len = 1000;
56 char error[error_len];
57
58 if (!socket_address_is_null(&_conf.cf_sa))
59 return;
60
61 r = resolve_socket_address_local(_conf.cf_ctl, &sa, error, error_len);
62 if (r != 0)
63 errx(1, "opening control socket '%s': %s",
64 _conf.cf_ctl, error);
65
66 memcpy(&_conf.cf_sa, &sa, sizeof(sa));
67 }
68
tcpcrypt_setparam(int param,void * val)69 void tcpcrypt_setparam(int param, void *val)
70 {
71 switch (param) {
72 case TCPCRYPT_PARAM_CTLPATH:
73 _conf.cf_ctl = strdup(val);
74 socket_address_clear(&_conf.cf_sa);
75 if (_conf.cf_s >= 0) {
76 close(_conf.cf_s);
77 _conf.cf_s = -1;
78 }
79 break;
80
81 default:
82 printf("Unknown param %d\n", param);
83 break;
84 }
85 }
86
87 #ifndef __WIN32__
bind_local_unix(int s)88 static void bind_local_unix(int s)
89 {
90 struct sockaddr_un sun;
91 socklen_t path_len;
92
93 memset(&sun, 0, sizeof(sun));
94 sun.sun_family = AF_UNIX;
95
96 if (OS_LINUX) {
97 /* request autobind to an abstract unix address */
98 path_len = 0;
99 } else {
100 /* this makes a mess, and breaks when pids get reused;
101 * for now it is probably best to configure an AF_INET control
102 * socket for non-linux systems
103 */
104 path_len = snprintf(sun.sun_path, sizeof(sun.sun_path),
105 "/tmp/libtcpcryptd-%d", getpid()) + 1;
106 }
107 if (bind(s, (struct sockaddr *) &sun, sizeof(sa_family_t) + path_len))
108 err(1, "local bind()");
109 }
110 #endif /* __WIN32__ */
111
ensure_control_socket_open(void)112 static void ensure_control_socket_open(void)
113 {
114 if (_conf.cf_s >= 0)
115 return;
116
117 ensure_control_addr_resolved();
118
119 _conf.cf_s = socket(_conf.cf_sa.addr.sa.sa_family, SOCK_DGRAM, 0);
120 if (_conf.cf_s == -1)
121 err(1, "socket()");
122
123 #ifndef __WIN32__
124 if (_conf.cf_sa.addr.sa.sa_family == AF_UNIX) {
125 bind_local_unix(_conf.cf_s);
126 }
127 #endif
128 }
129
130 /* Sets fields in `struct tcpcrypt_ctl` given in the pointers `ctl_addr` and
131 `ctl_port` from the sockaddr in `ss`. If `ss` is IPv6, attempts a
132 rudimentary IPv6->IPv4 "conversion" for IPv4-compatible/mapped
133 addresses. This will fail on real (non-IPv4-compatible/mapped) IPv6
134 addresses. Currently, tcpcrypt is *not* IPv6 compatible. */
set_ctl_sockaddr(union sockaddr_u * ss,in_addr_t * ctl_addr,uint16_t * ctl_port)135 static void set_ctl_sockaddr(union sockaddr_u *ss,
136 in_addr_t *ctl_addr,
137 uint16_t *ctl_port)
138 {
139 if (ss->storage.ss_family == AF_INET) {
140 *ctl_addr = ss->in.sin_addr.s_addr;
141 *ctl_port = ss->in.sin_port;
142 } else { // AF_INET6
143 if (IN6_IS_ADDR_V4COMPAT(&ss->in6.sin6_addr) ||
144 IN6_IS_ADDR_V4MAPPED(&ss->in6.sin6_addr)) {
145 #ifdef __WIN32__
146 assert(!"not implemented");
147 abort();
148 #else
149 #if !defined s6_addr32
150 # define s6_addr32 __u6_addr.__u6_addr32
151 #endif
152 *ctl_addr = ss->in6.sin6_addr.s6_addr32[3];
153 *ctl_port = ss->in6.sin6_port;
154 #endif /* __WIN32__ */
155 } else {
156 /* TODO: add IPv6 support */
157 printf("Non-IPv4-compatible IPv6 addresses not supported."
158 "Behavior of get/set_sockopt call is unreliable.\n");
159 }
160 }
161
162 #ifdef DEBUG_IPV6
163 fprintf(stderr, "* set_ctl_sockaddr: %s:%d\n",
164 inet_ntoa(*ctl_addr), ntohs(*ctl_port));
165 #endif
166 }
167
do_sockopt(uint32_t flags,int s,int level,int optname,void * optval,socklen_t * optlen)168 static int do_sockopt(uint32_t flags, int s, int level, int optname,
169 void *optval, socklen_t *optlen)
170 {
171 unsigned char *crap;
172 struct tcpcrypt_ctl *ctl;
173 union sockaddr_u ss;
174 socklen_t sl = sizeof ss;
175 int rc, len, i, port;
176 int set = flags & TCC_SET;
177
178 if (level != IPPROTO_TCP)
179 errx(1, "bad level");
180
181 /* XXX */
182 if (*optlen > MAX_LEN) {
183 if (flags & TCC_SET)
184 errx(1, "setsockopt too long %d", *optlen);
185
186 *optlen = MAX_LEN;
187 }
188
189 crap = alloca(sizeof(*ctl) + (*optlen));
190 ctl = (struct tcpcrypt_ctl*) crap;
191 if (!crap)
192 return -1;
193
194 memset(ctl, 0, sizeof(*ctl));
195 ctl->tcc_seq = _conf.cf_seq++;
196
197 for (i = 0; i < 2; i++) {
198 memset(&ss, 0, sizeof(ss));
199
200 if (getsockname(s, (struct sockaddr*) &ss, &sl) == -1)
201 err(1, "getsockname()");
202
203 if (ss.storage.ss_family == AF_INET)
204 port = ntohs(ss.in.sin_port);
205 else
206 port = ntohs(ss.in6.sin6_port);
207
208 if (i == 1) {
209 // printf("forced bind to %d\n", port);
210 break;
211 }
212
213 if (port)
214 break;
215
216 /* let's just home the app doesn't call bind again */
217 ss.in.sin_family = PF_INET;
218 ss.in.sin_port = 0;
219 ss.in.sin_addr.s_addr = INADDR_ANY;
220
221 if (bind(s, &ss.addr, sizeof(ss)) == -1)
222 err(1, "bind()");
223 }
224
225 set_ctl_sockaddr(&ss, &ctl->tcc_src.s_addr, &ctl->tcc_sport);
226
227 memset(&ss, 0, sl);
228 if (getpeername(s, (struct sockaddr*) &ss, &sl) == 0) {
229 set_ctl_sockaddr(&ss, &ctl->tcc_dst.s_addr, &ctl->tcc_dport);
230 }
231
232 ctl->tcc_flags = flags;
233 ctl->tcc_opt = optname;
234 ctl->tcc_dlen = *optlen;
235
236 len = sizeof(*ctl);
237
238 if (*optlen) {
239 memcpy(crap + len, optval, *optlen);
240 len += *optlen;
241 }
242
243 ensure_control_socket_open();
244 #if 0
245 {
246 char name[1001];
247 int n = socket_address_pretty(name, 1000, &_conf.cf_sa);
248 name[n] = '\0';
249 fprintf(stderr, "Control socket: %s\n", name);
250 }
251 #endif
252 rc = sendto(_conf.cf_s, crap, len, 0,
253 &_conf.cf_sa.addr.sa, _conf.cf_sa.addr_len);
254 if (rc == -1)
255 return -1;
256
257 if (rc != len)
258 errx(1, "short write %d/%d", rc, len);
259
260 rc = recv(_conf.cf_s, crap, len, 0);
261 if (rc == -1)
262 err(1, "recvmsg()");
263
264 if (rc == 0)
265 errx(1, "EOF");
266
267 if (rc < sizeof(*ctl) || (rc != sizeof(*ctl) + ctl->tcc_dlen))
268 errx(1, "short read");
269
270 *optlen = ctl->tcc_dlen;
271
272 if (!set)
273 memcpy(optval, crap + sizeof(*ctl), *optlen);
274
275 if (ctl->tcc_err) {
276 errno = ctl->tcc_err;
277 ctl->tcc_err = -1;
278 }
279
280 return ctl->tcc_err;
281 }
282
probe_imp()283 static void probe_imp()
284 {
285 int s;
286 int opt = TCP_CRYPT_APP_SUPPORT;
287
288 if (_conf.cf_imp != IMP_UNKNOWN)
289 return;
290
291 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
292 if (s == -1)
293 err(1, "socket()");
294
295 if (setsockopt(s, SOL_TCP, TCP_CRYPT, &opt, sizeof(opt)) != -1)
296 _conf.cf_imp = IMP_KERNEL;
297 else
298 _conf.cf_imp = IMP_USER;
299 #if 0
300 printf("Using %d implementation\n", _conf.cf_imp);
301 #endif
302 close(s);
303 }
304
setsockopt_kernel(int s,int level,int optname,const void * optval,socklen_t optlen)305 static int setsockopt_kernel(int s, int level, int optname, const void *optval,
306 socklen_t optlen)
307 {
308 unsigned char lame[2048];
309
310 if ((optlen + 4) > sizeof(lame))
311 return -1;
312
313 memcpy(lame, &optname, sizeof(int));
314
315 memcpy(&lame[sizeof(int)], optval, optlen);
316
317 optlen += sizeof(int);
318
319 return setsockopt(s, SOL_TCP, TCP_CRYPT, lame, optlen);
320 }
321
getsockopt_kernel(int s,int level,int optname,void * optval,socklen_t * optlen)322 static int getsockopt_kernel(int s, int level, int optname, void *optval,
323 socklen_t *optlen)
324 {
325 unsigned char lame[2048];
326 int rc;
327
328 if (*optlen > sizeof(lame))
329 return -1;
330
331 memcpy(lame, &optname, sizeof(int));
332
333 rc = getsockopt(s, SOL_TCP, TCP_CRYPT, lame, optlen);
334
335 if (rc == -1)
336 return rc;
337
338 memcpy(optval, lame, *optlen);
339
340 return 0;
341 }
342
tcpcrypt_getsockopt(int s,int level,int optname,void * optval,socklen_t * optlen)343 int tcpcrypt_getsockopt(int s, int level, int optname, void *optval,
344 socklen_t *optlen)
345 {
346 probe_imp();
347
348 if (_conf.cf_imp == IMP_KERNEL)
349 return getsockopt_kernel(s, level, optname, optval, optlen);
350
351 return do_sockopt(0, s, level, optname, optval, optlen);
352 }
353
tcpcrypt_setsockopt(int s,int level,int optname,const void * optval,socklen_t optlen)354 int tcpcrypt_setsockopt(int s, int level, int optname, const void *optval,
355 socklen_t optlen)
356 {
357 probe_imp();
358
359 if (_conf.cf_imp == IMP_KERNEL)
360 return setsockopt_kernel(s, level, optname, optval, optlen);
361
362 return do_sockopt(TCC_SET, s, level, optname, (void*) optval, &optlen);
363 }
364
365 /* for tcpcrypt_getsessid */
__open_socket_for_getsessid()366 static int __open_socket_for_getsessid()
367 {
368 int s;
369 struct sockaddr_in s_in;
370 #ifdef __WIN32__
371 WSADATA wsadata;
372 if (WSAStartup(MAKEWORD(1,1), &wsadata) == SOCKET_ERROR)
373 errx(1, "WSAStartup()");
374 #endif
375
376 memset(&s_in, 0, sizeof(s_in));
377 s_in.sin_family = PF_INET;
378 s_in.sin_port = 0;
379 s_in.sin_addr.s_addr = INADDR_ANY;
380
381 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
382 if (s == -1)
383 err(1, "socket()");
384
385 if (bind(s, (struct sockaddr*) &s_in, sizeof(s_in)) == -1)
386 err(1, "bind()");
387
388 return s;
389 }
390
tcpcrypt_getsessid(char * remote_ip,uint16_t remote_port,char * local_ip,uint16_t local_port)391 char *tcpcrypt_getsessid(char *remote_ip, uint16_t remote_port,
392 char *local_ip, uint16_t local_port)
393 {
394 /* mostly copied from tcnetstat.c */
395 static char static_sessid[512]; /* TODO: len */
396 unsigned char buf[2048];
397 unsigned int len = sizeof(buf);
398 struct tc_netstat *n = (struct tc_netstat*) buf;
399 int s, sl, i;
400 struct in_addr dip;
401
402 s = __open_socket_for_getsessid();
403
404 #ifndef __WIN32__
405 if (!inet_aton(remote_ip, &dip)) {
406 /* invalid remote_ip */
407 return NULL;
408 }
409 #else
410 dip.s_addr = inet_addr(remote_ip);
411 if (dip.s_addr = INADDR_NONE) {
412 /* invalid remote ip */
413 return NULL;
414 }
415 #endif
416
417 if (tcpcrypt_getsockopt(s, IPPROTO_TCP, TCP_CRYPT_NETSTAT, buf, &len) == -1)
418 err(1, "tcpcrypt_getsockopt()");
419
420 while (len > sizeof(*n)) {
421 sl = ntohs(n->tn_len);
422
423 assert(len >= sizeof(*n) + sl);
424
425 /* TODO: also check source ip/port */
426 if (memcmp(&dip, &n->tn_dip, sizeof(struct in_addr)) == 0 &&
427 ntohs(n->tn_dport) == remote_port) {
428 for (i = 0; i < sl; i++)
429 sprintf(&static_sessid[i*2], "%.2X", n->tn_sid[i]);
430 return static_sessid;
431 }
432
433 sl += sizeof(*n);
434 n = (struct tc_netstat*) ((unsigned long) n + sl);
435 len -= sl;
436 }
437 assert(len == 0);
438
439 return NULL;
440 }
441