/* * SPDX-License-Identifier: ISC * * Copyright (c) 2019-2022 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * This is an open source non-commercial project. Dear PVS-Studio, please check it. * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com */ #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_STDBOOL_H # include #else # include "compat/stdbool.h" #endif #include #include #include #include #include #include #include #include #include #ifndef HAVE_GETADDRINFO # include "compat/getaddrinfo.h" #endif #include "pathnames.h" #include "sudo_compat.h" #include "sudo_debug.h" #include "sudo_eventlog.h" #include "sudo_fatal.h" #include "sudo_gettext.h" #include "sudo_iolog.h" #include "sudo_util.h" #include "logsrvd.h" #if defined(HAVE_OPENSSL) # define DEFAULT_CA_CERT_PATH "/etc/ssl/sudo/cacert.pem" # define DEFAULT_SERVER_CERT_PATH "/etc/ssl/sudo/certs/logsrvd_cert.pem" # define DEFAULT_SERVER_KEY_PATH "/etc/ssl/sudo/private/logsrvd_key.pem" /* Evaluates to true if at least one TLS field is set, else false. */ # define TLS_CONFIGURED(_s) \ ((_s).tls_key_path != NULL || (_s).tls_cert_path != NULL || \ (_s).tls_cacert_path != NULL || (_s).tls_dhparams_path != NULL || \ (_s).tls_ciphers_v12 != NULL || (_s).tls_ciphers_v13 != NULL || \ (_s).tls_verify != -1) /* Evaluates to the relay-specific TLS setting, falling back to server. */ # define TLS_RELAY_STR(_c, _f) \ ((_c)->relay._f != NULL ? (_c)->relay._f : (_c)->server._f) # define TLS_RELAY_INT(_c, _f) \ ((_c)->relay._f != -1 ? (_c)->relay._f : (_c)->server._f) #endif enum server_log_type { SERVER_LOG_NONE, SERVER_LOG_STDERR, SERVER_LOG_SYSLOG, SERVER_LOG_FILE }; struct logsrvd_config; typedef bool (*logsrvd_conf_cb_t)(struct logsrvd_config *, const char *, size_t); struct logsrvd_config_entry { const char *conf_str; logsrvd_conf_cb_t setter; size_t offset; }; struct logsrvd_config_section { const char *name; struct logsrvd_config_entry *entries; }; struct address_list_container { unsigned int refcnt; struct server_address_list addrs; }; static struct logsrvd_config { struct logsrvd_config_server { struct address_list_container addresses; struct timespec timeout; bool tcp_keepalive; enum server_log_type log_type; FILE *log_stream; char *log_file; char *pid_file; #if defined(HAVE_OPENSSL) char *tls_key_path; char *tls_cert_path; char *tls_cacert_path; char *tls_dhparams_path; char *tls_ciphers_v12; char *tls_ciphers_v13; int tls_check_peer; int tls_verify; SSL_CTX *ssl_ctx; #endif } server; struct logsrvd_config_relay { struct address_list_container relays; struct timespec connect_timeout; struct timespec timeout; time_t retry_interval; char *relay_dir; bool tcp_keepalive; bool store_first; #if defined(HAVE_OPENSSL) char *tls_key_path; char *tls_cert_path; char *tls_cacert_path; char *tls_dhparams_path; char *tls_ciphers_v12; char *tls_ciphers_v13; int tls_check_peer; int tls_verify; SSL_CTX *ssl_ctx; #endif } relay; struct logsrvd_config_iolog { bool compress; bool flush; bool gid_set; bool log_passwords; uid_t uid; gid_t gid; mode_t mode; unsigned int maxseq; char *iolog_dir; char *iolog_file; void *passprompt_regex; } iolog; struct logsrvd_config_eventlog { int log_type; bool log_exit; enum eventlog_format log_format; } eventlog; struct logsrvd_config_syslog { unsigned int maxlen; int server_facility; int facility; int acceptpri; int rejectpri; int alertpri; } syslog; struct logsrvd_config_logfile { char *path; char *time_format; FILE *stream; } logfile; } *logsrvd_config; static bool logsrvd_warn_enable_stderr = true; /* eventlog getters */ bool logsrvd_conf_log_exit(void) { return logsrvd_config->eventlog.log_exit; } /* iolog getters */ uid_t logsrvd_conf_iolog_uid(void) { return logsrvd_config->iolog.uid; } gid_t logsrvd_conf_iolog_gid(void) { return logsrvd_config->iolog.gid; } mode_t logsrvd_conf_iolog_mode(void) { return logsrvd_config->iolog.mode; } const char * logsrvd_conf_iolog_dir(void) { return logsrvd_config->iolog.iolog_dir; } const char * logsrvd_conf_iolog_file(void) { return logsrvd_config->iolog.iolog_file; } bool logsrvd_conf_iolog_log_passwords(void) { return logsrvd_config->iolog.log_passwords; } void * logsrvd_conf_iolog_passprompt_regex(void) { return logsrvd_config->iolog.passprompt_regex; } /* server getters */ struct server_address_list * logsrvd_conf_server_listen_address(void) { return &logsrvd_config->server.addresses.addrs; } bool logsrvd_conf_server_tcp_keepalive(void) { return logsrvd_config->server.tcp_keepalive; } const char * logsrvd_conf_pid_file(void) { return logsrvd_config->server.pid_file; } struct timespec * logsrvd_conf_server_timeout(void) { if (sudo_timespecisset(&logsrvd_config->server.timeout)) { return &logsrvd_config->server.timeout; } return NULL; } #if defined(HAVE_OPENSSL) SSL_CTX * logsrvd_server_tls_ctx(void) { return logsrvd_config->server.ssl_ctx; } bool logsrvd_conf_server_tls_check_peer(void) { return logsrvd_config->server.tls_check_peer; } #endif /* relay getters */ struct server_address_list * logsrvd_conf_relay_address(void) { return &logsrvd_config->relay.relays.addrs; } const char * logsrvd_conf_relay_dir(void) { return logsrvd_config->relay.relay_dir; } bool logsrvd_conf_relay_store_first(void) { return logsrvd_config->relay.store_first; } bool logsrvd_conf_relay_tcp_keepalive(void) { return logsrvd_config->relay.tcp_keepalive; } struct timespec * logsrvd_conf_relay_timeout(void) { if (sudo_timespecisset(&logsrvd_config->relay.timeout)) { return &logsrvd_config->relay.timeout; } return NULL; } struct timespec * logsrvd_conf_relay_connect_timeout(void) { if (sudo_timespecisset(&logsrvd_config->relay.connect_timeout)) { return &logsrvd_config->relay.connect_timeout; } return NULL; } time_t logsrvd_conf_relay_retry_interval(void) { return logsrvd_config->relay.retry_interval; } #if defined(HAVE_OPENSSL) SSL_CTX * logsrvd_relay_tls_ctx(void) { if (logsrvd_config->relay.ssl_ctx != NULL) return logsrvd_config->relay.ssl_ctx; return logsrvd_config->server.ssl_ctx; } bool logsrvd_conf_relay_tls_check_peer(void) { if (logsrvd_config->relay.tls_check_peer != -1) return logsrvd_config->relay.tls_check_peer; return logsrvd_config->server.tls_check_peer; } #endif /* I/O log callbacks */ static bool cb_iolog_dir(struct logsrvd_config *config, const char *path, size_t offset) { debug_decl(cb_iolog_dir, SUDO_DEBUG_UTIL); free(config->iolog.iolog_dir); if ((config->iolog.iolog_dir = strdup(path)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } debug_return_bool(true); } static bool cb_iolog_file(struct logsrvd_config *config, const char *path, size_t offset) { debug_decl(cb_iolog_file, SUDO_DEBUG_UTIL); free(config->iolog.iolog_file); if ((config->iolog.iolog_file = strdup(path)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } debug_return_bool(true); } static bool cb_iolog_compress(struct logsrvd_config *config, const char *str, size_t offset) { int val; debug_decl(cb_iolog_compress, SUDO_DEBUG_UTIL); if ((val = sudo_strtobool(str)) == -1) debug_return_bool(false); config->iolog.compress = val; debug_return_bool(true); } static bool cb_iolog_log_passwords(struct logsrvd_config *config, const char *str, size_t offset) { int val; debug_decl(cb_iolog_log_passwords, SUDO_DEBUG_UTIL); if ((val = sudo_strtobool(str)) == -1) debug_return_bool(false); config->iolog.log_passwords = val; debug_return_bool(true); } static bool cb_iolog_flush(struct logsrvd_config *config, const char *str, size_t offset) { int val; debug_decl(cb_iolog_flush, SUDO_DEBUG_UTIL); if ((val = sudo_strtobool(str)) == -1) debug_return_bool(false); config->iolog.flush = val; debug_return_bool(true); } static bool cb_iolog_user(struct logsrvd_config *config, const char *user, size_t offset) { struct passwd *pw; debug_decl(cb_iolog_user, SUDO_DEBUG_UTIL); if ((pw = getpwnam(user)) == NULL) { sudo_warnx(U_("unknown user %s"), user); debug_return_bool(false); } config->iolog.uid = pw->pw_uid; if (!config->iolog.gid_set) config->iolog.gid = pw->pw_gid; debug_return_bool(true); } static bool cb_iolog_group(struct logsrvd_config *config, const char *group, size_t offset) { struct group *gr; debug_decl(cb_iolog_group, SUDO_DEBUG_UTIL); if ((gr = getgrnam(group)) == NULL) { sudo_warnx(U_("unknown group %s"), group); debug_return_bool(false); } config->iolog.gid = gr->gr_gid; config->iolog.gid_set = true; debug_return_bool(true); } static bool cb_iolog_mode(struct logsrvd_config *config, const char *str, size_t offset) { const char *errstr; mode_t mode; debug_decl(cb_iolog_mode, SUDO_DEBUG_UTIL); mode = sudo_strtomode(str, &errstr); if (errstr != NULL) { sudo_warnx(U_("unable to parse iolog mode %s"), str); debug_return_bool(false); } config->iolog.mode = mode; debug_return_bool(true); } static bool cb_iolog_maxseq(struct logsrvd_config *config, const char *str, size_t offset) { const char *errstr; unsigned int value; debug_decl(cb_iolog_maxseq, SUDO_DEBUG_UTIL); value = sudo_strtonum(str, 0, SESSID_MAX, &errstr); if (errstr != NULL) { if (errno != ERANGE) { sudo_warnx(U_("invalid value for %s: %s"), "maxseq", errstr); debug_return_bool(false); } /* Out of range, clamp to SESSID_MAX as documented. */ value = SESSID_MAX; } config->iolog.maxseq = value; debug_return_bool(true); } static bool cb_iolog_passprompt_regex(struct logsrvd_config *config, const char *str, size_t offset) { debug_decl(cb_iolog_passprompt_regex, SUDO_DEBUG_UTIL); if (config->iolog.passprompt_regex == NULL) { /* Lazy alloc of the passprompt regex handle. */ config->iolog.passprompt_regex = iolog_pwfilt_alloc(); if (config->iolog.passprompt_regex == NULL) debug_return_bool(false); } debug_return_bool(iolog_pwfilt_add(config->iolog.passprompt_regex, str)); } /* Server callbacks */ static bool append_address(struct server_address_list *addresses, const char *str, bool allow_wildcard) { struct addrinfo hints, *res, *res0 = NULL; char *sa_str = NULL, *sa_host = NULL; char *copy, *host, *port; bool tls, ret = false; int error; debug_decl(append_address, SUDO_DEBUG_UTIL); if ((copy = strdup(str)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } /* Parse host[:port] */ if (!iolog_parse_host_port(copy, &host, &port, &tls, DEFAULT_PORT, DEFAULT_PORT_TLS)) goto done; if (host[0] == '*' && host[1] == '\0') { if (!allow_wildcard) goto done; host = NULL; } #if !defined(HAVE_OPENSSL) if (tls) { sudo_warnx("%s", U_("TLS not supported")); goto done; } #endif /* Only make a single copy of the string + host for all addresses. */ if ((sa_str = sudo_rcstr_dup(str)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); goto done; } if (host != NULL && (sa_host = sudo_rcstr_dup(host)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); goto done; } /* Resolve host (and port if it is a service). */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION hints.ai_flags |= AI_NUMERICSERV; #endif error = getaddrinfo(host, port, &hints, &res0); if (error != 0) { sudo_gai_warn(error, U_("%s:%s"), host ? host : "*", port); goto done; } for (res = res0; res != NULL; res = res->ai_next) { struct server_address *addr; if ((addr = malloc(sizeof(*addr))) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); goto done; } addr->sa_str = sudo_rcstr_addref(sa_str); addr->sa_host = sudo_rcstr_addref(sa_host); memcpy(&addr->sa_un, res->ai_addr, res->ai_addrlen); addr->sa_size = res->ai_addrlen; addr->tls = tls; TAILQ_INSERT_TAIL(addresses, addr, entries); } ret = true; done: sudo_rcstr_delref(sa_str); sudo_rcstr_delref(sa_host); if (res0 != NULL) freeaddrinfo(res0); free(copy); debug_return_bool(ret); } static bool cb_server_listen_address(struct logsrvd_config *config, const char *str, size_t offset) { return append_address(&config->server.addresses.addrs, str, true); } static bool cb_server_timeout(struct logsrvd_config *config, const char *str, size_t offset) { time_t timeout; const char *errstr; debug_decl(cb_server_timeout, SUDO_DEBUG_UTIL); timeout = sudo_strtonum(str, 0, TIME_T_MAX, &errstr); if (errstr != NULL) debug_return_bool(false); config->server.timeout.tv_sec = timeout; debug_return_bool(true); } static bool cb_server_keepalive(struct logsrvd_config *config, const char *str, size_t offset) { int val; debug_decl(cb_server_keepalive, SUDO_DEBUG_UTIL); if ((val = sudo_strtobool(str)) == -1) debug_return_bool(false); config->server.tcp_keepalive = val; debug_return_bool(true); } static bool cb_server_pid_file(struct logsrvd_config *config, const char *str, size_t offset) { char *copy = NULL; debug_decl(cb_server_pid_file, SUDO_DEBUG_UTIL); /* An empty value means to disable the pid file. */ if (*str != '\0') { if (*str != '/') { sudo_warnx(U_("%s: not a fully qualified path"), str); debug_return_bool(false); } if ((copy = strdup(str)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } } free(config->server.pid_file); config->server.pid_file = copy; debug_return_bool(true); } static bool cb_server_log(struct logsrvd_config *config, const char *str, size_t offset) { char *copy = NULL; enum server_log_type log_type = SERVER_LOG_NONE; debug_decl(cb_server_log, SUDO_DEBUG_UTIL); /* An empty value means to disable the server log. */ if (*str != '\0') { if (*str == '/') { log_type = SERVER_LOG_FILE; if ((copy = strdup(str)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } } else if (strcmp(str, "stderr") == 0) { log_type = SERVER_LOG_STDERR; } else if (strcmp(str, "syslog") == 0) { log_type = SERVER_LOG_SYSLOG; } else { debug_return_bool(false); } } free(config->server.log_file); config->server.log_file = copy; config->server.log_type = log_type; debug_return_bool(true); } #if defined(HAVE_OPENSSL) static bool cb_tls_key(struct logsrvd_config *config, const char *path, size_t offset) { char **p = (char **)((char *)config + offset); debug_decl(cb_tls_key, SUDO_DEBUG_UTIL); free(*p); if ((*p = strdup(path)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } debug_return_bool(true); } static bool cb_tls_cacert(struct logsrvd_config *config, const char *path, size_t offset) { char **p = (char **)((char *)config + offset); debug_decl(cb_tls_cacert, SUDO_DEBUG_UTIL); free(*p); if ((*p = strdup(path)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } debug_return_bool(true); } static bool cb_tls_cert(struct logsrvd_config *config, const char *path, size_t offset) { char **p = (char **)((char *)config + offset); debug_decl(cb_tls_cert, SUDO_DEBUG_UTIL); free(*p); if ((*p = strdup(path)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } debug_return_bool(true); } static bool cb_tls_dhparams(struct logsrvd_config *config, const char *path, size_t offset) { char **p = (char **)((char *)config + offset); debug_decl(cb_tls_dhparams, SUDO_DEBUG_UTIL); free(*p); if ((*p = strdup(path)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } debug_return_bool(true); } static bool cb_tls_ciphers12(struct logsrvd_config *config, const char *str, size_t offset) { char **p = (char **)((char *)config + offset); debug_decl(cb_tls_ciphers12, SUDO_DEBUG_UTIL); free(*p); if ((*p = strdup(str)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } debug_return_bool(true); } static bool cb_tls_ciphers13(struct logsrvd_config *config, const char *str, size_t offset) { char **p = (char **)((char *)config + offset); debug_decl(cb_tls_ciphers13, SUDO_DEBUG_UTIL); free(*p); if ((*p = strdup(str)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } debug_return_bool(true); } static bool cb_tls_verify(struct logsrvd_config *config, const char *str, size_t offset) { int *p = (int *)((char *)config + offset); int val; debug_decl(cb_tls_verify, SUDO_DEBUG_UTIL); if ((val = sudo_strtobool(str)) == -1) debug_return_bool(false); *p = val; debug_return_bool(true); } static bool cb_tls_checkpeer(struct logsrvd_config *config, const char *str, size_t offset) { int *p = (int *)((char *)config + offset); int val; debug_decl(cb_tls_checkpeer, SUDO_DEBUG_UTIL); if ((val = sudo_strtobool(str)) == -1) debug_return_bool(false); *p = val; debug_return_bool(true); } #endif /* relay callbacks */ static bool cb_relay_host(struct logsrvd_config *config, const char *str, size_t offset) { return append_address(&config->relay.relays.addrs, str, false); } static bool cb_relay_timeout(struct logsrvd_config *config, const char *str, size_t offset) { time_t timeout; const char *errstr; debug_decl(cb_relay_timeout, SUDO_DEBUG_UTIL); timeout = sudo_strtonum(str, 0, TIME_T_MAX, &errstr); if (errstr != NULL) debug_return_bool(false); config->server.timeout.tv_sec = timeout; debug_return_bool(true); } static bool cb_relay_connect_timeout(struct logsrvd_config *config, const char *str, size_t offset) { time_t timeout; const char *errstr; debug_decl(cb_relay_connect_timeout, SUDO_DEBUG_UTIL); timeout = sudo_strtonum(str, 0, TIME_T_MAX, &errstr); if (errstr != NULL) debug_return_bool(false); config->relay.connect_timeout.tv_sec = timeout; debug_return_bool(true); } static bool cb_relay_dir(struct logsrvd_config *config, const char *str, size_t offset) { char *copy = NULL; debug_decl(cb_relay_dir, SUDO_DEBUG_UTIL); if ((copy = strdup(str)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } free(config->relay.relay_dir); config->relay.relay_dir = copy; debug_return_bool(true); } static bool cb_retry_interval(struct logsrvd_config *config, const char *str, size_t offset) { time_t interval; const char *errstr; debug_decl(cb_retry_interval, SUDO_DEBUG_UTIL); interval = sudo_strtonum(str, 0, TIME_T_MAX, &errstr); if (errstr != NULL) debug_return_bool(false); config->relay.retry_interval = interval; debug_return_bool(true); } static bool cb_relay_store_first(struct logsrvd_config *config, const char *str, size_t offset) { int val; debug_decl(cb_relay_store_first, SUDO_DEBUG_UTIL); if ((val = sudo_strtobool(str)) == -1) debug_return_bool(false); config->relay.store_first = val; debug_return_bool(true); } static bool cb_relay_keepalive(struct logsrvd_config *config, const char *str, size_t offset) { int val; debug_decl(cb_relay_keepalive, SUDO_DEBUG_UTIL); if ((val = sudo_strtobool(str)) == -1) debug_return_bool(false); config->relay.tcp_keepalive = val; debug_return_bool(true); } /* eventlog callbacks */ static bool cb_eventlog_type(struct logsrvd_config *config, const char *str, size_t offset) { debug_decl(cb_eventlog_type, SUDO_DEBUG_UTIL); if (strcmp(str, "none") == 0) config->eventlog.log_type = EVLOG_NONE; else if (strcmp(str, "syslog") == 0) config->eventlog.log_type = EVLOG_SYSLOG; else if (strcmp(str, "logfile") == 0) config->eventlog.log_type = EVLOG_FILE; else debug_return_bool(false); debug_return_bool(true); } static bool cb_eventlog_format(struct logsrvd_config *config, const char *str, size_t offset) { debug_decl(cb_eventlog_format, SUDO_DEBUG_UTIL); if (strcmp(str, "json") == 0) config->eventlog.log_format = EVLOG_JSON; else if (strcmp(str, "sudo") == 0) config->eventlog.log_format = EVLOG_SUDO; else debug_return_bool(false); debug_return_bool(true); } static bool cb_eventlog_exit(struct logsrvd_config *config, const char *str, size_t offset) { int val; debug_decl(cb_eventlog_exit, SUDO_DEBUG_UTIL); if ((val = sudo_strtobool(str)) == -1) debug_return_bool(false); config->eventlog.log_exit = val; debug_return_bool(true); } /* syslog callbacks */ static bool cb_syslog_maxlen(struct logsrvd_config *config, const char *str, size_t offset) { unsigned int maxlen; const char *errstr; debug_decl(cb_syslog_maxlen, SUDO_DEBUG_UTIL); maxlen = sudo_strtonum(str, 1, UINT_MAX, &errstr); if (errstr != NULL) debug_return_bool(false); config->syslog.maxlen = maxlen; debug_return_bool(true); } static bool cb_syslog_server_facility(struct logsrvd_config *config, const char *str, size_t offset) { int logfac; debug_decl(cb_syslog_server_facility, SUDO_DEBUG_UTIL); if (!sudo_str2logfac(str, &logfac)) { sudo_warnx(U_("unknown syslog facility %s"), str); debug_return_bool(false); } config->syslog.server_facility = logfac; debug_return_bool(true); } static bool cb_syslog_facility(struct logsrvd_config *config, const char *str, size_t offset) { int logfac; debug_decl(cb_syslog_facility, SUDO_DEBUG_UTIL); if (!sudo_str2logfac(str, &logfac)) { sudo_warnx(U_("unknown syslog facility %s"), str); debug_return_bool(false); } config->syslog.facility = logfac; debug_return_bool(true); } static bool cb_syslog_acceptpri(struct logsrvd_config *config, const char *str, size_t offset) { int logpri; debug_decl(cb_syslog_acceptpri, SUDO_DEBUG_UTIL); if (!sudo_str2logpri(str, &logpri)) { sudo_warnx(U_("unknown syslog priority %s"), str); debug_return_bool(false); } config->syslog.acceptpri = logpri; debug_return_bool(true); } static bool cb_syslog_rejectpri(struct logsrvd_config *config, const char *str, size_t offset) { int logpri; debug_decl(cb_syslog_rejectpri, SUDO_DEBUG_UTIL); if (!sudo_str2logpri(str, &logpri)) { sudo_warnx(U_("unknown syslog priority %s"), str); debug_return_bool(false); } config->syslog.rejectpri = logpri; debug_return_bool(true); } static bool cb_syslog_alertpri(struct logsrvd_config *config, const char *str, size_t offset) { int logpri; debug_decl(cb_syslog_alertpri, SUDO_DEBUG_UTIL); if (!sudo_str2logpri(str, &logpri)) { sudo_warnx(U_("unknown syslog priority %s"), str); debug_return_bool(false); } config->syslog.alertpri = logpri; debug_return_bool(true); } /* logfile callbacks */ static bool cb_logfile_path(struct logsrvd_config *config, const char *str, size_t offset) { char *copy = NULL; debug_decl(cb_logfile_path, SUDO_DEBUG_UTIL); if (*str != '/') { sudo_warnx(U_("%s: not a fully qualified path"), str); debug_return_bool(false); } if ((copy = strdup(str)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } free(config->logfile.path); config->logfile.path = copy; debug_return_bool(true); } static bool cb_logfile_time_format(struct logsrvd_config *config, const char *str, size_t offset) { char *copy = NULL; debug_decl(cb_logfile_time_format, SUDO_DEBUG_UTIL); if ((copy = strdup(str)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } free(config->logfile.time_format); config->logfile.time_format = copy; debug_return_bool(true); } void address_list_addref(struct server_address_list *al) { struct address_list_container *container = __containerof(al, struct address_list_container, addrs); container->refcnt++; } void address_list_delref(struct server_address_list *al) { struct address_list_container *container = __containerof(al, struct address_list_container, addrs); if (--container->refcnt == 0) { struct server_address *addr; while ((addr = TAILQ_FIRST(al))) { TAILQ_REMOVE(al, addr, entries); sudo_rcstr_delref(addr->sa_str); sudo_rcstr_delref(addr->sa_host); free(addr); } } } static struct logsrvd_config_entry server_conf_entries[] = { { "listen_address", cb_server_listen_address }, { "timeout", cb_server_timeout }, { "tcp_keepalive", cb_server_keepalive }, { "pid_file", cb_server_pid_file }, { "server_log", cb_server_log }, #if defined(HAVE_OPENSSL) { "tls_key", cb_tls_key, offsetof(struct logsrvd_config, server.tls_key_path) }, { "tls_cacert", cb_tls_cacert, offsetof(struct logsrvd_config, server.tls_cacert_path) }, { "tls_cert", cb_tls_cert, offsetof(struct logsrvd_config, server.tls_cert_path) }, { "tls_dhparams", cb_tls_dhparams, offsetof(struct logsrvd_config, server.tls_dhparams_path) }, { "tls_ciphers_v12", cb_tls_ciphers12, offsetof(struct logsrvd_config, server.tls_ciphers_v12) }, { "tls_ciphers_v13", cb_tls_ciphers13, offsetof(struct logsrvd_config, server.tls_ciphers_v13) }, { "tls_checkpeer", cb_tls_checkpeer, offsetof(struct logsrvd_config, server.tls_check_peer) }, { "tls_verify", cb_tls_verify, offsetof(struct logsrvd_config, server.tls_verify) }, #endif { NULL } }; static struct logsrvd_config_entry relay_conf_entries[] = { { "relay_host", cb_relay_host }, { "timeout", cb_relay_timeout }, { "connect_timeout", cb_relay_connect_timeout }, { "relay_dir", cb_relay_dir }, { "retry_interval", cb_retry_interval }, { "store_first", cb_relay_store_first }, { "tcp_keepalive", cb_relay_keepalive }, #if defined(HAVE_OPENSSL) { "tls_key", cb_tls_key, offsetof(struct logsrvd_config, relay.tls_key_path) }, { "tls_cacert", cb_tls_cacert, offsetof(struct logsrvd_config, relay.tls_cacert_path) }, { "tls_cert", cb_tls_cert, offsetof(struct logsrvd_config, relay.tls_cert_path) }, { "tls_dhparams", cb_tls_dhparams, offsetof(struct logsrvd_config, relay.tls_dhparams_path) }, { "tls_ciphers_v12", cb_tls_ciphers12, offsetof(struct logsrvd_config, relay.tls_ciphers_v12) }, { "tls_ciphers_v13", cb_tls_ciphers13, offsetof(struct logsrvd_config, relay.tls_ciphers_v13) }, { "tls_checkpeer", cb_tls_checkpeer, offsetof(struct logsrvd_config, relay.tls_check_peer) }, { "tls_verify", cb_tls_verify, offsetof(struct logsrvd_config, relay.tls_verify) }, #endif { NULL } }; static struct logsrvd_config_entry iolog_conf_entries[] = { { "iolog_dir", cb_iolog_dir }, { "iolog_file", cb_iolog_file }, { "iolog_flush", cb_iolog_flush }, { "iolog_compress", cb_iolog_compress }, { "iolog_user", cb_iolog_user }, { "iolog_group", cb_iolog_group }, { "iolog_mode", cb_iolog_mode }, { "log_passwords", cb_iolog_log_passwords }, { "maxseq", cb_iolog_maxseq }, { "passprompt_regex", cb_iolog_passprompt_regex }, { NULL } }; static struct logsrvd_config_entry eventlog_conf_entries[] = { { "log_type", cb_eventlog_type }, { "log_format", cb_eventlog_format }, { "log_exit", cb_eventlog_exit }, { NULL } }; static struct logsrvd_config_entry syslog_conf_entries[] = { { "maxlen", cb_syslog_maxlen }, { "server_facility", cb_syslog_server_facility }, { "facility", cb_syslog_facility }, { "reject_priority", cb_syslog_rejectpri }, { "accept_priority", cb_syslog_acceptpri }, { "alert_priority", cb_syslog_alertpri }, { NULL } }; static struct logsrvd_config_entry logfile_conf_entries[] = { { "path", cb_logfile_path }, { "time_format", cb_logfile_time_format }, { NULL } }; static struct logsrvd_config_section logsrvd_config_sections[] = { { "server", server_conf_entries }, { "relay", relay_conf_entries }, { "iolog", iolog_conf_entries }, { "eventlog", eventlog_conf_entries }, { "syslog", syslog_conf_entries }, { "logfile", logfile_conf_entries }, { NULL } }; static bool logsrvd_conf_parse(struct logsrvd_config *config, FILE *fp, const char *path) { struct logsrvd_config_section *conf_section = NULL; unsigned int lineno = 0; size_t linesize = 0; char *line = NULL; bool ret = false; debug_decl(logsrvd_conf_parse, SUDO_DEBUG_UTIL); while (sudo_parseln(&line, &linesize, &lineno, fp, 0) != -1) { struct logsrvd_config_entry *entry; char *ep, *val; /* Skip blank, comment or invalid lines. */ if (*line == '\0' || *line == ';') continue; /* New section */ if (line[0] == '[') { char *cp, *section_name = line + 1; if ((ep = strchr(section_name, ']')) == NULL) { sudo_warnx(U_("%s:%d unmatched '[': %s"), path, lineno, line); goto done; } for (cp = ep + 1; *cp != '\0'; cp++) { if (!isspace((unsigned char)*cp)) { sudo_warnx(U_("%s:%d garbage after ']': %s"), path, lineno, line); goto done; } } *ep = '\0'; for (conf_section = logsrvd_config_sections; conf_section->name != NULL; conf_section++) { if (strcasecmp(section_name, conf_section->name) == 0) break; } if (conf_section->name == NULL) { sudo_warnx(U_("%s:%d invalid config section: %s"), path, lineno, section_name); goto done; } continue; } if ((ep = strchr(line, '=')) == NULL) { sudo_warnx(U_("%s:%d invalid configuration line: %s"), path, lineno, line); goto done; } if (conf_section == NULL) { sudo_warnx(U_("%s:%d expected section name: %s"), path, lineno, line); goto done; } val = ep + 1; while (isspace((unsigned char)*val)) val++; while (ep > line && isspace((unsigned char)ep[-1])) ep--; *ep = '\0'; for (entry = conf_section->entries; entry->conf_str != NULL; entry++) { if (strcasecmp(line, entry->conf_str) == 0) { if (!entry->setter(config, val, entry->offset)) { sudo_warnx(U_("invalid value for %s: %s"), entry->conf_str, val); goto done; } break; } } if (entry->conf_str == NULL) { sudo_warnx(U_("%s:%d [%s] illegal key: %s"), path, lineno, conf_section->name, line); goto done; } } ret = true; done: free(line); debug_return_bool(ret); } static FILE * logsrvd_open_log_file(const char *path, int flags) { mode_t oldmask; FILE *fp = NULL; const char *omode; int fd; debug_decl(logsrvd_open_log_file, SUDO_DEBUG_UTIL); if (ISSET(flags, O_APPEND)) { omode = "a"; } else { omode = "w"; } oldmask = umask(S_IRWXG|S_IRWXO); fd = open(path, flags, S_IRUSR|S_IWUSR); (void)umask(oldmask); if (fd == -1 || (fp = fdopen(fd, omode)) == NULL) { sudo_warn(U_("unable to open log file %s"), path); if (fd != -1) close(fd); } debug_return_ptr(fp); } static FILE * logsrvd_open_eventlog(struct logsrvd_config *config) { int flags; debug_decl(logsrvd_open_eventlog, SUDO_DEBUG_UTIL); /* Cannot append to a JSON file. */ if (config->eventlog.log_format == EVLOG_JSON) { flags = O_RDWR|O_CREAT; } else { flags = O_WRONLY|O_APPEND|O_CREAT; } debug_return_ptr(logsrvd_open_log_file(config->logfile.path, flags)); } static FILE * logsrvd_stub_open_log(int type, const char *logfile) { /* Actual open already done by logsrvd_open_eventlog() */ return logsrvd_config->logfile.stream; } static void logsrvd_stub_close_log(int type, FILE *fp) { return; } /* Set eventlog configuration settings from logsrvd config. */ static void logsrvd_conf_eventlog_setconf(struct logsrvd_config *config) { debug_decl(logsrvd_conf_eventlog_setconf, SUDO_DEBUG_UTIL); eventlog_set_type(config->eventlog.log_type); eventlog_set_format(config->eventlog.log_format); eventlog_set_syslog_acceptpri(config->syslog.acceptpri); eventlog_set_syslog_rejectpri(config->syslog.rejectpri); eventlog_set_syslog_alertpri(config->syslog.alertpri); eventlog_set_syslog_maxlen(config->syslog.maxlen); eventlog_set_logpath(config->logfile.path); eventlog_set_time_fmt(config->logfile.time_format); eventlog_set_open_log(logsrvd_stub_open_log); eventlog_set_close_log(logsrvd_stub_close_log); debug_return; } /* Set I/O log configuration settings from logsrvd config. */ static void logsrvd_conf_iolog_setconf(struct logsrvd_config *config) { debug_decl(logsrvd_conf_iolog_setconf, SUDO_DEBUG_UTIL); iolog_set_defaults(); iolog_set_compress(config->iolog.compress); iolog_set_flush(config->iolog.flush); iolog_set_owner(config->iolog.uid, config->iolog.gid); iolog_set_mode(config->iolog.mode); iolog_set_maxseq(config->iolog.maxseq); debug_return; } /* * Conversation function for use by sudo_warn/sudo_fatal. * Logs to stdout/stderr. */ static int logsrvd_conv_stderr(int num_msgs, const struct sudo_conv_message msgs[], struct sudo_conv_reply replies[], struct sudo_conv_callback *callback) { int i; debug_decl(logsrvd_conv_stderr, SUDO_DEBUG_UTIL); for (i = 0; i < num_msgs; i++) { if (fputs(msgs[i].msg, stderr) == EOF) debug_return_int(-1); } debug_return_int(0); } /* * Conversation function for use by sudo_warn/sudo_fatal. * Acts as a no-op log sink. */ static int logsrvd_conv_none(int num_msgs, const struct sudo_conv_message msgs[], struct sudo_conv_reply replies[], struct sudo_conv_callback *callback) { /* Also write to stderr if still in the foreground. */ if (logsrvd_warn_enable_stderr) { (void)logsrvd_conv_stderr(num_msgs, msgs, replies, callback); } return 0; } /* * Conversation function for use by sudo_warn/sudo_fatal. * Logs to syslog. */ static int logsrvd_conv_syslog(int num_msgs, const struct sudo_conv_message msgs[], struct sudo_conv_reply replies[], struct sudo_conv_callback *callback) { char *buf = NULL, *cp = NULL; const char *progname; size_t proglen, bufsize = 0; int i; debug_decl(logsrvd_conv_syslog, SUDO_DEBUG_UTIL); if (logsrvd_config == NULL) { debug_return_int(logsrvd_conv_stderr(num_msgs, msgs, replies, callback)); } /* Also write to stderr if still in the foreground. */ if (logsrvd_warn_enable_stderr) { (void)logsrvd_conv_stderr(num_msgs, msgs, replies, callback); } /* * Concat messages into a flag string that we can syslog. */ progname = getprogname(); proglen = strlen(progname); for (i = 0; i < num_msgs; i++) { const char *msg = msgs[i].msg; size_t len = strlen(msg); size_t used = (size_t)(cp - buf); /* Strip leading "sudo_logsrvd: " prefix. */ if (strncmp(msg, progname, proglen) == 0) { msg += proglen; len -= proglen; if (len == 0) { /* Skip over ": " string that follows program name. */ if (i + 1 < num_msgs && strcmp(msgs[i + 1].msg, ": ") == 0) { i++; continue; } } else if (msg[0] == ':' && msg[1] == ' ') { /* Handle "progname: " */ msg += 2; len -= 2; } } /* Strip off trailing newlines. */ while (len > 1 && msg[len - 1] == '\n') len--; if (len == 0) continue; if (len >= bufsize - used) { bufsize += 1024; char *tmp = realloc(buf, bufsize); if (tmp == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); free(buf); debug_return_int(-1); } buf = tmp; cp = tmp + used; } memcpy(cp, msg, len); cp[len] = '\0'; cp += len; } if (buf != NULL) { openlog(progname, 0, logsrvd_config->syslog.server_facility); syslog(LOG_ERR, "%s", buf); free(buf); /* Restore old syslog settings. */ if (logsrvd_config->eventlog.log_type == EVLOG_SYSLOG) openlog("sudo", 0, logsrvd_config->syslog.facility); } debug_return_int(0); } /* * Conversation function for use by sudo_warn/sudo_fatal. * Logs to an already-open log file. */ static int logsrvd_conv_logfile(int num_msgs, const struct sudo_conv_message msgs[], struct sudo_conv_reply replies[], struct sudo_conv_callback *callback) { const char *progname; size_t proglen; int i; debug_decl(logsrvd_conv_logfile, SUDO_DEBUG_UTIL); if (logsrvd_config == NULL) { debug_return_int(logsrvd_conv_stderr(num_msgs, msgs, replies, callback)); } /* Also write to stderr if still in the foreground. */ if (logsrvd_warn_enable_stderr) { (void)logsrvd_conv_stderr(num_msgs, msgs, replies, callback); } if (logsrvd_config->server.log_stream == NULL) { errno = EBADF; debug_return_int(-1); } progname = getprogname(); proglen = strlen(progname); for (i = 0; i < num_msgs; i++) { const char *msg = msgs[i].msg; size_t len = strlen(msg); /* Strip leading "sudo_logsrvd: " prefix. */ if (strncmp(msg, progname, proglen) == 0) { msg += proglen; len -= proglen; if (len == 0) { /* Skip over ": " string that follows program name. */ if (i + 1 < num_msgs && strcmp(msgs[i + 1].msg, ": ") == 0) { i++; continue; } } else if (msg[0] == ':' && msg[1] == ' ') { /* Handle "progname: " */ msg += 2; len -= 2; } } if (fwrite(msg, len, 1, logsrvd_config->server.log_stream) != 1) debug_return_int(-1); } debug_return_int(0); } /* Free the specified struct logsrvd_config and its contents. */ static void logsrvd_conf_free(struct logsrvd_config *config) { debug_decl(logsrvd_conf_free, SUDO_DEBUG_UTIL); if (config == NULL) debug_return; /* struct logsrvd_config_server */ address_list_delref(&config->server.addresses.addrs); free(config->server.pid_file); free(config->server.log_file); if (config->server.log_stream != NULL) fclose(config->server.log_stream); #if defined(HAVE_OPENSSL) free(config->server.tls_key_path); free(config->server.tls_cert_path); free(config->server.tls_cacert_path); free(config->server.tls_dhparams_path); free(config->server.tls_ciphers_v12); free(config->server.tls_ciphers_v13); if (config->server.ssl_ctx != NULL) SSL_CTX_free(config->server.ssl_ctx); #endif /* struct logsrvd_config_relay */ address_list_delref(&config->relay.relays.addrs); free(config->relay.relay_dir); #if defined(HAVE_OPENSSL) free(config->relay.tls_key_path); free(config->relay.tls_cert_path); free(config->relay.tls_cacert_path); free(config->relay.tls_dhparams_path); free(config->relay.tls_ciphers_v12); free(config->relay.tls_ciphers_v13); if (config->relay.ssl_ctx != NULL) SSL_CTX_free(config->relay.ssl_ctx); #endif /* struct logsrvd_config_iolog */ free(config->iolog.iolog_dir); free(config->iolog.iolog_file); iolog_pwfilt_free(config->iolog.passprompt_regex); /* struct logsrvd_config_logfile */ free(config->logfile.path); free(config->logfile.time_format); if (config->logfile.stream != NULL) fclose(config->logfile.stream); free(config); debug_return; } /* Allocate a new struct logsrvd_config and set default values. */ static struct logsrvd_config * logsrvd_conf_alloc(void) { struct logsrvd_config *config; debug_decl(logsrvd_conf_alloc, SUDO_DEBUG_UTIL); if ((config = calloc(1, sizeof(*config))) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_ptr(NULL); } /* Relay defaults */ TAILQ_INIT(&config->relay.relays.addrs); config->relay.relays.refcnt = 1; config->relay.timeout.tv_sec = DEFAULT_SOCKET_TIMEOUT_SEC; config->relay.connect_timeout.tv_sec = DEFAULT_SOCKET_TIMEOUT_SEC; config->relay.tcp_keepalive = true; config->relay.retry_interval = 30; if (!cb_relay_dir(config, _PATH_SUDO_RELAY_DIR, 0)) goto bad; #if defined(HAVE_OPENSSL) config->relay.tls_verify = -1; config->relay.tls_check_peer = -1; #endif /* Server defaults */ TAILQ_INIT(&config->server.addresses.addrs); config->server.addresses.refcnt = 1; config->server.timeout.tv_sec = DEFAULT_SOCKET_TIMEOUT_SEC; config->server.tcp_keepalive = true; config->server.log_type = SERVER_LOG_SYSLOG; config->server.pid_file = strdup(_PATH_SUDO_LOGSRVD_PID); if (config->server.pid_file == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); goto bad; } #if defined(HAVE_OPENSSL) /* * Only set default CA and cert paths if the files actually exist. * This ensures we don't enable TLS by default when it is not configured. */ if (access(DEFAULT_CA_CERT_PATH, R_OK) == 0) { config->server.tls_cacert_path = strdup(DEFAULT_CA_CERT_PATH); if (config->server.tls_cacert_path == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); goto bad; } } if (access(DEFAULT_SERVER_CERT_PATH, R_OK) == 0) { config->server.tls_cert_path = strdup(DEFAULT_SERVER_CERT_PATH); if (config->server.tls_cert_path == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); goto bad; } } config->server.tls_key_path = strdup(DEFAULT_SERVER_KEY_PATH); if (config->server.tls_key_path == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); goto bad; } config->server.tls_verify = true; config->server.tls_check_peer = false; #endif /* I/O log defaults */ config->iolog.compress = false; config->iolog.flush = true; config->iolog.mode = S_IRUSR|S_IWUSR; config->iolog.maxseq = SESSID_MAX; if (!cb_iolog_dir(config, _PATH_SUDO_IO_LOGDIR, 0)) goto bad; if (!cb_iolog_file(config, "%{seq}", 0)) goto bad; config->iolog.uid = ROOT_UID; config->iolog.gid = ROOT_GID; config->iolog.gid_set = false; config->iolog.log_passwords = true; /* Event log defaults */ config->eventlog.log_type = EVLOG_SYSLOG; config->eventlog.log_format = EVLOG_SUDO; config->eventlog.log_exit = false; /* Syslog defaults */ config->syslog.maxlen = 960; config->syslog.server_facility = LOG_DAEMON; if (!cb_syslog_facility(config, LOGFAC, 0)) { sudo_warnx(U_("unknown syslog facility %s"), LOGFAC); goto bad; } if (!cb_syslog_acceptpri(config, PRI_SUCCESS, 0)) { sudo_warnx(U_("unknown syslog priority %s"), PRI_SUCCESS); goto bad; } if (!cb_syslog_rejectpri(config, PRI_FAILURE, 0)) { sudo_warnx(U_("unknown syslog priority %s"), PRI_FAILURE); goto bad; } if (!cb_syslog_alertpri(config, PRI_FAILURE, 0)) { sudo_warnx(U_("unknown syslog priority %s"), PRI_FAILURE); goto bad; } /* Log file defaults */ if (!cb_logfile_time_format(config, "%h %e %T", 0)) goto bad; if (!cb_logfile_path(config, _PATH_SUDO_LOGFILE, 0)) goto bad; debug_return_ptr(config); bad: logsrvd_conf_free(config); debug_return_ptr(NULL); } static bool logsrvd_conf_apply(struct logsrvd_config *config) { #if defined(HAVE_OPENSSL) struct server_address *addr; #endif debug_decl(logsrvd_conf_apply, SUDO_DEBUG_UTIL); /* There can be multiple passprompt regular expressions. */ if (config->iolog.passprompt_regex == NULL) { if (!cb_iolog_passprompt_regex(config, PASSPROMPT_REGEX, 0)) debug_return_bool(false); } /* There can be multiple addresses so we can't set a default earlier. */ if (TAILQ_EMPTY(&config->server.addresses.addrs)) { /* Enable plaintext listender. */ if (!cb_server_listen_address(config, "*:" DEFAULT_PORT, 0)) debug_return_bool(false); #if defined(HAVE_OPENSSL) /* If a certificate was specified, enable the TLS listener too. */ if (config->server.tls_cert_path != NULL) { if (!cb_server_listen_address(config, "*:" DEFAULT_PORT_TLS "(tls)", 0)) debug_return_bool(false); } } else { /* Check that TLS configuration is valid. */ TAILQ_FOREACH(addr, &config->server.addresses.addrs, entries) { if (!addr->tls) continue; /* * If a TLS listener was explicitly enabled but the cert path * was not, use the default. */ if (config->server.tls_cert_path == NULL) { config->server.tls_cert_path = strdup(DEFAULT_SERVER_CERT_PATH); if (config->server.tls_cert_path == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } } break; } #endif /* HAVE_OPENSSL */ } #if defined(HAVE_OPENSSL) TAILQ_FOREACH(addr, &config->server.addresses.addrs, entries) { if (!addr->tls) continue; /* Create a TLS context for the server. */ config->server.ssl_ctx = init_tls_context( config->server.tls_cacert_path, config->server.tls_cert_path, config->server.tls_key_path, config->server.tls_dhparams_path, config->server.tls_ciphers_v12, config->server.tls_ciphers_v13, config->server.tls_verify); if (config->server.ssl_ctx == NULL) { sudo_warnx("%s", U_("unable to initialize server TLS context")); debug_return_bool(false); } break; } if (TLS_CONFIGURED(config->relay)) { TAILQ_FOREACH(addr, &config->relay.relays.addrs, entries) { if (!addr->tls) continue; /* Create a TLS context for the relay. */ config->relay.ssl_ctx = init_tls_context( TLS_RELAY_STR(config, tls_cacert_path), TLS_RELAY_STR(config, tls_cert_path), TLS_RELAY_STR(config, tls_key_path), TLS_RELAY_STR(config, tls_dhparams_path), TLS_RELAY_STR(config, tls_ciphers_v12), TLS_RELAY_STR(config, tls_ciphers_v13), TLS_RELAY_INT(config, tls_verify)); if (config->relay.ssl_ctx == NULL) { sudo_warnx("%s", U_("unable to initialize relay TLS context")); debug_return_bool(false); } break; } } #endif /* HAVE_OPENSSL */ /* Clear store_first if not relaying. */ if (TAILQ_EMPTY(&config->relay.relays.addrs)) config->relay.store_first = false; /* Open server log if specified. */ switch (config->server.log_type) { case SERVER_LOG_SYSLOG: sudo_warn_set_conversation(logsrvd_conv_syslog); break; case SERVER_LOG_FILE: config->server.log_stream = logsrvd_open_log_file(config->server.log_file, O_WRONLY|O_APPEND|O_CREAT); if (config->server.log_stream == NULL) debug_return_bool(false); sudo_warn_set_conversation(logsrvd_conv_logfile); break; case SERVER_LOG_NONE: sudo_warn_set_conversation(logsrvd_conv_none); break; case SERVER_LOG_STDERR: /* Default is stderr. */ sudo_warn_set_conversation(NULL); break; default: sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "cannot open unknown log type %d", config->eventlog.log_type); break; } /* Open event log if specified. */ switch (config->eventlog.log_type) { case EVLOG_SYSLOG: openlog("sudo", 0, config->syslog.facility); break; case EVLOG_FILE: config->logfile.stream = logsrvd_open_eventlog(config); if (config->logfile.stream == NULL) debug_return_bool(false); break; case EVLOG_NONE: break; default: sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "cannot open unknown log type %d", config->eventlog.log_type); break; } /* * Update event and I/O log library config and install the new * logsrvd config. We must not fail past this point or the event * and I/O log config will be inconsistent with the logsrvd config. */ logsrvd_conf_iolog_setconf(config); logsrvd_conf_eventlog_setconf(config); logsrvd_conf_free(logsrvd_config); logsrvd_config = config; debug_return_bool(true); } /* * Read .ini style logsrvd.conf file. * Note that we use '#' not ';' for the comment character. */ bool logsrvd_conf_read(const char *path) { struct logsrvd_config *config; bool ret = false; FILE *fp = NULL; debug_decl(logsrvd_conf_read, SUDO_DEBUG_UTIL); config = logsrvd_conf_alloc(); if ((fp = fopen(path, "r")) == NULL) { if (errno != ENOENT) { sudo_warn("%s", path); goto done; } } else { if (!logsrvd_conf_parse(config, fp, path)) goto done; } /* Install new config */ if (logsrvd_conf_apply(config)) { config = NULL; ret = true; } done: logsrvd_conf_free(config); if (fp != NULL) fclose(fp); debug_return_bool(ret); } void logsrvd_conf_cleanup(void) { debug_decl(logsrvd_conf_cleanup, SUDO_DEBUG_UTIL); logsrvd_conf_free(logsrvd_config); logsrvd_config = NULL; debug_return; } void logsrvd_warn_stderr(bool enabled) { logsrvd_warn_enable_stderr = enabled; }