diff options
Diffstat (limited to 'contrib/openbsm/bin/auditdistd/parse.y')
-rw-r--r-- | contrib/openbsm/bin/auditdistd/parse.y | 856 |
1 files changed, 856 insertions, 0 deletions
diff --git a/contrib/openbsm/bin/auditdistd/parse.y b/contrib/openbsm/bin/auditdistd/parse.y new file mode 100644 index 0000000..fbe1000 --- /dev/null +++ b/contrib/openbsm/bin/auditdistd/parse.y @@ -0,0 +1,856 @@ +%{ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/parse.y#5 $ + */ + +#include <config/config.h> + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/sysctl.h> + +#include <arpa/inet.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#ifndef HAVE_STRLCPY +#include <compat/strlcpy.h> +#endif + +#include "auditdistd.h" +#include "pjdlog.h" + +extern int depth; +extern int lineno; + +extern FILE *yyin; +extern char *yytext; + +static struct adist_config *lconfig; +static struct adist_host *curhost; +#define SECTION_GLOBAL 0 +#define SECTION_SENDER 1 +#define SECTION_RECEIVER 2 +static int cursection; + +/* Sender section. */ +static char depth1_source[ADIST_ADDRSIZE]; +static int depth1_checksum; +static int depth1_compression; +/* Sender and receiver sections. */ +static char depth1_directory[PATH_MAX]; + +static bool adjust_directory(char *path); +static bool family_supported(int family); + +extern void yyrestart(FILE *); +%} + +%token CB +%token CERTFILE +%token DIRECTORY +%token FINGERPRINT +%token HOST +%token KEYFILE +%token LISTEN +%token NAME +%token OB +%token PASSWORD +%token PIDFILE +%token RECEIVER REMOTE +%token SENDER SOURCE +%token TIMEOUT + +/* +%type <num> checksum_type +%type <num> compression_type +*/ + +%union +{ + int num; + char *str; +} + +%token <num> NUM +%token <str> STR + +%% + +statements: + | + statements statement + ; + +statement: + name_statement + | + pidfile_statement + | + timeout_statement + | + sender_statement + | + receiver_statement + ; + +name_statement: NAME STR + { + PJDLOG_RASSERT(depth == 0, + "The name variable can only be specificed in the global section."); + + if (lconfig->adc_name[0] != '\0') { + pjdlog_error("The name variable is specified twice."); + free($2); + return (1); + } + if (strlcpy(lconfig->adc_name, $2, + sizeof(lconfig->adc_name)) >= + sizeof(lconfig->adc_name)) { + pjdlog_error("The name value is too long."); + free($2); + return (1); + } + free($2); + } + ; + +pidfile_statement: PIDFILE STR + { + PJDLOG_RASSERT(depth == 0, + "The pidfile variable can only be specificed in the global section."); + + if (lconfig->adc_pidfile[0] != '\0') { + pjdlog_error("The pidfile variable is specified twice."); + free($2); + return (1); + } + if (strcmp($2, "none") != 0 && $2[0] != '/') { + pjdlog_error("The pidfile variable must be set to absolute pathname or \"none\"."); + free($2); + return (1); + } + if (strlcpy(lconfig->adc_pidfile, $2, + sizeof(lconfig->adc_pidfile)) >= + sizeof(lconfig->adc_pidfile)) { + pjdlog_error("The pidfile value is too long."); + free($2); + return (1); + } + free($2); + } + ; + +timeout_statement: TIMEOUT NUM + { + PJDLOG_ASSERT(depth == 0); + + lconfig->adc_timeout = $2; + } + ; + +sender_statement: SENDER sender_start sender_entries CB + { + PJDLOG_ASSERT(depth == 0); + PJDLOG_ASSERT(cursection == SECTION_SENDER); + + /* Configure defaults. */ + if (depth1_checksum == -1) + depth1_checksum = ADIST_CHECKSUM_NONE; + if (depth1_compression == -1) + depth1_compression = ADIST_COMPRESSION_NONE; + if (depth1_directory[0] == '\0') { + (void)strlcpy(depth1_directory, ADIST_DIRECTORY_SENDER, + sizeof(depth1_directory)); + } + /* Empty depth1_source is ok. */ + TAILQ_FOREACH(curhost, &lconfig->adc_hosts, adh_next) { + if (curhost->adh_role != ADIST_ROLE_SENDER) + continue; + if (curhost->adh_checksum == -1) + curhost->adh_checksum = depth1_checksum; + if (curhost->adh_compression == -1) + curhost->adh_compression = depth1_compression; + if (curhost->adh_directory[0] == '\0') { + (void)strlcpy(curhost->adh_directory, + depth1_directory, + sizeof(curhost->adh_directory)); + } + if (curhost->adh_localaddr[0] == '\0') { + (void)strlcpy(curhost->adh_localaddr, + depth1_source, + sizeof(curhost->adh_localaddr)); + } + } + cursection = SECTION_GLOBAL; + } + ; + +sender_start: OB + { + PJDLOG_ASSERT(depth == 1); + PJDLOG_ASSERT(cursection == SECTION_GLOBAL); + + cursection = SECTION_SENDER; + depth1_checksum = -1; + depth1_compression = -1; + depth1_source[0] = '\0'; + depth1_directory[0] = '\0'; + +#ifndef HAVE_AUDIT_SYSCALLS + pjdlog_error("Sender functionality is not available."); + return (1); +#endif + } + ; + +sender_entries: + | + sender_entries sender_entry + ; + +sender_entry: + source_statement + | + directory_statement +/* + | + checksum_statement + | + compression_statement +*/ + | + sender_host_statement + ; + +receiver_statement: RECEIVER receiver_start receiver_entries CB + { + PJDLOG_ASSERT(depth == 0); + PJDLOG_ASSERT(cursection == SECTION_RECEIVER); + + /* + * If not listen addresses were specified, + * configure default ones. + */ + if (TAILQ_EMPTY(&lconfig->adc_listen)) { + struct adist_listen *lst; + + if (family_supported(AF_INET)) { + lst = calloc(1, sizeof(*lst)); + if (lst == NULL) { + pjdlog_error("Unable to allocate memory for listen address."); + return (1); + } + (void)strlcpy(lst->adl_addr, + ADIST_LISTEN_TLS_TCP4, + sizeof(lst->adl_addr)); + TAILQ_INSERT_TAIL(&lconfig->adc_listen, lst, adl_next); + } else { + pjdlog_debug(1, + "No IPv4 support in the kernel, not listening on IPv4 address."); + } + if (family_supported(AF_INET6)) { + lst = calloc(1, sizeof(*lst)); + if (lst == NULL) { + pjdlog_error("Unable to allocate memory for listen address."); + return (1); + } + (void)strlcpy(lst->adl_addr, + ADIST_LISTEN_TLS_TCP6, + sizeof(lst->adl_addr)); + TAILQ_INSERT_TAIL(&lconfig->adc_listen, lst, adl_next); + } else { + pjdlog_debug(1, + "No IPv6 support in the kernel, not listening on IPv6 address."); + } + if (TAILQ_EMPTY(&lconfig->adc_listen)) { + pjdlog_error("No address to listen on."); + return (1); + } + } + /* Configure defaults. */ + if (depth1_directory[0] == '\0') { + (void)strlcpy(depth1_directory, + ADIST_DIRECTORY_RECEIVER, + sizeof(depth1_directory)); + } + TAILQ_FOREACH(curhost, &lconfig->adc_hosts, adh_next) { + if (curhost->adh_role != ADIST_ROLE_RECEIVER) + continue; + if (curhost->adh_directory[0] == '\0') { + if (snprintf(curhost->adh_directory, + sizeof(curhost->adh_directory), "%s/%s", + depth1_directory, curhost->adh_name) >= + (ssize_t)sizeof(curhost->adh_directory)) { + pjdlog_error("Directory value is too long."); + return (1); + } + } + } + cursection = SECTION_GLOBAL; + } + ; + +receiver_start: OB + { + PJDLOG_ASSERT(depth == 1); + PJDLOG_ASSERT(cursection == SECTION_GLOBAL); + + cursection = SECTION_RECEIVER; + depth1_directory[0] = '\0'; + } + ; + +receiver_entries: + | + receiver_entries receiver_entry + ; + +receiver_entry: + listen_statement + | + directory_statement + | + certfile_statement + | + keyfile_statement + | + receiver_host_statement + ; + +/* +checksum_statement: CHECKSUM checksum_type + { + PJDLOG_ASSERT(cursection == SECTION_SENDER); + + switch (depth) { + case 1: + depth1_checksum = $2; + break; + case 2: + PJDLOG_ASSERT(curhost != NULL); + curhost->adh_checksum = $2; + break; + default: + PJDLOG_ABORT("checksum at wrong depth level"); + } + } + ; + +checksum_type: + NONE { $$ = ADIST_CHECKSUM_NONE; } + | + CRC32 { $$ = ADIST_CHECKSUM_CRC32; } + | + SHA256 { $$ = ADIST_CHECKSUM_SHA256; } + ; + +compression_statement: COMPRESSION compression_type + { + PJDLOG_ASSERT(cursection == SECTION_SENDER); + + switch (depth) { + case 1: + depth1_compression = $2; + break; + case 2: + PJDLOG_ASSERT(curhost != NULL); + curhost->adh_compression = $2; + break; + default: + PJDLOG_ABORT("compression at wrong depth level"); + } + } + ; + +compression_type: + NONE { $$ = ADIST_COMPRESSION_NONE; } + | + LZF { $$ = ADIST_COMPRESSION_LZF; } + ; +*/ + +directory_statement: DIRECTORY STR + { + PJDLOG_ASSERT(cursection == SECTION_SENDER || + cursection == SECTION_RECEIVER); + + switch (depth) { + case 1: + if (strlcpy(depth1_directory, $2, + sizeof(depth1_directory)) >= + sizeof(depth1_directory)) { + pjdlog_error("Directory value is too long."); + free($2); + return (1); + } + if (!adjust_directory(depth1_directory)) + return (1); + break; + case 2: + if (cursection == SECTION_SENDER || $2[0] == '/') { + if (strlcpy(curhost->adh_directory, $2, + sizeof(curhost->adh_directory)) >= + sizeof(curhost->adh_directory)) { + pjdlog_error("Directory value is too long."); + free($2); + return (1); + } + } else /* if (cursection == SECTION_RECEIVER) */ { + if (depth1_directory[0] == '\0') { + pjdlog_error("Directory path must be absolute."); + free($2); + return (1); + } + if (snprintf(curhost->adh_directory, + sizeof(curhost->adh_directory), "%s/%s", + depth1_directory, $2) >= + (ssize_t)sizeof(curhost->adh_directory)) { + pjdlog_error("Directory value is too long."); + free($2); + return (1); + } + } + break; + default: + PJDLOG_ABORT("directory at wrong depth level"); + } + free($2); + } + ; + +source_statement: SOURCE STR + { + PJDLOG_RASSERT(cursection == SECTION_SENDER, + "The source variable must be in sender section."); + + switch (depth) { + case 1: + if (strlcpy(depth1_source, $2, + sizeof(depth1_source)) >= + sizeof(depth1_source)) { + pjdlog_error("Source value is too long."); + free($2); + return (1); + } + break; + case 2: + if (strlcpy(curhost->adh_localaddr, $2, + sizeof(curhost->adh_localaddr)) >= + sizeof(curhost->adh_localaddr)) { + pjdlog_error("Source value is too long."); + free($2); + return (1); + } + break; + } + free($2); + } + ; + +fingerprint_statement: FINGERPRINT STR + { + PJDLOG_ASSERT(cursection == SECTION_SENDER); + PJDLOG_ASSERT(depth == 2); + + if (strncasecmp($2, "SHA256=", 7) != 0) { + pjdlog_error("Invalid fingerprint value."); + free($2); + return (1); + } + if (strlcpy(curhost->adh_fingerprint, $2, + sizeof(curhost->adh_fingerprint)) >= + sizeof(curhost->adh_fingerprint)) { + pjdlog_error("Fingerprint value is too long."); + free($2); + return (1); + } + free($2); + } + ; + +password_statement: PASSWORD STR + { + PJDLOG_ASSERT(cursection == SECTION_SENDER || + cursection == SECTION_RECEIVER); + PJDLOG_ASSERT(depth == 2); + + if (strlcpy(curhost->adh_password, $2, + sizeof(curhost->adh_password)) >= + sizeof(curhost->adh_password)) { + pjdlog_error("Password value is too long."); + bzero($2, strlen($2)); + free($2); + return (1); + } + bzero($2, strlen($2)); + free($2); + } + ; + +certfile_statement: CERTFILE STR + { + PJDLOG_ASSERT(cursection == SECTION_RECEIVER); + PJDLOG_ASSERT(depth == 1); + + if (strlcpy(lconfig->adc_certfile, $2, + sizeof(lconfig->adc_certfile)) >= + sizeof(lconfig->adc_certfile)) { + pjdlog_error("Certfile value is too long."); + free($2); + return (1); + } + free($2); + } + ; + +keyfile_statement: KEYFILE STR + { + PJDLOG_ASSERT(cursection == SECTION_RECEIVER); + PJDLOG_ASSERT(depth == 1); + + if (strlcpy(lconfig->adc_keyfile, $2, + sizeof(lconfig->adc_keyfile)) >= + sizeof(lconfig->adc_keyfile)) { + pjdlog_error("Keyfile value is too long."); + free($2); + return (1); + } + free($2); + } + ; + +listen_statement: LISTEN STR + { + struct adist_listen *lst; + + PJDLOG_ASSERT(depth == 1); + PJDLOG_ASSERT(cursection == SECTION_RECEIVER); + + lst = calloc(1, sizeof(*lst)); + if (lst == NULL) { + pjdlog_error("Unable to allocate memory for listen address."); + free($2); + return (1); + } + if (strlcpy(lst->adl_addr, $2, sizeof(lst->adl_addr)) >= + sizeof(lst->adl_addr)) { + pjdlog_error("listen argument is too long."); + free($2); + free(lst); + return (1); + } + TAILQ_INSERT_TAIL(&lconfig->adc_listen, lst, adl_next); + free($2); + } + ; + +sender_host_statement: HOST host_start OB sender_host_entries CB + { + /* Put it onto host list. */ + TAILQ_INSERT_TAIL(&lconfig->adc_hosts, curhost, adh_next); + curhost = NULL; + } + ; + +receiver_host_statement: HOST host_start OB receiver_host_entries CB + { + /* Put it onto host list. */ + TAILQ_INSERT_TAIL(&lconfig->adc_hosts, curhost, adh_next); + curhost = NULL; + } + ; + +host_start: STR + { + /* Check if there is no duplicate entry. */ + TAILQ_FOREACH(curhost, &lconfig->adc_hosts, adh_next) { + if (strcmp(curhost->adh_name, $1) != 0) + continue; + if (curhost->adh_role == ADIST_ROLE_SENDER && + cursection == SECTION_RECEIVER) { + continue; + } + if (curhost->adh_role == ADIST_ROLE_RECEIVER && + cursection == SECTION_SENDER) { + continue; + } + pjdlog_error("%s host %s is configured more than once.", + curhost->adh_role == ADIST_ROLE_SENDER ? + "Sender" : "Receiver", curhost->adh_name); + free($1); + return (1); + } + + curhost = calloc(1, sizeof(*curhost)); + if (curhost == NULL) { + pjdlog_error("Unable to allocate memory for host configuration."); + free($1); + return (1); + } + if (strlcpy(curhost->adh_name, $1, sizeof(curhost->adh_name)) >= + sizeof(curhost->adh_name)) { + pjdlog_error("Host name is too long."); + free($1); + return (1); + } + free($1); + curhost->adh_role = cursection == SECTION_SENDER ? + ADIST_ROLE_SENDER : ADIST_ROLE_RECEIVER; + curhost->adh_version = ADIST_VERSION; + curhost->adh_localaddr[0] = '\0'; + curhost->adh_remoteaddr[0] = '\0'; + curhost->adh_remote = NULL; + curhost->adh_directory[0] = '\0'; + curhost->adh_password[0] = '\0'; + curhost->adh_fingerprint[0] = '\0'; + curhost->adh_worker_pid = 0; + curhost->adh_conn = NULL; + } + ; + +sender_host_entries: + | + sender_host_entries sender_host_entry + ; + +sender_host_entry: + source_statement + | + remote_statement + | + directory_statement + | + fingerprint_statement + | + password_statement +/* + | + checksum_statement + | + compression_statement +*/ + ; + +receiver_host_entries: + | + receiver_host_entries receiver_host_entry + ; + +receiver_host_entry: + remote_statement + | + directory_statement + | + password_statement + ; + +remote_statement: REMOTE STR + { + PJDLOG_ASSERT(depth == 2); + PJDLOG_ASSERT(cursection == SECTION_SENDER || + cursection == SECTION_RECEIVER); + + if (strlcpy(curhost->adh_remoteaddr, $2, + sizeof(curhost->adh_remoteaddr)) >= + sizeof(curhost->adh_remoteaddr)) { + pjdlog_error("Remote value is too long."); + free($2); + return (1); + } + free($2); + } + ; + +%% + +static bool +family_supported(int family) +{ + int sock; + + sock = socket(family, SOCK_STREAM, 0); + if (sock == -1 && errno == EPROTONOSUPPORT) + return (false); + if (sock >= 0) + (void)close(sock); + return (true); +} + +static bool +adjust_directory(char *path) +{ + size_t len; + + len = strlen(path); + for (;;) { + if (len == 0) { + pjdlog_error("Directory path is empty."); + return (false); + } + if (path[len - 1] != '/') + break; + len--; + path[len] = '\0'; + } + if (path[0] != '/') { + pjdlog_error("Directory path must be absolute."); + return (false); + } + return (true); +} + +static int +my_name(char *name, size_t size) +{ + char buf[MAXHOSTNAMELEN]; + char *pos; + + if (gethostname(buf, sizeof(buf)) < 0) { + pjdlog_errno(LOG_ERR, "gethostname() failed"); + return (-1); + } + + /* First component of the host name. */ + pos = strchr(buf, '.'); + if (pos == NULL) + (void)strlcpy(name, buf, size); + else + (void)strlcpy(name, buf, MIN((size_t)(pos - buf + 1), size)); + + if (name[0] == '\0') { + pjdlog_error("Empty host name."); + return (-1); + } + + return (0); +} + +void +yyerror(const char *str) +{ + + pjdlog_error("Unable to parse configuration file at line %d near '%s': %s", + lineno, yytext, str); +} + +struct adist_config * +yy_config_parse(const char *config, bool exitonerror) +{ + int ret; + + curhost = NULL; + cursection = SECTION_GLOBAL; + depth = 0; + lineno = 0; + + lconfig = calloc(1, sizeof(*lconfig)); + if (lconfig == NULL) { + pjdlog_error("Unable to allocate memory for configuration."); + if (exitonerror) + exit(EX_TEMPFAIL); + return (NULL); + } + TAILQ_INIT(&lconfig->adc_hosts); + TAILQ_INIT(&lconfig->adc_listen); + lconfig->adc_name[0] = '\0'; + lconfig->adc_timeout = -1; + lconfig->adc_pidfile[0] = '\0'; + lconfig->adc_certfile[0] = '\0'; + lconfig->adc_keyfile[0] = '\0'; + + yyin = fopen(config, "r"); + if (yyin == NULL) { + pjdlog_errno(LOG_ERR, "Unable to open configuration file %s", + config); + yy_config_free(lconfig); + if (exitonerror) + exit(EX_OSFILE); + return (NULL); + } + yyrestart(yyin); + ret = yyparse(); + fclose(yyin); + if (ret != 0) { + yy_config_free(lconfig); + if (exitonerror) + exit(EX_CONFIG); + return (NULL); + } + + /* + * Let's see if everything is set up. + */ + if (lconfig->adc_name[0] == '\0' && my_name(lconfig->adc_name, + sizeof(lconfig->adc_name)) == -1) { + yy_config_free(lconfig); + if (exitonerror) + exit(EX_CONFIG); + return (NULL); + } + if (lconfig->adc_timeout == -1) + lconfig->adc_timeout = ADIST_TIMEOUT; + if (lconfig->adc_pidfile[0] == '\0') { + (void)strlcpy(lconfig->adc_pidfile, ADIST_PIDFILE, + sizeof(lconfig->adc_pidfile)); + } + if (lconfig->adc_certfile[0] == '\0') { + (void)strlcpy(lconfig->adc_certfile, ADIST_CERTFILE, + sizeof(lconfig->adc_certfile)); + } + if (lconfig->adc_keyfile[0] == '\0') { + (void)strlcpy(lconfig->adc_keyfile, ADIST_KEYFILE, + sizeof(lconfig->adc_keyfile)); + } + + return (lconfig); +} + +void +yy_config_free(struct adist_config *config) +{ + struct adist_host *adhost; + struct adist_listen *lst; + + while ((lst = TAILQ_FIRST(&config->adc_listen)) != NULL) { + TAILQ_REMOVE(&config->adc_listen, lst, adl_next); + free(lst); + } + while ((adhost = TAILQ_FIRST(&config->adc_hosts)) != NULL) { + TAILQ_REMOVE(&config->adc_hosts, adhost, adh_next); + bzero(adhost, sizeof(*adhost)); + free(adhost); + } + free(config); +} |