Blame src/bind_config.c

Packit 6c0a39
/*
Packit 6c0a39
 * bind_config.c - Parse the SSH server configuration file
Packit 6c0a39
 *
Packit 6c0a39
 * This file is part of the SSH Library
Packit 6c0a39
 *
Packit 6c0a39
 * Copyright (c) 2019 by Red Hat, Inc.
Packit 6c0a39
 *
Packit 6c0a39
 * Author: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is free software; you can redistribute it and/or modify
Packit 6c0a39
 * it under the terms of the GNU Lesser General Public License as published by
Packit 6c0a39
 * the Free Software Foundation; either version 2.1 of the License, or (at your
Packit 6c0a39
 * option) any later version.
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is distributed in the hope that it will be useful, but
Packit 6c0a39
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 6c0a39
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit 6c0a39
 * License for more details.
Packit 6c0a39
 *
Packit 6c0a39
 * You should have received a copy of the GNU Lesser General Public License
Packit 6c0a39
 * along with the SSH Library; see the file COPYING.  If not, write to
Packit 6c0a39
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
Packit 6c0a39
 * MA 02111-1307, USA.
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
#include "config.h"
Packit 6c0a39
Packit 6c0a39
#include <ctype.h>
Packit 6c0a39
#include <stdio.h>
Packit 6c0a39
#include <string.h>
Packit 6c0a39
#include <stdlib.h>
Packit 6c0a39
#ifdef HAVE_GLOB_H
Packit 6c0a39
# include <glob.h>
Packit 6c0a39
#endif
Packit 6c0a39
Packit 6c0a39
#include "libssh/bind.h"
Packit 6c0a39
#include "libssh/bind_config.h"
Packit 6c0a39
#include "libssh/config_parser.h"
Packit 6c0a39
#include "libssh/priv.h"
Packit 6c0a39
#include "libssh/server.h"
Packit 6c0a39
#include "libssh/options.h"
Packit 6c0a39
Packit 6c0a39
#define MAX_LINE_SIZE 1024
Packit 6c0a39
Packit 6c0a39
/* Flags used for the parser state */
Packit 6c0a39
#define PARSING     1
Packit 6c0a39
#define IN_MATCH    (1<<1)
Packit 6c0a39
Packit 6c0a39
struct ssh_bind_config_keyword_table_s {
Packit 6c0a39
  const char *name;
Packit 6c0a39
  enum ssh_bind_config_opcode_e opcode;
Packit 6c0a39
  bool allowed_in_match;
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
static struct ssh_bind_config_keyword_table_s
Packit 6c0a39
ssh_bind_config_keyword_table[] = {
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "include",
Packit 6c0a39
        .opcode = BIND_CFG_INCLUDE
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "hostkey",
Packit 6c0a39
        .opcode = BIND_CFG_HOSTKEY
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "listenaddress",
Packit 6c0a39
        .opcode = BIND_CFG_LISTENADDRESS
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "port",
Packit 6c0a39
        .opcode = BIND_CFG_PORT
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "loglevel",
Packit 6c0a39
        .opcode = BIND_CFG_LOGLEVEL,
Packit 6c0a39
        .allowed_in_match = true,
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "ciphers",
Packit 6c0a39
        .opcode = BIND_CFG_CIPHERS
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "macs",
Packit 6c0a39
        .opcode = BIND_CFG_MACS
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "kexalgorithms",
Packit 6c0a39
        .opcode = BIND_CFG_KEXALGORITHMS
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "match",
Packit 6c0a39
        .opcode = BIND_CFG_MATCH,
Packit 6c0a39
        .allowed_in_match = true
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "pubkeyacceptedkeytypes",
Packit 6c0a39
        .opcode = BIND_CFG_PUBKEY_ACCEPTED_KEY_TYPES,
Packit 6c0a39
        .allowed_in_match = true
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "hostkeyalgorithms",
Packit 6c0a39
        .opcode = BIND_CFG_HOSTKEY_ALGORITHMS,
Packit 6c0a39
        .allowed_in_match = true
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .opcode = BIND_CFG_UNKNOWN,
Packit 6c0a39
    }
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
enum ssh_bind_config_match_e {
Packit 6c0a39
    BIND_MATCH_UNKNOWN = -1,
Packit 6c0a39
    BIND_MATCH_ALL,
Packit 6c0a39
    BIND_MATCH_USER,
Packit 6c0a39
    BIND_MATCH_GROUP,
Packit 6c0a39
    BIND_MATCH_HOST,
Packit 6c0a39
    BIND_MATCH_LOCALADDRESS,
Packit 6c0a39
    BIND_MATCH_LOCALPORT,
Packit 6c0a39
    BIND_MATCH_RDOMAIN,
Packit 6c0a39
    BIND_MATCH_ADDRESS,
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
struct ssh_bind_config_match_keyword_table_s {
Packit 6c0a39
    const char *name;
Packit 6c0a39
    enum ssh_bind_config_match_e opcode;
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
static struct ssh_bind_config_match_keyword_table_s
Packit 6c0a39
ssh_bind_config_match_keyword_table[] = {
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "all",
Packit 6c0a39
        .opcode = BIND_MATCH_ALL
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "user",
Packit 6c0a39
        .opcode = BIND_MATCH_USER
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "group",
Packit 6c0a39
        .opcode = BIND_MATCH_GROUP
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "host",
Packit 6c0a39
        .opcode = BIND_MATCH_HOST
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "localaddress",
Packit 6c0a39
        .opcode = BIND_MATCH_LOCALADDRESS
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "localport",
Packit 6c0a39
        .opcode = BIND_MATCH_LOCALPORT
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "rdomain",
Packit 6c0a39
        .opcode = BIND_MATCH_RDOMAIN
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .name   = "address",
Packit 6c0a39
        .opcode = BIND_MATCH_ADDRESS
Packit 6c0a39
    },
Packit 6c0a39
    {
Packit 6c0a39
        .opcode = BIND_MATCH_UNKNOWN
Packit 6c0a39
    },
Packit 6c0a39
};
Packit 6c0a39
Packit 6c0a39
static enum ssh_bind_config_opcode_e
Packit 6c0a39
ssh_bind_config_get_opcode(char *keyword, uint32_t *parser_flags)
Packit 6c0a39
{
Packit 6c0a39
    int i;
Packit 6c0a39
Packit 6c0a39
    for (i = 0; ssh_bind_config_keyword_table[i].name != NULL; i++) {
Packit 6c0a39
        if (strcasecmp(keyword, ssh_bind_config_keyword_table[i].name) == 0) {
Packit 6c0a39
            if ((*parser_flags & IN_MATCH) &&
Packit 6c0a39
                !(ssh_bind_config_keyword_table[i].allowed_in_match))
Packit 6c0a39
            {
Packit 6c0a39
                return BIND_CFG_NOT_ALLOWED_IN_MATCH;
Packit 6c0a39
            }
Packit 6c0a39
            return ssh_bind_config_keyword_table[i].opcode;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return BIND_CFG_UNKNOWN;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static int
Packit 6c0a39
ssh_bind_config_parse_line(ssh_bind bind,
Packit 6c0a39
                           const char *line,
Packit 6c0a39
                           unsigned int count,
Packit 6c0a39
                           uint32_t *parser_flags,
Packit 6c0a39
                           uint8_t *seen);
Packit 6c0a39
Packit 6c0a39
static void local_parse_file(ssh_bind bind,
Packit 6c0a39
                             const char *filename,
Packit 6c0a39
                             uint32_t *parser_flags,
Packit 6c0a39
                             uint8_t *seen)
Packit 6c0a39
{
Packit 6c0a39
    FILE *f;
Packit 6c0a39
    char line[MAX_LINE_SIZE] = {0};
Packit 6c0a39
    unsigned int count = 0;
Packit 6c0a39
    int rv;
Packit 6c0a39
Packit 6c0a39
    f = fopen(filename, "r");
Packit 6c0a39
    if (f == NULL) {
Packit 6c0a39
        SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
Packit 6c0a39
                filename);
Packit 6c0a39
        return;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    SSH_LOG(SSH_LOG_PACKET, "Reading additional configuration data from %s",
Packit 6c0a39
            filename);
Packit 6c0a39
Packit 6c0a39
    while (fgets(line, sizeof(line), f)) {
Packit 6c0a39
        count++;
Packit 6c0a39
        rv = ssh_bind_config_parse_line(bind, line, count, parser_flags, seen);
Packit 6c0a39
        if (rv < 0) {
Packit 6c0a39
            fclose(f);
Packit 6c0a39
            return;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    fclose(f);
Packit 6c0a39
    return;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
Packit 6c0a39
static void local_parse_glob(ssh_bind bind,
Packit 6c0a39
                             const char *fileglob,
Packit 6c0a39
                             uint32_t *parser_flags,
Packit 6c0a39
                             uint8_t *seen)
Packit 6c0a39
{
Packit 6c0a39
    glob_t globbuf = {
Packit 6c0a39
        .gl_flags = 0,
Packit 6c0a39
    };
Packit 6c0a39
    int rt;
Packit 6c0a39
    u_int i;
Packit 6c0a39
Packit 6c0a39
    rt = glob(fileglob, GLOB_TILDE, NULL, &globbuf);
Packit 6c0a39
    if (rt == GLOB_NOMATCH) {
Packit 6c0a39
        globfree(&globbuf);
Packit 6c0a39
        return;
Packit 6c0a39
    } else if (rt != 0) {
Packit 6c0a39
        SSH_LOG(SSH_LOG_RARE, "Glob error: %s",
Packit 6c0a39
                fileglob);
Packit 6c0a39
        globfree(&globbuf);
Packit 6c0a39
        return;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    for (i = 0; i < globbuf.gl_pathc; i++) {
Packit 6c0a39
        local_parse_file(bind, globbuf.gl_pathv[i], parser_flags, seen);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    globfree(&globbuf);
Packit 6c0a39
}
Packit 6c0a39
#endif /* HAVE_GLOB HAVE_GLOB_GL_FLAGS_MEMBER */
Packit 6c0a39
Packit 6c0a39
static enum ssh_bind_config_match_e
Packit 6c0a39
ssh_bind_config_get_match_opcode(const char *keyword)
Packit 6c0a39
{
Packit 6c0a39
    size_t i;
Packit 6c0a39
Packit 6c0a39
    for (i = 0; ssh_bind_config_match_keyword_table[i].name != NULL; i++) {
Packit 6c0a39
        if (strcasecmp(keyword, ssh_bind_config_match_keyword_table[i].name) == 0) {
Packit 6c0a39
            return ssh_bind_config_match_keyword_table[i].opcode;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return BIND_MATCH_UNKNOWN;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static int
Packit 6c0a39
ssh_bind_config_parse_line(ssh_bind bind,
Packit 6c0a39
                           const char *line,
Packit 6c0a39
                           unsigned int count,
Packit 6c0a39
                           uint32_t *parser_flags,
Packit 6c0a39
                           uint8_t *seen)
Packit 6c0a39
{
Packit 6c0a39
    enum ssh_bind_config_opcode_e opcode;
Packit 6c0a39
    const char *p = NULL;
Packit 6c0a39
    char *s = NULL, *x = NULL;
Packit 6c0a39
    char *keyword = NULL;
Packit 6c0a39
    size_t len;
Packit 6c0a39
Packit 6c0a39
    int rc = 0;
Packit 6c0a39
Packit 6c0a39
    if (bind == NULL) {
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if ((line == NULL) || (parser_flags == NULL)) {
Packit 6c0a39
        ssh_set_error_invalid(bind);
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    x = s = strdup(line);
Packit 6c0a39
    if (s == NULL) {
Packit 6c0a39
        ssh_set_error_oom(bind);
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* Remove trailing spaces */
Packit 6c0a39
    for (len = strlen(s) - 1; len > 0; len--) {
Packit 6c0a39
        if (! isspace(s[len])) {
Packit 6c0a39
            break;
Packit 6c0a39
        }
Packit 6c0a39
        s[len] = '\0';
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    keyword = ssh_config_get_token(&s);
Packit 6c0a39
    if (keyword == NULL || *keyword == '#' ||
Packit 6c0a39
            *keyword == '\0' || *keyword == '\n') {
Packit 6c0a39
        SAFE_FREE(x);
Packit 6c0a39
        return 0;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    opcode = ssh_bind_config_get_opcode(keyword, parser_flags);
Packit 6c0a39
    if ((*parser_flags & PARSING) &&
Packit 6c0a39
            opcode != BIND_CFG_HOSTKEY &&
Packit 6c0a39
            opcode != BIND_CFG_INCLUDE &&
Packit 6c0a39
            opcode != BIND_CFG_MATCH &&
Packit 6c0a39
            opcode > BIND_CFG_UNSUPPORTED) { /* Ignore all unknown types here */
Packit 6c0a39
        /* Skip all the options that were already applied */
Packit 6c0a39
        if (seen[opcode] != 0) {
Packit 6c0a39
            SAFE_FREE(x);
Packit 6c0a39
            return 0;
Packit 6c0a39
        }
Packit 6c0a39
        seen[opcode] = 1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    switch (opcode) {
Packit 6c0a39
    case BIND_CFG_INCLUDE:
Packit 6c0a39
        p = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
        if (p && (*parser_flags & PARSING)) {
Packit 6c0a39
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
Packit 6c0a39
            local_parse_glob(bind, p, parser_flags, seen);
Packit 6c0a39
#else
Packit 6c0a39
            local_parse_file(bind, p, parser_flags, seen);
Packit 6c0a39
#endif /* HAVE_GLOB */
Packit 6c0a39
        }
Packit 6c0a39
        break;
Packit 6c0a39
Packit 6c0a39
    case BIND_CFG_HOSTKEY:
Packit 6c0a39
        p = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
        if (p && (*parser_flags & PARSING)) {
Packit 6c0a39
            rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HOSTKEY, p);
Packit 6c0a39
            if (rc != 0) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                        "line %d: Failed to set Hostkey value '%s'",
Packit 6c0a39
                        count, p);
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
        break;
Packit 6c0a39
    case BIND_CFG_LISTENADDRESS:
Packit 6c0a39
        p = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
        if (p && (*parser_flags & PARSING)) {
Packit 6c0a39
            rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDADDR, p);
Packit 6c0a39
            if (rc != 0) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                        "line %d: Failed to set ListenAddress value '%s'",
Packit 6c0a39
                        count, p);
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
        break;
Packit 6c0a39
    case BIND_CFG_PORT:
Packit 6c0a39
        p = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
        if (p && (*parser_flags & PARSING)) {
Packit 6c0a39
            rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDPORT_STR, p);
Packit 6c0a39
            if (rc != 0) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                        "line %d: Failed to set Port value '%s'",
Packit 6c0a39
                        count, p);
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
        break;
Packit 6c0a39
    case BIND_CFG_CIPHERS:
Packit 6c0a39
        p = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
        if (p && (*parser_flags & PARSING)) {
Packit 6c0a39
            rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_CIPHERS_C_S, p);
Packit 6c0a39
            if (rc != 0) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                        "line %d: Failed to set C->S Ciphers value '%s'",
Packit 6c0a39
                        count, p);
Packit 6c0a39
                break;
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_CIPHERS_S_C, p);
Packit 6c0a39
            if (rc != 0) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                        "line %d: Failed to set S->C Ciphers value '%s'",
Packit 6c0a39
                        count, p);
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
        break;
Packit 6c0a39
    case BIND_CFG_MACS:
Packit 6c0a39
        p = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
        if (p && (*parser_flags & PARSING)) {
Packit 6c0a39
            rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HMAC_C_S, p);
Packit 6c0a39
            if (rc != 0) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                        "line %d: Failed to set C->S MAC value '%s'",
Packit 6c0a39
                        count, p);
Packit 6c0a39
                break;
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HMAC_S_C, p);
Packit 6c0a39
            if (rc != 0) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                        "line %d: Failed to set S->C MAC value '%s'",
Packit 6c0a39
                        count, p);
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
        break;
Packit 6c0a39
    case BIND_CFG_LOGLEVEL:
Packit 6c0a39
        p = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
        if (p && (*parser_flags & PARSING)) {
Packit 6c0a39
            int value = -1;
Packit 6c0a39
Packit 6c0a39
            if (strcasecmp(p, "quiet") == 0) {
Packit 6c0a39
                value = SSH_LOG_NONE;
Packit 6c0a39
            } else if (strcasecmp(p, "fatal") == 0 ||
Packit 6c0a39
                    strcasecmp(p, "error")== 0 ||
Packit 6c0a39
                    strcasecmp(p, "info") == 0) {
Packit 6c0a39
                value = SSH_LOG_WARN;
Packit 6c0a39
            } else if (strcasecmp(p, "verbose") == 0) {
Packit 6c0a39
                value = SSH_LOG_INFO;
Packit 6c0a39
            } else if (strcasecmp(p, "DEBUG") == 0 ||
Packit 6c0a39
                    strcasecmp(p, "DEBUG1") == 0) {
Packit 6c0a39
                value = SSH_LOG_DEBUG;
Packit 6c0a39
            } else if (strcasecmp(p, "DEBUG2") == 0 ||
Packit 6c0a39
                    strcasecmp(p, "DEBUG3") == 0) {
Packit 6c0a39
                value = SSH_LOG_TRACE;
Packit 6c0a39
            }
Packit 6c0a39
            if (value != -1) {
Packit 6c0a39
                rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_LOG_VERBOSITY,
Packit 6c0a39
                        &value);
Packit 6c0a39
                if (rc != 0) {
Packit 6c0a39
                    SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                            "line %d: Failed to set LogLevel value '%s'",
Packit 6c0a39
                            count, p);
Packit 6c0a39
                }
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
        break;
Packit 6c0a39
    case BIND_CFG_KEXALGORITHMS:
Packit 6c0a39
        p = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
        if (p && (*parser_flags & PARSING)) {
Packit 6c0a39
            rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_KEY_EXCHANGE, p);
Packit 6c0a39
            if (rc != 0) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                        "line %d: Failed to set KexAlgorithms value '%s'",
Packit 6c0a39
                        count, p);
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
        break;
Packit 6c0a39
    case BIND_CFG_MATCH: {
Packit 6c0a39
        bool negate;
Packit 6c0a39
        int result = PARSING;
Packit 6c0a39
        size_t args = 0;
Packit 6c0a39
        enum ssh_bind_config_match_e opt;
Packit 6c0a39
        const char *p2 = NULL;
Packit 6c0a39
Packit 6c0a39
        /* The options set in Match blocks should be applied when a connection
Packit 6c0a39
         * is accepted, and not right away when parsing the file (as it is
Packit 6c0a39
         * currently done). This means the configuration files should be parsed
Packit 6c0a39
         * again or the options set in the Match blocks should be stored and
Packit 6c0a39
         * applied as necessary. */
Packit 6c0a39
Packit 6c0a39
        /* If this is the first Match block, erase the seen table to allow
Packit 6c0a39
         * options to be overridden. Erasing the seen table was the easiest way
Packit 6c0a39
         * to allow overriding an option, but only for the first occurrence of
Packit 6c0a39
         * an option in a Match block. This is sufficient for the current
Packit 6c0a39
         * implementation which supports only the 'All' criterion, meaning the
Packit 6c0a39
         * options can be applied right away. */
Packit 6c0a39
        if (!(*parser_flags & IN_MATCH)) {
Packit 6c0a39
            memset(seen, 0x00, BIND_CFG_MAX * sizeof(uint8_t));
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        /* In this line the PARSING bit is cleared from the flags */
Packit 6c0a39
        *parser_flags = IN_MATCH;
Packit 6c0a39
        do {
Packit 6c0a39
            p = p2 = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
            if (p == NULL || p[0] == '\0') {
Packit 6c0a39
                break;
Packit 6c0a39
            }
Packit 6c0a39
            args++;
Packit 6c0a39
            SSH_LOG(SSH_LOG_TRACE, "line %d: Processing Match keyword '%s'",
Packit 6c0a39
                    count, p);
Packit 6c0a39
Packit 6c0a39
            /* If the option is prefixed with ! the result should be negated */
Packit 6c0a39
            negate = false;
Packit 6c0a39
            if (p[0] == '!') {
Packit 6c0a39
                negate = true;
Packit 6c0a39
                p++;
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            opt = ssh_bind_config_get_match_opcode(p);
Packit 6c0a39
            switch (opt) {
Packit 6c0a39
            case BIND_MATCH_ALL:
Packit 6c0a39
                p = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
                if ((args == 1) && (p == NULL || p[0] == '\0')) {
Packit 6c0a39
                    /* The "all" keyword does not accept arguments or modifiers
Packit 6c0a39
                     */
Packit 6c0a39
                    if (negate == true) {
Packit 6c0a39
                        result = 0;
Packit 6c0a39
                    }
Packit 6c0a39
                    break;
Packit 6c0a39
                }
Packit 6c0a39
                ssh_set_error(bind, SSH_FATAL,
Packit 6c0a39
                              "line %d: ERROR - Match all cannot be combined with "
Packit 6c0a39
                              "other Match attributes", count);
Packit 6c0a39
                SAFE_FREE(x);
Packit 6c0a39
                return -1;
Packit 6c0a39
            case BIND_MATCH_USER:
Packit 6c0a39
            case BIND_MATCH_GROUP:
Packit 6c0a39
            case BIND_MATCH_HOST:
Packit 6c0a39
            case BIND_MATCH_LOCALADDRESS:
Packit 6c0a39
            case BIND_MATCH_LOCALPORT:
Packit 6c0a39
            case BIND_MATCH_RDOMAIN:
Packit 6c0a39
            case BIND_MATCH_ADDRESS:
Packit 6c0a39
                /* Only "All" is supported for now */
Packit 6c0a39
                /* Skip one argument */
Packit 6c0a39
                p = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
                if (p == NULL || p[0] == '\0') {
Packit 6c0a39
                    SSH_LOG(SSH_LOG_WARN, "line %d: Match keyword "
Packit 6c0a39
                            "'%s' requires argument\n", count, p2);
Packit 6c0a39
                    SAFE_FREE(x);
Packit 6c0a39
                    return -1;
Packit 6c0a39
                }
Packit 6c0a39
                args++;
Packit 6c0a39
                SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                        "line %d: Unsupported Match keyword '%s', ignoring\n",
Packit 6c0a39
                        count,
Packit 6c0a39
                        p2);
Packit 6c0a39
                result = 0;
Packit 6c0a39
                break;
Packit 6c0a39
            case BIND_MATCH_UNKNOWN:
Packit 6c0a39
            default:
Packit 6c0a39
                ssh_set_error(bind, SSH_FATAL,
Packit 6c0a39
                              "ERROR - Unknown argument '%s' for Match keyword", p);
Packit 6c0a39
                SAFE_FREE(x);
Packit 6c0a39
                return -1;
Packit 6c0a39
            }
Packit 6c0a39
        } while (p != NULL && p[0] != '\0');
Packit 6c0a39
        if (args == 0) {
Packit 6c0a39
            ssh_set_error(bind, SSH_FATAL,
Packit 6c0a39
                          "ERROR - Match keyword requires an argument");
Packit 6c0a39
            SAFE_FREE(x);
Packit 6c0a39
            return -1;
Packit 6c0a39
        }
Packit 6c0a39
        /* This line only sets the PARSING flag if all checks passed */
Packit 6c0a39
        *parser_flags |= result;
Packit 6c0a39
        break;
Packit 6c0a39
    }
Packit 6c0a39
    case BIND_CFG_PUBKEY_ACCEPTED_KEY_TYPES:
Packit 6c0a39
        p = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
        if (p && (*parser_flags & PARSING)) {
Packit 6c0a39
            rc = ssh_bind_options_set(bind,
Packit 6c0a39
                                 SSH_BIND_OPTIONS_PUBKEY_ACCEPTED_KEY_TYPES, p);
Packit 6c0a39
            if (rc != 0) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                        "line %d: Failed to set PubKeyAcceptedKeyTypes value '%s'",
Packit 6c0a39
                        count, p);
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
        break;
Packit 6c0a39
    case BIND_CFG_HOSTKEY_ALGORITHMS:
Packit 6c0a39
        p = ssh_config_get_str_tok(&s, NULL);
Packit 6c0a39
        if (p && (*parser_flags & PARSING)) {
Packit 6c0a39
            rc = ssh_bind_options_set(bind,
Packit 6c0a39
                                 SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, p);
Packit 6c0a39
            if (rc != 0) {
Packit 6c0a39
                SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                        "line %d: Failed to set HostkeyAlgorithms value '%s'",
Packit 6c0a39
                        count, p);
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
        break;
Packit 6c0a39
    case BIND_CFG_NOT_ALLOWED_IN_MATCH:
Packit 6c0a39
        SSH_LOG(SSH_LOG_WARN, "Option not allowed in Match block: %s, line: %d",
Packit 6c0a39
                keyword, count);
Packit 6c0a39
        break;
Packit 6c0a39
    case BIND_CFG_UNKNOWN:
Packit 6c0a39
        SSH_LOG(SSH_LOG_WARN, "Unknown option: %s, line: %d",
Packit 6c0a39
                keyword, count);
Packit 6c0a39
        break;
Packit 6c0a39
    case BIND_CFG_UNSUPPORTED:
Packit 6c0a39
        SSH_LOG(SSH_LOG_WARN, "Unsupported option: %s, line: %d",
Packit 6c0a39
                keyword, count);
Packit 6c0a39
        break;
Packit 6c0a39
    case BIND_CFG_NA:
Packit 6c0a39
        SSH_LOG(SSH_LOG_WARN, "Option not applicable: %s, line: %d",
Packit 6c0a39
                keyword, count);
Packit 6c0a39
        break;
Packit 6c0a39
    default:
Packit 6c0a39
        ssh_set_error(bind, SSH_FATAL, "ERROR - unimplemented opcode: %d",
Packit 6c0a39
                opcode);
Packit 6c0a39
        SAFE_FREE(x);
Packit 6c0a39
        return -1;
Packit 6c0a39
        break;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    SAFE_FREE(x);
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
Packit 6c0a39
{
Packit 6c0a39
    char line[MAX_LINE_SIZE] = {0};
Packit 6c0a39
    unsigned int count = 0;
Packit 6c0a39
    FILE *f;
Packit 6c0a39
    uint32_t parser_flags;
Packit 6c0a39
    int rv;
Packit 6c0a39
Packit 6c0a39
    /* This local table is used during the parsing of the current file (and
Packit 6c0a39
     * files included recursively in this file) to prevent an option to be
Packit 6c0a39
     * redefined, i.e. the first value set is kept. But this DO NOT prevent the
Packit 6c0a39
     * option to be redefined later by another file. */
Packit 6c0a39
    uint8_t seen[BIND_CFG_MAX] = {0};
Packit 6c0a39
Packit 6c0a39
    f = fopen(filename, "r");
Packit 6c0a39
    if (f == NULL) {
Packit 6c0a39
        return 0;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    SSH_LOG(SSH_LOG_PACKET, "Reading configuration data from %s", filename);
Packit 6c0a39
Packit 6c0a39
    parser_flags = PARSING;
Packit 6c0a39
    while (fgets(line, sizeof(line), f)) {
Packit 6c0a39
        count++;
Packit 6c0a39
        rv = ssh_bind_config_parse_line(bind, line, count, &parser_flags, seen);
Packit 6c0a39
        if (rv) {
Packit 6c0a39
            fclose(f);
Packit 6c0a39
            return -1;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    fclose(f);
Packit 6c0a39
    return 0;
Packit 6c0a39
}