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