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 #include "sys.h"
30
31 #include "log.h"
32 #include "defaults.h"
33
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/ioctl.h>
37 #include <sys/uio.h>
38 #include <sys/stat.h>
39 #include <sys/file.h>
40 #include <sys/un.h>
41 #include <sys/time.h>
42 #include <net/if.h>
43 #include <netinet/in.h>
44 #include <netdb.h>
45 #include <fcntl.h>
46 #include <pwd.h>
47 #include <grp.h>
48 #include <fts.h>
49 #include <unistd.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <errno.h>
54
55 #ifndef _SC_NPROCESSORS_ONLN
56 #include <sys/sysctl.h>
57 #endif /* !_SC_NPROCESSORS_ONLN */
58
59 #if HAVE_DARWIN_LIBPROC
60 #include <libproc.h>
61 #endif
62
63 #include <event2/util.h>
64
65 /*
66 * Permanently drop from root privileges to an unprivileged user account.
67 * Sets the real, effective and stored user and group ID and the list of
68 * ancillary groups. This is only safe if the effective user ID is 0.
69 * If username is unset and the effective uid != uid, drop privs to uid.
70 * This is to support setuid bit configurations.
71 * If groupname is set, it will be used instead of the user's default primary
72 * group.
73 * If jaildir is set, also chroot to jaildir after reading system files
74 * but before dropping privileges.
75 * Returns 0 on success, -1 on failure.
76 */
77 int
sys_privdrop(const char * username,const char * groupname,const char * jaildir)78 sys_privdrop(const char *username, const char *groupname, const char *jaildir)
79 {
80 struct passwd *pw = NULL;
81 struct group *gr = NULL;
82 int ret = -1;
83
84 if (groupname) {
85 errno = 0;
86 if (!(gr = getgrnam(groupname))) {
87 log_err_level_printf(LOG_CRIT, "Failed to getgrnam group '%s': %s\n",
88 groupname, strerror(errno));
89 goto error;
90 }
91 }
92
93 if (username) {
94 errno = 0;
95 if (!(pw = getpwnam(username))) {
96 log_err_level_printf(LOG_CRIT, "Failed to getpwnam user '%s': %s\n",
97 username, strerror(errno));
98 goto error;
99 }
100
101 if (gr != NULL) {
102 pw->pw_gid = gr->gr_gid;
103 }
104
105 if (initgroups(username, pw->pw_gid) == -1) {
106 log_err_level_printf(LOG_CRIT, "Failed to initgroups user '%s': %s\n",
107 username, strerror(errno));
108 goto error;
109 }
110 }
111
112 if (jaildir) {
113 if (chroot(jaildir) == -1) {
114 log_err_level_printf(LOG_CRIT, "Failed to chroot to '%s': %s\n",
115 jaildir, strerror(errno));
116 goto error;
117 }
118 if (chdir("/") == -1) {
119 log_err_level_printf(LOG_CRIT, "Failed to chdir to '/': %s\n",
120 strerror(errno));
121 goto error;
122 }
123 }
124
125 if (username) {
126 if (setgid(pw->pw_gid) == -1) {
127 log_err_level_printf(LOG_CRIT, "Failed to setgid to %i: %s\n",
128 pw->pw_gid, strerror(errno));
129 goto error;
130 }
131 if (setuid(pw->pw_uid) == -1) {
132 log_err_level_printf(LOG_CRIT, "Failed to setuid to %i: %s\n",
133 pw->pw_uid, strerror(errno));
134 goto error;
135 }
136 } else if (getuid() != geteuid()) {
137 if (setuid(getuid()) == -1) {
138 log_err_level_printf(LOG_CRIT, "Failed to setuid(getuid()): %s\n",
139 strerror(errno));
140 goto error;
141 }
142 }
143
144 ret = 0;
145 error:
146 if (pw) {
147 endpwent();
148 }
149 if (gr) {
150 endgrent();
151 }
152 return ret;
153 }
154
155 /*
156 * If the user exists and on successful lookup, return 0 and if uid != NULL,
157 * write the uid of *username* to the value pointed to by uid.
158 * Return -1 on failure or if the user does not exist.
159 */
160 int
sys_uid(const char * username,uid_t * uid)161 sys_uid(const char *username, uid_t *uid)
162 {
163 struct passwd *pw;
164 int rv;
165
166 errno = 0;
167 if (!(pw = getpwnam(username))) {
168 if (errno != 0 && errno != ENOENT) {
169 log_err_level_printf(LOG_CRIT, "Failed to load user '%s': %s (%i)\n",
170 username, strerror(errno), errno);
171 }
172 rv = -1;
173 } else {
174 if (uid)
175 *uid = pw->pw_uid;
176 rv = 0;
177 }
178 endpwent();
179 return rv;
180 }
181
182 /*
183 * Returns 1 if username can be loaded from user database, 0 otherwise.
184 */
185 int
sys_isuser(const char * username)186 sys_isuser(const char *username)
187 {
188 return sys_uid(username, NULL) == 0;
189 }
190
191 /*
192 * If the group exists and on successful lookup, return 0 and if gid != NULL,
193 * write the gid of *groupname* to the value pointed to by gid.
194 * Return -1 on failure or if the group does not exist.
195 */
196 int
sys_gid(const char * groupname,gid_t * gid)197 sys_gid(const char *groupname, gid_t *gid)
198 {
199 struct group *gr;
200 int rv;
201
202 errno = 0;
203 if (!(gr = getgrnam(groupname))) {
204 if (errno != 0 && errno != ENOENT) {
205 log_err_level_printf(LOG_CRIT, "Failed to load group '%s': %s (%i)\n",
206 groupname, strerror(errno), errno);
207 }
208 rv = -1;
209 } else {
210 if (gid)
211 *gid = gr->gr_gid;
212 rv = 0;
213 }
214 endgrent();
215 return rv;
216 }
217
218 /*
219 * Returns 1 if groupname can be loaded from group database, 0 otherwise.
220 */
221 int
sys_isgroup(const char * groupname)222 sys_isgroup(const char *groupname)
223 {
224 return sys_gid(groupname, NULL) == 0;
225 }
226
227 /*
228 * Returns 1 if username is equivalent to the current effective UID.
229 * Returns 0 otherwise.
230 */
231 int
sys_isgeteuid(const char * username)232 sys_isgeteuid(const char *username)
233 {
234 uid_t uid;
235
236 if (sys_uid(username, &uid) == -1)
237 return 0;
238 if (uid == geteuid())
239 return 1;
240 return 0;
241 }
242
243 /*
244 * Open and lock process ID file fn.
245 * Returns open file descriptor on success or -1 on errors.
246 */
247 int
sys_pidf_open(const char * fn)248 sys_pidf_open(const char *fn)
249 {
250 int fd;
251
252 if ((fd = open(fn, O_RDWR|O_CREAT, DFLT_PIDFMODE)) == -1) {
253 log_err_level_printf(LOG_CRIT, "Failed to open '%s': %s\n", fn,
254 strerror(errno));
255 return -1;
256 }
257 if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
258 log_err_level_printf(LOG_CRIT, "Failed to lock '%s': %s\n", fn,
259 strerror(errno));
260 close(fd);
261 return -1;
262 }
263
264 return fd;
265 }
266
267 /*
268 * Write process ID to open process ID file descriptor fd.
269 * Returns 0 on success, -1 on errors.
270 */
271 int
sys_pidf_write(int fd)272 sys_pidf_write(int fd)
273 {
274 char pidbuf[4*sizeof(pid_t)];
275 int rv;
276 ssize_t n;
277
278 rv = snprintf(pidbuf, sizeof(pidbuf), "%d\n", getpid());
279 if (rv == -1 || rv >= (int)sizeof(pidbuf))
280 return -1;
281
282 n = write(fd, pidbuf, strlen(pidbuf));
283 if (n < (ssize_t)strlen(pidbuf))
284 return -1;
285
286 rv = fsync(fd);
287 if (rv == -1)
288 return -1;
289
290 rv = fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
291 if (rv == -1)
292 return -1;
293
294 return 0;
295 }
296
297 /*
298 * Close and remove open process ID file before quitting.
299 */
300 void
sys_pidf_close(int fd,const char * fn)301 sys_pidf_close(int fd, const char *fn)
302 {
303 unlink(fn);
304 close(fd);
305 }
306
307 /*
308 * Determine address family of addr
309 */
310 int
sys_get_af(const char * addr)311 sys_get_af(const char *addr)
312 {
313 if (strstr(addr, ":"))
314 return AF_INET6;
315 else if (!strpbrk(addr, "abcdefghijklmnopqrstu"
316 "vwxyzABCDEFGHIJKLMNOP"
317 "QRSTUVWXYZ-"))
318 return AF_INET;
319 else
320 return AF_UNSPEC;
321 }
322
323 /*
324 * Parse an ascii host/IP and port tuple into a sockaddr_storage.
325 * On success, returns address family and fills in addr, addrlen.
326 * Returns -1 on error.
327 */
328 int
sys_sockaddr_parse(struct sockaddr_storage * addr,socklen_t * addrlen,char * naddr,char * nport,int af,int flags)329 sys_sockaddr_parse(struct sockaddr_storage *addr, socklen_t *addrlen,
330 char *naddr, char *nport, int af, int flags)
331 {
332 struct evutil_addrinfo hints;
333 struct evutil_addrinfo *ai;
334 int rv;
335
336 memset(&hints, 0, sizeof(hints));
337 hints.ai_family = af;
338 hints.ai_socktype = SOCK_STREAM;
339 hints.ai_protocol = IPPROTO_TCP;
340 hints.ai_flags = EVUTIL_AI_ADDRCONFIG | flags;
341 rv = evutil_getaddrinfo(naddr, nport, &hints, &ai);
342 if (rv != 0) {
343 log_err_level_printf(LOG_CRIT, "Cannot resolve address '%s' port '%s': %s\n",
344 naddr, nport, gai_strerror(rv));
345 return -1;
346 }
347 memcpy(addr, ai->ai_addr, ai->ai_addrlen);
348 *addrlen = ai->ai_addrlen;
349 af = ai->ai_family;
350 freeaddrinfo(ai);
351 return af;
352 }
353
354 /*
355 * Converts an IPv4/IPv6 sockaddr into printable string representations of the
356 * host and the service (port) part. Writes allocated buffers to *host and
357 * *serv which must both be freed by the caller. Neither *host nor *port are
358 * freed by this function before newly allocating.
359 * Returns 0 on success, -1 otherwise. When -1 is returned, pointers in *host
360 * and *serv are invalid and must not be used nor freed by the caller.
361 */
362 int
sys_sockaddr_str(struct sockaddr * addr,socklen_t addrlen,char ** host,char ** serv)363 sys_sockaddr_str(struct sockaddr *addr, socklen_t addrlen,
364 char **host, char **serv)
365 {
366 char tmphost[INET6_ADDRSTRLEN];
367 int rv;
368 size_t hostsz;
369
370 *serv = malloc(6); /* max decimal digits of short plus terminator */
371 if (!*serv) {
372 log_err_level_printf(LOG_CRIT, "Cannot allocate memory\n");
373 return -1;
374 }
375 rv = getnameinfo(addr, addrlen,
376 tmphost, sizeof(tmphost),
377 *serv, 6,
378 NI_NUMERICHOST | NI_NUMERICSERV);
379 if (rv != 0) {
380 log_err_level_printf(LOG_CRIT, "Cannot get nameinfo for socket address: %s\n",
381 gai_strerror(rv));
382 free(*serv);
383 return -1;
384 }
385 hostsz = strlen(tmphost) + 1; /* including terminator */
386 *host = malloc(hostsz);
387 if (!*host) {
388 log_err_level_printf(LOG_CRIT, "Cannot allocate memory\n");
389 free(*serv);
390 return -1;
391 }
392 memcpy(*host, tmphost, hostsz);
393 return 0;
394 }
395
396 /*
397 * Sanitizes a valid IPv4 or IPv6 address for use in a filename, i.e. removes
398 * characters that are invalid on NTFS and replaces them with more innocent
399 * characters. The function assumes that the input is a valid IPv4 or IPv6
400 * address; it is not a generic filename sanitizer.
401 *
402 * Returns a copy of string s that must be freed by the caller.
403 *
404 * Invalid NTFS characters are < > : " / \ | ? * according to
405 * https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247.aspx
406 */
407 char *
sys_ip46str_sanitize(const char * s)408 sys_ip46str_sanitize(const char *s)
409 {
410 char *copy, *p;
411
412 copy = strdup(s);
413 if (!copy)
414 return NULL;
415 p = copy;
416 while (*p) {
417 switch (*p) {
418 case ':':
419 case '%':
420 *p = '_';
421 break;
422 }
423 p++;
424 }
425
426 return copy;
427 }
428
429 /*
430 * Returns 1 if path points to an existing directory node in the filesystem.
431 * Returns 0 if path is NULL, does not exist, or points to a file of some kind.
432 */
433 int
sys_isdir(const char * path)434 sys_isdir(const char *path)
435 {
436 struct stat s;
437
438 if (stat(path, &s) == -1) {
439 if (errno != ENOENT) {
440 log_err_level_printf(LOG_CRIT, "Error stating file: %s (%i)\n",
441 strerror(errno), errno);
442 }
443 return 0;
444 }
445 if (s.st_mode & S_IFDIR)
446 return 1;
447 return 0;
448 }
449
450 /*
451 * Create directory including parent directories with mode_t.
452 * Mode of existing parent directories is not changed.
453 * Returns 0 on success, -1 and sets errno on error.
454 */
455 int
sys_mkpath(const char * path,mode_t mode)456 sys_mkpath(const char *path, mode_t mode)
457 {
458 char parent[strlen(path)+1];
459 char *p;
460
461 memcpy(parent, path, sizeof(parent));
462
463 p = parent;
464 do {
465 /* skip leading '/' characters */
466 while (*p == '/') p++;
467 p = strchr(p, '/');
468 if (p) {
469 /* overwrite '/' to terminate the string at the next
470 * parent directory */
471 *p = '\0';
472 }
473
474 struct stat sbuf;
475 if (stat(parent, &sbuf) == -1) {
476 if (errno == ENOENT) {
477 if (mkdir(parent, mode) != 0)
478 return -1;
479 } else {
480 return -1;
481 }
482 } else if (!S_ISDIR(sbuf.st_mode)) {
483 errno = ENOTDIR;
484 return -1;
485 }
486
487 if (p) {
488 /* replace the overwritten slash */
489 *p = '/';
490 p++;
491 }
492 } while (p);
493
494 return 0;
495 }
496
497 /*
498 * Return realpath(dirname(path)) + / + basename(path) in a newly allocated
499 * string. Returns NULL on failure and sets errno to ENOENT if the directory
500 * part does not exist.
501 */
502 char *
sys_realdir(const char * path)503 sys_realdir(const char *path)
504 {
505 char *sep, *udir, *rdir, *p;
506 int rerrno, rv;
507
508 if (path[0] == '\0') {
509 errno = EINVAL;
510 return NULL;
511 }
512
513 udir = strdup(path);
514 if (!udir)
515 return NULL;
516
517 sep = strrchr(udir, '/');
518 if (!sep) {
519 free(udir);
520 rv = asprintf(&udir, "./%s", path);
521 if (rv == -1)
522 return NULL;
523 sep = udir + 1;
524 } else if (sep == udir) {
525 return udir;
526 }
527 *sep = '\0';
528 rdir = realpath(udir, NULL);
529 if (!rdir) {
530 rerrno = errno;
531 free(udir);
532 errno = rerrno;
533 return NULL;
534 }
535 rv = asprintf(&p, "%s/%s", rdir, sep + 1);
536 rerrno = errno;
537 free(rdir);
538 free(udir);
539 errno = rerrno;
540 if (rv == -1)
541 return NULL;
542 return p;
543 }
544
545 /*
546 * Portably get the number of CPU cores online in the system.
547 */
548 uint32_t
sys_get_cpu_cores(void)549 sys_get_cpu_cores(void)
550 {
551 #ifdef _SC_NPROCESSORS_ONLN
552 return sysconf(_SC_NPROCESSORS_ONLN);
553 #else /* !_SC_NPROCESSORS_ONLN */
554 int mib[2];
555 uint32_t n;
556 size_t len = sizeof(n);
557
558 mib[0] = CTL_HW;
559 mib[1] = HW_AVAILCPU;
560 sysctl(mib, sizeof(mib)/sizeof(int), &n, &len, NULL, 0);
561
562 if (n < 1) {
563 mib[1] = HW_NCPU;
564 sysctl(mib, sizeof(mib)/sizeof(int), &n, &len, NULL, 0);
565 if (n < 1) {
566 n = 1;
567 }
568 }
569 return n;
570 #endif /* !_SC_NPROCESSORS_ONLN */
571 }
572
573 /*
574 * Send a message and optional file descriptor on a connected AF_UNIX
575 * SOCKET_DGRAM socket s. Returns the return value of sendmsg().
576 * If fd is -1, no file descriptor is passed.
577 */
578 ssize_t
sys_sendmsgfd(int sock,void * buf,size_t bufsz,int fd)579 sys_sendmsgfd(int sock, void *buf, size_t bufsz, int fd)
580 {
581 struct iovec iov;
582 struct msghdr msg;
583 struct cmsghdr *cmsg;
584 char cmsgbuf[CMSG_SPACE(sizeof(int))];
585 ssize_t n;
586
587 iov.iov_base = buf;
588 iov.iov_len = bufsz;
589
590 msg.msg_name = NULL;
591 msg.msg_namelen = 0;
592 msg.msg_iov = &iov;
593 msg.msg_iovlen = 1;
594 msg.msg_flags = 0;
595
596 if (fd != -1) {
597 msg.msg_control = cmsgbuf;
598 msg.msg_controllen = sizeof(cmsgbuf);
599 memset(cmsgbuf, 0, sizeof(cmsgbuf));
600
601 cmsg = CMSG_FIRSTHDR(&msg);
602 if (!cmsg)
603 return -1;
604 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
605 cmsg->cmsg_level = SOL_SOCKET;
606 cmsg->cmsg_type = SCM_RIGHTS;
607
608 *((int *) CMSG_DATA(cmsg)) = fd;
609 } else {
610 msg.msg_control = NULL;
611 msg.msg_controllen = 0;
612 }
613 do {
614 #ifdef MSG_NOSIGNAL
615 n = sendmsg(sock, &msg, MSG_NOSIGNAL);
616 #else /* !MSG_NOSIGNAL */
617 n = sendmsg(sock, &msg, 0);
618 #endif /* !MSG_NOSIGNAL */
619 } while (n == -1 && errno == EINTR);
620 return n;
621 }
622
623 /*
624 * Receive a message and optional file descriptor on a connected AF_UNIX
625 * SOCKET_DGRAM socket s. Returns the return value of recvmsg()/recv()
626 * and sets errno to EINVAL if the received message is malformed.
627 * If pfd is NULL, no file descriptor is received; if a file descriptor was
628 * part of the received message and pfd is NULL, then the kernel will close it.
629 */
630 ssize_t
sys_recvmsgfd(int sock,void * buf,size_t bufsz,int * pfd)631 sys_recvmsgfd(int sock, void *buf, size_t bufsz, int *pfd)
632 {
633 ssize_t n;
634
635 if (pfd) {
636 struct iovec iov;
637 struct msghdr msg;
638 struct cmsghdr *cmsg;
639 unsigned char cmsgbuf[CMSG_SPACE(sizeof(int))];
640
641 iov.iov_base = buf;
642 iov.iov_len = bufsz;
643
644 msg.msg_name = NULL;
645 msg.msg_namelen = 0;
646 msg.msg_iov = &iov;
647 msg.msg_iovlen = 1;
648 msg.msg_control = cmsgbuf;
649 msg.msg_controllen = sizeof(cmsgbuf);
650 do {
651 n = recvmsg(sock, &msg, 0);
652 } while (n == -1 && errno == EINTR);
653 if (n <= 0)
654 return n;
655 cmsg = CMSG_FIRSTHDR(&msg);
656 if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
657 if (cmsg->cmsg_level != SOL_SOCKET) {
658 errno = EINVAL;
659 return -1;
660 }
661 if (cmsg->cmsg_type != SCM_RIGHTS) {
662 errno = EINVAL;
663 return -1;
664 }
665 *pfd = *((int *) CMSG_DATA(cmsg));
666 } else {
667 *pfd = -1;
668 }
669 } else {
670 do {
671 n = recv(sock, buf, bufsz, 0);
672 } while (n == -1 && errno == EINTR);
673 }
674 return n;
675 }
676
677 /* vim: set noet ft=c: */
678