2012-04-13 12:47:30 +00:00
|
|
|
/*
|
|
|
|
* SSLsplit - transparent and scalable SSL/TLS interception
|
2014-01-06 13:09:18 +00:00
|
|
|
* Copyright (c) 2009-2014, Daniel Roethlisberger <daniel@roe.ch>
|
2012-04-13 12:47:30 +00:00
|
|
|
* All rights reserved.
|
|
|
|
* http://www.roe.ch/SSLsplit
|
|
|
|
*
|
|
|
|
* 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 unmodified, 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.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "sys.h"
|
|
|
|
|
|
|
|
#include "log.h"
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <grp.h>
|
|
|
|
#include <fts.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#ifndef _SC_NPROCESSORS_ONLN
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#endif /* !_SC_NPROCESSORS_ONLN */
|
|
|
|
|
2014-10-18 19:46:44 +00:00
|
|
|
#if HAVE_DARWIN_LIBPROC
|
|
|
|
#include <libproc.h>
|
|
|
|
#endif
|
|
|
|
|
2012-04-13 12:47:30 +00:00
|
|
|
#include <event2/util.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Permanently drop from root privileges to an unprivileged user account.
|
|
|
|
* Sets the real, effective and stored user and group ID and the list of
|
|
|
|
* ancillary groups. This is only safe if the effective user ID is 0.
|
|
|
|
* If username is unset and the effective uid != uid, drop privs to uid.
|
|
|
|
* This is to support setuid bit configurations.
|
2014-10-18 06:34:51 +00:00
|
|
|
* If groupname is set, it will be used instead of the user's default primary
|
|
|
|
* group.
|
2012-04-13 12:47:30 +00:00
|
|
|
* If jaildir is set, also chroot to jaildir after reading system files
|
|
|
|
* but before dropping privileges.
|
|
|
|
* Returns 0 on success, -1 on failure.
|
|
|
|
*/
|
|
|
|
int
|
2014-10-18 06:34:51 +00:00
|
|
|
sys_privdrop(const char *username, const char *groupname, const char *jaildir)
|
2012-04-13 12:47:30 +00:00
|
|
|
{
|
|
|
|
struct passwd *pw = NULL;
|
2014-10-18 06:34:51 +00:00
|
|
|
struct group *gr = NULL;
|
2012-04-13 12:47:30 +00:00
|
|
|
int ret = -1;
|
|
|
|
|
2014-10-18 06:34:51 +00:00
|
|
|
if (groupname) {
|
|
|
|
if (!(gr = getgrnam(groupname))) {
|
|
|
|
log_err_printf("Failed to getgrnam group '%s': %s\n",
|
|
|
|
groupname, strerror(errno));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-13 12:47:30 +00:00
|
|
|
if (username) {
|
|
|
|
if (!(pw = getpwnam(username))) {
|
|
|
|
log_err_printf("Failed to getpwnam user '%s': %s\n",
|
|
|
|
username, strerror(errno));
|
|
|
|
goto error;
|
|
|
|
}
|
2014-10-18 06:34:51 +00:00
|
|
|
|
|
|
|
if (gr != NULL) {
|
|
|
|
pw->pw_gid = gr->gr_gid;
|
|
|
|
}
|
|
|
|
|
2012-04-13 12:47:30 +00:00
|
|
|
if (initgroups(username, pw->pw_gid) == -1) {
|
|
|
|
log_err_printf("Failed to initgroups user '%s': %s\n",
|
|
|
|
username, strerror(errno));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (jaildir) {
|
|
|
|
if (chroot(jaildir) == -1) {
|
|
|
|
log_err_printf("Failed to chroot to '%s': %s\n",
|
|
|
|
jaildir, strerror(errno));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (chdir("/") == -1) {
|
|
|
|
log_err_printf("Failed to chdir to '/': %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (username) {
|
|
|
|
if (setgid(pw->pw_gid) == -1) {
|
|
|
|
log_err_printf("Failed to setgid to %i: %s\n",
|
|
|
|
pw->pw_gid, strerror(errno));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (setuid(pw->pw_uid) == -1) {
|
|
|
|
log_err_printf("Failed to setuid to %i: %s\n",
|
|
|
|
pw->pw_uid, strerror(errno));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
} else if (getuid() != geteuid()) {
|
|
|
|
if (setuid(getuid()) == -1) {
|
|
|
|
log_err_printf("Failed to setuid(getuid()): %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
error:
|
|
|
|
if (pw) {
|
|
|
|
endpwent();
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open and lock process ID file fn.
|
|
|
|
* Returns open file descriptor on success or -1 on errors.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sys_pidf_open(const char *fn)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if ((fd = open(fn, O_RDWR|O_CREAT, 0640)) == -1) {
|
|
|
|
log_err_printf("Failed to open '%s': %s\n", fn,
|
|
|
|
strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
|
|
|
|
log_err_printf("Failed to lock '%s': %s\n", fn,
|
|
|
|
strerror(errno));
|
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write process ID to open process ID file descriptor fd.
|
|
|
|
* Returns 0 on success, -1 on errors.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sys_pidf_write(int fd)
|
|
|
|
{
|
|
|
|
char pidbuf[4*sizeof(pid_t)];
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
rv = snprintf(pidbuf, sizeof(pidbuf), "%d\n", getpid());
|
|
|
|
if (rv == -1 || rv >= (int)sizeof(pidbuf))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
write(fd, pidbuf, strlen(pidbuf));
|
|
|
|
fsync(fd);
|
|
|
|
|
|
|
|
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Close and remove open process ID file before quitting.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
sys_pidf_close(int fd, const char *fn)
|
|
|
|
{
|
|
|
|
unlink(fn);
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
2014-10-18 20:16:50 +00:00
|
|
|
/*
|
|
|
|
* Converts a local uid into a printable string representation.
|
|
|
|
* Returns an allocated buffer which must be freed by caller, or NULL on error.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
sys_user_str(uid_t uid)
|
|
|
|
{
|
|
|
|
int bufsize;
|
2014-11-13 21:17:13 +00:00
|
|
|
if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
|
2014-10-18 20:16:50 +00:00
|
|
|
log_err_printf("failed to get password bufsize: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buffer[bufsize];
|
|
|
|
struct passwd pwd, *result = NULL;
|
|
|
|
if (getpwuid_r(uid, &pwd, buffer, bufsize, &result) != 0 || !result) {
|
|
|
|
/* no entry found; return the integer representation */
|
|
|
|
char *name;
|
|
|
|
if (asprintf(&name, "%llu", (long long) uid) == -1) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
return strdup(pwd.pw_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Converts a local gid into a printable string representation.
|
|
|
|
* Returns an allocated buffer which must be freed by caller, or NULL on error.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
sys_group_str(gid_t gid)
|
|
|
|
{
|
|
|
|
int bufsize;
|
|
|
|
if ((bufsize = sysconf(_SC_GETGR_R_SIZE_MAX)) == -1) {
|
|
|
|
log_err_printf("failed to get group bufsize: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buffer[bufsize];
|
|
|
|
struct group grp, *result = NULL;
|
|
|
|
if (getgrgid_r(gid, &grp, buffer, bufsize, &result) != 0 || !result) {
|
|
|
|
/* no entry found; return the integer representation */
|
|
|
|
char *name;
|
|
|
|
if (asprintf(&name, "%llu", (long long) gid) == -1) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
return strdup(grp.gr_name);
|
|
|
|
}
|
|
|
|
|
2012-04-13 12:47:30 +00:00
|
|
|
/*
|
|
|
|
* Parse an ascii host/IP and port tuple into a sockaddr_storage.
|
|
|
|
* On success, returns address family and fills in addr, addrlen.
|
2012-12-06 15:03:30 +00:00
|
|
|
* Returns -1 on error.
|
2012-04-13 12:47:30 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
sys_sockaddr_parse(struct sockaddr_storage *addr, socklen_t *addrlen,
|
|
|
|
char *naddr, char *nport, int af, int flags)
|
|
|
|
{
|
|
|
|
struct evutil_addrinfo hints;
|
|
|
|
struct evutil_addrinfo *ai;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_family = af;
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
hints.ai_flags = EVUTIL_AI_ADDRCONFIG | flags;
|
|
|
|
rv = evutil_getaddrinfo(naddr, nport, &hints, &ai);
|
|
|
|
if (rv != 0) {
|
|
|
|
log_err_printf("Cannot resolve address '%s' port '%s': %s\n",
|
|
|
|
naddr, nport, gai_strerror(rv));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
memcpy(addr, ai->ai_addr, ai->ai_addrlen);
|
|
|
|
*addrlen = ai->ai_addrlen;
|
|
|
|
af = ai->ai_family;
|
|
|
|
freeaddrinfo(ai);
|
|
|
|
return af;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Converts an IPv4/IPv6 sockaddr into a printable string representation.
|
|
|
|
* Returns an allocated buffer which must be freed by caller, or NULL on error.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
sys_sockaddr_str(struct sockaddr *addr, socklen_t addrlen)
|
|
|
|
{
|
|
|
|
char host[INET6_ADDRSTRLEN], serv[6];
|
|
|
|
char *buf;
|
|
|
|
int rv;
|
|
|
|
size_t bufsz;
|
|
|
|
|
|
|
|
bufsz = sizeof(host) + sizeof(serv) + 3;
|
|
|
|
buf = malloc(bufsz);
|
|
|
|
if (!buf) {
|
|
|
|
log_err_printf("Cannot allocate memory\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
rv = getnameinfo(addr, addrlen, host, sizeof(host), serv, sizeof(serv),
|
|
|
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
|
|
|
if (rv != 0) {
|
|
|
|
log_err_printf("Cannot get nameinfo for socket address: %s\n",
|
|
|
|
gai_strerror(rv));
|
|
|
|
free(buf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
snprintf(buf, bufsz, "[%s]:%s", host, serv);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns 1 if path points to an existing directory node in the filesystem.
|
|
|
|
* Returns 0 if path is NULL, does not exist, or points to a file of some kind.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sys_isdir(const char *path)
|
|
|
|
{
|
|
|
|
struct stat s;
|
|
|
|
|
|
|
|
if (stat(path, &s) == -1)
|
|
|
|
return 0;
|
|
|
|
if (s.st_mode & S_IFDIR)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Iterate over all files in a directory hierarchy, calling the callback
|
|
|
|
* cb for each file, passing the filename and arg as arguments. Files and
|
|
|
|
* directories beginning with a dot are skipped, symlinks are followed.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sys_dir_eachfile(const char *dirname, sys_dir_eachfile_cb_t cb, void *arg)
|
|
|
|
{
|
|
|
|
FTS *tree;
|
|
|
|
FTSENT *node;
|
|
|
|
char * paths[2];
|
|
|
|
|
|
|
|
paths[1] = NULL;
|
|
|
|
paths[0] = strdup(dirname);
|
|
|
|
if (!paths[0])
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
tree = fts_open(paths, FTS_NOCHDIR | FTS_LOGICAL, NULL);
|
|
|
|
if (!tree) {
|
|
|
|
log_err_printf("Cannot open directory '%s': %s\n",
|
|
|
|
dirname, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((node = fts_read(tree))) {
|
|
|
|
if (node->fts_level > 0 && node->fts_name[0] == '.')
|
|
|
|
fts_set(tree, node, FTS_SKIP);
|
|
|
|
else if (node->fts_info & FTS_F) {
|
|
|
|
cb(node->fts_path, arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (errno) {
|
|
|
|
log_err_printf("Error reading directory entry: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fts_close(tree);
|
|
|
|
|
|
|
|
free(paths[0]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Portably get the number of CPU cores online in the system.
|
|
|
|
*/
|
|
|
|
uint32_t
|
|
|
|
sys_get_cpu_cores(void)
|
|
|
|
{
|
|
|
|
#ifdef _SC_NPROCESSORS_ONLN
|
|
|
|
return sysconf(_SC_NPROCESSORS_ONLN);
|
|
|
|
#else /* !_SC_NPROCESSORS_ONLN */
|
|
|
|
int mib[2];
|
|
|
|
uint32_t n;
|
|
|
|
size_t len = sizeof(n);
|
|
|
|
|
|
|
|
mib[0] = CTL_HW;
|
|
|
|
mib[1] = HW_AVAILCPU;
|
|
|
|
sysctl(mib, sizeof(mib)/sizeof(int), &n, &len, NULL, 0);
|
|
|
|
|
|
|
|
if (n < 1) {
|
|
|
|
mib[1] = HW_NCPU;
|
|
|
|
sysctl(mib, sizeof(mib)/sizeof(int), &n, &len, NULL, 0);
|
|
|
|
if (n < 1) {
|
|
|
|
n = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
#endif /* !_SC_NPROCESSORS_ONLN */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vim: set noet ft=c: */
|