cmake: Add option WITH_HERMETIC_USR

Add a cmake option to enable hermetic-usr, i.e., use of config files in /usr/.
If turned on, GLOBAL_*_CONFIG is prepended with /usr/ and defined as
USR_GLOBAL_*_CONFIG. Config lookup follows this path GLOBAL_*_CONFIG ->
USR_GLOBAL_*_CONFIG.

Introduce a ssh_config_parse primitive. This avoids convoluted checks for file
presence (without modifing the behaviour of ssh_config_parse_file) and allows
marking whether the config is global at the call site.

Signed-off-by: Lucas Mulling <lucas.mulling@suse.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
Lucas Mulling
2025-02-17 14:13:53 -03:00
committed by Jakub Jelen
parent 6b83aa9a40
commit 3372c2ad78
7 changed files with 119 additions and 57 deletions

View File

@@ -249,9 +249,15 @@ message(STATUS "Benchmarks: ${WITH_BENCHMARKS}")
message(STATUS "Symbol versioning: ${WITH_SYMBOL_VERSIONING}") message(STATUS "Symbol versioning: ${WITH_SYMBOL_VERSIONING}")
message(STATUS "Allow ABI break: ${WITH_ABI_BREAK}") message(STATUS "Allow ABI break: ${WITH_ABI_BREAK}")
message(STATUS "Release is final: ${WITH_FINAL}") message(STATUS "Release is final: ${WITH_FINAL}")
if (WITH_HERMETIC_USR)
message(STATUS "User global client config: ${USR_GLOBAL_CLIENT_CONFIG}")
endif ()
message(STATUS "Global client config: ${GLOBAL_CLIENT_CONFIG}") message(STATUS "Global client config: ${GLOBAL_CLIENT_CONFIG}")
if (WITH_SERVER) if (WITH_SERVER)
message(STATUS "Global bind config: ${GLOBAL_BIND_CONFIG}") if (WITH_HERMETIC_USR)
message(STATUS "User global bind config: ${USR_GLOBAL_BIND_CONFIG}")
endif ()
message(STATUS "Global bind config: ${GLOBAL_BIND_CONFIG}")
endif() endif()
message(STATUS "********************************************") message(STATUS "********************************************")

View File

@@ -27,6 +27,7 @@ option(WITH_INSECURE_NONE "Enable insecure none cipher and MAC algorithms (not s
option(WITH_EXEC "Enable libssh to execute arbitrary commands from configuration files or options (match exec, proxy commands and OpenSSH-based proxy-jumps)." ON) option(WITH_EXEC "Enable libssh to execute arbitrary commands from configuration files or options (match exec, proxy commands and OpenSSH-based proxy-jumps)." ON)
option(FUZZ_TESTING "Build with fuzzer for the server and client (automatically enables none cipher!)" OFF) option(FUZZ_TESTING "Build with fuzzer for the server and client (automatically enables none cipher!)" OFF)
option(PICKY_DEVELOPER "Build with picky developer flags" OFF) option(PICKY_DEVELOPER "Build with picky developer flags" OFF)
option(WITH_HERMETIC_USR "Build with support for hermetic /usr/" OFF)
if (WITH_ZLIB) if (WITH_ZLIB)
set(WITH_LIBZ ON) set(WITH_LIBZ ON)
@@ -59,6 +60,11 @@ if (NOT GLOBAL_CLIENT_CONFIG)
set(GLOBAL_CLIENT_CONFIG "/etc/ssh/ssh_config") set(GLOBAL_CLIENT_CONFIG "/etc/ssh/ssh_config")
endif (NOT GLOBAL_CLIENT_CONFIG) endif (NOT GLOBAL_CLIENT_CONFIG)
if (WITH_HERMETIC_USR)
set(USR_GLOBAL_BIND_CONFIG "/usr${GLOBAL_BIND_CONFIG}")
set(USR_GLOBAL_CLIENT_CONFIG "/usr${GLOBAL_CLIENT_CONFIG}")
endif (WITH_HERMETIC_USR)
if (FUZZ_TESTING) if (FUZZ_TESTING)
set(WITH_INSECURE_NONE ON) set(WITH_INSECURE_NONE ON)
endif (FUZZ_TESTING) endif (FUZZ_TESTING)

View File

@@ -9,9 +9,11 @@
#cmakedefine SOURCEDIR "${SOURCEDIR}" #cmakedefine SOURCEDIR "${SOURCEDIR}"
/* Global bind configuration file path */ /* Global bind configuration file path */
#cmakedefine USR_GLOBAL_BIND_CONFIG "${USR_GLOBAL_BIND_CONFIG}"
#cmakedefine GLOBAL_BIND_CONFIG "${GLOBAL_BIND_CONFIG}" #cmakedefine GLOBAL_BIND_CONFIG "${GLOBAL_BIND_CONFIG}"
/* Global client configuration file path */ /* Global client configuration file path */
#cmakedefine USR_GLOBAL_CLIENT_CONFIG "${USR_GLOBAL_CLIENT_CONFIG}"
#cmakedefine GLOBAL_CLIENT_CONFIG "${GLOBAL_CLIENT_CONFIG}" #cmakedefine GLOBAL_CLIENT_CONFIG "${GLOBAL_CLIENT_CONFIG}"
/************************** HEADER FILES *************************/ /************************** HEADER FILES *************************/

View File

@@ -49,9 +49,10 @@
#endif #endif
#endif #endif
#include <stdarg.h>
#include <stdint.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef _MSC_VER #ifdef _MSC_VER
typedef int mode_t; typedef int mode_t;

View File

@@ -25,6 +25,7 @@
extern "C" { extern "C" {
#endif #endif
int ssh_config_parse(ssh_session session, FILE *fp, bool global);
int ssh_config_parse_file(ssh_session session, const char *filename); int ssh_config_parse_file(ssh_session session, const char *filename);
int ssh_config_parse_string(ssh_session session, const char *input); int ssh_config_parse_string(ssh_session session, const char *input);
int ssh_options_set_algo(ssh_session session, int ssh_options_set_algo(ssh_session session,

View File

@@ -1449,6 +1449,32 @@ ssh_config_parse_line(ssh_session session,
return 0; return 0;
} }
/* @brief Parse configuration from a file pointer
*
* @params[in] session The ssh session
* @params[in] fp A valid file pointer
* @params[in] global Whether the config is global or not
*
* @returns 0 on successful parsing the configuration file, -1 on error
*/
int ssh_config_parse(ssh_session session, FILE *fp, bool global)
{
char line[MAX_LINE_SIZE] = {0};
unsigned int count = 0;
int parsing, rv;
parsing = 1;
while (fgets(line, sizeof(line), fp)) {
count++;
rv = ssh_config_parse_line(session, line, count, &parsing, 0, global);
if (rv < 0) {
return -1;
}
}
return 0;
}
/* @brief Parse configuration file and set the options to the given session /* @brief Parse configuration file and set the options to the given session
* *
* @params[in] session The ssh session * @params[in] session The ssh session
@@ -1458,36 +1484,32 @@ ssh_config_parse_line(ssh_session session,
*/ */
int ssh_config_parse_file(ssh_session session, const char *filename) int ssh_config_parse_file(ssh_session session, const char *filename)
{ {
char line[MAX_LINE_SIZE] = {0}; FILE *fp;
unsigned int count = 0; int rv;
FILE *f;
int parsing, rv;
bool global = 0; bool global = 0;
f = fopen(filename, "r"); fp = fopen(filename, "r");
if (f == NULL) { if (fp == NULL) {
return 0; return 0;
} }
rv = strcmp(filename, GLOBAL_CLIENT_CONFIG); rv = strcmp(filename, GLOBAL_CLIENT_CONFIG);
#ifdef USR_GLOBAL_CLIENT_CONFIG
if (rv != 0) {
rv = strcmp(filename, USR_GLOBAL_CLIENT_CONFIG);
}
#endif
if (rv == 0) { if (rv == 0) {
global = true; global = true;
} }
SSH_LOG(SSH_LOG_PACKET, "Reading configuration data from %s", filename); SSH_LOG(SSH_LOG_PACKET, "Reading configuration data from %s", filename);
parsing = 1; rv = ssh_config_parse(session, fp, global);
while (fgets(line, sizeof(line), f)) {
count++;
rv = ssh_config_parse_line(session, line, count, &parsing, 0, global);
if (rv < 0) {
fclose(f);
return -1;
}
}
fclose(f); fclose(fp);
return 0; return rv;
} }
/* @brief Parse configuration string and set the options to the given session /* @brief Parse configuration string and set the options to the given session

View File

@@ -26,6 +26,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#ifndef _WIN32 #ifndef _WIN32
#include <pwd.h> #include <pwd.h>
#else #else
@@ -1814,6 +1815,8 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
* *
* @param filename The options file to use, if NULL the default * @param filename The options file to use, if NULL the default
* ~/.ssh/config and /etc/ssh/ssh_config will be used. * ~/.ssh/config and /etc/ssh/ssh_config will be used.
* If complied with support for hermetic-usr,
* /usr/etc/ssh/ssh_config will be used last.
* *
* @return 0 on success, < 0 on error. * @return 0 on success, < 0 on error.
* *
@@ -1821,48 +1824,63 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
*/ */
int ssh_options_parse_config(ssh_session session, const char *filename) int ssh_options_parse_config(ssh_session session, const char *filename)
{ {
char *expanded_filename; char *expanded_filename;
int r; int r;
FILE *fp;
if (session == NULL) { if (session == NULL) {
return -1; return -1;
} }
if (session->opts.host == NULL) { if (session->opts.host == NULL) {
ssh_set_error_invalid(session); ssh_set_error_invalid(session);
return -1; return -1;
} }
if (session->opts.sshdir == NULL) { if (session->opts.sshdir == NULL) {
r = ssh_options_set(session, SSH_OPTIONS_SSH_DIR, NULL); r = ssh_options_set(session, SSH_OPTIONS_SSH_DIR, NULL);
if (r < 0) { if (r < 0) {
ssh_set_error_oom(session); ssh_set_error_oom(session);
return -1; return -1;
} }
} }
/* set default filename */ /* set default filename */
if (filename == NULL) { if (filename == NULL) {
expanded_filename = ssh_path_expand_escape(session, "%d/config"); expanded_filename = ssh_path_expand_escape(session, "%d/config");
} else { } else {
expanded_filename = ssh_path_expand_escape(session, filename); expanded_filename = ssh_path_expand_escape(session, filename);
} }
if (expanded_filename == NULL) { if (expanded_filename == NULL) {
return -1; return -1;
} }
r = ssh_config_parse_file(session, expanded_filename); r = ssh_config_parse_file(session, expanded_filename);
if (r < 0) { if (r < 0) {
goto out; goto out;
} }
if (filename == NULL) { if (filename == NULL) {
r = ssh_config_parse_file(session, GLOBAL_CLIENT_CONFIG); if ((fp = fopen(GLOBAL_CLIENT_CONFIG, "r")) != NULL) {
} filename = GLOBAL_CLIENT_CONFIG;
#ifdef USR_GLOBAL_CLIENT_CONFIG
} else if ((fp = fopen(USR_GLOBAL_CLIENT_CONFIG, "r")) != NULL) {
filename = USR_GLOBAL_CLIENT_CONFIG;
#endif
}
/* Do not process the default configuration as part of connection again */ if (fp) {
session->opts.config_processed = true; SSH_LOG(SSH_LOG_PACKET,
"Reading configuration data from %s",
filename);
r = ssh_config_parse(session, fp, true);
fclose(fp);
}
}
/* Do not process the default configuration as part of connection again */
session->opts.config_processed = true;
out: out:
free(expanded_filename); free(expanded_filename);
return r; return r;
} }
int ssh_options_apply(ssh_session session) int ssh_options_apply(ssh_session session)
@@ -2706,7 +2724,13 @@ int ssh_bind_options_parse_config(ssh_bind sshbind, const char *filename)
/* If the global default configuration hasn't been processed yet, process it /* If the global default configuration hasn't been processed yet, process it
* before the provided configuration. */ * before the provided configuration. */
if (!(sshbind->config_processed)) { if (!(sshbind->config_processed)) {
rc = ssh_bind_config_parse_file(sshbind, GLOBAL_BIND_CONFIG); if (access(GLOBAL_BIND_CONFIG, F_OK) == 0) {
rc = ssh_bind_config_parse_file(sshbind, GLOBAL_BIND_CONFIG);
#ifdef USR_GLOBAL_BIND_CONFIG
} else {
rc = ssh_bind_config_parse_file(sshbind, USR_GLOBAL_BIND_CONFIG);
#endif
}
if (rc != 0) { if (rc != 0) {
return rc; return rc;
} }