diff options
Diffstat (limited to 'src/bind_config.c')
-rw-r--r-- | src/bind_config.c | 147 |
1 files changed, 113 insertions, 34 deletions
diff --git a/src/bind_config.c b/src/bind_config.c index ace5a0ac..a4c7a8d7 100644 --- a/src/bind_config.c +++ b/src/bind_config.c @@ -189,18 +189,29 @@ ssh_bind_config_parse_line(ssh_bind bind, const char *line, unsigned int count, uint32_t *parser_flags, - uint8_t *seen); - -static void local_parse_file(ssh_bind bind, - const char *filename, - uint32_t *parser_flags, - uint8_t *seen) + uint8_t *seen, + unsigned int depth); + +#define LIBSSH_BIND_CONF_MAX_DEPTH 16 +static void +local_parse_file(ssh_bind bind, + const char *filename, + uint32_t *parser_flags, + uint8_t *seen, + unsigned int depth) { FILE *f; char line[MAX_LINE_SIZE] = {0}; unsigned int count = 0; int rv; + if (depth > LIBSSH_BIND_CONF_MAX_DEPTH) { + ssh_set_error(bind, SSH_FATAL, + "ERROR - Too many levels of configuration includes " + "when processing file '%s'", filename); + return; + } + f = fopen(filename, "r"); if (f == NULL) { SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load", @@ -213,7 +224,7 @@ static void local_parse_file(ssh_bind bind, while (fgets(line, sizeof(line), f)) { count++; - rv = ssh_bind_config_parse_line(bind, line, count, parser_flags, seen); + rv = ssh_bind_config_parse_line(bind, line, count, parser_flags, seen, depth); if (rv < 0) { fclose(f); return; @@ -228,7 +239,8 @@ static void local_parse_file(ssh_bind bind, static void local_parse_glob(ssh_bind bind, const char *fileglob, uint32_t *parser_flags, - uint8_t *seen) + uint8_t *seen, + unsigned int depth) { glob_t globbuf = { .gl_flags = 0, @@ -248,7 +260,7 @@ static void local_parse_glob(ssh_bind bind, } for (i = 0; i < globbuf.gl_pathc; i++) { - local_parse_file(bind, globbuf.gl_pathv[i], parser_flags, seen); + local_parse_file(bind, globbuf.gl_pathv[i], parser_flags, seen, depth); } globfree(&globbuf); @@ -274,7 +286,8 @@ ssh_bind_config_parse_line(ssh_bind bind, const char *line, unsigned int count, uint32_t *parser_flags, - uint8_t *seen) + uint8_t *seen, + unsigned int depth) { enum ssh_bind_config_opcode_e opcode; const char *p = NULL; @@ -288,7 +301,12 @@ ssh_bind_config_parse_line(ssh_bind bind, return -1; } - if ((line == NULL) || (parser_flags == NULL)) { + /* Ignore empty lines */ + if (line == NULL || *line == '\0') { + return 0; + } + + if (parser_flags == NULL) { ssh_set_error_invalid(bind); return -1; } @@ -333,9 +351,9 @@ ssh_bind_config_parse_line(ssh_bind bind, p = ssh_config_get_str_tok(&s, NULL); if (p && (*parser_flags & PARSING)) { #if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER) - local_parse_glob(bind, p, parser_flags, seen); + local_parse_glob(bind, p, parser_flags, seen, depth + 1); #else - local_parse_file(bind, p, parser_flags, seen); + local_parse_file(bind, p, parser_flags, seen, depth + 1); #endif /* HAVE_GLOB */ } break; @@ -345,7 +363,7 @@ ssh_bind_config_parse_line(ssh_bind bind, if (p && (*parser_flags & PARSING)) { rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HOSTKEY, p); if (rc != 0) { - SSH_LOG(SSH_LOG_WARN, + SSH_LOG(SSH_LOG_TRACE, "line %d: Failed to set Hostkey value '%s'", count, p); } @@ -356,7 +374,7 @@ ssh_bind_config_parse_line(ssh_bind bind, if (p && (*parser_flags & PARSING)) { rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDADDR, p); if (rc != 0) { - SSH_LOG(SSH_LOG_WARN, + SSH_LOG(SSH_LOG_TRACE, "line %d: Failed to set ListenAddress value '%s'", count, p); } @@ -367,7 +385,7 @@ ssh_bind_config_parse_line(ssh_bind bind, if (p && (*parser_flags & PARSING)) { rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDPORT_STR, p); if (rc != 0) { - SSH_LOG(SSH_LOG_WARN, + SSH_LOG(SSH_LOG_TRACE, "line %d: Failed to set Port value '%s'", count, p); } @@ -378,7 +396,7 @@ ssh_bind_config_parse_line(ssh_bind bind, if (p && (*parser_flags & PARSING)) { rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_CIPHERS_C_S, p); if (rc != 0) { - SSH_LOG(SSH_LOG_WARN, + SSH_LOG(SSH_LOG_TRACE, "line %d: Failed to set C->S Ciphers value '%s'", count, p); break; @@ -386,7 +404,7 @@ ssh_bind_config_parse_line(ssh_bind bind, rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_CIPHERS_S_C, p); if (rc != 0) { - SSH_LOG(SSH_LOG_WARN, + SSH_LOG(SSH_LOG_TRACE, "line %d: Failed to set S->C Ciphers value '%s'", count, p); } @@ -397,7 +415,7 @@ ssh_bind_config_parse_line(ssh_bind bind, if (p && (*parser_flags & PARSING)) { rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HMAC_C_S, p); if (rc != 0) { - SSH_LOG(SSH_LOG_WARN, + SSH_LOG(SSH_LOG_TRACE, "line %d: Failed to set C->S MAC value '%s'", count, p); break; @@ -405,7 +423,7 @@ ssh_bind_config_parse_line(ssh_bind bind, rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HMAC_S_C, p); if (rc != 0) { - SSH_LOG(SSH_LOG_WARN, + SSH_LOG(SSH_LOG_TRACE, "line %d: Failed to set S->C MAC value '%s'", count, p); } @@ -419,10 +437,10 @@ ssh_bind_config_parse_line(ssh_bind bind, if (strcasecmp(p, "quiet") == 0) { value = SSH_LOG_NONE; } else if (strcasecmp(p, "fatal") == 0 || - strcasecmp(p, "error")== 0 || - strcasecmp(p, "info") == 0) { + strcasecmp(p, "error")== 0) { value = SSH_LOG_WARN; - } else if (strcasecmp(p, "verbose") == 0) { + } else if (strcasecmp(p, "verbose") == 0 || + strcasecmp(p, "info") == 0) { value = SSH_LOG_INFO; } else if (strcasecmp(p, "DEBUG") == 0 || strcasecmp(p, "DEBUG1") == 0) { @@ -435,7 +453,7 @@ ssh_bind_config_parse_line(ssh_bind bind, rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &value); if (rc != 0) { - SSH_LOG(SSH_LOG_WARN, + SSH_LOG(SSH_LOG_TRACE, "line %d: Failed to set LogLevel value '%s'", count, p); } @@ -447,7 +465,7 @@ ssh_bind_config_parse_line(ssh_bind bind, if (p && (*parser_flags & PARSING)) { rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_KEY_EXCHANGE, p); if (rc != 0) { - SSH_LOG(SSH_LOG_WARN, + SSH_LOG(SSH_LOG_TRACE, "line %d: Failed to set KexAlgorithms value '%s'", count, p); } @@ -522,13 +540,13 @@ ssh_bind_config_parse_line(ssh_bind bind, /* Skip one argument */ p = ssh_config_get_str_tok(&s, NULL); if (p == NULL || p[0] == '\0') { - SSH_LOG(SSH_LOG_WARN, "line %d: Match keyword " + SSH_LOG(SSH_LOG_TRACE, "line %d: Match keyword " "'%s' requires argument\n", count, p2); SAFE_FREE(x); return -1; } args++; - SSH_LOG(SSH_LOG_WARN, + SSH_LOG(SSH_LOG_DEBUG, "line %d: Unsupported Match keyword '%s', ignoring\n", count, p2); @@ -558,7 +576,7 @@ ssh_bind_config_parse_line(ssh_bind bind, rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_PUBKEY_ACCEPTED_KEY_TYPES, p); if (rc != 0) { - SSH_LOG(SSH_LOG_WARN, + SSH_LOG(SSH_LOG_TRACE, "line %d: Failed to set PubKeyAcceptedKeyTypes value '%s'", count, p); } @@ -570,26 +588,26 @@ ssh_bind_config_parse_line(ssh_bind bind, rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, p); if (rc != 0) { - SSH_LOG(SSH_LOG_WARN, + SSH_LOG(SSH_LOG_TRACE, "line %d: Failed to set HostkeyAlgorithms value '%s'", count, p); } } break; case BIND_CFG_NOT_ALLOWED_IN_MATCH: - SSH_LOG(SSH_LOG_WARN, "Option not allowed in Match block: %s, line: %d", + SSH_LOG(SSH_LOG_DEBUG, "Option not allowed in Match block: %s, line: %d", keyword, count); break; case BIND_CFG_UNKNOWN: - SSH_LOG(SSH_LOG_WARN, "Unknown option: %s, line: %d", + SSH_LOG(SSH_LOG_TRACE, "Unknown option: %s, line: %d", keyword, count); break; case BIND_CFG_UNSUPPORTED: - SSH_LOG(SSH_LOG_WARN, "Unsupported option: %s, line: %d", + SSH_LOG(SSH_LOG_TRACE, "Unsupported option: %s, line: %d", keyword, count); break; case BIND_CFG_NA: - SSH_LOG(SSH_LOG_WARN, "Option not applicable: %s, line: %d", + SSH_LOG(SSH_LOG_TRACE, "Option not applicable: %s, line: %d", keyword, count); break; default: @@ -628,7 +646,7 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename) parser_flags = PARSING; while (fgets(line, sizeof(line), f)) { count++; - rv = ssh_bind_config_parse_line(bind, line, count, &parser_flags, seen); + rv = ssh_bind_config_parse_line(bind, line, count, &parser_flags, seen, 0); if (rv) { fclose(f); return -1; @@ -638,3 +656,64 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename) fclose(f); return 0; } + +/* @brief Parse configuration string and set the options to the given bind session + * + * @params[in] bind The ssh bind session + * @params[in] input Null terminated string containing the configuration + * + * @returns SSH_OK on successful parsing the configuration string, + * SSH_ERROR on error + */ +int ssh_bind_config_parse_string(ssh_bind bind, const char *input) +{ + char line[MAX_LINE_SIZE] = {0}; + const char *c = input, *line_start = input; + unsigned int line_num = 0, line_len; + uint32_t parser_flags; + int rv; + + /* This local table is used during the parsing of the current file (and + * files included recursively in this file) to prevent an option to be + * redefined, i.e. the first value set is kept. But this DO NOT prevent the + * option to be redefined later by another file. */ + uint8_t seen[BIND_CFG_MAX] = {0}; + + SSH_LOG(SSH_LOG_DEBUG, "Reading bind configuration data from string:"); + SSH_LOG(SSH_LOG_DEBUG, "START\n%s\nEND", input); + + parser_flags = PARSING; + while (1) { + line_num++; + line_start = c; + c = strchr(line_start, '\n'); + if (c == NULL) { + /* if there is no newline at the end of the string */ + c = strchr(line_start, '\0'); + } + if (c == NULL) { + /* should not happen, would mean a string without trailing '\0' */ + SSH_LOG(SSH_LOG_WARN, "No trailing '\\0' in config string"); + return SSH_ERROR; + } + line_len = c - line_start; + if (line_len > MAX_LINE_SIZE - 1) { + SSH_LOG(SSH_LOG_WARN, "Line %u too long: %u characters", + line_num, line_len); + return SSH_ERROR; + } + memcpy(line, line_start, line_len); + line[line_len] = '\0'; + SSH_LOG(SSH_LOG_DEBUG, "Line %u: %s", line_num, line); + rv = ssh_bind_config_parse_line(bind, line, line_num, &parser_flags, seen, 0); + if (rv < 0) { + return SSH_ERROR; + } + if (*c == '\0') { + break; + } + c++; + } + + return SSH_OK; +} |