From ce66485ee6eb50fe30b9187b60c818d8353dd337 Mon Sep 17 00:00:00 2001 From: Alan Jowett Date: Sun, 19 Apr 2020 21:39:18 -0600 Subject: [PATCH] Uses fork/exec to limit memory footprint of ocserv-worker processes Capture all the required worker process state in a protobuf and pass to worker via env. Snapshot all config files to ensure ocserv-sm and ocserv-worker remain in sync. Split ocserv-worker functionality into it's own executable with minimal dependencies. Resolves: #285 Signed-off-by: Alan Jowett alanjo@microsoft.com --- .gitignore | 1 + .gitlab-ci.yml | 2 + NEWS | 1 + doc/design.md | 5 +- src/Makefile.am | 73 ++++---- src/acct/pam.h | 2 +- src/acct/radius.h | 2 +- src/auth/gssapi.h | 2 +- src/auth/pam.h | 2 +- src/auth/plain.h | 2 +- src/auth/radius.h | 2 +- src/common-config.h | 2 +- src/common/base64-helper.h | 2 +- src/common/common.c | 4 + src/common/common.h | 6 +- src/common/hmac.c | 2 +- src/common/hmac.h | 2 +- src/common/snapshot.c | 337 ++++++++++++++++++++++++++++++++++++ src/common/snapshot.h | 90 ++++++++++ src/common/system.h | 4 +- src/config.c | 270 +++++++++++++++++++++++++++-- src/defs.h | 2 +- src/gettime.h | 2 +- src/html.h | 2 +- src/icmp-ping.h | 2 +- src/ip-lease.h | 2 +- src/ip-util.h | 2 +- src/ipc.proto | 38 ++++ src/isolate.c | 177 +++++++++++++++++++ src/isolate.h | 33 ++++ src/main-ban.h | 2 +- src/main-sec-mod-cmd.c | 22 --- src/main.c | 327 +++++++++++++++++++---------------- src/main.h | 9 +- src/proc-search.h | 2 +- src/route-add.h | 2 +- src/script-list.h | 2 +- src/sec-mod-acct.h | 2 +- src/sec-mod-auth.h | 2 +- src/sec-mod-resume.h | 2 +- src/sec-mod-sup-config.h | 2 +- src/sec-mod.h | 2 +- src/setproctitle.h | 2 +- src/str.h | 2 +- src/sup-config/file.h | 2 +- src/sup-config/radius.h | 2 +- src/tlslib.h | 2 +- src/tun.h | 2 +- src/vhost.h | 2 +- src/vpn.h | 2 +- src/worker-auth.c | 1 + src/worker-bandwidth.h | 2 +- src/worker-privs.c | 35 ++++ src/worker.c | 344 +++++++++++++++++++++++++++++++++++++ src/worker.h | 2 +- tests/test-sighup | 4 +- 56 files changed, 1592 insertions(+), 261 deletions(-) create mode 100644 src/common/snapshot.c create mode 100644 src/common/snapshot.h create mode 100644 src/isolate.c create mode 100644 src/isolate.h create mode 100644 src/worker.c diff --git a/.gitignore b/.gitignore index 5489ad3e..4e95293a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ ocserv*.tar.xz* *.gcda *.gcno ocserv +ocserv-worker configure autom4te.cache aclocal.m4 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4f479451..3ddb0510 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -140,6 +140,7 @@ RPM/epel7: - CURDIR=$(pwd) - TARFILE=$(find ./ -name '*.tar.xz') - RPMVERSION=$(cat /usr/local/rpms/ocserv/*.spec|grep ^Version|awk '{print $2}') + - sed -i '/^%{_sbindir}\/ocserv$/ a %{_sbindir}/ocserv-worker' /usr/local/rpms/ocserv/ocserv.spec - NEWVERSION=$(echo $TARFILE|sed -e 's/ocserv-//' -e 's/\.tar\.xz//' -e 's|./||') - echo "tarfile $TARFILE" && echo "rpm $RPMVERSION" && echo "new $NEWVERSION" - cp $TARFILE /usr/local/rpms/ocserv @@ -176,6 +177,7 @@ RPM/epel8: - CURDIR=$(pwd) - TARFILE=$(find ./ -name '*.tar.xz') - RPMVERSION=$(cat /usr/local/rpms/ocserv/*.spec|grep ^Version|awk '{print $2}') + - sed -i '/^%{_sbindir}\/ocserv$/ a %{_sbindir}/ocserv-worker' /usr/local/rpms/ocserv/ocserv.spec - NEWVERSION=$(echo $TARFILE|sed -e 's/ocserv-//' -e 's/\.tar\.xz//' -e 's|./||') - echo "tarfile $TARFILE" && echo "rpm $RPMVERSION" && echo "new $NEWVERSION" - cp $TARFILE /usr/local/rpms/ocserv diff --git a/NEWS b/NEWS index afc9ed15..dcb253ad 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ or ocserv-secmod (#283). - Disable TCP queuing on the TLS port. - Fix leak of GnuTLS session when DTLS connection is re-established (#293). +- Switch from fork to fork/exec model to achieve better scaling and ASLR protection (#285). * Version 1.0.1 (released 2020-04-09) diff --git a/doc/design.md b/doc/design.md index 87ce0915..64746a16 100644 --- a/doc/design.md +++ b/doc/design.md @@ -16,9 +16,12 @@ See https://ocserv.gitlab.io/www/technical.html The main component consists of the process which is tasked to: - * Listen for incoming TCP connections and fork a new worker process + * Listen for incoming TCP connections and fork/exec a new worker process to handle it. - See main.c + * State is passed between main process and worker via an environment + variable. + * Listen for incomping UDP "connections" and forward the packet stream to the appropriate worker process. - See main.c diff --git a/src/Makefile.am b/src/Makefile.am index 1587d745..29b69a56 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,15 +19,33 @@ EXTRA_DIST = version.inc.in version.inc \ ipc.proto ctl.proto http-heads.gperf common.mk ocserv-fw -sbin_PROGRAMS = ocserv +sbin_PROGRAMS = ocserv ocserv-worker bin_SCRIPTS = ocserv-fw +ocserv_SOURCES = $(CORE_SOURCES) $(AUTH_SOURCES) $(ACCT_SOURCES) \ + main.c main-auth.c main-ban.c main-ban.h main-ctl-unix.c main-proc.c \ + main-sec-mod-cmd.c main-user.c main-worker-cmd.c proc-search.c \ + proc-search.h route-add.c route-add.h sec-mod.c sec-mod.h sec-mod-acct.h \ + sec-mod-auth.c sec-mod-auth.h sec-mod-cookies.c sec-mod-db.c \ + sec-mod-resume.c sec-mod-resume.h sec-mod-sup-config.c sec-mod-sup-config.h + +ocserv_LDADD = $(CORE_LDADD) + +ocserv_worker_CPPFLAGS = $(AM_CPPFLAGS) -DOCSERV_WORKER_PROCESS +ocserv_worker_SOURCES = $(CORE_SOURCES) \ + html.c html.h http-heads.h worker.c worker.h worker-auth.c \ + worker-bandwidth.c worker-bandwidth.h worker-http.c worker-http-handlers.c \ + worker-kkdcp.c worker-misc.c worker-privs.c worker-proxyproto.c \ + worker-resume.c worker-vpn.c + +ocserv_worker_LDADD = $(CORE_LDADD) + noinst_LIBRARIES = libipc.a # Authentication module sources -AUTH_SOURCES=auth/pam.c auth/pam.h auth/plain.c auth/plain.h auth/radius.c auth/radius.h \ - auth/common.c auth/common.h auth/gssapi.h auth/gssapi.c auth-unix.c \ - auth-unix.h +AUTH_SOURCES=auth/common.c auth/common.h auth/gssapi.c auth/gssapi.h \ + auth/pam.c auth/pam.h auth/plain.c auth/plain.h auth/radius.c \ + auth/radius.h auth-unix.c auth-unix.h if ENABLE_OIDC_AUTH AUTH_SOURCES += auth/openidconnect.c auth/openidconnect.h @@ -35,43 +53,30 @@ endif ACCT_SOURCES=acct/radius.c acct/radius.h acct/pam.c acct/pam.h -ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \ - main-worker-cmd.c ip-lease.c ip-lease.h vhost.h main-proc.c \ - vpn.h tlslib.h log.c tun.c tun.h config-kkdcp.c \ - config.c worker-resume.c worker.h sec-mod-resume.c main.h \ - worker-http-handlers.c html.c html.h worker-http.c \ - main-user.c worker-misc.c route-add.c route-add.h worker-privs.c \ - sec-mod.c sec-mod-db.c sec-mod-auth.c sec-mod-auth.h sec-mod.h \ - script-list.h $(AUTH_SOURCES) $(ACCT_SOURCES) \ - icmp-ping.c icmp-ping.h worker-kkdcp.c subconfig.c \ - sec-mod-sup-config.c sec-mod-sup-config.h \ - sup-config/file.c sup-config/file.h main-sec-mod-cmd.c \ - sup-config/radius.c sup-config/radius.h \ - worker-bandwidth.c worker-bandwidth.h main-ctl.h \ - vasprintf.c vasprintf.h worker-proxyproto.c config-ports.c \ - proc-search.c proc-search.h http-heads.h ip-util.c ip-util.h \ - main-ban.c main-ban.h common-config.h valid-hostname.c \ - str.c str.h gettime.h $(CCAN_SOURCES) $(HTTP_PARSER_SOURCES) \ - sec-mod-acct.h setproctitle.c setproctitle.h sec-mod-resume.h \ - sec-mod-cookies.c defs.h inih/ini.c inih/ini.h \ - common/hmac.h common/hmac.c - - +CORE_SOURCES = $(CCAN_SOURCES) $(HTTP_PARSER_SOURCES) \ + common/hmac.c common/hmac.h common/snapshot.c common/snapshot.h \ + common-config.h config.c config-kkdcp.c config-ports.c defs.h gettime.h \ + icmp-ping.c icmp-ping.h inih/ini.c inih/ini.h ip-lease.c ip-lease.h \ + ip-util.c ip-util.h isolate.h isolate.c log.c main.h main-ctl.h \ + script-list.h setproctitle.c setproctitle.h str.c str.h subconfig.c \ + sup-config/file.c sup-config/file.h sup-config/radius.c \ + sup-config/radius.h tlslib.c tlslib.h tun.c tun.h valid-hostname.c \ + vasprintf.c vasprintf.h vhost.h vpn.h if ENABLE_COMPRESSION -ocserv_SOURCES += lzs.c lzs.h +CORE_SOURCES += lzs.c lzs.h endif if HAVE_GSSAPI -ocserv_SOURCES += kkdcp_asn1_tab.c kkdcp.asn +CORE_SOURCES += kkdcp_asn1_tab.c kkdcp.asn endif if LOCAL_HTTP_PARSER HTTP_PARSER_SOURCES = http-parser/http_parser.c http-parser/http_parser.h endif -ocserv_LDADD = ../gl/libgnu.a libccan.a libcommon.a -ocserv_LDADD += $(LIBGNUTLS_LIBS) $(PAM_LIBS) $(LIBUTIL) \ +CORE_LDADD = ../gl/libgnu.a libccan.a libcommon.a +CORE_LDADD += $(LIBGNUTLS_LIBS) $(PAM_LIBS) $(LIBUTIL) \ $(LIBSECCOMP) $(LIBWRAP) $(LIBCRYPT) $(NEEDED_HTTP_PARSER_LIBS) \ $(NEEDED_LIBPROTOBUF_LIBS) $(LIBSYSTEMD) $(LIBTALLOC_LIBS) \ $(RADCLI_LIBS) $(LIBLZ4_LIBS) $(LIBKRB5_LIBS) \ @@ -80,11 +85,9 @@ ocserv_LDADD += $(LIBGNUTLS_LIBS) $(PAM_LIBS) $(LIBUTIL) \ $(CODE_COVERAGE_LDFLAGS) if ENABLE_OIDC_AUTH -ocserv_LDADD += $(LIBCURL_LIBS) $(CJOSE_LIBS) $(JANSSON_LIBS) +CORE_LDADD += $(LIBCURL_LIBS) $(CJOSE_LIBS) $(JANSSON_LIBS) endif -ocserv_SOURCES += main-ctl-unix.c - libipc_a_SOURCES=ctl.pb-c.c ctl.pb-c.h ipc.pb-c.h ipc.pb-c.c ipc.pb-c.c: ipc.proto @@ -164,9 +167,9 @@ endif # libpcl if PCL -ocserv_LDADD += $(PCL_LIBS) +CORE_LDADD += $(PCL_LIBS) else -ocserv_LDADD += libpcl.a +CORE_LDADD += libpcl.a AM_CPPFLAGS += -I$(srcdir)/pcl/ noinst_LIBRARIES += libpcl.a diff --git a/src/acct/pam.h b/src/acct/pam.h index 1ab10a7f..055ff025 100644 --- a/src/acct/pam.h +++ b/src/acct/pam.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/acct/radius.h b/src/acct/radius.h index f4f4880a..b6b10a36 100644 --- a/src/acct/radius.h +++ b/src/acct/radius.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/auth/gssapi.h b/src/auth/gssapi.h index 9623258f..b5c56aaa 100644 --- a/src/auth/gssapi.h +++ b/src/auth/gssapi.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/auth/pam.h b/src/auth/pam.h index 2fe4eae5..b850e956 100644 --- a/src/auth/pam.h +++ b/src/auth/pam.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/auth/plain.h b/src/auth/plain.h index 2f6e3ae7..0d386776 100644 --- a/src/auth/plain.h +++ b/src/auth/plain.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/auth/radius.h b/src/auth/radius.h index f8586e62..dcc766d9 100644 --- a/src/auth/radius.h +++ b/src/auth/radius.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/common-config.h b/src/common-config.h index d2bdbf30..dddf4fa8 100644 --- a/src/common-config.h +++ b/src/common-config.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/common/base64-helper.h b/src/common/base64-helper.h index e3c0fc43..66450577 100644 --- a/src/common/base64-helper.h +++ b/src/common/base64-helper.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/common/common.c b/src/common/common.c index d47bc84c..6f506e3f 100644 --- a/src/common/common.c +++ b/src/common/common.c @@ -35,6 +35,9 @@ #include "defs.h" #include "common/base64-helper.h" +int saved_argc = 0; +char **saved_argv = NULL; + const char *_vhost_prefix(const char *name) { static char tmp[128]; @@ -878,3 +881,4 @@ size_t oc_strlcpy(char *dst, char const *src, size_t siz) } #endif + diff --git a/src/common/common.h b/src/common/common.h index 36d9f0da..d6cb95e7 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -6,7 +6,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. @@ -138,4 +138,8 @@ size_t oc_strlcpy(char *dst, char const *src, size_t siz); #define SAFE_ID_SIZE (BASE64_ENCODE_RAW_LENGTH(20)+1) char *calc_safe_id(const uint8_t *data, unsigned size, char *output, unsigned output_size); + +extern int saved_argc; +extern char **saved_argv; + #endif diff --git a/src/common/hmac.c b/src/common/hmac.c index e7476249..e8d43a93 100644 --- a/src/common/hmac.c +++ b/src/common/hmac.c @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/common/hmac.h b/src/common/hmac.h index 1d72ba6e..0f0fb8de 100644 --- a/src/common/hmac.h +++ b/src/common/hmac.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/common/snapshot.c b/src/common/snapshot.c new file mode 100644 index 00000000..c44334d6 --- /dev/null +++ b/src/common/snapshot.c @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2020 Microsoft Corporation + * + * Author: Alan Jowett + * + * This file is part of ocserv. + * + * ocserv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define ERRSTR "error: " +#define WARNSTR "warning: " +#define NOTESTR "note: " + +typedef struct snapshot_t { + struct htable ht; + void *pool; + const char *tmp_filename_template; +} snapshot_t; + +typedef struct snapshot_entry_t { + uint32_t fd; + const char name[]; +} snapshot_entry_t; + +typedef struct htable_iter snapshot_iter_t; + +static size_t snapshot_hash_filename(const char *file_name) +{ + return hash64(file_name, strlen(file_name), 0); +} + +static size_t snapshot_rehash(const void *elem, void *priv) +{ + snapshot_entry_t *entry = (snapshot_entry_t *) elem; + return snapshot_hash_filename(entry->name); +} + +static snapshot_entry_t *snapshot_find(struct snapshot_t *snapshot, + const char *filename) +{ + struct htable_iter iter; + size_t hash = snapshot_hash_filename(filename); + snapshot_entry_t *entry = htable_firstval(&snapshot->ht, &iter, hash); + while (entry != NULL) { + if (strcmp(entry->name, filename) == 0) { + break; + } + entry = htable_nextval(&snapshot->ht, &iter, hash); + } + return entry; +} + +static int snapshot_file_name_from_fd(int fd, char *file_name, + size_t file_name_length) +{ + int ret = snprintf(file_name, file_name_length, "/proc/self/fd/%d", fd); + if (ret >= file_name_length) { + return -1; + } else { + return 0; + } +} + +static int snapshot_add_entry(snapshot_t * snapshot, const char *filename, + int fd) +{ + int retval = -1; + snapshot_entry_t *entry = NULL; + size_t file_name_length = strlen(filename) + 1; + entry = + (snapshot_entry_t *) talloc_zero_array(snapshot->pool, char, + sizeof(uint32_t) + + file_name_length); + if (entry == NULL) + goto cleanup; + + entry->fd = fd; + strlcpy((char *)entry->name, filename, file_name_length); + + if (!htable_add + (&snapshot->ht, snapshot_hash_filename(entry->name), entry)) + goto cleanup; + + entry = NULL; + retval = 0; + cleanup: + if (entry) + talloc_free(entry); + + return retval; +} + +int snapshot_init(void *pool, struct snapshot_t **snapshot, const char *prefix) +{ + snapshot_t *new_snapshot = NULL; + size_t tmp_filename_template_length = strlen(prefix) + 7; + + new_snapshot = talloc_zero(pool, snapshot_t); + if (new_snapshot == NULL) + goto cleanup; + + new_snapshot->pool = pool; + + new_snapshot->tmp_filename_template = + talloc_array(pool, char, tmp_filename_template_length); + + if (snprintf + ((char *)new_snapshot->tmp_filename_template, + tmp_filename_template_length, "%sXXXXXX", + prefix) >= tmp_filename_template_length) + goto cleanup; + + htable_init(&new_snapshot->ht, snapshot_rehash, new_snapshot); + + *snapshot = new_snapshot; + new_snapshot = NULL; + cleanup: + if (new_snapshot != NULL) { + if (new_snapshot->tmp_filename_template != NULL) + talloc_free((char *)new_snapshot-> + tmp_filename_template); + talloc_free(new_snapshot); + } + + if ((*snapshot) != NULL) { + return 0; + } else { + return -1; + } +} + +void snapshot_terminate(struct snapshot_t *snapshot) +{ + struct htable_iter iter; + snapshot_entry_t *entry = htable_first(&snapshot->ht, &iter); + while (entry != NULL) { + htable_delval(&snapshot->ht, &iter); + close(entry->fd); + talloc_free(entry); + entry = htable_next(&snapshot->ht, &iter); + } +} + +int snapshot_create(struct snapshot_t *snapshot, const char *filename) +{ + int ret = -1; + char buffer[4096]; + char tmp_file_name[_POSIX_PATH_MAX]; + int fd_in = -1; + int fd_out = -1; + snapshot_entry_t *entry = NULL; + + if (filename == NULL) + return 0; + + strlcpy(tmp_file_name, snapshot->tmp_filename_template, + _POSIX_PATH_MAX); + + fd_in = open(filename, O_RDONLY); + if (fd_in == -1) { + fprintf(stderr, ERRSTR "cannot open file %s\n", filename); + goto cleanup; + } + + fd_out = mkstemp(tmp_file_name); + if (fd_out == -1) { + int err = errno; + fprintf(stderr, ERRSTR "cannot create temp file '%s' : %s\n", + tmp_file_name, strerror(err)); + goto cleanup; + } + // After opening the output file, unlink it to make it anonymous + unlink(tmp_file_name); + + for (;;) { + int byteRead = read(fd_in, buffer, sizeof(buffer)); + int bytesWritten; + if (byteRead == 0) { + break; + } else if (byteRead == -1) { + int err = errno; + fprintf(stderr, ERRSTR " reading %s failed %s\n", + filename, strerror(err)); + goto cleanup; + } else { + bytesWritten = write(fd_out, buffer, byteRead); + if (bytesWritten != byteRead) { + int err = errno; + fprintf(stderr, + ERRSTR " writing %s failed %s\n", + tmp_file_name, strerror(err)); + goto cleanup; + } + } + } + + lseek(fd_out, 0, SEEK_SET); + + close(fd_in); + fd_in = -1; + + entry = snapshot_find(snapshot, filename); + if (entry != NULL) { + close(entry->fd); + entry->fd = fd_out; + } else { + if (snapshot_add_entry(snapshot, filename, fd_out) != 0) + goto cleanup; + } + + fd_out = -1; + ret = 0; + entry = NULL; + + cleanup: + if (fd_in != -1) + close(fd_in); + + if (fd_out != -1) + close(fd_out); + + if (entry != NULL) { + if (entry->fd != -1) + close(entry->fd); + talloc_free(entry); + } + + return ret; +} + +int snapshot_first(struct snapshot_t *snapshot, struct htable_iter *iter, + int *fd, const char **file_name) +{ + snapshot_entry_t *entry = htable_first(&snapshot->ht, iter); + if (entry == NULL) { + return -1; + } else { + *fd = entry->fd; + *file_name = entry->name; + return 0; + } +} + +int snapshot_next(struct snapshot_t *snapshot, struct htable_iter *iter, + int *fd, const char **file_name) +{ + snapshot_entry_t *entry = htable_next(&snapshot->ht, iter); + if (entry == NULL) { + return -1; + } else { + *fd = entry->fd; + *file_name = entry->name; + return 0; + } +} + +int snapshot_restore_entry(struct snapshot_t *snapshot, int fd, + const char *file_name) +{ + int ret = snapshot_add_entry(snapshot, file_name, fd); + if (ret < 0) + return ret; + + return 0; +} + +size_t snapshot_entry_count(struct snapshot_t * snapshot) +{ + struct htable_iter iter; + size_t count = 0; + snapshot_entry_t *entry = htable_first(&snapshot->ht, &iter); + + while (entry != NULL) { + entry = htable_next(&snapshot->ht, &iter); + count++; + } + + return count; +} + +int snapshot_lookup_filename(struct snapshot_t *snapshot, const char *file_name, + char **snapshot_file_name) +{ + int ret = -1; + char fd_path[128]; + char *new_file_name = NULL; + snapshot_entry_t *entry = snapshot_find(snapshot, file_name); + if (entry == NULL) + goto cleanup; + + if (snapshot_file_name_from_fd(entry->fd, fd_path, sizeof(fd_path)) < 0) + goto cleanup; + + new_file_name = talloc_strdup(snapshot->pool, fd_path); + if (new_file_name == NULL) + goto cleanup; + + *snapshot_file_name = new_file_name; + new_file_name = NULL; + + ret = 0; + + cleanup: + if (new_file_name != NULL) + talloc_free(new_file_name); + + return ret; +} diff --git a/src/common/snapshot.h b/src/common/snapshot.h new file mode 100644 index 00000000..b55a8481 --- /dev/null +++ b/src/common/snapshot.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 Microsoft Corporation + * + * Author: Alan Jowett + * + * This file is part of ocserv. + * + * ocserv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ + +#ifndef SNAPSHOT_H +#define SNAPSHOT_H + +struct snapshot_t; +struct snapshot_iter_t; + +/** + * snapshot_init - initialize the file snapshot collection + * @pool: talloc context + * @snapshot: file snapshot created + * @path: path in the filesystem + */ +int snapshot_init(void *pool, struct snapshot_t **snapshot, const char *path); + +/** + * snapshot_terminate - release the file snapshot collection + * @snapshot: file snapshot collection + */ +void snapshot_terminate(struct snapshot_t *snapshot); + +/** + * snapshot_create - create a snapshot of a file and add it to the collection + * @snapshot: file snapshot collection + * @filename: file to snapshot + * Note: Replaces any files in the collection with the same name. + */ +int snapshot_create(struct snapshot_t *snapshot, const char *filename); + +/** + * snapshot_entry_count - get a count of the entries + * @snapshot: file snapshot collection + */ +size_t snapshot_entry_count(struct snapshot_t *snapshot); + +/** + * snapshot_first - start iterating over the snapshot entries + * @snapshot: file snapshot collection + * @iter: opaque iterator + * @fd: fd found + * @file_name: filename found + * @return: 0 on success, non-zero on failure + */ +int snapshot_first(struct snapshot_t *snapshot, struct htable_iter *iter, + int *fd, const char **file_name); + +/** + * snapshot_first - continue iterating over the snapshot entries + * @snapshot: file snapshot collection + * @iter: opaque iterator + * @fd: fd found + * @file_name: filename found + * @return: 0 on success, non-zero on failure + */ +int snapshot_next(struct snapshot_t *snapshot, struct htable_iter *iter, + int *fd, const char **file_name); + +/** + * snapshot_restore - put an entry back into the snapshot collection + * @snapshot: file snapshot collection + * @fd: fd to add + * @file_name: filename to add + */ +int snapshot_restore_entry(struct snapshot_t *snapshot, int fd, + const char *file_name); + +int snapshot_lookup_filename(struct snapshot_t *snapshot, const char *file_name, + char **snapshot_file_name); + +#endif diff --git a/src/common/system.h b/src/common/system.h index 18b52025..5c4b1013 100644 --- a/src/common/system.h +++ b/src/common/system.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. @@ -43,4 +43,6 @@ SIGHANDLER_T ocsignal(int signum, SIGHANDLER_T handler); int check_upeer_id(const char *mod, int debug, int cfg, uid_t uid, uid_t gid, uid_t *ruid, pid_t *pid); + + #endif diff --git a/src/config.c b/src/config.c index 0625cd12..db19b7d7 100644 --- a/src/config.c +++ b/src/config.c @@ -53,9 +53,11 @@ #include #include #include +#include #include "common-config.h" #include +#include #define OLD_DEFAULT_CFG_FILE "/etc/ocserv.conf" #define DEFAULT_CFG_FILE "/etc/ocserv/ocserv.conf" @@ -133,6 +135,15 @@ static void check_cfg(vhost_cfg_st *vhost, vhost_cfg_st *defvhost, unsigned sile varname++; \ } +struct snapshot_t * config_snapshot = NULL; + +char ** pam_auth_group_list = NULL; +char ** gssapi_auth_group_list = NULL; +char ** plain_auth_group_list = NULL; +unsigned pam_auth_group_list_size = 0; +unsigned gssapi_auth_group_list_size = 0; +unsigned plain_auth_group_list_size = 0; + /* Parses the string ::1/prefix, to return prefix * and modify the string to contain the network only. @@ -183,6 +194,7 @@ static auth_types_st avail_auth_types[] = #endif }; + static void figure_auth_funcs(void *pool, const char *vhostname, struct perm_cfg_st *config, char **auth, unsigned auth_size, unsigned primary) @@ -767,10 +779,12 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co READ_STRING(vhost->perm_config.srk_pin_file); } else if (strcmp(name, "ca-cert") == 0) { READ_STRING(vhost->perm_config.ca); +#if !defined(OCSERV_WORKER_PROCESS) } else if (strcmp(name, "key-pin") == 0) { READ_STRING(vhost->perm_config.key_pin); } else if (strcmp(name, "srk-pin") == 0) { READ_STRING(vhost->perm_config.srk_pin); +#endif } else if (strcmp(name, "socket-file") == 0) { if (!PWARN_ON_VHOST_STRDUP(vhost->name, "socket-file", socket_file_prefix)) PREAD_STRING(pool, vhost->perm_config.socket_file_prefix); @@ -876,9 +890,11 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co READ_TF(config->enable_compression); } else if (strcmp(name, "compression-algo-priority") == 0) { if (!WARN_ON_VHOST_ONLY(vhost->name, "compression-algo-priority")) { +#if defined(OCSERV_WORKER_PROCESS) if (switch_comp_priority(pool, value) == 0) { fprintf(stderr, WARNSTR"invalid compression modstring %s\n", value); } +#endif } } else if (strcmp(name, "no-compress-limit") == 0) { READ_NUMERIC(config->no_compress_limit); @@ -1077,9 +1093,29 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co enum { CFG_FLAG_RELOAD = (1<<0), - CFG_FLAG_SECMOD = (1<<1) + CFG_FLAG_SECMOD = (1<<1), + CFG_FLAG_WORKER = (1<<2) }; +static void replace_file_with_snapshot(char ** file_name) +{ + char * snapshot_file_name; + if (*file_name == NULL) { + return; + } + + if (snapshot_lookup_filename( + config_snapshot, + *file_name, + &snapshot_file_name) < 0) { + fprintf(stderr, ERRSTR"cannot find snapshot for file %s\n", *file_name); + exit(1); + } + + talloc_free(*file_name); + *file_name = snapshot_file_name; +} + static void parse_cfg_file(void *pool, const char *file, struct list_head *head, unsigned flags) { @@ -1094,15 +1130,62 @@ static void parse_cfg_file(void *pool, const char *file, struct list_head *head, ctx.reload = (flags&CFG_FLAG_RELOAD)?1:0; ctx.head = head; - /* parse configuration - */ - ret = ini_parse(file, cfg_ini_handler, &ctx); - if (ret < 0 && file != NULL && strcmp(file, DEFAULT_CFG_FILE) == 0) - ret = ini_parse(OLD_DEFAULT_CFG_FILE, cfg_ini_handler, &ctx); + // Worker always reads from snapshot + if ((flags & CFG_FLAG_WORKER) == CFG_FLAG_WORKER) { + char * snapshot_file = NULL; + + if ((snapshot_lookup_filename(config_snapshot, file, &snapshot_file) < 0) && + (snapshot_lookup_filename(config_snapshot, OLD_DEFAULT_CFG_FILE, &snapshot_file) < 0)) { + fprintf(stderr, ERRSTR"snapshot_lookup failed for file %s\n", file); + exit(1); + } + + ret = ini_parse(snapshot_file, cfg_ini_handler, &ctx); + if (ret < 0) { + fprintf(stderr, ERRSTR"cannot load config file %s\n", file); + exit(1); + } + talloc_free(snapshot_file); + + // Walk the config, replacing filename with the snapshot equivalent + list_for_each(head, vhost, list) { + size_t index; + replace_file_with_snapshot(&vhost->perm_config.dh_params_file); + replace_file_with_snapshot(&vhost->perm_config.config->ocsp_response); + for (index = 0; index < vhost->perm_config.cert_size; index ++) { + replace_file_with_snapshot(&vhost->perm_config.cert[index]); + } + } + } else { + const char * cfg_file = file; + + /* parse configuration + */ + ret = ini_parse(cfg_file, cfg_ini_handler, &ctx); + if (ret < 0 && file != NULL && strcmp(file, DEFAULT_CFG_FILE) == 0) { + cfg_file = OLD_DEFAULT_CFG_FILE; + ret = ini_parse(cfg_file, cfg_ini_handler, &ctx); + } + + if (ret < 0) { + fprintf(stderr, ERRSTR"cannot load config file %s\n", cfg_file); + exit(1); + } + + ret = snapshot_create(config_snapshot, cfg_file); + if (ret < 0){ + fprintf(stderr, ERRSTR"cannot snapshot config file %s\n", cfg_file); + exit(1); + } + list_for_each(head, vhost, list) { + size_t index; + snapshot_create(config_snapshot, vhost->perm_config.dh_params_file); + snapshot_create(config_snapshot, vhost->perm_config.config->ocsp_response); + for (index = 0; index < vhost->perm_config.cert_size; index ++) { + snapshot_create(config_snapshot, vhost->perm_config.cert[index]); + } + } - if (ret < 0) { - fprintf(stderr, ERRSTR"cannot load config file %s\n", file); - exit(1); } /* apply configuration not yet applied. @@ -1128,6 +1211,20 @@ static void parse_cfg_file(void *pool, const char *file, struct list_head *head, if (vhost->auto_select_group != 0 && vhost->perm_config.auth[0].amod != NULL && vhost->perm_config.auth[0].amod->group_list != NULL) { vhost->perm_config.auth[0].amod->group_list(config, vhost->perm_config.auth[0].additional, &config->group_list, &config->group_list_size); + switch (vhost->perm_config.auth[0].amod->type) { + case AUTH_TYPE_PAM|AUTH_TYPE_USERNAME_PASS: + pam_auth_group_list = config->group_list; + pam_auth_group_list_size = config->group_list_size; + break; + case AUTH_TYPE_GSSAPI: + gssapi_auth_group_list = config->group_list; + gssapi_auth_group_list_size = config->group_list_size; + break; + case AUTH_TYPE_PLAIN|AUTH_TYPE_USERNAME_PASS: + plain_auth_group_list = config->group_list; + plain_auth_group_list_size = config->group_list_size; + break; + } } if (vhost->expose_iroutes != 0) { @@ -1428,7 +1525,7 @@ void usage(void) fprintf(stderr, "Please send bug reports to: "PACKAGE_BUGREPORT"\n"); } -int cmd_parser (void *pool, int argc, char **argv, struct list_head *head) +int cmd_parser (void *pool, int argc, char **argv, struct list_head *head, bool worker) { unsigned test_only = 0; int c; @@ -1481,7 +1578,7 @@ int cmd_parser (void *pool, int argc, char **argv, struct list_head *head) exit(1); } - parse_cfg_file(pool, cfg_file, head, 0); + parse_cfg_file(pool, cfg_file, head, worker ? CFG_FLAG_WORKER : 0); if (test_only) exit(0); @@ -1696,3 +1793,154 @@ void clear_old_configs(struct list_head *head) } } } + +// ocserv and ocserv-worker both load and parse the configuration files. +// As part of the process of loading the config files, auth / acct methods +// are enabled based on the content of the acct_mod_st and auth_mod_st tables. +// These auth tables are present in the auth sub-subsystem. Linking against +// the auth subsystem pulls in a very large set of dependent binaries which +// increases the overall memory footprint. To avoid this, we provide stub +// versions of acct_mod_st and auth_mod_st tables that the ocserv-worker +// process can link against. +#if defined(OCSERV_WORKER_PROCESS) + +// Group information is populated by the auth subsystem. +// When compiles as part of ocserv-worker, the auth subsystem is not present. +// To work around this, the group information is passed from ocserv-main to +// ocserv-worker, which then caches it and returns it when queried. +static void pam_group_list(void *pool, void *_additional, char ***groupname, unsigned *groupname_size) +{ + *groupname = pam_auth_group_list; + *groupname_size = pam_auth_group_list_size; +} + +static void gssapi_group_list(void *pool, void *_additional, char ***groupname, unsigned *groupname_size) +{ + *groupname = gssapi_auth_group_list; + *groupname_size = gssapi_auth_group_list_size; +} + +static void plain_group_list(void *pool, void *_additional, char ***groupname, unsigned *groupname_size) +{ + *groupname = plain_auth_group_list; + *groupname_size = plain_auth_group_list_size; +} + +const struct acct_mod_st radius_acct_funcs = { + .type = ACCT_TYPE_RADIUS, + .auth_types = ALL_AUTH_TYPES, + .vhost_init = NULL, + .vhost_deinit = NULL, + .open_session = NULL, + .close_session = NULL, + .session_stats = NULL +}; + +const struct acct_mod_st pam_acct_funcs = { + .type = ACCT_TYPE_PAM, + .auth_types = ALL_AUTH_TYPES, + .open_session = NULL, + .close_session = NULL, +}; + +const struct auth_mod_st pam_auth_funcs = { + .type = AUTH_TYPE_PAM | AUTH_TYPE_USERNAME_PASS, + .auth_init = NULL, + .auth_deinit = NULL, + .auth_msg = NULL, + .auth_pass = NULL, + .auth_group = NULL, + .auth_user = NULL, + .group_list = pam_group_list +}; + +const struct auth_mod_st gssapi_auth_funcs = { + .type = AUTH_TYPE_GSSAPI, + .auth_init = NULL, + .auth_deinit = NULL, + .auth_msg = NULL, + .auth_pass = NULL, + .auth_user = NULL, + .auth_group = NULL, + .vhost_init = NULL, + .vhost_deinit = NULL, + .group_list = gssapi_group_list +}; + +const struct auth_mod_st plain_auth_funcs = { + .type = AUTH_TYPE_PLAIN | AUTH_TYPE_USERNAME_PASS, + .allows_retries = 1, + .vhost_init = NULL, + .auth_init = NULL, + .auth_deinit = NULL, + .auth_msg = NULL, + .auth_pass = NULL, + .auth_user = NULL, + .auth_group = NULL, + .group_list = plain_group_list +}; + + +const struct auth_mod_st radius_auth_funcs = { + .type = AUTH_TYPE_RADIUS | AUTH_TYPE_USERNAME_PASS, + .allows_retries = 1, + .vhost_init = NULL, + .vhost_deinit = NULL, + .auth_init = NULL, + .auth_deinit = NULL, + .auth_msg = NULL, + .auth_pass = NULL, + .auth_user = NULL, + .auth_group = NULL, + .group_list = NULL +}; + +const struct auth_mod_st oidc_auth_funcs = { + .type = AUTH_TYPE_OIDC, + .allows_retries = 1, + .vhost_init = NULL, + .vhost_deinit = NULL, + .auth_init = NULL, + .auth_deinit = NULL, + .auth_msg = NULL, + .auth_pass = NULL, + .auth_user = NULL, + .auth_group = NULL, + .group_list = NULL +}; + + +#else +int get_cert_names(struct worker_st * ws, const gnutls_datum_t * raw) +{ + return -1; +} +#endif + +char secmod_socket_file_name_socket_file[_POSIX_PATH_MAX] = {0}; + +void restore_secmod_socket_file_name(const char * save_path) +{ + strlcpy(secmod_socket_file_name_socket_file, save_path, sizeof(secmod_socket_file_name_socket_file)); +} + +/* Creates a permanent filename to use for secmod to main communication + */ +const char *secmod_socket_file_name(struct perm_cfg_st *perm_config) +{ + unsigned int rnd; + int ret; + + if (secmod_socket_file_name_socket_file[0] != 0) + return secmod_socket_file_name_socket_file; + + ret = gnutls_rnd(GNUTLS_RND_NONCE, &rnd, sizeof(rnd)); + if (ret < 0) + exit(1); + + /* make socket name */ + snprintf(secmod_socket_file_name_socket_file, sizeof(secmod_socket_file_name_socket_file), "%s.%x", + perm_config->socket_file_prefix, rnd); + + return secmod_socket_file_name_socket_file; +} diff --git a/src/defs.h b/src/defs.h index 5f2430d1..34cb8006 100644 --- a/src/defs.h +++ b/src/defs.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/gettime.h b/src/gettime.h index a0f6847f..83989a9b 100644 --- a/src/gettime.h +++ b/src/gettime.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/html.h b/src/html.h index 4627700b..1db9b280 100644 --- a/src/html.h +++ b/src/html.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/icmp-ping.h b/src/icmp-ping.h index 4e6ce439..c9259f8a 100644 --- a/src/icmp-ping.h +++ b/src/icmp-ping.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/ip-lease.h b/src/ip-lease.h index 89fa9ca8..2f5a1423 100644 --- a/src/ip-lease.h +++ b/src/ip-lease.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/ip-util.h b/src/ip-util.h index e5456499..c61ef59f 100644 --- a/src/ip-util.h +++ b/src/ip-util.h @@ -6,7 +6,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/ipc.proto b/src/ipc.proto index d1108b8f..f37024d7 100644 --- a/src/ipc.proto +++ b/src/ipc.proto @@ -147,6 +147,37 @@ message udp_fd_msg required bytes data = 2; /* the first packet in the fd */ } +message snapshot_entry_msg +{ + required uint32 file_descriptor = 1; + required string file_name = 2; +} + +/* WORKER_STARTUP */ +message worker_startup_msg +{ + enum CONN_TYPE { + TCP = 0; + UDP = 1; + UNIX = 2; + } + + required bytes secmod_addr = 1; + required uint32 cmd_fd = 2; + required uint32 conn_fd = 3; + required CONN_TYPE conn_type = 4; + required string remote_ip_str = 5; + required string our_ip_str = 6; + required uint64 session_start_time = 7; + required bytes remote_addr = 8; + required bytes our_addr = 9; + required bytes sec_auth_init_hmac = 10; + repeated snapshot_entry_msg snapshot_entries = 11; + repeated string pam_auth_group_list = 12; + repeated string gssapi_auth_group_list = 13; + repeated string plain_auth_group_list = 14; +} + /* SESSION_INFO */ message session_info_msg { @@ -341,3 +372,10 @@ message secm_list_cookies_reply_msg /* SECM_BAN_IP: sent from sec-mod to main */ /* same as: ban_ip_msg */ + + +/* snapshot_state */ +message snapshot_state_msg +{ + +} \ No newline at end of file diff --git a/src/isolate.c b/src/isolate.c new file mode 100644 index 00000000..4bf1d857 --- /dev/null +++ b/src/isolate.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include +#include + + +#include +#include + +void init_fd_limits_default(main_server_st * s) +{ +#ifdef RLIMIT_NOFILE + int ret = getrlimit(RLIMIT_NOFILE, &s->fd_limits_default_set); + if (ret < 0) { + fprintf(stderr, "error in getrlimit: %s\n", strerror(errno)); + exit(1); + } +#endif +} + +/* Adjusts the file descriptor limits for the main or worker processes + */ +void update_fd_limits(main_server_st * s, unsigned main) +{ +#ifdef RLIMIT_NOFILE + struct rlimit new_set; + unsigned max; + int ret; + + if (main) { + if (GETCONFIG(s)->max_clients > 0 + && GETCONFIG(s)->max_clients > + s->fd_limits_default_set.rlim_cur) + max = GETCONFIG(s)->max_clients + 32; + else + max = MAX(4 * 1024, s->fd_limits_default_set.rlim_cur); + + if (max > s->fd_limits_default_set.rlim_cur) { + new_set.rlim_cur = max; + new_set.rlim_max = s->fd_limits_default_set.rlim_max; + ret = setrlimit(RLIMIT_NOFILE, &new_set); + if (ret < 0) { + fprintf(stderr, + "error in setrlimit(%u): %s (cur: %u)\n", + max, strerror(errno), + (unsigned)s->fd_limits_default_set. + rlim_cur); + } + } + } else { + /* set limits for worker processes */ + ret = setrlimit(RLIMIT_NOFILE, &s->fd_limits_default_set); + if (ret < 0) { + mslog(s, NULL, LOG_INFO, + "cannot update file limit(%u): %s\n", + (unsigned)s->fd_limits_default_set.rlim_cur, + strerror(errno)); + } + } +#endif +} + +void set_self_oom_score_adj(main_server_st * s) +{ +#ifdef __linux__ + const char proc_self_oom_adj_score_path[] = "/proc/self/oom_score_adj"; + const char oom_adj_score_value[] = "1000"; + size_t written = 0; + int fd; + + fd = open(proc_self_oom_adj_score_path, O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd == -1) { + int e = errno; + mslog(s, NULL, LOG_ERR, "cannot open %s: %s", + proc_self_oom_adj_score_path, strerror(e)); + goto cleanup; + } + + written = write(fd, oom_adj_score_value, sizeof(oom_adj_score_value)); + if (written != sizeof(oom_adj_score_value)) { + int e = errno; + mslog(s, NULL, LOG_ERR, "cannot write %s: %s", + proc_self_oom_adj_score_path, strerror(e)); + goto cleanup; + } + + cleanup: + if (fd) { + close(fd); + } +#endif +} + + +void drop_privileges(main_server_st * s) +{ + int ret, e; + struct rlimit rl; + + if (GETPCONFIG(s)->chroot_dir) { + ret = chdir(GETPCONFIG(s)->chroot_dir); + if (ret != 0) { + e = errno; + mslog(s, NULL, LOG_ERR, "cannot chdir to %s: %s", + GETPCONFIG(s)->chroot_dir, strerror(e)); + exit(1); + } + + ret = chroot(GETPCONFIG(s)->chroot_dir); + if (ret != 0) { + e = errno; + mslog(s, NULL, LOG_ERR, "cannot chroot to %s: %s", + GETPCONFIG(s)->chroot_dir, strerror(e)); + exit(1); + } + } + + if (GETPCONFIG(s)->gid != -1 && (getgid() == 0 || getegid() == 0)) { + ret = setgid(GETPCONFIG(s)->gid); + if (ret < 0) { + e = errno; + mslog(s, NULL, LOG_ERR, "cannot set gid to %d: %s\n", + (int)GETPCONFIG(s)->gid, strerror(e)); + exit(1); + } + + ret = setgroups(1, &GETPCONFIG(s)->gid); + if (ret < 0) { + e = errno; + mslog(s, NULL, LOG_ERR, "cannot set groups to %d: %s\n", + (int)GETPCONFIG(s)->gid, strerror(e)); + exit(1); + } + } + + if (GETPCONFIG(s)->uid != -1 && (getuid() == 0 || geteuid() == 0)) { + ret = setuid(GETPCONFIG(s)->uid); + if (ret < 0) { + e = errno; + mslog(s, NULL, LOG_ERR, "cannot set uid to %d: %s\n", + (int)GETPCONFIG(s)->uid, strerror(e)); + exit(1); + + } + } + + update_fd_limits(s, 0); + + rl.rlim_cur = 0; + rl.rlim_max = 0; + ret = setrlimit(RLIMIT_NPROC, &rl); + if (ret < 0) { + e = errno; + mslog(s, NULL, LOG_ERR, "cannot enforce NPROC limit: %s\n", + strerror(e)); + } +} \ No newline at end of file diff --git a/src/isolate.h b/src/isolate.h new file mode 100644 index 00000000..cdc6b87a --- /dev/null +++ b/src/isolate.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ISOLATE_H +# define ISOLATE_H + + +void init_fd_limits_default(struct main_server_st * s); + +/* Adjusts the file descriptor limits for the main or worker processes + */ +void update_fd_limits(struct main_server_st * s, unsigned main); + +void set_self_oom_score_adj(struct main_server_st * s); + +void drop_privileges(struct main_server_st * s); + +#endif \ No newline at end of file diff --git a/src/main-ban.h b/src/main-ban.h index 485f1492..609a8eb7 100644 --- a/src/main-ban.h +++ b/src/main-ban.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/main-sec-mod-cmd.c b/src/main-sec-mod-cmd.c index ffa065aa..1791e1b3 100644 --- a/src/main-sec-mod-cmd.c +++ b/src/main-sec-mod-cmd.c @@ -713,28 +713,6 @@ int secmod_reload(main_server_st * s) return 0; } -/* Creates a permanent filename to use for secmod to main communication - */ -const char *secmod_socket_file_name(struct perm_cfg_st *perm_config) -{ - unsigned int rnd; - int ret; - static char socket_file[_POSIX_PATH_MAX] = {0}; - - if (socket_file[0] != 0) - return socket_file; - - ret = gnutls_rnd(GNUTLS_RND_NONCE, &rnd, sizeof(rnd)); - if (ret < 0) - exit(1); - - /* make socket name */ - snprintf(socket_file, sizeof(socket_file), "%s.%x", - perm_config->socket_file_prefix, rnd); - - return socket_file; -} - static void clear_unneeded_mem(struct list_head *vconfig) { vhost_cfg_st *vhost = NULL; diff --git a/src/main.c b/src/main.c index 7945abfa..53db7289 100644 --- a/src/main.c +++ b/src/main.c @@ -63,6 +63,9 @@ #include #include #include +#include +#include +#include #ifdef HAVE_GSSAPI # include @@ -71,8 +74,15 @@ extern const ASN1_ARRAY_TYPE kkdcp_asn1_tab[]; ASN1_TYPE _kkdcp_pkix1_asn = ASN1_TYPE_EMPTY; #endif -int saved_argc = 0; -char **saved_argv = NULL; +// Name of environment variable used to pass worker_startup_msg +// between ocserv-main and ocserv-worker. +#define OCSERV_ENV_WORKER_STARTUP_MSG "OCSERV_WORKER_STARTUP_MSG" + +extern struct snapshot_t * config_snapshot; + + +int worker_argc = 0; +char **worker_argv = NULL; static void listen_watcher_cb (EV_P_ ev_io *w, int revents); @@ -91,6 +101,8 @@ ev_signal int_sig_watcher; ev_signal reload_sig_watcher; ev_child child_watcher; +static int set_env_from_ws(main_server_st * ws); + static void add_listener(void *pool, struct listen_list_st *list, int fd, int family, int socktype, int protocol, struct sockaddr* addr, socklen_t addr_len) @@ -467,139 +479,6 @@ int y; return; } -/* Adjusts the file descriptor limits for the main or worker processes - */ -static void update_fd_limits(main_server_st *s, unsigned main) -{ -#ifdef RLIMIT_NOFILE - static struct rlimit def_set; - struct rlimit new_set; - unsigned max; - int ret; - - if (main) { - ret = getrlimit(RLIMIT_NOFILE, &def_set); - if (ret < 0) { - fprintf(stderr, "error in getrlimit: %s\n", strerror(errno)); - exit(1); - } - - if (GETCONFIG(s)->max_clients > 0 && GETCONFIG(s)->max_clients > def_set.rlim_cur) - max = GETCONFIG(s)->max_clients + 32; - else - max = MAX(4*1024, def_set.rlim_cur); - - if (max > def_set.rlim_cur) { - new_set.rlim_cur = max; - new_set.rlim_max = def_set.rlim_max; - ret = setrlimit(RLIMIT_NOFILE, &new_set); - if (ret < 0) { - fprintf(stderr, "error in setrlimit(%u): %s (cur: %u)\n", max, strerror(errno), (unsigned)def_set.rlim_cur); - } - } - } else { - /* set limits for worker processes */ - ret = setrlimit(RLIMIT_NOFILE, &def_set); - if (ret < 0) { - mslog(s, NULL, LOG_INFO, "cannot update file limit(%u): %s\n", (unsigned)def_set.rlim_cur, strerror(errno)); - } - } -#endif -} - - -static void drop_privileges(main_server_st* s) -{ - int ret, e; - struct rlimit rl; - - if (GETPCONFIG(s)->chroot_dir) { - ret = chdir(GETPCONFIG(s)->chroot_dir); - if (ret != 0) { - e = errno; - mslog(s, NULL, LOG_ERR, "cannot chdir to %s: %s", GETPCONFIG(s)->chroot_dir, strerror(e)); - exit(1); - } - - ret = chroot(GETPCONFIG(s)->chroot_dir); - if (ret != 0) { - e = errno; - mslog(s, NULL, LOG_ERR, "cannot chroot to %s: %s", GETPCONFIG(s)->chroot_dir, strerror(e)); - exit(1); - } - } - - if (GETPCONFIG(s)->gid != -1 && (getgid() == 0 || getegid() == 0)) { - ret = setgid(GETPCONFIG(s)->gid); - if (ret < 0) { - e = errno; - mslog(s, NULL, LOG_ERR, "cannot set gid to %d: %s\n", - (int) GETPCONFIG(s)->gid, strerror(e)); - exit(1); - } - - ret = setgroups(1, &GETPCONFIG(s)->gid); - if (ret < 0) { - e = errno; - mslog(s, NULL, LOG_ERR, "cannot set groups to %d: %s\n", - (int) GETPCONFIG(s)->gid, strerror(e)); - exit(1); - } - } - - if (GETPCONFIG(s)->uid != -1 && (getuid() == 0 || geteuid() == 0)) { - ret = setuid(GETPCONFIG(s)->uid); - if (ret < 0) { - e = errno; - mslog(s, NULL, LOG_ERR, "cannot set uid to %d: %s\n", - (int) GETPCONFIG(s)->uid, strerror(e)); - exit(1); - - } - } - - update_fd_limits(s, 0); - - rl.rlim_cur = 0; - rl.rlim_max = 0; - ret = setrlimit(RLIMIT_NPROC, &rl); - if (ret < 0) { - e = errno; - mslog(s, NULL, LOG_ERR, "cannot enforce NPROC limit: %s\n", - strerror(e)); - } -} - -void set_self_oom_score_adj(main_server_st* s) -{ -#ifdef __linux__ - const char proc_self_oom_adj_score_path[] = "/proc/self/oom_score_adj"; - const char oom_adj_score_value[] = "1000"; - size_t written = 0; - int fd; - - fd = open(proc_self_oom_adj_score_path, O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (fd == -1) { - int e = errno; - mslog(s, NULL, LOG_ERR, "cannot open %s: %s", proc_self_oom_adj_score_path, strerror(e)); - goto cleanup; - } - - written = write(fd, oom_adj_score_value, sizeof(oom_adj_score_value)); - if (written != sizeof(oom_adj_score_value)) { - int e = errno; - mslog(s, NULL, LOG_ERR, "cannot write %s: %s", proc_self_oom_adj_score_path, strerror(e)); - goto cleanup; - } - -cleanup: - if (fd) { - close(fd); - } -#endif -} - - /* clears the server listen_list and proc_list. To be used after fork(). * It frees unused memory and descriptors. */ @@ -1122,6 +1001,9 @@ static void listen_watcher_cb (EV_P_ ev_io *w, int revents) int cmd_fd[2]; pid_t pid; hmac_component_st hmac_components[3]; + char path[_POSIX_PATH_MAX]; + char worker_path[_POSIX_PATH_MAX]; + size_t path_length; if (ltmp->sock_type == SOCK_TYPE_TCP || ltmp->sock_type == SOCK_TYPE_UNIX) { /* connection on TCP port */ @@ -1200,6 +1082,7 @@ static void listen_watcher_cb (EV_P_ ev_io *w, int revents) ws->cmd_fd = cmd_fd[1]; ws->tun_fd = -1; ws->dtls_tptr.fd = -1; + set_cloexec_flag(fd, false); ws->conn_fd = fd; ws->conn_type = stype; ws->session_start_time = time(0); @@ -1219,22 +1102,23 @@ static void listen_watcher_cb (EV_P_ ev_io *w, int revents) // Clear the HMAC key safe_memset((uint8_t*)s->hmac_key, 0, sizeof(s->hmac_key)); - /* Drop privileges after this point */ - drop_privileges(s); + set_env_from_ws(s); + path_length = readlink("/proc/self/exe", path, sizeof(path)-1); + if (path_length == -1) { + mslog(s, NULL, LOG_ERR, "readlink failed %s", strerror(ret)); + exit(1); + } + path[path_length] = '\0'; + if (snprintf(worker_path, sizeof(worker_path), "%s-worker", path) >= sizeof(worker_path)) { + mslog(s, NULL, LOG_ERR, "snprint of path %s and ocserv-worker failed", path); + exit(1); + } - /* creds and config are not allocated - * under s. - */ - talloc_free(s); -#ifdef HAVE_MALLOC_TRIM - /* try to return all the pages we've freed to - * the operating system, to prevent the child from - * accessing them. That's totally unreliable, so - * sensitive data have to be overwritten anyway. */ - malloc_trim(0); -#endif - vpn_server(ws); - exit(0); + worker_argv[0] = worker_path; + execv(worker_path, worker_argv); + ret = errno; + mslog(s, NULL, LOG_ERR, "exec %s failed %s", worker_path, strerror(ret)); + exit(1); } else if (pid == -1) { fork_failed: mslog(s, NULL, LOG_ERR, "fork failed"); @@ -1327,6 +1211,8 @@ static void syserr_cb (const char *msg) abort(); } +extern char secmod_socket_file_name_socket_file[_POSIX_PATH_MAX]; + int main(int argc, char** argv) { int e; @@ -1337,6 +1223,7 @@ int main(int argc, char** argv) void *main_pool, *config_pool; main_server_st *s; char *str; + int i; #ifdef DEBUG_LEAKS talloc_enable_leak_report_full(); @@ -1358,6 +1245,11 @@ int main(int argc, char** argv) exit(1); } + if (snapshot_init(config_pool, &config_snapshot, "/tmp/ocserv_") < 0) { + fprintf(stderr, "failed to init snapshot"); + exit(1); + } + s = talloc_zero(main_pool, main_server_st); if (s == NULL) { fprintf(stderr, "memory error\n"); @@ -1374,6 +1266,23 @@ int main(int argc, char** argv) exit(1); } + // getopt processing mutates argv. Save a copy to pass to the child. + worker_argc = argc; + worker_argv = talloc_zero_array(main_pool, char*, worker_argc + 1); + if (!worker_argv) { + fprintf(stderr, "memory error\n"); + exit(1); + } + for (i = 0; i < argc; i ++) { + worker_argv[i] = talloc_strdup(main_pool, argv[i]); + if (!worker_argv[i]) { + fprintf(stderr, "memory error\n"); + exit(1); + } + } + + init_fd_limits_default(s); + str = getenv("OCSERV_ALLOW_BROKEN_CLIENTS"); if (str && str[0] == '1' && str[1] == 0) allow_broken_clients = 1; @@ -1399,7 +1308,7 @@ int main(int argc, char** argv) } list_head_init(s->vconfig); - ret = cmd_parser(config_pool, argc, argv, s->vconfig); + ret = cmd_parser(config_pool, argc, argv, s->vconfig, false); if (ret < 0) { fprintf(stderr, "Error in arguments\n"); exit(1); @@ -1503,6 +1412,8 @@ int main(int argc, char** argv) } #endif + init_fd_limits_default(s); + /* increase the number of our allowed file descriptors */ update_fd_limits(s, 1); @@ -1559,6 +1470,8 @@ int main(int argc, char** argv) remove(GETPCONFIG(s)->occtl_socket_file); remove_pid_file(); + snapshot_terminate(config_snapshot); + clear_lists(s); clear_vhosts(s->vconfig); talloc_free(s->config_pool); @@ -1567,3 +1480,115 @@ int main(int argc, char** argv) return 0; } + +extern char ** pam_auth_group_list; +extern char ** gssapi_auth_group_list; +extern char ** plain_auth_group_list; +extern unsigned pam_auth_group_list_size; +extern unsigned gssapi_auth_group_list_size; +extern unsigned plain_auth_group_list_size; + +static int set_env_from_ws(main_server_st *s) +{ + worker_st *ws = s->ws; + WorkerStartupMsg msg = WORKER_STARTUP_MSG__INIT; + size_t msg_size; + uint8_t *msg_buffer = NULL; + size_t string_size = 0; + char *string_buffer = NULL; + int ret = 0; + SnapshotEntryMsg **entries = NULL; + SnapshotEntryMsg entry_template = SNAPSHOT_ENTRY_MSG__INIT; + size_t entry_count; + size_t index = 0; + struct htable_iter iter; + + msg.secmod_addr.data = (uint8_t *)&ws->secmod_addr; + msg.secmod_addr.len = ws->secmod_addr_len; + msg.cmd_fd = ws->cmd_fd; + msg.conn_fd = ws->conn_fd; + msg.conn_type = (WorkerStartupMsg__CONNTYPE)ws->conn_type; + msg.remote_ip_str = ws->remote_ip_str; + msg.our_ip_str = ws->our_ip_str; + msg.session_start_time = ws->session_start_time; + msg.remote_addr.data = (uint8_t *)&ws->remote_addr; + msg.remote_addr.len = ws->remote_addr_len; + msg.our_addr.data = (uint8_t *)&ws->our_addr; + msg.our_addr.len = ws->our_addr_len; + msg.sec_auth_init_hmac.data = (uint8_t *)ws->sec_auth_init_hmac; + msg.sec_auth_init_hmac.len = sizeof(ws->sec_auth_init_hmac); + + entry_count = snapshot_entry_count(config_snapshot); + + entries = talloc_zero_array(s, SnapshotEntryMsg *, entry_count); + if (!entries) + goto cleanup; + + for (index = 0; index < entry_count; index++) { + int fd; + const char *file_name; + if (index == 0) { + snapshot_first(config_snapshot, &iter, &fd, &file_name); + } else { + snapshot_next(config_snapshot, &iter, &fd, &file_name); + } + + entries[index] = talloc_zero(s, SnapshotEntryMsg); + *entries[index] = entry_template; + entries[index]->file_descriptor = fd; + entries[index]->file_name = (char *)file_name; + } + + msg.n_snapshot_entries = entry_count; + msg.snapshot_entries = entries; + + msg.n_gssapi_auth_group_list = gssapi_auth_group_list_size; + msg.gssapi_auth_group_list = gssapi_auth_group_list; + msg.n_pam_auth_group_list = pam_auth_group_list_size; + msg.pam_auth_group_list = pam_auth_group_list; + msg.n_plain_auth_group_list = plain_auth_group_list_size; + msg.plain_auth_group_list = plain_auth_group_list; + + msg_size = worker_startup_msg__get_packed_size(&msg); + if (msg_size == 0) { + mslog(s, NULL, LOG_ERR, "worker_startup_msg__get_packed_size failed\n"); + goto cleanup; + } + + msg_buffer = talloc_size(ws, msg_size); + if (!msg_buffer) { + mslog(s, NULL, LOG_ERR, "talloc_size failed\n"); + goto cleanup; + } + msg_size = worker_startup_msg__pack(&msg, msg_buffer); + if (msg_size == 0) { + mslog(s, NULL, LOG_ERR, "worker_startup_msg__pack failed\n"); + goto cleanup; + } + string_size = BASE64_ENCODE_RAW_LENGTH(msg_size) + 1; + string_buffer = talloc_size(ws, string_size); + if (!msg_buffer) { + mslog(s, NULL, LOG_ERR, "talloc_size failed\n"); + goto cleanup; + } + + oc_base64_encode((const char *)msg_buffer, msg_size, string_buffer, string_size); + if (setenv(OCSERV_ENV_WORKER_STARTUP_MSG, string_buffer, 1)) { + mslog(s, NULL, LOG_ERR, "setenv failed\n"); + goto cleanup; + } + + ret = 1; + +cleanup: + if (entries) + talloc_free(entries); + + if (msg_buffer) + talloc_free(msg_buffer); + + if (string_buffer) + talloc_free(string_buffer); + + return ret; +} diff --git a/src/main.h b/src/main.h index 8f19ad07..cc9f27c3 100644 --- a/src/main.h +++ b/src/main.h @@ -6,7 +6,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. @@ -52,7 +52,7 @@ extern ev_timer maintainance_watcher; #define MAIN_MAINTENANCE_TIME (900) -int cmd_parser (void *pool, int argc, char **argv, struct list_head *head); +int cmd_parser (void *pool, int argc, char **argv, struct list_head *head, bool worker); struct listener_st { ev_io io; @@ -261,6 +261,10 @@ typedef struct main_server_st { /* used as temporary buffer (currently by forward_udp_to_owner) */ uint8_t msg_buffer[MAX_MSG_SIZE]; + +#ifdef RLIMIT_NOFILE + struct rlimit fd_limits_default_set; +#endif } main_server_st; void clear_lists(main_server_st *s); @@ -362,6 +366,7 @@ int send_socket_msg_to_worker(main_server_st* s, struct proc_st* proc, uint8_t c int secmod_reload(main_server_st * s); const char *secmod_socket_file_name(struct perm_cfg_st *perm_config); +void restore_secmod_socket_file_name(const char * save_path); void clear_vhosts(struct list_head *head); void request_reload(int signo); diff --git a/src/proc-search.h b/src/proc-search.h index e4fd2c3f..6e0f163e 100644 --- a/src/proc-search.h +++ b/src/proc-search.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/route-add.h b/src/route-add.h index 036367d1..ee5662de 100644 --- a/src/route-add.h +++ b/src/route-add.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/script-list.h b/src/script-list.h index b45aa83c..8820bb68 100644 --- a/src/script-list.h +++ b/src/script-list.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/sec-mod-acct.h b/src/sec-mod-acct.h index 12ba87ab..d7c1f4cc 100644 --- a/src/sec-mod-acct.h +++ b/src/sec-mod-acct.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/sec-mod-auth.h b/src/sec-mod-auth.h index 7d8681fd..18b62082 100644 --- a/src/sec-mod-auth.h +++ b/src/sec-mod-auth.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/sec-mod-resume.h b/src/sec-mod-resume.h index d86e3788..83e628e0 100644 --- a/src/sec-mod-resume.h +++ b/src/sec-mod-resume.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/sec-mod-sup-config.h b/src/sec-mod-sup-config.h index 8fb48af4..4bafc3ab 100644 --- a/src/sec-mod-sup-config.h +++ b/src/sec-mod-sup-config.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/sec-mod.h b/src/sec-mod.h index c6b5f44c..a3294d75 100644 --- a/src/sec-mod.h +++ b/src/sec-mod.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/setproctitle.h b/src/setproctitle.h index 18d1d466..d1ab5044 100644 --- a/src/setproctitle.h +++ b/src/setproctitle.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/str.h b/src/str.h index 782fa992..d9899e21 100644 --- a/src/str.h +++ b/src/str.h @@ -5,7 +5,7 @@ * * This file is part of GnuTLS. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/sup-config/file.h b/src/sup-config/file.h index cb1f5c77..241cdc04 100644 --- a/src/sup-config/file.h +++ b/src/sup-config/file.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/sup-config/radius.h b/src/sup-config/radius.h index 554e7ab9..80e447ad 100644 --- a/src/sup-config/radius.h +++ b/src/sup-config/radius.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/tlslib.h b/src/tlslib.h index c96c6377..63488c18 100644 --- a/src/tlslib.h +++ b/src/tlslib.h @@ -6,7 +6,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/tun.h b/src/tun.h index 9e05845b..9c4bf41d 100644 --- a/src/tun.h +++ b/src/tun.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/vhost.h b/src/vhost.h index 32c5b789..deafa158 100644 --- a/src/vhost.h +++ b/src/vhost.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/vpn.h b/src/vpn.h index 7fe8612d..8b3766af 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/worker-auth.c b/src/worker-auth.c index c2cc03b5..40ea3a7e 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -1718,3 +1718,4 @@ int post_auth_handler(worker_st * ws, unsigned http_ver) talloc_free(msg); return ret; } + diff --git a/src/worker-bandwidth.h b/src/worker-bandwidth.h index c5096582..3e1c369f 100644 --- a/src/worker-bandwidth.h +++ b/src/worker-bandwidth.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/src/worker-privs.c b/src/worker-privs.c index 9425bb29..6640b5ae 100644 --- a/src/worker-privs.c +++ b/src/worker-privs.c @@ -29,6 +29,7 @@ #include #include #include +#include #include /* libseccomp 2.4.2 broke accidentally the API. Work around it. */ @@ -45,16 +46,46 @@ #ifdef USE_SECCOMP_TRAP # define _SECCOMP_ERR SCMP_ACT_TRAP +#include +#include +void sigsys_action(int sig, siginfo_t * info, void* ucontext) +{ + char * call_addr = *backtrace_symbols(&info->si_call_addr, 1); + fprintf(stderr, "Function %s called disabled syscall %d\n", call_addr, info->si_syscall); + exit(1); +} + +int set_sigsys_handler(struct worker_st *ws) +{ + struct sigaction sa = {}; + + sa.sa_sigaction = sigsys_action; + sa.sa_flags = SA_SIGINFO; + + return sigaction(SIGSYS, &sa, NULL); +} #else # define _SECCOMP_ERR SCMP_ACT_ERRNO(ENOSYS) +int set_sigsys_handler(struct worker_st *ws) +{ + return 0; +} #endif + + int disable_system_calls(struct worker_st *ws) { int ret; scmp_filter_ctx ctx; vhost_cfg_st *vhost = NULL; + if (set_sigsys_handler(ws)) + { + oclog(ws, LOG_ERR, "set_sigsys_handler"); + return -1; + } + ctx = seccomp_init(_SECCOMP_ERR); if (ctx == NULL) { oclog(ws, LOG_DEBUG, "could not initialize seccomp"); @@ -130,6 +161,10 @@ int disable_system_calls(struct worker_st *ws) ADD_SYSCALL(socket, 0); ADD_SYSCALL(connect, 0); + ADD_SYSCALL(openat, 0); + ADD_SYSCALL(fstat, 0); + ADD_SYSCALL(lseek, 0); + ADD_SYSCALL(getsockopt, 0); ADD_SYSCALL(setsockopt, 0); diff --git a/src/worker.c b/src/worker.c new file mode 100644 index 00000000..319e011b --- /dev/null +++ b/src/worker.c @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include + +#include +#include "setproctitle.h" +#ifdef HAVE_LIBWRAP +#include +#endif + +#ifdef HAVE_LIBSYSTEMD +#include +#endif +#include +#include +#include +#include +#include + +#ifdef HAVE_GSSAPI +#include + +extern const ASN1_ARRAY_TYPE kkdcp_asn1_tab[]; +ASN1_TYPE _kkdcp_pkix1_asn = ASN1_TYPE_EMPTY; +#endif + +// Name of environment variable used to pass worker_startup_msg +// between ocserv-main and ocserv-worker. +#define OCSERV_ENV_WORKER_STARTUP_MSG "OCSERV_WORKER_STARTUP_MSG" + +extern struct snapshot_t *config_snapshot; + +int syslog_open = 0; +sigset_t sig_default_set; +static unsigned allow_broken_clients = 0; + +static int set_ws_from_env(worker_st * ws); + +extern char secmod_socket_file_name_socket_file[_POSIX_PATH_MAX]; + +int main(int argc, char **argv) +{ + int ret, flags; + void *worker_pool; + void *main_pool, *config_pool; + main_server_st *s; + worker_st *ws; + char *str; + +#ifdef DEBUG_LEAKS + talloc_enable_leak_report_full(); +#endif + + if (!getenv(OCSERV_ENV_WORKER_STARTUP_MSG)) { + fprintf(stderr, + "This application is part of ocserv and should not be run in isolation\n"); + exit(1); + } + + /* main pool */ + main_pool = talloc_init("main"); + if (main_pool == NULL) { + fprintf(stderr, "talloc init error\n"); + exit(1); + } + + config_pool = talloc_init("config"); + if (config_pool == NULL) { + fprintf(stderr, "talloc init error\n"); + exit(1); + } + + if (snapshot_init(config_pool, &config_snapshot, "/tmp/ocserv_") < 0) { + fprintf(stderr, "failed to init snapshot"); + exit(-1); + } + + s = talloc_zero(main_pool, main_server_st); + if (s == NULL) { + fprintf(stderr, "memory error\n"); + exit(1); + } + s->main_pool = main_pool; + s->config_pool = config_pool; + s->stats.start_time = s->stats.last_reset = time(0); + s->top_fd = -1; + s->ctl_fd = -1; + + worker_pool = talloc_init("worker"); + if (worker_pool == NULL) { + fprintf(stderr, "talloc init error\n"); + exit(1); + } + + s->ws = talloc_zero(worker_pool, worker_st); + ws = s->ws; + + if (ws == NULL) { + fprintf(stderr, "talloc init error\n"); + exit(1); + } + + if (!set_ws_from_env(ws)) { + return 1; + } + + restore_secmod_socket_file_name(ws->secmod_addr.sun_path); + + // Close stdout and stderr early to avoid spurious logs + /* we don't need them */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + + str = getenv("OCSERV_ALLOW_BROKEN_CLIENTS"); + if (str && str[0] == '1' && str[1] == 0) + allow_broken_clients = 1; + + sigemptyset(&sig_default_set); + + ocsignal(SIGPIPE, SIG_IGN); + + /* Initialize GnuTLS */ + tls_global_init(); + + /* load configuration */ + s->vconfig = talloc_zero(config_pool, struct list_head); + if (s->vconfig == NULL) { + fprintf(stderr, "memory error\n"); + exit(1); + } + list_head_init(s->vconfig); + + ret = cmd_parser(config_pool, argc, argv, s->vconfig, true); + if (ret < 0) { + fprintf(stderr, "Error in arguments\n"); + exit(1); + } + + snapshot_terminate(config_snapshot); + config_snapshot = NULL; + + flags = LOG_PID | LOG_NDELAY; +#ifdef LOG_PERROR + if (GETPCONFIG(s)->debug != 0) + flags |= LOG_PERROR; +#endif + openlog("ocserv", flags, LOG_DAEMON); + syslog_open = 1; +#ifdef HAVE_LIBWRAP + allow_severity = LOG_DAEMON | LOG_INFO; + deny_severity = LOG_DAEMON | LOG_WARNING; +#endif + +#ifdef HAVE_GSSAPI + /* Initialize kkdcp structures */ + ret = asn1_array2tree(kkdcp_asn1_tab, &_kkdcp_pkix1_asn, NULL); + if (ret != ASN1_SUCCESS) { + mslog(s, NULL, LOG_ERR, "KKDCP ASN.1 initialization error"); + exit(1); + } +#endif + + init_fd_limits_default(s); + + sigprocmask(SIG_SETMASK, &sig_default_set, NULL); + + setproctitle(PACKAGE_NAME "-worker"); + kill_on_parent_kill(SIGTERM); + + ws->main_pool = s->main_pool; + ws->vconfig = s->vconfig; + + ws->tun_fd = -1; + ws->dtls_tptr.fd = -1; + + /* Drop privileges after this point */ + drop_privileges(s); + + vpn_server(ws); + + return 0; +} + +extern char **pam_auth_group_list; +extern char **gssapi_auth_group_list; +extern char **plain_auth_group_list; +extern unsigned pam_auth_group_list_size; +extern unsigned gssapi_auth_group_list_size; +extern unsigned plain_auth_group_list_size; + +static int clone_array(void *pool, char **input_array, size_t input_array_size, + char ***output_array) +{ + int ret = 0; + int index; + char **array = talloc_zero_array(pool, char *, input_array_size); + if (array == NULL) { + goto cleanup; + } + + for (index = 0; index < input_array_size; index++) { + array[index] = talloc_strdup(pool, input_array[index]); + if (array[index] == NULL) { + goto cleanup; + } + } + + *output_array = array; + array = NULL; + ret = 1; + cleanup: + if (array != NULL) { + for (index = 0; index < input_array_size; index++) { + if (array[index] != NULL) { + talloc_free(array[index]); + } + } + talloc_free(array); + } + return ret; +} + +static int set_ws_from_env(worker_st * ws) +{ + PROTOBUF_ALLOCATOR(pa, ws); + WorkerStartupMsg *msg = NULL; + const char *string_buffer = getenv(OCSERV_ENV_WORKER_STARTUP_MSG); + size_t string_size = strlen(string_buffer); + size_t msg_size; + uint8_t *msg_buffer = NULL; + int ret = 0; + int tmp_fd = -1; + size_t index; + + if (string_buffer == NULL) { + fprintf(stderr, "environment variable not set\n"); + goto cleanup; + } + + if (!oc_base64_decode_alloc + (ws, string_buffer, string_size, (char **)&msg_buffer, &msg_size)) { + fprintf(stderr, "oc_base64_decode_alloc failed\n"); + goto cleanup; + } + + msg = worker_startup_msg__unpack(&pa, msg_size, msg_buffer); + if (!msg) { + fprintf(stderr, "worker_startup_msg__unpack failed\n"); + goto cleanup; + } + + if (msg->secmod_addr.len > sizeof(ws->secmod_addr)) { + fprintf(stderr, "msg->secmod_addr.len too large\n"); + goto cleanup; + } + if (msg->remote_addr.len > sizeof(ws->remote_addr)) { + fprintf(stderr, "msg->remote_addr.len too large\n"); + goto cleanup; + } + if (msg->our_addr.len > sizeof(ws->our_addr)) { + fprintf(stderr, "msg->our_addr.len too large\n"); + goto cleanup; + } + if (msg->sec_auth_init_hmac.len > sizeof(ws->sec_auth_init_hmac)) { + fprintf(stderr, "msg->sec_auth_init_hmac.len too large\n"); + goto cleanup; + } + + ws->secmod_addr_len = msg->secmod_addr.len; + memcpy(&ws->secmod_addr, msg->secmod_addr.data, msg->secmod_addr.len); + + ws->cmd_fd = msg->cmd_fd; + ws->conn_fd = msg->conn_fd; + ws->conn_type = (sock_type_t) msg->conn_type; + ws->session_start_time = msg->session_start_time; + ws->remote_addr_len = msg->remote_addr.len; + memcpy(&ws->remote_addr, msg->remote_addr.data, msg->remote_addr.len); + if (msg->our_addr.data != NULL) + memcpy(&ws->our_addr, msg->our_addr.data, msg->our_addr.len); + + memcpy((void *)ws->sec_auth_init_hmac, msg->sec_auth_init_hmac.data, + msg->sec_auth_init_hmac.len); + + strlcpy(ws->remote_ip_str, msg->remote_ip_str, + sizeof(ws->remote_ip_str)); + strlcpy(ws->our_ip_str, msg->our_ip_str, sizeof(ws->our_ip_str)); + + for (index = 0; index < msg->n_snapshot_entries; index++) { + int fd = msg->snapshot_entries[index]->file_descriptor; + const char *file_name = msg->snapshot_entries[index]->file_name; + if (snapshot_restore_entry(config_snapshot, fd, file_name) != 0) + goto cleanup; + } + + if (!clone_array + (ws, msg->pam_auth_group_list, msg->n_pam_auth_group_list, + &pam_auth_group_list)) + goto cleanup; + pam_auth_group_list_size = (unsigned)msg->n_pam_auth_group_list; + + if (!clone_array + (ws, msg->plain_auth_group_list, msg->n_plain_auth_group_list, + &plain_auth_group_list)) + goto cleanup; + plain_auth_group_list_size = (unsigned)msg->n_plain_auth_group_list; + + if (!clone_array + (ws, msg->gssapi_auth_group_list, msg->n_gssapi_auth_group_list, + &gssapi_auth_group_list)) + goto cleanup; + gssapi_auth_group_list_size = (unsigned)msg->n_gssapi_auth_group_list; + + ret = 1; + + cleanup: + if (tmp_fd != -1) + close(tmp_fd); + + if (msg_buffer) + talloc_free(msg_buffer); + + if (msg) + worker_startup_msg__free_unpacked(msg, &pa); + + return ret; +} diff --git a/src/worker.h b/src/worker.h index 783466b1..ef9b88de 100644 --- a/src/worker.h +++ b/src/worker.h @@ -5,7 +5,7 @@ * * This file is part of ocserv. * - * The GnuTLS is free software; you can redistribute it and/or + * ocserv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. diff --git a/tests/test-sighup b/tests/test-sighup index 307b7b87..cbcaafa1 100755 --- a/tests/test-sighup +++ b/tests/test-sighup @@ -49,14 +49,14 @@ echo -n "Connecting to obtain cookie (with certificate)... " echo ok sed -i 's/^auth = "certificate"/#auth = "certificate"/g' ${CONFIG} -sed -i 's|^#auth = "plain[.\/data/test1.passwd]"|auth = "plain[.\/data/test1.passwd]"|g' ${CONFIG} +sed -i 's/^#auth = "plain/auth = "plain/g' ${CONFIG} sleep 10 echo "Reloading server" kill -HUP $PID sleep 5 echo -n "Connecting to obtain cookie (with certificate)... " -( LD_PRELOAD=libsocket_wrapper.so $OPENCONNECT -q $ADDRESS:$PORT --sslkey ${srcdir}/certs/user-key.pem -c ${srcdir}/certs/user-cert.pem --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 --cookieonly /dev/null 2>&1 ) || +( LD_PRELOAD=libsocket_wrapper.so $OPENCONNECT -q $ADDRESS:$PORT --sslkey ${srcdir}/certs/user-key.pem -c ${srcdir}/certs/user-cert.pem --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 --cookieonly /dev/null 2>&1 ) && fail $PID "Could not connect with certificate!" echo ok