2012-11-30 23:50:07 +00:00
|
|
|
%{
|
|
|
|
/*-
|
|
|
|
* 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.
|
|
|
|
*
|
2012-12-01 00:02:31 +00:00
|
|
|
* $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/parse.y#5 $
|
2012-11-30 23:50:07 +00:00
|
|
|
*/
|
|
|
|
|
2012-12-01 00:02:31 +00:00
|
|
|
#include <config/config.h>
|
2012-11-30 23:50:07 +00:00
|
|
|
|
|
|
|
#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"
|
2012-12-01 00:02:31 +00:00
|
|
|
#include "pjdlog.h"
|
2012-11-30 23:50:07 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|