1 /*-
2  * SSLsplit - transparent SSL/TLS interception
3  * https://www.roe.ch/SSLsplit
4  *
5  * Copyright (c) 2009-2019, Daniel Roethlisberger <daniel@roe.ch>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  * 1. Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #ifdef __FreeBSD__
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/socket.h>
33 #include <sys/socketvar.h>
34 #include <sys/sysctl.h>
35 #include <sys/file.h>
36 #include <sys/user.h>
37 
38 #include <netinet/in.h>
39 #include <netinet/in_pcb.h>
40 #include <netinet/tcp.h>
41 #include <netinet/tcp_seq.h>
42 #include <netinet/tcp_var.h>
43 #include <arpa/inet.h>
44 #endif /* __FreeBSD__ */
45 
46 #include "proc.h"
47 
48 #include "log.h"
49 #include "attrib.h"
50 
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <errno.h>
55 
56 #ifdef HAVE_DARWIN_LIBPROC
57 #include <libproc.h>
58 #endif /* HAVE_DARWIN_LIBPROC */
59 
60 
61 /*
62  * Local process lookup.
63  */
64 
65 
66 #ifdef __FreeBSD__
67 
68 /*
69  * Get the list of open files from the kernel and do basic consistency checks.
70  * If successful, returns 0, and *pxfiles will receive a pointer to the
71  * received xfiles structure and *pnxfiles the number of file records in it.
72  * If unsuccessful, returns -1 and *pxfiles will be NULL.
73  * Caller is responsible to free() *pxfiles after use.
74  */
75 static int
proc_freebsd_getfiles(struct xfile ** pxfiles,int * pnxfiles)76 proc_freebsd_getfiles(struct xfile **pxfiles, int *pnxfiles)
77 {
78 	int mib[4];
79 	size_t sz;
80 
81 	mib[0] = CTL_KERN;
82 	mib[1] = KERN_FILE;
83 	mib[2] = mib[3] = 0;
84 
85 	for (;;) {
86 		if (sysctl(mib, 2, NULL, &sz, NULL, 0) < 0) {
87 			*pxfiles = NULL;
88 			return -1;
89 		}
90 		if (!(*pxfiles = malloc(sz))) {
91 			return -1;
92 		}
93 		if (sysctl(mib, 2, *pxfiles, &sz, NULL, 0) < 0) {
94 			free(*pxfiles);
95 			if (errno == ENOMEM)
96 				continue;
97 			*pxfiles = NULL;
98 			return -1;
99 		}
100 		break;
101 	}
102 
103 	if (sz > 0 && (*pxfiles)->xf_size != sizeof **pxfiles) {
104 		log_err_printf("struct xfile size mismatch\n");
105 		return -1;
106 	}
107 	*pnxfiles = sz / sizeof **pxfiles;
108 
109 	return 0;
110 }
111 
112 /*
113  * Get the list of active TCP connections and do basic consistency checks.
114  * If successful, returns 0, and *pxig will receive a pointer to the
115  * received data structure, *pexig a pointer to the end of the buffer.
116  * If unsuccessful, returns -1 and *pxig will be NULL.
117  * Caller is responsible to free() *pxig after use.
118  */
119 static int
proc_freebsd_gettcppcblist(struct xinpgen ** pxig,struct xinpgen ** pexig)120 proc_freebsd_gettcppcblist(struct xinpgen **pxig, struct xinpgen **pexig)
121 {
122 	int mib[4];
123 	size_t sz;
124 	int retry = 5;
125 
126 	mib[0] = CTL_NET;
127 	mib[1] = PF_INET;
128 	mib[2] = IPPROTO_TCP;
129 	mib[3] = TCPCTL_PCBLIST;
130 	do {
131 		for (;;) {
132 			if (sysctl(mib, 4, NULL, &sz, NULL, 0) < 0) {
133 				*pxig = NULL;
134 				return -1;
135 			}
136 			if (!(*pxig = malloc(sz))) {
137 				return -1;
138 			}
139 			if (sysctl(mib, 4, *pxig, &sz, NULL, 0) < 0) {
140 				free(*pxig);
141 				if (errno == ENOMEM)
142 					continue;
143 				*pxig = NULL;
144 				return -1;
145 			}
146 			break;
147 		}
148 
149 		*pexig = (struct xinpgen *)(void *)
150 		         ((char *)(*pxig) + sz - sizeof(**pexig));
151 		if ((*pxig)->xig_len != sizeof(**pxig) ||
152 		    (*pexig)->xig_len != sizeof(**pexig)) {
153 			log_err_printf("struct xinpgen size mismatch\n");
154 			free(*pxig);
155 			*pxig = NULL;
156 			return -1;
157 		}
158 	} while ((*pxig)->xig_gen != (*pexig)->xig_gen && retry--);
159 
160 	/* check if first and last record are from same generation */
161 	if ((*pxig)->xig_gen != (*pexig)->xig_gen) {
162 		log_err_level_printf(LOG_WARNING, "Data inconsistent "
163 		               "(xig->xig_gen != exig->xig_gen)\n");
164 	}
165 
166 	return 0;
167 }
168 
169 int
proc_freebsd_pid_for_addr(pid_t * result,struct sockaddr * src_addr,UNUSED socklen_t src_addrlen)170 proc_freebsd_pid_for_addr(pid_t *result, struct sockaddr *src_addr,
171                           UNUSED socklen_t src_addrlen)
172 {
173 	struct xfile *xfiles;
174 	int nxfiles;
175 	struct xfile *xf;
176 
177 	struct xinpgen *xig, *exig, *txig;
178 	struct xtcpcb *xtp;
179 #if __FreeBSD_version >= 1200026
180 	struct xinpcb *inp;
181 #else
182 	struct inpcb *inp;
183 #endif
184 	struct xsocket *so;
185 
186 	if (proc_freebsd_getfiles(&xfiles, &nxfiles) == -1) {
187 		return -1;
188 	}
189 
190 	if (proc_freebsd_gettcppcblist(&xig, &exig) == -1) {
191 		free(xfiles);
192 		return -1;
193 	}
194 
195 	for (txig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len);
196 	     txig < exig;
197 	     txig = (struct xinpgen *)(void *)((char *)txig + txig->xig_len)) {
198 		xtp = (struct xtcpcb *)txig;
199 		if (xtp->xt_len != sizeof *xtp) {
200 			free(xfiles);
201 			free(xig);
202 			return -1;
203 		}
204 		inp = &xtp->xt_inp;
205 #if __FreeBSD_version >= 1200026
206 		so = &inp->xi_socket;
207 #else
208 		so = &xtp->xt_socket;
209 #endif
210 
211 		if (!(so->so_state & SS_ISCONNECTED))
212 			/* we are only interested in connected sockets */
213 			continue;
214 
215 		if ((inp->inp_vflag & INP_IPV4) &&
216 		    (src_addr->sa_family == AF_INET)) {
217 			struct sockaddr_in *src_sai =
218 					(struct sockaddr_in *)src_addr;
219 
220 			if (src_sai->sin_addr.s_addr != inp->inp_laddr.s_addr) {
221 				continue;
222 			}
223 
224 			if (src_sai->sin_port != inp->inp_lport) {
225 				continue;
226 			}
227 		} else if ((inp->inp_vflag & INP_IPV6) &&
228 		          (src_addr->sa_family == AF_INET6)) {
229 			struct sockaddr_in6 *src_sai =
230 					(struct sockaddr_in6 *)src_addr;
231 
232 			if (memcmp(src_sai->sin6_addr.s6_addr, inp->in6p_laddr.s6_addr, 16) != 0) {
233 				continue;
234 			}
235 
236 			if (src_sai->sin6_port != inp->inp_lport) {
237 				continue;
238 			}
239 		} else {
240 			/* other address family */
241 			continue;
242 		}
243 
244 		/* valid match */
245 
246 		/* only do this if we have a match */
247 		xf = NULL;
248 		for (int i = 0; i < nxfiles; ++i) {
249 			if (so->xso_so == xfiles[i].xf_data) {
250 				/* there can be several processes sharing a
251 				 * connected socket file descriptor */
252 				xf = &xfiles[i];
253 			}
254 		}
255 		if (!xf)
256 			continue;
257 		*result = xf->xf_pid;
258 		break;
259 	}
260 
261 	free(xfiles);
262 	free(xig);
263 	return 0;
264 }
265 
266 int
proc_freebsd_get_info(pid_t pid,char ** path,uid_t * uid,gid_t * gid)267 proc_freebsd_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) {
268 	static struct kinfo_proc proc;
269 	size_t len;
270 	int mib[4];
271 	char buf[PATH_MAX];
272 
273 	mib[0] = CTL_KERN;
274 	mib[1] = KERN_PROC;
275 	mib[2] = KERN_PROC_PATHNAME;
276 	mib[3] = (int)pid;
277 	len = sizeof(buf);
278 	if (sysctl(mib, 4, buf, &len, NULL, 0) == -1) {
279 		if (errno != ESRCH) {
280 			log_err_printf("Failed to get proc pathname: %s (%i)",
281 			               strerror(errno), errno);
282 		}
283 		*path = NULL;
284 	} else {
285 		*path = strdup(buf);
286 	}
287 
288 	mib[0] = CTL_KERN;
289 	mib[1] = KERN_PROC;
290 	mib[2] = KERN_PROC_PID;
291 	mib[3] = (int)pid;
292 	len = sizeof proc;
293 	if (sysctl(mib, 4, &proc, &len, NULL, 0) == -1) {
294 		if (errno != ESRCH) {
295 			log_err_printf("Failed to get proc info: %s (%i)",
296 			               strerror(errno), errno);
297 		}
298 		*uid = -1;
299 		*gid = -1;
300 	} else {
301 		if (*path == NULL)
302 			*path = strdup(proc.ki_comm);
303 		*uid = proc.ki_uid;
304 		*gid = proc.ki_groups[0];
305 	}
306 
307 	return 0;
308 }
309 
310 #endif /* __FreeBSD__ */
311 
312 
313 #ifdef HAVE_DARWIN_LIBPROC
314 
315 int
proc_darwin_pid_for_addr(pid_t * result,struct sockaddr * src_addr,UNUSED socklen_t src_addrlen)316 proc_darwin_pid_for_addr(pid_t *result, struct sockaddr *src_addr,
317                          UNUSED socklen_t src_addrlen)
318 {
319 	pid_t *pids = NULL;
320 	struct proc_fdinfo *fds = NULL;
321 	int ret = -1;
322 
323 	/* default result if no pid matches */
324 	*result = -1;
325 
326 	/* iterate over all pids to find a matching socket */
327 	int pid_count = proc_listallpids(NULL, 0);
328 	if (pid_count <= 0)
329 		goto errout1;
330 	pids = malloc(sizeof(pid_t) * pid_count);
331 	if (!pids) {
332 		goto errout1;
333 	}
334 
335 	pid_count = proc_listallpids(pids, sizeof(pid_t) * pid_count);
336 	if (pid_count <= 0)
337 		goto errout2;
338 
339 	for (int i = 0; i < pid_count; i++) {
340 		pid_t pid = pids[i];
341 
342 		/* fetch fd info for this pid */
343 		int fd_count = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
344 		if (fd_count <= 0) {
345 			/* failed to fetch pidinfo; process may have exited */
346 			continue;
347 		}
348 
349 		if (fds) {
350 			free(fds);
351 		}
352 		fds = malloc(PROC_PIDLISTFD_SIZE * fd_count);
353 		if (!fds) {
354 			goto errout2;
355 		}
356 		fd_count = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds,
357 		                        sizeof(fds[0]) * fd_count);
358 
359 		/* look for a matching socket file descriptor */
360 		for (int j = 0; j < fd_count; j++) {
361 			struct proc_fdinfo *fd = &fds[j];
362 			struct socket_fdinfo sinfo;
363 
364 			if (fd->proc_fdtype != PROX_FDTYPE_SOCKET) {
365 				continue;
366 			}
367 
368 			if (proc_pidfdinfo(pid, fd->proc_fd,
369 			                   PROC_PIDFDSOCKETINFO,
370 			                   &sinfo,
371 			                   sizeof(struct socket_fdinfo)) <= 0) {
372 				/* process may have exited or socket may have
373 				 * been released. */
374 				continue;
375 			}
376 
377 			if (sinfo.psi.soi_kind != SOCKINFO_TCP) {
378 				continue;
379 			}
380 
381 			uint16_t sock_lport = sinfo.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport;
382 			if (sinfo.psi.soi_family == AF_INET &&
383 			    src_addr->sa_family == AF_INET) {
384 				struct sockaddr_in *src_sai = (struct sockaddr_in *)src_addr;
385 
386 				if (src_sai->sin_addr.s_addr != sinfo.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_46.i46a_addr4.s_addr) {
387 					continue;
388 				}
389 
390 				if (src_sai->sin_port != sock_lport) {
391 					continue;
392 				}
393 			} else if (sinfo.psi.soi_family == AF_INET6 &&
394 			           src_addr->sa_family == AF_INET6) {
395 				struct sockaddr_in6 *src_sai = (struct sockaddr_in6 *)src_addr;
396 
397 				if (memcmp(src_sai->sin6_addr.s6_addr, sinfo.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_6.s6_addr, 16) != 0) {
398 					continue;
399 				}
400 
401 				if (src_sai->sin6_port != sock_lport) {
402 					continue;
403 				}
404 			} else {
405 				/* other address family */
406 				continue;
407 			}
408 
409 			/* valid match */
410 			*result = pid;
411 			goto success;
412 		}
413 	}
414 
415 success:
416 	ret = 0;
417 	free(fds);
418 errout2:
419 	free(pids);
420 errout1:
421 	return ret;
422 }
423 
424 /*
425  * Fetch process info for the given pid.
426  * On success, returns 0 and fills in path, uid, and gid.
427  * Caller must free returned path string.
428  * Returns -1 on failure, or if unsupported on this platform.
429  */
430 int
proc_darwin_get_info(pid_t pid,char ** path,uid_t * uid,gid_t * gid)431 proc_darwin_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) {
432 	/* fetch process structure */
433 	struct proc_bsdinfo bsd_info;
434 	if (proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &bsd_info,
435 	                 sizeof(bsd_info)) <= 0) {
436 		return -1;
437 	}
438 
439 	*uid = bsd_info.pbi_uid;
440 	*gid = bsd_info.pbi_gid;
441 
442 	/* fetch process path */
443 	*path = malloc(PROC_PIDPATHINFO_MAXSIZE);
444 	if (!*path) {
445 		return -1;
446 	}
447 	int path_len = proc_pidpath(pid, *path, PROC_PIDPATHINFO_MAXSIZE);
448 	if (path_len <= 0) {
449 		free(*path);
450 		*path = NULL;
451 		return -1;
452 	}
453 
454 	return 0;
455 }
456 
457 #endif /* HAVE_DARWIN_LIBPROC */
458 
459 /* vim: set noet ft=c: */
460 
461 
462 
463