diff options
Diffstat (limited to '')
-rw-r--r-- | src/daemon.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/src/daemon.c b/src/daemon.c new file mode 100644 index 0000000..67ef86e --- /dev/null +++ b/src/daemon.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2016-2021, OARC, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "daemon.h" +#include "log.h" +#include "memzero.h" + +void drop_privileges(void) +{ + struct rlimit rss; + struct passwd pwd; + struct passwd* result = 0; + size_t pwdBufSize; + char* pwdBuf; + unsigned int s; + uid_t oldUID = getuid(); + uid_t oldGID = getgid(); + uid_t dropUID; + gid_t dropGID; + const char* user; + struct group* grp = 0; + + /* + * Security: getting UID and GUID for nobody + */ + pwdBufSize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (pwdBufSize == -1) + pwdBufSize = 16384; + + pwdBuf = calloc(pwdBufSize, sizeof(char)); + if (pwdBuf == NULL) { + fprintf(stderr, "unable to allocate buffer for pwdBuf\n"); + exit(1); + } + + user = options.user ? options.user : DROPTOUSER; + if (options.group) { + if (!(grp = getgrnam(options.group))) { + if (errno) { + fprintf(stderr, "Unable to get group %s: %s\n", options.group, strerror(errno)); + } else { + fprintf(stderr, "Group %s not found, existing.\n", options.group); + } + exit(1); + } + } + + s = getpwnam_r(user, &pwd, pwdBuf, pwdBufSize, &result); + if (result == NULL) { + if (s == 0) { + fprintf(stderr, "User %s not found, exiting.\n", user); + exit(1); + } else { + fprintf(stderr, "issue with getpwnnam_r call, exiting.\n"); + exit(1); + } + } + + dropUID = pwd.pw_uid; + dropGID = grp ? grp->gr_gid : pwd.pw_gid; + dnscap_memzero(pwdBuf, pwdBufSize); + free(pwdBuf); + + /* + * Security section: setting memory limit and dropping privileges to nobody + */ + getrlimit(RLIMIT_DATA, &rss); + if (mem_limit_set) { + rss.rlim_cur = mem_limit; + rss.rlim_max = mem_limit; + if (setrlimit(RLIMIT_DATA, &rss) == -1) { + fprintf(stderr, "Unable to set the memory limit, exiting\n"); + exit(1); + } + } + +#if HAVE_SETRESGID + if (setresgid(dropGID, dropGID, dropGID) < 0) { + fprintf(stderr, "Unable to drop GID to %s: %s\n", options.group ? options.group : user, strerror(errno)); + exit(1); + } +#elif HAVE_SETREGID + if (setregid(dropGID, dropGID) < 0) { + fprintf(stderr, "Unable to drop GID to %s: %s\n", options.group ? options.group : user, strerror(errno)); + exit(1); + } +#elif HAVE_SETEGID + if (setegid(dropGID) < 0) { + fprintf(stderr, "Unable to drop GID to %s: %s\n", options.group ? options.group : user, strerror(errno)); + exit(1); + } +#endif + +#if HAVE_INITGROUPS + if (initgroups(pwd.pw_name, dropGID) < 0) { + fprintf(stderr, "Unable to init supplemental groups for %s: %s\n", user, strerror(errno)); + exit(1); + } +#elif HAVE_SETGROUPS + if (setgroups(0, NULL) < 0) { + fprintf(stderr, "Unable to drop supplemental groups: %s\n", strerror(errno)); + exit(1); + } +#endif + +#if HAVE_SETRESUID + if (setresuid(dropUID, dropUID, dropUID) < 0) { + fprintf(stderr, "Unable to drop UID to %s: %s\n", user, strerror(errno)); + exit(1); + } +#elif HAVE_SETREUID + if (setreuid(dropUID, dropUID) < 0) { + fprintf(stderr, "Unable to drop UID to %s: %s\n", user, strerror(errno)); + exit(1); + } +#elif HAVE_SETEUID + if (seteuid(dropUID) < 0) { + fprintf(stderr, "Unable to drop UID to %s: %s\n", user, strerror(errno)); + exit(1); + } +#endif + + /* + * Testing if privileges are dropped + */ + if (oldGID != getgid() && (setgid(oldGID) == 1 && setegid(oldGID) != 1)) { + fprintf(stderr, "Able to restore back to root, exiting.\n"); + fprintf(stderr, "currentUID:%u currentGID:%u\n", getuid(), getgid()); + exit(1); + } + if ((oldUID != getuid() && getuid() == 0) && (setuid(oldUID) != 1 && seteuid(oldUID) != 1)) { + fprintf(stderr, "Able to restore back to root, exiting.\n"); + fprintf(stderr, "currentUID:%u currentGID:%u\n", getgid(), getgid()); + exit(1); + } + +#ifdef USE_SECCOMP + if (use_seccomp == FALSE) { + return; + } + +#if 0 + /* + * Setting SCMP_ACT_TRAP means the process will get + * a SIGSYS signal when a bad syscall is executed + * This is for debugging and should be monitored. + */ + + scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_TRAP); +#endif + + /* + * SCMP_ACT_KILL tells the kernel to kill the process + * when a syscall we did not filter on is called. + * This should be uncommented in production. + */ + scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL); + + if (ctx == NULL) { + fprintf(stderr, "Unable to create seccomp-bpf context\n"); + exit(1); + } + + int r = 0; + r |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 0); + r |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(uname), 0); + r |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0); + r |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0); + r |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0); + r |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0); + r |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0); + r |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0); + r |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0); + r |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0); + r |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(select), 0); + r |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat), 0); + + if (r != 0) { + fprintf(stderr, "Unable to apply seccomp-bpf filter\n"); + seccomp_release(ctx); + exit(1); + } + + r = seccomp_load(ctx); + + if (r < 0) { + seccomp_release(ctx); + fprintf(stderr, "Unable to load seccomp-bpf filter\n"); + exit(1); + } +#endif +} + +void daemonize(void) +{ + pid_t pid; +#ifdef TIOCNOTTY + int i; +#endif + if ((pid = fork()) < 0) { + logerr("fork failed: %s", strerror(errno)); + exit(1); + } else if (pid > 0) + exit(0); + openlog("dnscap", 0, LOG_DAEMON); + if (setsid() < 0) { + logerr("setsid failed: %s", strerror(errno)); + exit(1); + } +#ifdef TIOCNOTTY + if ((i = open("/dev/tty", O_RDWR)) >= 0) { + ioctl(i, TIOCNOTTY, NULL); + close(i); + } +#endif + logerr("Backgrounded as pid %u", getpid()); +} |