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