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_printf("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_printf("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_printf("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_printf("Failed to chroot to '%s': %s\n",
115 jaildir, strerror(errno));
116 goto error;
117 }
118 if (chdir("/") == -1) {
119 log_err_printf("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_printf("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_printf("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_printf("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_printf("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_printf("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_printf("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_printf("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 * Converts a local uid into a printable string representation.
309 * Returns an allocated buffer which must be freed by caller, or NULL on error.
310 */
311 char *
sys_user_str(uid_t uid)312 sys_user_str(uid_t uid)
313 {
314 static int bufsize = 0;
315
316 if (!bufsize) {
317 /* on some platforms this compiles, but does not succeed */
318 if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
319 bufsize = 64;
320 }
321 }
322
323 char *buf, *newbuf;
324 struct passwd pwd, *result = NULL;
325 int rv;
326 char *name;
327
328 if (!(buf = malloc(bufsize)))
329 return NULL;
330
331 do {
332 rv = getpwuid_r(uid, &pwd, buf, bufsize, &result);
333 if (rv == 0) {
334 if (result) {
335 name = strdup(pwd.pw_name);
336 free(buf);
337 return name;
338 }
339 free(buf);
340
341 /* no entry found; return the integer representation */
342 if (asprintf(&name, "%llu", (long long) uid) < 0) {
343 return NULL;
344 }
345 return name;
346 }
347 bufsize *= 2;
348 if (!(newbuf = realloc(buf, bufsize))) {
349 free(buf);
350 return NULL;
351 }
352 buf = newbuf;
353 } while (rv == ERANGE);
354
355 free(buf);
356 log_err_printf("Failed to lookup uid: %s (%i)\n", strerror(rv), rv);
357 return NULL;
358 }
359
360 /*
361 * Converts a local gid into a printable string representation.
362 * Returns an allocated buffer which must be freed by caller, or NULL on error.
363 */
364 char *
sys_group_str(gid_t gid)365 sys_group_str(gid_t gid)
366 {
367 static int bufsize = 0;
368
369 if (!bufsize) {
370 /* on some platforms this compiles, but does not succeed */
371 if ((bufsize = sysconf(_SC_GETGR_R_SIZE_MAX)) == -1) {
372 bufsize = 64;
373 }
374 }
375
376 char *buf, *newbuf;
377 struct group grp, *result = NULL;
378 int rv;
379 char *name;
380
381 if (!(buf = malloc(bufsize)))
382 return NULL;
383
384 do {
385 rv = getgrgid_r(gid, &grp, buf, bufsize, &result);
386 if (rv == 0) {
387 if (result) {
388 name = strdup(grp.gr_name);
389 free(buf);
390 return name;
391 }
392 free(buf);
393
394 /* no entry found; return the integer representation */
395 if (asprintf(&name, "%llu", (long long) gid) < 0) {
396 return NULL;
397 }
398 return name;
399 }
400 bufsize *= 2;
401 if (!(newbuf = realloc(buf, bufsize))) {
402 free(buf);
403 return NULL;
404 }
405 buf = newbuf;
406 } while (rv == ERANGE);
407
408 free(buf);
409 log_err_printf("Failed to lookup gid: %s (%i)\n", strerror(rv), rv);
410 return NULL;
411 }
412
413 /*
414 * Determine address family of addr
415 */
416 int
sys_get_af(const char * addr)417 sys_get_af(const char *addr)
418 {
419 if (strstr(addr, ":"))
420 return AF_INET6;
421 else if (!strpbrk(addr, "abcdefghijklmnopqrstu"
422 "vwxyzABCDEFGHIJKLMNOP"
423 "QRSTUVWXYZ-"))
424 return AF_INET;
425 else
426 return AF_UNSPEC;
427 }
428
429 /*
430 * Parse an ascii host/IP and port tuple into a sockaddr_storage.
431 * On success, returns address family and fills in addr, addrlen.
432 * Returns -1 on error.
433 */
434 int
sys_sockaddr_parse(struct sockaddr_storage * addr,socklen_t * addrlen,char * naddr,char * nport,int af,int flags)435 sys_sockaddr_parse(struct sockaddr_storage *addr, socklen_t *addrlen,
436 char *naddr, char *nport, int af, int flags)
437 {
438 struct evutil_addrinfo hints;
439 struct evutil_addrinfo *ai;
440 int rv;
441
442 memset(&hints, 0, sizeof(hints));
443 hints.ai_family = af;
444 hints.ai_socktype = SOCK_STREAM;
445 hints.ai_protocol = IPPROTO_TCP;
446 hints.ai_flags = EVUTIL_AI_ADDRCONFIG | flags;
447 rv = evutil_getaddrinfo(naddr, nport, &hints, &ai);
448 if (rv != 0) {
449 log_err_printf("Cannot resolve address '%s' port '%s': %s\n",
450 naddr, nport, gai_strerror(rv));
451 return -1;
452 }
453 memcpy(addr, ai->ai_addr, ai->ai_addrlen);
454 *addrlen = ai->ai_addrlen;
455 af = ai->ai_family;
456 freeaddrinfo(ai);
457 return af;
458 }
459
460 /*
461 * Converts an IPv4/IPv6 sockaddr into printable string representations of the
462 * host and the service (port) part. Writes allocated buffers to *host and
463 * *serv which must both be freed by the caller. Neither *host nor *port are
464 * freed by this function before newly allocating.
465 * Returns 0 on success, -1 otherwise. When -1 is returned, pointers in *host
466 * and *serv are invalid and must not be used nor freed by the caller.
467 */
468 int
sys_sockaddr_str(struct sockaddr * addr,socklen_t addrlen,char ** host,char ** serv)469 sys_sockaddr_str(struct sockaddr *addr, socklen_t addrlen,
470 char **host, char **serv)
471 {
472 char tmphost[INET6_ADDRSTRLEN];
473 int rv;
474 size_t hostsz;
475
476 *serv = malloc(6); /* max decimal digits of short plus terminator */
477 if (!*serv) {
478 log_err_printf("Cannot allocate memory\n");
479 return -1;
480 }
481 rv = getnameinfo(addr, addrlen,
482 tmphost, sizeof(tmphost),
483 *serv, 6,
484 NI_NUMERICHOST | NI_NUMERICSERV);
485 if (rv != 0) {
486 log_err_printf("Cannot get nameinfo for socket address: %s\n",
487 gai_strerror(rv));
488 free(*serv);
489 return -1;
490 }
491 hostsz = strlen(tmphost) + 1; /* including terminator */
492 *host = malloc(hostsz);
493 if (!*host) {
494 log_err_printf("Cannot allocate memory\n");
495 free(*serv);
496 return -1;
497 }
498 memcpy(*host, tmphost, hostsz);
499 return 0;
500 }
501
502 /*
503 * Sanitizes a valid IPv4 or IPv6 address for use in a filename, i.e. removes
504 * characters that are invalid on NTFS and replaces them with more innocent
505 * characters. The function assumes that the input is a valid IPv4 or IPv6
506 * address; it is not a generic filename sanitizer.
507 *
508 * Returns a copy of string s that must be freed by the caller.
509 *
510 * Invalid NTFS characters are < > : " / \ | ? * according to
511 * https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247.aspx
512 */
513 char *
sys_ip46str_sanitize(const char * s)514 sys_ip46str_sanitize(const char *s)
515 {
516 char *copy, *p;
517
518 copy = strdup(s);
519 if (!copy)
520 return NULL;
521 p = copy;
522 while (*p) {
523 switch (*p) {
524 case ':':
525 case '%':
526 *p = '_';
527 break;
528 }
529 p++;
530 }
531
532 return copy;
533 }
534
535 /*
536 * Returns the MTU of the interface with name *ifname* or 0 on errors.
537 */
538 size_t
sys_get_mtu(const char * ifname)539 sys_get_mtu(const char *ifname)
540 {
541 struct ifreq ifr;
542 size_t ifnamelen;
543 int s;
544
545 ifnamelen = strlen(ifname);
546 if (ifnamelen > sizeof(ifr.ifr_name) + 1)
547 return 0;
548 memcpy(ifr.ifr_name, ifname, ifnamelen);
549 ifr.ifr_name[ifnamelen] = '\0';
550
551 s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
552 if (s == -1)
553 return 0;
554 if (ioctl(s, SIOCGIFMTU, &ifr) == -1) {
555 close(s);
556 return 0;
557 }
558 close(s);
559 return ifr.ifr_mtu;
560 }
561
562 /*
563 * Returns 1 if path points to an existing directory node in the filesystem.
564 * Returns 0 if path is NULL, does not exist, or points to a file of some kind.
565 */
566 int
sys_isdir(const char * path)567 sys_isdir(const char *path)
568 {
569 struct stat s;
570
571 if (stat(path, &s) == -1) {
572 if (errno != ENOENT) {
573 log_err_printf("Error stating file: %s (%i)\n",
574 strerror(errno), errno);
575 }
576 return 0;
577 }
578 if (s.st_mode & S_IFDIR)
579 return 1;
580 return 0;
581 }
582
583 /*
584 * Create directory including parent directories with mode_t.
585 * Mode of existing parent directories is not changed.
586 * Returns 0 on success, -1 and sets errno on error.
587 */
588 int
sys_mkpath(const char * path,mode_t mode)589 sys_mkpath(const char *path, mode_t mode)
590 {
591 char parent[strlen(path)+1];
592 char *p;
593
594 memcpy(parent, path, sizeof(parent));
595
596 p = parent;
597 do {
598 /* skip leading '/' characters */
599 while (*p == '/') p++;
600 p = strchr(p, '/');
601 if (p) {
602 /* overwrite '/' to terminate the string at the next
603 * parent directory */
604 *p = '\0';
605 }
606
607 struct stat sbuf;
608 if (stat(parent, &sbuf) == -1) {
609 if (errno == ENOENT) {
610 if (mkdir(parent, mode) != 0)
611 return -1;
612 } else {
613 return -1;
614 }
615 } else if (!S_ISDIR(sbuf.st_mode)) {
616 errno = ENOTDIR;
617 return -1;
618 }
619
620 if (p) {
621 /* replace the overwritten slash */
622 *p = '/';
623 p++;
624 }
625 } while (p);
626
627 return 0;
628 }
629
630 /*
631 * Return realpath(dirname(path)) + / + basename(path) in a newly allocated
632 * string. Returns NULL on failure and sets errno to ENOENT if the directory
633 * part does not exist.
634 */
635 char *
sys_realdir(const char * path)636 sys_realdir(const char *path)
637 {
638 char *sep, *udir, *rdir, *p;
639 int rerrno, rv;
640
641 if (path[0] == '\0') {
642 errno = EINVAL;
643 return NULL;
644 }
645
646 udir = strdup(path);
647 if (!udir)
648 return NULL;
649
650 sep = strrchr(udir, '/');
651 if (!sep) {
652 free(udir);
653 rv = asprintf(&udir, "./%s", path);
654 if (rv == -1)
655 return NULL;
656 sep = udir + 1;
657 } else if (sep == udir) {
658 return udir;
659 }
660 *sep = '\0';
661 rdir = realpath(udir, NULL);
662 if (!rdir) {
663 rerrno = errno;
664 free(udir);
665 errno = rerrno;
666 return NULL;
667 }
668 rv = asprintf(&p, "%s/%s", rdir, sep + 1);
669 rerrno = errno;
670 free(rdir);
671 free(udir);
672 errno = rerrno;
673 if (rv == -1)
674 return NULL;
675 return p;
676 }
677
678 /*
679 * Iterate over all files in a directory hierarchy, calling the callback
680 * cb for each file, passing the filename and arg as arguments. Files and
681 * directories beginning with a dot are skipped, symlinks are followed.
682 */
683 int
sys_dir_eachfile(const char * dirname,sys_dir_eachfile_cb_t cb,void * arg)684 sys_dir_eachfile(const char *dirname, sys_dir_eachfile_cb_t cb, void *arg)
685 {
686 FTS *tree;
687 FTSENT *node;
688 char * paths[2];
689 int rv = 0;
690
691 paths[1] = NULL;
692 paths[0] = strdup(dirname);
693 if (!paths[0])
694 return -1;
695
696 tree = fts_open(paths, FTS_NOCHDIR | FTS_LOGICAL, NULL);
697 if (!tree) {
698 log_err_printf("Cannot open directory '%s': %s\n",
699 dirname, strerror(errno));
700 rv = -1;
701 goto out1;
702 }
703
704 while ((node = fts_read(tree))) {
705 if (node->fts_level > 0 && node->fts_name[0] == '.')
706 fts_set(tree, node, FTS_SKIP);
707 else if (node->fts_info & FTS_F) {
708 rv = cb(node->fts_path, arg);
709 if (rv == -1)
710 goto out2;
711 }
712 }
713 if (errno) {
714 log_err_printf("Error reading directory entry: %s\n",
715 strerror(errno));
716 rv = -1;
717 goto out2;
718 }
719
720 out2:
721 fts_close(tree);
722
723 out1:
724 free(paths[0]);
725 return rv;
726 }
727
728 /*
729 * Portably get the number of CPU cores online in the system.
730 */
731 uint32_t
sys_get_cpu_cores(void)732 sys_get_cpu_cores(void)
733 {
734 #ifdef _SC_NPROCESSORS_ONLN
735 return sysconf(_SC_NPROCESSORS_ONLN);
736 #else /* !_SC_NPROCESSORS_ONLN */
737 int mib[2];
738 uint32_t n;
739 size_t len = sizeof(n);
740
741 mib[0] = CTL_HW;
742 mib[1] = HW_AVAILCPU;
743 sysctl(mib, sizeof(mib)/sizeof(int), &n, &len, NULL, 0);
744
745 if (n < 1) {
746 mib[1] = HW_NCPU;
747 sysctl(mib, sizeof(mib)/sizeof(int), &n, &len, NULL, 0);
748 if (n < 1) {
749 n = 1;
750 }
751 }
752 return n;
753 #endif /* !_SC_NPROCESSORS_ONLN */
754 }
755
756 /*
757 * Send a message and optional file descriptor on a connected AF_UNIX
758 * SOCKET_DGRAM socket s. Returns the return value of sendmsg().
759 * If fd is -1, no file descriptor is passed.
760 */
761 ssize_t
sys_sendmsgfd(int sock,void * buf,size_t bufsz,int fd)762 sys_sendmsgfd(int sock, void *buf, size_t bufsz, int fd)
763 {
764 struct iovec iov;
765 struct msghdr msg;
766 struct cmsghdr *cmsg;
767 char cmsgbuf[CMSG_SPACE(sizeof(int))];
768 ssize_t n;
769
770 iov.iov_base = buf;
771 iov.iov_len = bufsz;
772
773 msg.msg_name = NULL;
774 msg.msg_namelen = 0;
775 msg.msg_iov = &iov;
776 msg.msg_iovlen = 1;
777 msg.msg_flags = 0;
778
779 if (fd != -1) {
780 msg.msg_control = cmsgbuf;
781 msg.msg_controllen = sizeof(cmsgbuf);
782 memset(cmsgbuf, 0, sizeof(cmsgbuf));
783
784 cmsg = CMSG_FIRSTHDR(&msg);
785 if (!cmsg)
786 return -1;
787 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
788 cmsg->cmsg_level = SOL_SOCKET;
789 cmsg->cmsg_type = SCM_RIGHTS;
790
791 *((int *) CMSG_DATA(cmsg)) = fd;
792 } else {
793 msg.msg_control = NULL;
794 msg.msg_controllen = 0;
795 }
796 do {
797 #ifdef MSG_NOSIGNAL
798 n = sendmsg(sock, &msg, MSG_NOSIGNAL);
799 #else /* !MSG_NOSIGNAL */
800 n = sendmsg(sock, &msg, 0);
801 #endif /* !MSG_NOSIGNAL */
802 } while (n == -1 && errno == EINTR);
803 return n;
804 }
805
806 /*
807 * Receive a message and optional file descriptor on a connected AF_UNIX
808 * SOCKET_DGRAM socket s. Returns the return value of recvmsg()/recv()
809 * and sets errno to EINVAL if the received message is malformed.
810 * If pfd is NULL, no file descriptor is received; if a file descriptor was
811 * part of the received message and pfd is NULL, then the kernel will close it.
812 */
813 ssize_t
sys_recvmsgfd(int sock,void * buf,size_t bufsz,int * pfd)814 sys_recvmsgfd(int sock, void *buf, size_t bufsz, int *pfd)
815 {
816 ssize_t n;
817
818 if (pfd) {
819 struct iovec iov;
820 struct msghdr msg;
821 struct cmsghdr *cmsg;
822 unsigned char cmsgbuf[CMSG_SPACE(sizeof(int))];
823
824 iov.iov_base = buf;
825 iov.iov_len = bufsz;
826
827 msg.msg_name = NULL;
828 msg.msg_namelen = 0;
829 msg.msg_iov = &iov;
830 msg.msg_iovlen = 1;
831 msg.msg_control = cmsgbuf;
832 msg.msg_controllen = sizeof(cmsgbuf);
833 do {
834 n = recvmsg(sock, &msg, 0);
835 } while (n == -1 && errno == EINTR);
836 if (n <= 0)
837 return n;
838 cmsg = CMSG_FIRSTHDR(&msg);
839 if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
840 if (cmsg->cmsg_level != SOL_SOCKET) {
841 errno = EINVAL;
842 return -1;
843 }
844 if (cmsg->cmsg_type != SCM_RIGHTS) {
845 errno = EINVAL;
846 return -1;
847 }
848 *pfd = *((int *) CMSG_DATA(cmsg));
849 } else {
850 *pfd = -1;
851 }
852 } else {
853 do {
854 n = recv(sock, buf, bufsz, 0);
855 } while (n == -1 && errno == EINTR);
856 }
857 return n;
858 }
859
860 /*
861 * Format AF_UNIX socket address into printable string.
862 * Returns newly allocated string that must be freed by caller.
863 */
864 static char *
sys_afunix_str(struct sockaddr * addr,socklen_t addrlen)865 sys_afunix_str(struct sockaddr *addr, socklen_t addrlen)
866 {
867 struct sockaddr_un *sun = (struct sockaddr_un *)addr;
868 char *name;
869 int rv;
870
871 if (addrlen == sizeof(sa_family_t)) {
872 rv = asprintf(&name, "unnmd");
873 } else if (sun->sun_path[0] == '\0') {
874 /* abstract sockets is a Linux feature */
875 rv = asprintf(&name, "abstr:%02x:%02x:%02x:%02x",
876 sun->sun_path[1],
877 sun->sun_path[2],
878 sun->sun_path[3],
879 sun->sun_path[4]);
880 } else {
881 rv = asprintf(&name, "pname:%s", sun->sun_path);
882 }
883 if (rv == -1)
884 name = NULL;
885 return name;
886 }
887
888 /*
889 * Dump all open file descriptors to stdout - poor man's lsof/fstat/sockstat
890 */
891 void
sys_dump_fds(void)892 sys_dump_fds(void)
893 {
894 int maxfd = 0;
895
896 #ifdef F_MAXFD
897 if (!maxfd && ((maxfd = fcntl(0, F_MAXFD)) == -1)) {
898 fprintf(stderr, "fcntl(0, F_MAXFD) failed: %s (%i)\n",
899 strerror(errno), errno);
900 }
901 #endif /* F_MAXFD */
902 #ifdef _SC_OPEN_MAX
903 if (!maxfd && ((maxfd = sysconf(_SC_OPEN_MAX)) == -1)) {
904 fprintf(stderr, "sysconf(_SC_OPEN_MAX) failed: %s (%i)\n",
905 strerror(errno), errno);
906 }
907 #endif /* _SC_OPEN_MAX */
908 if (!maxfd)
909 maxfd = 65535;
910
911 for (int fd = 0; fd <= maxfd; fd++) {
912 struct stat st;
913
914 if (fstat(fd, &st) == -1) {
915 continue;
916 }
917
918 printf("%5d:", fd);
919 switch (st.st_mode & S_IFMT) {
920 case S_IFBLK: printf(" blkdev"); break;
921 case S_IFCHR: printf(" chrdev"); break;
922 case S_IFDIR: printf(" dir "); break;
923 case S_IFIFO: printf(" fifo "); break;
924 case S_IFLNK: printf(" lnkfil"); break;
925 case S_IFREG: printf(" regfil"); break;
926 case S_IFSOCK: printf(" socket"); break;
927 default: printf(" unknwn"); break;
928 }
929
930 if ((st.st_mode & S_IFMT) == S_IFSOCK) {
931 int lrv, frv, arv;
932 struct sockaddr_storage lss, fss;
933 socklen_t lsslen = sizeof(lss);
934 socklen_t fsslen = sizeof(fss);
935 char *laddrstr, *faddrstr;
936
937 lrv = getsockname(fd, (struct sockaddr *)&lss, &lsslen);
938 frv = getpeername(fd, (struct sockaddr *)&fss, &fsslen);
939
940 switch (lss.ss_family) {
941 case AF_INET:
942 case AF_INET6: {
943 if (lrv == 0) {
944 char *host, *port;
945 if (sys_sockaddr_str(
946 (struct sockaddr *)&lss,
947 lsslen,
948 &host, &port) != 0) {
949 laddrstr = strdup("?");
950 } else {
951 arv = asprintf(&laddrstr,
952 "[%s]:%s",
953 host, port);
954 if (arv == -1)
955 laddrstr = NULL;
956 free(host);
957 free(port);
958 }
959 } else {
960 laddrstr = strdup("n/a");
961 }
962 if (frv == 0) {
963 char *host, *port;
964 if (sys_sockaddr_str(
965 (struct sockaddr *)&fss,
966 fsslen,
967 &host, &port) != 0) {
968 faddrstr = strdup("?");
969 } else {
970 arv = asprintf(&faddrstr,
971 "[%s]:%s",
972 host, port);
973 if (arv == -1)
974 faddrstr = NULL;
975 free(host);
976 free(port);
977 }
978 } else {
979 faddrstr = strdup("n/a");
980 }
981 printf(" %-6s %s -> %s",
982 lss.ss_family == AF_INET ? "in" : "in6",
983 laddrstr, faddrstr);
984 free(laddrstr);
985 free(faddrstr);
986 break;
987 }
988 case AF_UNIX: {
989 if (lrv == 0) {
990 laddrstr = sys_afunix_str((struct sockaddr *)&lss, lsslen);
991 } else {
992 laddrstr = strdup("n/a");
993 }
994 if (frv == 0) {
995 faddrstr = sys_afunix_str((struct sockaddr *)&fss, fsslen);
996 } else {
997 faddrstr = strdup("n/a");
998 }
999 printf(" unix %s -> %s", laddrstr, faddrstr);
1000 free(laddrstr);
1001 free(faddrstr);
1002 break;
1003 }
1004 case AF_UNSPEC: {
1005 printf(" unspec");
1006 break;
1007 }
1008 default:
1009 printf(" (%i)", lss.ss_family);
1010 }
1011 }
1012 printf("\n");
1013 }
1014 }
1015
1016 static int sys_rand_seeded = 0;
1017
1018 static void
sys_rand_seed(void)1019 sys_rand_seed(void) {
1020 struct timeval seed;
1021
1022 if (gettimeofday(&seed, NULL) == -1) {
1023 srandom((unsigned)time(NULL));
1024 } else {
1025 srandom((unsigned)(seed.tv_sec ^ seed.tv_usec));
1026 }
1027 sys_rand_seeded = 1;
1028 }
1029
1030 uint16_t
sys_rand16(void)1031 sys_rand16(void) {
1032 if (unlikely(!sys_rand_seeded))
1033 sys_rand_seed();
1034 return random();
1035 }
1036
1037 uint32_t
sys_rand32(void)1038 sys_rand32(void) {
1039 if (unlikely(!sys_rand_seeded))
1040 sys_rand_seed();
1041 return random();
1042 }
1043
1044 /* vim: set noet ft=c: */
1045
1046