mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-04-17 11:46:39 +02:00
Add known hosts checking for SFTP storage driver.
By default require a known hosts match as part of the SFTP storage driver's authentication process, i.e. repo-sftp-host-key-check-type=strict. The match is expected to be found in the default list or in a list of known hosts files provided by the user. An exception is made if a fingerprint has been manually configured with repo-sftp-host-fingerprint or repo-sftp-host-key-check-type=accept-new can be used to automatically add new hosts. Also allow host key verification to be skipped, as before, but require the user to explicitly set this (repo-sftp-host-key-check-type=none) rather than it being the default.
This commit is contained in:
parent
f5c730fd03
commit
ce9ba0fade
@ -15,6 +15,18 @@
|
||||
<p>Add <br-option>--repo-storage-tag</br-option> option to create object tags.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<github-pull-request id="2125"/>
|
||||
|
||||
<release-item-contributor-list>
|
||||
<release-item-contributor id="reid.thompson"/>
|
||||
<release-item-reviewer id="stephen.frost"/>
|
||||
<release-item-reviewer id="david.steele"/>
|
||||
</release-item-contributor-list>
|
||||
|
||||
<p>Add known hosts checking for <proper>SFTP</proper> storage driver.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<commit subject="Adjust Wait object to be more accurate when nested."/>
|
||||
<commit subject="Aggregate error retries in ErrorRetry output."/>
|
||||
|
@ -2579,6 +2579,14 @@
|
||||
|
||||
<p>Commands are run exactly as if the repository were stored on a local disk.</p>
|
||||
|
||||
<execute-list host="{[host-pg1]}">
|
||||
<title>Add sftp-server fingerprint to known_hosts file since <br-option>repo4-sftp-host-key-check-type</br-option> defaults to <quote>strict</quote></title>
|
||||
|
||||
<execute user="postgres" user-force="y">
|
||||
<exe-cmd>ssh-keyscan -H {[host-sftp]} >> {[pg-home-path]}/.ssh/known_hosts 2>/dev/null</exe-cmd>
|
||||
</execute>
|
||||
</execute-list>
|
||||
|
||||
<execute-list host="{[host-pg1]}">
|
||||
<title>Create the stanza</title>
|
||||
|
||||
|
@ -2379,6 +2379,23 @@ option:
|
||||
type: string
|
||||
required: false
|
||||
command: repo-type
|
||||
depend:
|
||||
option: repo-sftp-host-key-check-type
|
||||
list:
|
||||
- fingerprint
|
||||
- strict
|
||||
|
||||
repo-sftp-host-key-check-type:
|
||||
section: global
|
||||
group: repo
|
||||
type: string-id
|
||||
default: strict
|
||||
allow-list:
|
||||
- accept-new
|
||||
- fingerprint
|
||||
- none
|
||||
- strict
|
||||
command: repo-type
|
||||
depend: repo-sftp-host
|
||||
|
||||
repo-sftp-host-key-hash-type:
|
||||
@ -2408,6 +2425,18 @@ option:
|
||||
command: repo-type
|
||||
depend: repo-sftp-host
|
||||
|
||||
repo-sftp-known-host:
|
||||
section: global
|
||||
group: repo
|
||||
type: list
|
||||
required: false
|
||||
command: repo-type
|
||||
depend:
|
||||
option: repo-sftp-host-key-check-type
|
||||
list:
|
||||
- accept-new
|
||||
- strict
|
||||
|
||||
repo-sftp-private-key-file:
|
||||
section: global
|
||||
group: repo
|
||||
|
@ -1021,17 +1021,34 @@
|
||||
<summary>SFTP repository host fingerprint.</summary>
|
||||
|
||||
<text>
|
||||
<p>SFTP repository host fingerprint generation should match the <setting>repo-sftp-host-key-hash-type</setting>. Generate the fingeprint via <code>awk '{print $2}' ssh_host_xxx_key.pub | base64 -d | (md5sum or sha1sum) -b</code>. The ssh host keys are normally found in the <path>/etc/ssh</path> directory.</p>
|
||||
<p>SFTP repository host fingerprint generation should match the <setting>repo-sftp-host-key-hash-type</setting>. Generate the fingerprint via <code>awk '{print $2}' ssh_host_xxx_key.pub | base64 -d | (md5sum or sha1sum) -b</code>. The ssh host keys are normally found in the <path>/etc/ssh</path> directory.</p>
|
||||
</text>
|
||||
|
||||
<example>f84e172dfead7aeeeae6c1fdfb5aa8cf</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-sftp-host-key-check-type" name="SFTP Host Key Check Type">
|
||||
<summary>SFTP host key check type.</summary>
|
||||
|
||||
<text>
|
||||
<p>The following SFTP host key check types are supported:</p>
|
||||
<list>
|
||||
<list-item><id>strict</id> - <backrest/> will never automatically add host keys to the ~/.ssh/known_hosts file, and refuses to connect to hosts whose host key has changed or is not found in the known hosts files. This option forces the user to manually add all new hosts.</list-item>
|
||||
<list-item><id>accept-new</id> - <backrest/> will automatically add new host keys to the user's known hosts file, but will not permit connections to hosts with changed host keys.</list-item>
|
||||
<list-item><id>fingerprint</id> - <backrest/> will check the host key against the fingerprint specified by the <br-option>repo-sftp-host-fingerprint</br-option> option.</list-item>
|
||||
<list-item><id>none</id> - no host key checking will be performed.</list-item>
|
||||
</list>
|
||||
</text>
|
||||
|
||||
<default>yes</default>
|
||||
<example>accept-new</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-sftp-host-key-hash-type" name="SFTP Repository Host Key Hash Type">
|
||||
<summary>SFTP repository host key hash type.</summary>
|
||||
|
||||
<text>
|
||||
<p>SFTP repository host key hash type. Declares the hash type to be used to compute the digest of the remote system's host key on SSH startup. Newer versions of libssh2 support <id>sha256</id> in addition to md5 and sha1.</p>
|
||||
<p>SFTP repository host key hash type. Declares the hash type to be used to compute the digest of the remote system's host key on SSH startup. Newer versions of <id>libssh2</id> support <id>sha256</id> in addition to md5 and sha1.</p>
|
||||
</text>
|
||||
|
||||
<example>sha256</example>
|
||||
@ -1057,6 +1074,16 @@
|
||||
<example>pg-backup</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-sftp-known-host" name="SFTP known hosts file">
|
||||
<summary>SFTP known hosts file.</summary>
|
||||
|
||||
<text>
|
||||
<p>A known hosts file to search for an SFTP host match during authentication. When unspecified, <backrest/> will default to searching <file>~/.ssh/known_hosts</file>, <file>~/.ssh/known_hosts2</file>, <file>/etc/ssh/ssh_known_hosts</file>, and <file>/etc/ssh/ssh_known_hosts2</file>. If configured with one or more file paths, <backrest/> will search those for a match. File paths must be full or leading tilde paths. The <setting>repo-sftp-known-host</setting> option can be passed multiple times to specify more than one known hosts file to search. To utilize known hosts file checking <setting>repo-sftp-host-fingerprint</setting> must not be specified. See also <setting>repo-sftp-host-check-type</setting> option.</p>
|
||||
</text>
|
||||
|
||||
<example>/home/postgres/.ssh/known_hosts</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-sftp-private-key-file" name="SFTP Repository Private Key File">
|
||||
<summary>SFTP private key file.</summary>
|
||||
|
||||
|
@ -121,7 +121,7 @@ groupNameFromId(gid_t groupId)
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
// Currently userHome() and userHomeFromId() are only used when building with libssh2
|
||||
// Currently userHome() and userHomeFromId() are only used if we are building with libssh2
|
||||
#ifdef HAVE_LIBSSH2
|
||||
|
||||
FN_EXTERN const String *
|
||||
|
@ -136,7 +136,7 @@ Option constants
|
||||
#define CFGOPT_TYPE "type"
|
||||
#define CFGOPT_VERBOSE "verbose"
|
||||
|
||||
#define CFG_OPTION_TOTAL 177
|
||||
#define CFG_OPTION_TOTAL 179
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Option value constants
|
||||
@ -270,6 +270,15 @@ Option value constants
|
||||
#define CFGOPTVAL_REPO_S3_URI_STYLE_PATH STRID5("path", 0x450300)
|
||||
#define CFGOPTVAL_REPO_S3_URI_STYLE_PATH_Z "path"
|
||||
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_ACCEPT_NEW STRID5("accept-new", 0x2e576e9028c610)
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_ACCEPT_NEW_Z "accept-new"
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT STRID5("fingerprint", 0x51c9942453b9260)
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT_Z "fingerprint"
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_NONE STRID5("none", 0x2b9ee0)
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_NONE_Z "none"
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_STRICT STRID5("strict", 0x2834ca930)
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_STRICT_Z "strict"
|
||||
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_HASH_TYPE_MD5 STRID5("md5", 0x748d0)
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_HASH_TYPE_MD5_Z "md5"
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_HASH_TYPE_SHA1 STRID6("sha1", 0x7412131)
|
||||
@ -511,9 +520,11 @@ typedef enum
|
||||
cfgOptRepoS3UriStyle,
|
||||
cfgOptRepoSftpHost,
|
||||
cfgOptRepoSftpHostFingerprint,
|
||||
cfgOptRepoSftpHostKeyCheckType,
|
||||
cfgOptRepoSftpHostKeyHashType,
|
||||
cfgOptRepoSftpHostPort,
|
||||
cfgOptRepoSftpHostUser,
|
||||
cfgOptRepoSftpKnownHost,
|
||||
cfgOptRepoSftpPrivateKeyFile,
|
||||
cfgOptRepoSftpPrivateKeyPassphrase,
|
||||
cfgOptRepoSftpPublicKeyFile,
|
||||
|
@ -389,6 +389,57 @@ cfgLoadUpdateOption(void)
|
||||
}
|
||||
}
|
||||
|
||||
// Error if repo-sftp--host-key-check-type is explicitly set to anything other than fingerprint and repo-sftp-host-fingerprint
|
||||
// is also specified. For backward compatibility we need to allow repo-sftp-host-fingerprint when
|
||||
// repo-sftp-host-key-check-type defaults to yes, but emit a warning to let the user know to change the configuration. Also
|
||||
// set repo-sftp-host-key-check-type=fingerprint so other code does not need to know about this exception.
|
||||
for (unsigned int repoIdx = 0; repoIdx < cfgOptionGroupIdxTotal(cfgOptGrpRepo); repoIdx++)
|
||||
{
|
||||
if (cfgOptionIdxTest(cfgOptRepoSftpHostKeyCheckType, repoIdx))
|
||||
{
|
||||
if (cfgOptionIdxTest(cfgOptRepoSftpHostFingerprint, repoIdx))
|
||||
{
|
||||
if (cfgOptionIdxSource(cfgOptRepoSftpHostKeyCheckType, repoIdx) == cfgSourceDefault)
|
||||
{
|
||||
LOG_WARN_FMT(
|
||||
"option '%s' without option '%s' = '" CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT_Z "' is"
|
||||
" deprecated\n"
|
||||
"HINT: set option '%s=" CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT_Z "'",
|
||||
cfgOptionIdxName(cfgOptRepoSftpHostFingerprint, repoIdx),
|
||||
cfgOptionIdxName(cfgOptRepoSftpHostKeyCheckType, repoIdx),
|
||||
cfgOptionIdxName(cfgOptRepoSftpHostKeyCheckType, repoIdx));
|
||||
|
||||
cfgOptionIdxSet(
|
||||
cfgOptRepoSftpHostKeyCheckType, repoIdx, cfgSourceDefault,
|
||||
VARSTRZ(CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT_Z));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, repoIdx) !=
|
||||
CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT)
|
||||
{
|
||||
THROW_FMT(
|
||||
OptionInvalidError,
|
||||
"option '%s' not valid without option '%s' = '" CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT_Z
|
||||
"'",
|
||||
cfgOptionIdxName(cfgOptRepoSftpHostFingerprint, repoIdx),
|
||||
cfgOptionIdxName(cfgOptRepoSftpHostKeyCheckType, repoIdx));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, repoIdx) ==
|
||||
CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT)
|
||||
{
|
||||
THROW_FMT(
|
||||
OptionRequiredError, "%s command requires option: %s", cfgCommandName(),
|
||||
cfgOptionIdxName(cfgOptRepoSftpHostFingerprint, repoIdx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,7 @@ static const StringPub parseRuleValueStr[] =
|
||||
PARSE_RULE_STRPUB("shared"), // val/str
|
||||
PARSE_RULE_STRPUB("ssh"), // val/str
|
||||
PARSE_RULE_STRPUB("storage.googleapis.com"), // val/str
|
||||
PARSE_RULE_STRPUB("strict"), // val/str
|
||||
PARSE_RULE_STRPUB("text"), // val/str
|
||||
PARSE_RULE_STRPUB("warn"), // val/str
|
||||
PARSE_RULE_STRPUB(CFGOPTDEF_CONFIG_PATH), // val/str
|
||||
@ -104,6 +105,7 @@ typedef enum
|
||||
parseRuleValStrQT_shared_QT, // val/str/enum
|
||||
parseRuleValStrQT_ssh_QT, // val/str/enum
|
||||
parseRuleValStrQT_storage_DT_googleapis_DT_com_QT, // val/str/enum
|
||||
parseRuleValStrQT_strict_QT, // val/str/enum
|
||||
parseRuleValStrQT_text_QT, // val/str/enum
|
||||
parseRuleValStrQT_warn_QT, // val/str/enum
|
||||
parseRuleValStrCFGOPTDEF_CONFIG_PATH, // val/str/enum
|
||||
@ -118,6 +120,7 @@ Rule StringIds
|
||||
|
||||
static const StringId parseRuleValueStrId[] =
|
||||
{
|
||||
STRID5("accept-new", 0x2e576e9028c610), // val/strid
|
||||
STRID5("aes-256-cbc", 0xc43dfbbcdcca10), // val/strid
|
||||
STRID5("asc", 0xe610), // val/strid
|
||||
STRID5("auto", 0x7d2a10), // val/strid
|
||||
@ -131,6 +134,7 @@ static const StringId parseRuleValueStrId[] =
|
||||
STRID5("detail", 0x1890d0a40), // val/strid
|
||||
STRID5("diff", 0x319240), // val/strid
|
||||
STRID5("error", 0x127ca450), // val/strid
|
||||
STRID5("fingerprint", 0x51c9942453b9260), // val/strid
|
||||
STRID5("full", 0x632a60), // val/strid
|
||||
STRID5("gcs", 0x4c670), // val/strid
|
||||
STRID5("gz", 0x3470), // val/strid
|
||||
@ -162,6 +166,7 @@ static const StringId parseRuleValueStrId[] =
|
||||
STRID5("shutdown", 0x75de4a55130), // val/strid
|
||||
STRID5("ssh", 0x22730), // val/strid
|
||||
STRID5("standby", 0x6444706930), // val/strid
|
||||
STRID5("strict", 0x2834ca930), // val/strid
|
||||
STRID5("text", 0xa60b40), // val/strid
|
||||
STRID5("time", 0x2b5340), // val/strid
|
||||
STRID5("tls", 0x4d940), // val/strid
|
||||
@ -175,6 +180,7 @@ static const StringId parseRuleValueStrId[] =
|
||||
|
||||
typedef enum
|
||||
{
|
||||
parseRuleValStrIdAcceptNew, // val/strid/enum
|
||||
parseRuleValStrIdAes256Cbc, // val/strid/enum
|
||||
parseRuleValStrIdAsc, // val/strid/enum
|
||||
parseRuleValStrIdAuto, // val/strid/enum
|
||||
@ -188,6 +194,7 @@ typedef enum
|
||||
parseRuleValStrIdDetail, // val/strid/enum
|
||||
parseRuleValStrIdDiff, // val/strid/enum
|
||||
parseRuleValStrIdError, // val/strid/enum
|
||||
parseRuleValStrIdFingerprint, // val/strid/enum
|
||||
parseRuleValStrIdFull, // val/strid/enum
|
||||
parseRuleValStrIdGcs, // val/strid/enum
|
||||
parseRuleValStrIdGz, // val/strid/enum
|
||||
@ -219,6 +226,7 @@ typedef enum
|
||||
parseRuleValStrIdShutdown, // val/strid/enum
|
||||
parseRuleValStrIdSsh, // val/strid/enum
|
||||
parseRuleValStrIdStandby, // val/strid/enum
|
||||
parseRuleValStrIdStrict, // val/strid/enum
|
||||
parseRuleValStrIdText, // val/strid/enum
|
||||
parseRuleValStrIdTime, // val/strid/enum
|
||||
parseRuleValStrIdTls, // val/strid/enum
|
||||
@ -8041,13 +8049,108 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
|
||||
( // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-host-fingerprint
|
||||
( // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoType), // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoSftpHostKeyCheckType), // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdFingerprint), // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdStrict), // opt/repo-sftp-host-fingerprint
|
||||
), // opt/repo-sftp-host-fingerprint
|
||||
), // opt/repo-sftp-host-fingerprint
|
||||
), // opt/repo-sftp-host-fingerprint
|
||||
), // opt/repo-sftp-host-fingerprint
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-sftp-host-key-check-type
|
||||
( // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_NAME("repo-sftp-host-key-check-type"), // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeStringId), // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_REQUIRED(true), // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-sftp-host-key-check-type
|
||||
// opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-sftp-host-key-check-type
|
||||
( // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-key-check-type
|
||||
), // opt/repo-sftp-host-key-check-type
|
||||
// opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/repo-sftp-host-key-check-type
|
||||
( // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-key-check-type
|
||||
), // opt/repo-sftp-host-key-check-type
|
||||
// opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/repo-sftp-host-key-check-type
|
||||
( // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-key-check-type
|
||||
), // opt/repo-sftp-host-key-check-type
|
||||
// opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/repo-sftp-host-key-check-type
|
||||
( // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-key-check-type
|
||||
), // opt/repo-sftp-host-key-check-type
|
||||
// opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTIONAL // opt/repo-sftp-host-key-check-type
|
||||
( // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-sftp-host-key-check-type
|
||||
( // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-host-key-check-type
|
||||
( // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoType), // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-sftp-host-key-check-type
|
||||
), // opt/repo-sftp-host-key-check-type
|
||||
// opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTIONAL_ALLOW_LIST // opt/repo-sftp-host-key-check-type
|
||||
( // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdAcceptNew), // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdFingerprint), // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdNone), // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdStrict), // opt/repo-sftp-host-key-check-type
|
||||
), // opt/repo-sftp-host-key-check-type
|
||||
// opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_OPTIONAL_DEFAULT // opt/repo-sftp-host-key-check-type
|
||||
( // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdStrict), // opt/repo-sftp-host-key-check-type
|
||||
PARSE_RULE_VAL_STR(parseRuleValStrQT_strict_QT), // opt/repo-sftp-host-key-check-type
|
||||
), // opt/repo-sftp-host-key-check-type
|
||||
), // opt/repo-sftp-host-key-check-type
|
||||
), // opt/repo-sftp-host-key-check-type
|
||||
), // opt/repo-sftp-host-key-check-type
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-sftp-host-key-hash-type
|
||||
( // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_NAME("repo-sftp-host-key-hash-type"), // opt/repo-sftp-host-key-hash-type
|
||||
@ -8307,6 +8410,88 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
|
||||
), // opt/repo-sftp-host-user
|
||||
), // opt/repo-sftp-host-user
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-sftp-known-host
|
||||
( // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_NAME("repo-sftp-known-host"), // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeList), // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_REQUIRED(false), // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_MULTI(true), // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-sftp-known-host
|
||||
// opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-sftp-known-host
|
||||
( // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-known-host
|
||||
), // opt/repo-sftp-known-host
|
||||
// opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/repo-sftp-known-host
|
||||
( // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-known-host
|
||||
), // opt/repo-sftp-known-host
|
||||
// opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/repo-sftp-known-host
|
||||
( // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-known-host
|
||||
), // opt/repo-sftp-known-host
|
||||
// opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/repo-sftp-known-host
|
||||
( // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-known-host
|
||||
), // opt/repo-sftp-known-host
|
||||
// opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTIONAL // opt/repo-sftp-known-host
|
||||
( // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-sftp-known-host
|
||||
( // opt/repo-sftp-known-host
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-known-host
|
||||
( // opt/repo-sftp-known-host
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoSftpHostKeyCheckType), // opt/repo-sftp-known-host
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdAcceptNew), // opt/repo-sftp-known-host
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdStrict), // opt/repo-sftp-known-host
|
||||
), // opt/repo-sftp-known-host
|
||||
), // opt/repo-sftp-known-host
|
||||
), // opt/repo-sftp-known-host
|
||||
), // opt/repo-sftp-known-host
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-sftp-private-key-file
|
||||
( // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_NAME("repo-sftp-private-key-file"), // opt/repo-sftp-private-key-file
|
||||
@ -10795,10 +10980,11 @@ static const uint8_t optionResolveOrder[] =
|
||||
cfgOptRepoS3Token, // opt-resolve-order
|
||||
cfgOptRepoS3UriStyle, // opt-resolve-order
|
||||
cfgOptRepoSftpHost, // opt-resolve-order
|
||||
cfgOptRepoSftpHostFingerprint, // opt-resolve-order
|
||||
cfgOptRepoSftpHostKeyCheckType, // opt-resolve-order
|
||||
cfgOptRepoSftpHostKeyHashType, // opt-resolve-order
|
||||
cfgOptRepoSftpHostPort, // opt-resolve-order
|
||||
cfgOptRepoSftpHostUser, // opt-resolve-order
|
||||
cfgOptRepoSftpKnownHost, // opt-resolve-order
|
||||
cfgOptRepoSftpPrivateKeyFile, // opt-resolve-order
|
||||
cfgOptRepoSftpPrivateKeyPassphrase, // opt-resolve-order
|
||||
cfgOptRepoSftpPublicKeyFile, // opt-resolve-order
|
||||
@ -10826,4 +11012,5 @@ static const uint8_t optionResolveOrder[] =
|
||||
cfgOptRepoHostKeyFile, // opt-resolve-order
|
||||
cfgOptRepoS3Key, // opt-resolve-order
|
||||
cfgOptRepoS3KeySecret, // opt-resolve-order
|
||||
cfgOptRepoSftpHostFingerprint, // opt-resolve-order
|
||||
};
|
||||
|
@ -22,17 +22,30 @@ storageSftpHelper(const unsigned int repoIdx, const bool write, StoragePathExpre
|
||||
|
||||
ASSERT(cfgOptionIdxStrId(cfgOptRepoType, repoIdx) == STORAGE_SFTP_TYPE);
|
||||
|
||||
FUNCTION_LOG_RETURN(
|
||||
STORAGE,
|
||||
storageSftpNewP(
|
||||
cfgOptionIdxStr(cfgOptRepoPath, repoIdx), cfgOptionIdxStr(cfgOptRepoSftpHost, repoIdx),
|
||||
cfgOptionIdxUInt(cfgOptRepoSftpHostPort, repoIdx), cfgOptionIdxStr(cfgOptRepoSftpHostUser, repoIdx),
|
||||
cfgOptionUInt64(cfgOptIoTimeout), cfgOptionIdxStr(cfgOptRepoSftpPrivateKeyFile, repoIdx),
|
||||
cfgOptionIdxStrId(cfgOptRepoSftpHostKeyHashType, repoIdx), .write = write,
|
||||
.pathExpressionFunction = pathExpressionCallback, .modeFile = STORAGE_MODE_FILE_DEFAULT,
|
||||
.modePath = STORAGE_MODE_PATH_DEFAULT, .keyPub = cfgOptionIdxStrNull(cfgOptRepoSftpPublicKeyFile, repoIdx),
|
||||
.keyPassphrase = cfgOptionIdxStrNull(cfgOptRepoSftpPrivateKeyPassphrase, repoIdx),
|
||||
.hostFingerprint = cfgOptionIdxStrNull(cfgOptRepoSftpHostFingerprint, repoIdx)));
|
||||
Storage *result = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
const StringList *const knownHosts = strLstNewVarLst(cfgOptionIdxLst(cfgOptRepoSftpKnownHost, repoIdx));
|
||||
|
||||
MEM_CONTEXT_PRIOR_BEGIN()
|
||||
{
|
||||
result = storageSftpNewP(
|
||||
cfgOptionIdxStr(cfgOptRepoPath, repoIdx), cfgOptionIdxStr(cfgOptRepoSftpHost, repoIdx),
|
||||
cfgOptionIdxUInt(cfgOptRepoSftpHostPort, repoIdx), cfgOptionIdxStr(cfgOptRepoSftpHostUser, repoIdx),
|
||||
cfgOptionUInt64(cfgOptIoTimeout), cfgOptionIdxStr(cfgOptRepoSftpPrivateKeyFile, repoIdx),
|
||||
cfgOptionIdxStrId(cfgOptRepoSftpHostKeyHashType, repoIdx), .write = write,
|
||||
.pathExpressionFunction = pathExpressionCallback, .modeFile = STORAGE_MODE_FILE_DEFAULT,
|
||||
.modePath = STORAGE_MODE_PATH_DEFAULT, .keyPub = cfgOptionIdxStrNull(cfgOptRepoSftpPublicKeyFile, repoIdx),
|
||||
.keyPassphrase = cfgOptionIdxStrNull(cfgOptRepoSftpPrivateKeyPassphrase, repoIdx),
|
||||
.hostKeyCheckType = cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, repoIdx),
|
||||
.hostFingerprint = cfgOptionIdxStrNull(cfgOptRepoSftpHostFingerprint, repoIdx), .knownHosts = knownHosts);
|
||||
}
|
||||
MEM_CONTEXT_PRIOR_END();
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(STORAGE, result);
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBSSH2
|
||||
|
@ -12,6 +12,7 @@ SFTP Storage
|
||||
#include "common/log.h"
|
||||
#include "common/regExp.h"
|
||||
#include "common/user.h"
|
||||
#include "storage/posix/storage.h"
|
||||
#include "storage/sftp/read.h"
|
||||
#include "storage/sftp/storage.intern.h"
|
||||
#include "storage/sftp/write.h"
|
||||
@ -37,6 +38,199 @@ struct StorageSftp
|
||||
TimeMSec timeout; // Session timeout
|
||||
};
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Return known host key type based on host key type
|
||||
***********************************************************************************************************************************/
|
||||
static int
|
||||
storageSftpKnownHostKeyType(const int hostKeyType)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(INT, hostKeyType);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
int result;
|
||||
|
||||
switch (hostKeyType)
|
||||
{
|
||||
case LIBSSH2_HOSTKEY_TYPE_RSA:
|
||||
result = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
|
||||
break;
|
||||
|
||||
case LIBSSH2_HOSTKEY_TYPE_DSS:
|
||||
result = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
|
||||
break;
|
||||
|
||||
#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
|
||||
case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
|
||||
result = LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384
|
||||
case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
|
||||
result = LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521
|
||||
case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
|
||||
result = LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
|
||||
case LIBSSH2_HOSTKEY_TYPE_ED25519:
|
||||
result = LIBSSH2_KNOWNHOST_KEY_ED25519;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN(INT, result);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Return a match failed message based on known host check failure type
|
||||
***********************************************************************************************************************************/
|
||||
static const char *
|
||||
storageSftpKnownHostCheckpFailureMsg(const int rc)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(INT, rc);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
const char *result;
|
||||
|
||||
switch (rc)
|
||||
{
|
||||
case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
|
||||
result = "LIBSSH2_KNOWNHOST_CHECK_FAILURE";
|
||||
break;
|
||||
|
||||
case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
|
||||
result = "not found in known hosts files: LIBSSH2_KNOWNHOST_CHECK_NOTFOUND";
|
||||
break;
|
||||
|
||||
case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
|
||||
result = "mismatch in known hosts files: LIBSSH2_KNOWNHOST_CHECK_MISMATCH";
|
||||
break;
|
||||
|
||||
default:
|
||||
result = "unknown failure";
|
||||
break;
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN_CONST(STRINGZ, result);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Rewrite the user's known_hosts file with a new entry
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
storageSftpUpdateKnownHostsFile(
|
||||
StorageSftp *const this, const int hostKeyType, const String *const host, const char *const hostKey, const size_t hostKeyLen)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(STORAGE_SFTP, this);
|
||||
FUNCTION_LOG_PARAM(INT, hostKeyType);
|
||||
FUNCTION_LOG_PARAM(STRING, host);
|
||||
FUNCTION_LOG_PARAM(STRINGZ, hostKey);
|
||||
FUNCTION_LOG_PARAM(SIZE, hostKeyLen);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
char *libSsh2ErrMsg;
|
||||
int libSsh2ErrMsgLen;
|
||||
int rc;
|
||||
|
||||
// Init a known host collection for the user's known_hosts file
|
||||
const char *const userKnownHostsFile = strZ(strNewFmt("%s%s", strZ(userHome()), "/.ssh/known_hosts"));
|
||||
LIBSSH2_KNOWNHOSTS *const userKnownHostsList = libssh2_knownhost_init(this->session);
|
||||
|
||||
LOG_WARN_FMT("host '%s' not found in known hosts files, attempting to add host to '%s'", strZ(host), userKnownHostsFile);
|
||||
|
||||
if (userKnownHostsList == NULL)
|
||||
{
|
||||
// Get the libssh2 error message and emit warning
|
||||
rc = libssh2_session_last_error(this->session, &libSsh2ErrMsg, &libSsh2ErrMsgLen, 0);
|
||||
|
||||
LOG_WARN_FMT(
|
||||
"libssh2_knownhost_init failed for '%s' for update: libssh2 errno [%d] %s", userKnownHostsFile, rc, libSsh2ErrMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read the user's known_hosts file entries into the collection. libssh2_knownhost_readfile() returns the number of
|
||||
// successfully loaded hosts or a negative value on error, an empty known hosts file will return 0.
|
||||
if ((rc = libssh2_knownhost_readfile(userKnownHostsList, userKnownHostsFile, LIBSSH2_KNOWNHOST_FILE_OPENSSH)) < 0)
|
||||
{
|
||||
// Missing known_hosts file will return LIBSSH2_ERROR_FILE. Possibly issues other than missing may return this.
|
||||
if (rc == LIBSSH2_ERROR_FILE)
|
||||
{
|
||||
// If user's known_hosts file is non-existant, create an empty one for libssh2 to operate on
|
||||
const Storage *const sshStorage =
|
||||
storagePosixNewP(
|
||||
strNewFmt("%s%s", strZ(userHome()), "/.ssh"), .modeFile = 0600, .modePath = 0700, .write = true);
|
||||
|
||||
if (!storageExistsP(sshStorage, strNewFmt("%s", "known_hosts")))
|
||||
storagePutP(storageNewWriteP(sshStorage, strNewFmt("%s", "known_hosts")), NULL);
|
||||
|
||||
// Try to load the user's known_hosts file entries into the collection again
|
||||
rc = libssh2_knownhost_readfile(userKnownHostsList, userKnownHostsFile, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
|
||||
}
|
||||
}
|
||||
|
||||
// If the user's known_hosts file was read successfully, add the host to the collection and rewrite the file
|
||||
if (rc >= 0)
|
||||
{
|
||||
// Check for a supported known host key type
|
||||
const int knownHostKeyType = storageSftpKnownHostKeyType(hostKeyType);
|
||||
|
||||
if (knownHostKeyType != 0)
|
||||
{
|
||||
// Add host to the internal list
|
||||
if (libssh2_knownhost_addc(
|
||||
userKnownHostsList, strZ(host), NULL, hostKey, hostKeyLen,
|
||||
strZ(strNewZ("Generated from " PROJECT_NAME)), strlen(strZ(strNewZ("Generated from " PROJECT_NAME))),
|
||||
LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | knownHostKeyType, NULL) == 0)
|
||||
{
|
||||
// Rewrite the updated known_hosts file
|
||||
rc = libssh2_knownhost_writefile(userKnownHostsList, userKnownHostsFile, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
|
||||
|
||||
if (rc != 0)
|
||||
LOG_WARN_FMT(PROJECT_NAME " unable to write '%s' for update", userKnownHostsFile);
|
||||
else
|
||||
LOG_WARN_FMT(PROJECT_NAME " added new host '%s' to '%s'", strZ(host), userKnownHostsFile);
|
||||
}
|
||||
else
|
||||
LOG_WARN_FMT(PROJECT_NAME " failed to add '%s' to known_hosts internal list", strZ(host));
|
||||
}
|
||||
else
|
||||
LOG_WARN_FMT("unsupported key type [%d], unable to update knownhosts for '%s'", hostKeyType, strZ(host));
|
||||
}
|
||||
else
|
||||
{
|
||||
// On readfile failure warn that we're unable to update the user's known_hosts file
|
||||
rc = libssh2_session_last_error(this->session, &libSsh2ErrMsg, &libSsh2ErrMsgLen, 0);
|
||||
|
||||
LOG_WARN_FMT(
|
||||
"libssh2 unable to read '%s' for update: libssh2 errno [%d] %s\n"
|
||||
"HINT: does '%s' exist with proper permissions?", userKnownHostsFile, rc, libSsh2ErrMsg, userKnownHostsFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Free the user's known hosts list
|
||||
if (userKnownHostsList)
|
||||
libssh2_knownhost_free(userKnownHostsList);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Free libssh2 resources
|
||||
***********************************************************************************************************************************/
|
||||
@ -307,6 +501,53 @@ storageSftpInfo(THIS_VOID, const String *const file, const StorageInfoLevel leve
|
||||
FUNCTION_LOG_RETURN(STORAGE_INFO, result);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Build known hosts file list. If knownHosts is empty build the default file list, otherwise build the list provided. knownHosts
|
||||
requires full path and/or leading tilde path entries.
|
||||
***********************************************************************************************************************************/
|
||||
static StringList *
|
||||
storageSftpKnownHostsFilesList(const StringList *const knownHosts)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(STRING_LIST, knownHosts);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
StringList *const result = strLstNew();
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
if (strLstEmpty(knownHosts))
|
||||
{
|
||||
// Create default file list
|
||||
strLstAddFmt(result, "%s%s", strZ(userHome()), "/.ssh/known_hosts");
|
||||
strLstAddFmt(result, "%s%s", strZ(userHome()), "/.ssh/known_hosts2");
|
||||
strLstAddZ(result, "/etc/ssh/ssh_known_hosts");
|
||||
strLstAddZ(result, "/etc/ssh/ssh_known_hosts2");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Process the known host list entries and add them to the result list
|
||||
for (unsigned int listIdx = 0; listIdx < strLstSize(knownHosts); listIdx++)
|
||||
{
|
||||
// Get the trimmed file path and add it to the result list
|
||||
const String *const filePath = strTrim(strLstGet(knownHosts, listIdx));
|
||||
|
||||
if (strBeginsWithZ(filePath, "~/"))
|
||||
{
|
||||
// Replace leading tilde with space, trim space, prepend user home path and add to the result list
|
||||
strLstAddFmt(
|
||||
result, "%s%s", strZ(userHome()), strZ(strTrim(strSub(filePath, (size_t)strChr(filePath, '~') + 1))));
|
||||
}
|
||||
else
|
||||
strLstAdd(result, filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(STRING_LIST, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
static String *
|
||||
storageSftpExpandTildePath(const String *const tildePath)
|
||||
@ -664,6 +905,7 @@ storageSftpPathCreate(
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
static bool
|
||||
storageSftpPathRemove(THIS_VOID, const String *const path, const bool recurse, const StorageInterfacePathRemoveParam param)
|
||||
{
|
||||
@ -793,7 +1035,9 @@ storageSftpNew(
|
||||
FUNCTION_LOG_PARAM(STRING_ID, hostKeyHashType);
|
||||
FUNCTION_LOG_PARAM(STRING, param.keyPub);
|
||||
FUNCTION_TEST_PARAM(STRING, param.keyPassphrase);
|
||||
FUNCTION_LOG_PARAM(STRING_ID, param.hostKeyCheckType);
|
||||
FUNCTION_LOG_PARAM(STRING, param.hostFingerprint);
|
||||
FUNCTION_LOG_PARAM(STRING_LIST, param.knownHosts);
|
||||
FUNCTION_LOG_PARAM(MODE, param.modeFile);
|
||||
FUNCTION_LOG_PARAM(MODE, param.modePath);
|
||||
FUNCTION_LOG_PARAM(BOOL, param.write);
|
||||
@ -806,7 +1050,6 @@ storageSftpNew(
|
||||
ASSERT(user != NULL);
|
||||
ASSERT(keyPriv != NULL);
|
||||
ASSERT(hostKeyHashType != 0);
|
||||
|
||||
// Initialize user module
|
||||
userInit();
|
||||
|
||||
@ -871,19 +1114,21 @@ storageSftpNew(
|
||||
#endif // LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
|
||||
default:
|
||||
THROW_FMT(
|
||||
ServiceError, "requested ssh2 hostkey hash type (%s) not available", strZ(strIdToStr(hostKeyHashType)));
|
||||
THROW_FMT(ServiceError, "requested ssh2 hostkey hash type (%s) not available", strZ(strIdToStr(hostKeyHashType)));
|
||||
break;
|
||||
}
|
||||
|
||||
const char *binaryFingerprint = libssh2_hostkey_hash(this->session, hashType);
|
||||
|
||||
if (binaryFingerprint == NULL)
|
||||
THROW_FMT(ServiceError, "libssh2 hostkey hash failed: libssh2 errno [%d]", libssh2_session_last_errno(this->session));
|
||||
|
||||
// Compare fingerprint if provided
|
||||
if (param.hostFingerprint != NULL)
|
||||
// Compare fingerprint if provided else check known hosts files for a match
|
||||
if (param.hostKeyCheckType == SFTP_STRICT_HOSTKEY_CHECKING_FINGERPRINT)
|
||||
{
|
||||
const char *const binaryFingerprint = libssh2_hostkey_hash(this->session, hashType);
|
||||
|
||||
if (binaryFingerprint == NULL)
|
||||
{
|
||||
THROW_FMT(
|
||||
ServiceError, "libssh2 hostkey hash failed: libssh2 errno [%d]", libssh2_session_last_errno(this->session));
|
||||
}
|
||||
|
||||
// 256 bytes is large enough to hold the hex representation of currently supported hash types. The hex encoded version
|
||||
// requires twice as much space (hashSize * 2) as the raw version.
|
||||
char fingerprint[256];
|
||||
@ -897,6 +1142,115 @@ storageSftpNew(
|
||||
fingerprint, strZ(param.hostFingerprint));
|
||||
}
|
||||
}
|
||||
else if (param.hostKeyCheckType != SFTP_STRICT_HOSTKEY_CHECKING_NONE)
|
||||
{
|
||||
// Init the known host collection
|
||||
LIBSSH2_KNOWNHOSTS *const knownHostsList = libssh2_knownhost_init(this->session);
|
||||
|
||||
if (knownHostsList == NULL)
|
||||
THROW_FMT(ServiceError, "failure during libssh2_knownhost_init");
|
||||
|
||||
// Get the list of known host files to search
|
||||
const StringList *const knownHostsPathList = storageSftpKnownHostsFilesList(param.knownHosts);
|
||||
|
||||
// Loop through the list of known host files
|
||||
for (unsigned int listIdx = 0; listIdx < strLstSize(knownHostsPathList); listIdx++)
|
||||
{
|
||||
const char *const currentKnownHostFile = strZNull(strLstGet(knownHostsPathList, listIdx));
|
||||
|
||||
// Read the known hosts file entries into the collection, log message for readfile status.
|
||||
// libssh2_knownhost_readfile() returns the number of successfully loaded hosts or a negative value on error, an
|
||||
// empty known hosts file will return 0.
|
||||
if ((rc = libssh2_knownhost_readfile(knownHostsList, currentKnownHostFile, LIBSSH2_KNOWNHOST_FILE_OPENSSH)) <= 0)
|
||||
{
|
||||
if (rc == 0)
|
||||
LOG_DETAIL_FMT("libssh2 '%s' file is empty", currentKnownHostFile);
|
||||
else
|
||||
{
|
||||
char *libSsh2ErrMsg;
|
||||
int libSsh2ErrMsgLen;
|
||||
|
||||
// Get the libssh2 error message
|
||||
rc = libssh2_session_last_error(this->session, &libSsh2ErrMsg, &libSsh2ErrMsgLen, 0);
|
||||
|
||||
LOG_DETAIL_FMT(
|
||||
"libssh2 read '%s' failed: libssh2 errno [%d] %s", currentKnownHostFile, rc, libSsh2ErrMsg);
|
||||
}
|
||||
}
|
||||
else
|
||||
LOG_DETAIL_FMT("libssh2 read '%s' succeeded", currentKnownHostFile);
|
||||
}
|
||||
|
||||
// Get the remote host key
|
||||
size_t hostKeyLen;
|
||||
int hostKeyType;
|
||||
const char *const hostKey = libssh2_session_hostkey(this->session, &hostKeyLen, &hostKeyType);
|
||||
|
||||
// Check for a match in known hosts files else throw an error if no host key was retrieved
|
||||
if (hostKey != NULL)
|
||||
{
|
||||
rc = libssh2_knownhost_checkp(
|
||||
knownHostsList, strZ(host), (int)port, hostKey, hostKeyLen,
|
||||
LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW, NULL);
|
||||
|
||||
// Handle check success/failure
|
||||
if (rc == LIBSSH2_KNOWNHOST_CHECK_MATCH)
|
||||
LOG_DETAIL_FMT("known hosts match found for '%s'", strZ(host));
|
||||
else
|
||||
{
|
||||
// Handle failure to match in a similar manner as ssh_config StrictHostKeyChecking. If this flag is set to
|
||||
// "strict", never automatically add host keys to the ~/.ssh/known_hosts file, and refuse to connect to hosts
|
||||
// whose host key has changed. This option forces the user to manually add all new hosts. If this flag is set to
|
||||
// "accept-new" then automatically add new host keys to the user known hosts files, but do not permit
|
||||
// connections to hosts with changed host keys.
|
||||
switch (param.hostKeyCheckType)
|
||||
{
|
||||
case SFTP_STRICT_HOSTKEY_CHECKING_STRICT:
|
||||
{
|
||||
// Throw an error when set to strict and we have any result other than match
|
||||
libssh2_knownhost_free(knownHostsList);
|
||||
|
||||
THROW_FMT(
|
||||
ServiceError, "known hosts failure: '%s' %s [%d]: check type [%s]", strZ(host),
|
||||
storageSftpKnownHostCheckpFailureMsg(rc), rc, strZ(strIdToStr(param.hostKeyCheckType)));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
ASSERT(param.hostKeyCheckType == SFTP_STRICT_HOSTKEY_CHECKING_ACCEPT_NEW);
|
||||
|
||||
// Throw an error when set to accept-new and match fails or mismatches else add the new host key to the
|
||||
// user's known_hosts file
|
||||
if (rc == LIBSSH2_KNOWNHOST_CHECK_MISMATCH || rc == LIBSSH2_KNOWNHOST_CHECK_FAILURE)
|
||||
{
|
||||
// Free the known hosts list
|
||||
libssh2_knownhost_free(knownHostsList);
|
||||
|
||||
THROW_FMT(
|
||||
ServiceError, "known hosts failure: '%s': %s [%d]: check type [%s]", strZ(host),
|
||||
storageSftpKnownHostCheckpFailureMsg(rc), rc,
|
||||
strZ(strIdToStr(param.hostKeyCheckType)));
|
||||
}
|
||||
else
|
||||
storageSftpUpdateKnownHostsFile(this, hostKeyType, host, hostKey, hostKeyLen);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW_FMT(
|
||||
ServiceError,
|
||||
"libssh2_session_hostkey failed to get hostkey: libssh2 error [%d]", libssh2_session_last_errno(this->session));
|
||||
}
|
||||
|
||||
// Free the known hosts list
|
||||
libssh2_knownhost_free(knownHostsList);
|
||||
}
|
||||
|
||||
// Perform public key authorization, expand leading tilde key file paths if needed
|
||||
String *const privKeyPath = regExpMatchOne(STRDEF("^ *~"), keyPriv) ? storageSftpExpandTildePath(keyPriv) : strDup(keyPriv);
|
||||
@ -929,6 +1283,7 @@ storageSftpNew(
|
||||
" generate the keypair"));
|
||||
}
|
||||
|
||||
// Init the sftp session
|
||||
do
|
||||
{
|
||||
this->sftpSession = libssh2_sftp_init(this->session);
|
||||
|
@ -13,6 +13,14 @@ Storage type
|
||||
|
||||
#ifdef HAVE_LIBSSH2
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
SFTP StrictHostKeyChecking
|
||||
***********************************************************************************************************************************/
|
||||
#define SFTP_STRICT_HOSTKEY_CHECKING_ACCEPT_NEW STRID5("accept-new", 0x2e576e9028c610)
|
||||
#define SFTP_STRICT_HOSTKEY_CHECKING_FINGERPRINT STRID5("fingerprint", 0x51c9942453b9260)
|
||||
#define SFTP_STRICT_HOSTKEY_CHECKING_NONE STRID5("none", 0x2b9ee0)
|
||||
#define SFTP_STRICT_HOSTKEY_CHECKING_STRICT STRID5("strict", 0x2834ca930)
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constructors
|
||||
***********************************************************************************************************************************/
|
||||
@ -25,7 +33,9 @@ typedef struct StorageSftpNewParam
|
||||
StoragePathExpressionCallback *pathExpressionFunction;
|
||||
const String *keyPub;
|
||||
const String *keyPassphrase;
|
||||
StringId hostKeyCheckType;
|
||||
const String *hostFingerprint;
|
||||
const StringList *knownHosts;
|
||||
} StorageSftpNewParam;
|
||||
|
||||
#define storageSftpNewP(path, host, port, user, timeout, keyPriv, hostKeyHashType, ...) \
|
||||
|
@ -1322,6 +1322,7 @@ sub configCreate
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-host-user'} = TEST_USER;
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-private-key-file'} = testRunGet()->basePath() . SSH_PRIVATE_KEY;
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-public-key-file'} = testRunGet()->basePath() . SSH_PUBLIC_KEY;
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-host-key-check-type'} = "none";
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-path'} = $self->repoPath();
|
||||
|
||||
# At what count do we hit diminishing returns
|
||||
|
@ -165,6 +165,180 @@ libssh2_init(int flags)
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_knownhost_addc
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_knownhost_addc(
|
||||
LIBSSH2_KNOWNHOSTS *hosts, const char *host, const char *salt, const char *key, size_t keylen, const char *comment,
|
||||
size_t commentlen, int typemask, struct libssh2_knownhost **store)
|
||||
{
|
||||
// Avoid compiler complaining of unused param
|
||||
(void)store;
|
||||
|
||||
if (hosts == NULL)
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
|
||||
"libssh2 script function 'libssh2_knownhost_adddc', expects hosts to be not NULL");
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_KNOWNHOST_ADDC,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(host)),
|
||||
varNewStrZ(salt)),
|
||||
varNewStrZ(key)),
|
||||
varNewUInt64(keylen)),
|
||||
varNewStrZ(comment)),
|
||||
varNewUInt64(commentlen)),
|
||||
varNewInt(typemask)),
|
||||
(HrnLibSsh2 *)hosts);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_knownhost_checkp
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_knownhost_checkp(
|
||||
LIBSSH2_KNOWNHOSTS *hosts, const char *host, int port, const char *key, size_t keylen, int typemask,
|
||||
struct libssh2_knownhost **knownhost)
|
||||
{
|
||||
// Avoid compiler complaining of unused param
|
||||
(void)knownhost;
|
||||
|
||||
if (hosts == NULL)
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
|
||||
"libssh2 script function 'libssh2_knownhost_checkp', expects hosts to be not NULL");
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_KNOWNHOST_CHECKP,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(host)),
|
||||
varNewInt(port)),
|
||||
varNewStrZ(key)),
|
||||
varNewUInt64(keylen)),
|
||||
varNewInt(typemask)),
|
||||
(HrnLibSsh2 *)hosts);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_knownhost_free
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS *hosts)
|
||||
{
|
||||
if (hosts == NULL)
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
|
||||
"libssh2 script function 'libssh2_session_knownhost_free', expects hosts to be not NULL");
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_knownhost_init
|
||||
***********************************************************************************************************************************/
|
||||
LIBSSH2_KNOWNHOSTS *
|
||||
libssh2_knownhost_init(LIBSSH2_SESSION *session)
|
||||
{
|
||||
HrnLibSsh2 *hrnLibSsh2 = hrnLibSsh2ScriptRun(HRNLIBSSH2_KNOWNHOST_INIT, NULL, (HrnLibSsh2 *)session);
|
||||
|
||||
return hrnLibSsh2->resultNull ? NULL : (LIBSSH2_KNOWNHOSTS *)hrnLibSsh2;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_knownhost_readfile
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_knownhost_readfile(LIBSSH2_KNOWNHOSTS *hosts, const char *filename, int type)
|
||||
{
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_KNOWNHOST_READFILE,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(filename)),
|
||||
varNewInt(type)),
|
||||
(HrnLibSsh2 *)hosts);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_knownhost_writefile
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_knownhost_writefile(LIBSSH2_KNOWNHOSTS *hosts, const char *filename, int type)
|
||||
{
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_KNOWNHOST_WRITEFILE,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(filename)),
|
||||
varNewInt(type)),
|
||||
(HrnLibSsh2 *)hosts);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_session_hostkey
|
||||
***********************************************************************************************************************************/
|
||||
const char *
|
||||
libssh2_session_hostkey(LIBSSH2_SESSION *session, size_t *len, int *type)
|
||||
{
|
||||
HrnLibSsh2 *hrnLibSsh2 = hrnLibSsh2ScriptRun(HRNLIBSSH2_SESSION_HOSTKEY, NULL, (HrnLibSsh2 *)session);
|
||||
|
||||
*len = (size_t)hrnLibSsh2->len;
|
||||
*type = (int)hrnLibSsh2->type;
|
||||
|
||||
return hrnLibSsh2->resultNull ? NULL : (const char *)hrnLibSsh2->resultZ;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_session_init
|
||||
***********************************************************************************************************************************/
|
||||
@ -197,28 +371,21 @@ libssh2_session_init_ex(
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_session_set_blocking
|
||||
Shim for libssh2_session_last_error
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
libssh2_session_set_blocking(LIBSSH2_SESSION *session, int blocking)
|
||||
int
|
||||
libssh2_session_last_error(LIBSSH2_SESSION *session, char **errmsg, int *errmsg_len, int want_buf)
|
||||
{
|
||||
if (session == NULL)
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
|
||||
"libssh2 script function 'libssh2_session_set_blocking', expects session to be not NULL");
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
// Avoid compiler complaining of unused params
|
||||
(void)errmsg_len;
|
||||
(void)want_buf;
|
||||
|
||||
if (!(INT_MIN <= blocking && blocking <= INT_MAX))
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
|
||||
"libssh2 script function 'libssh2_session_set_blocking', expects blocking to be an integer value");
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
HrnLibSsh2 *hrnLibSsh2 = hrnLibSsh2ScriptRun(HRNLIBSSH2_SESSION_LAST_ERROR, NULL, (HrnLibSsh2 *)session);
|
||||
|
||||
return;
|
||||
if (hrnLibSsh2->errMsg != NULL)
|
||||
*errmsg = hrnLibSsh2->errMsg;
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@ -268,6 +435,14 @@ libssh2_userauth_publickey_fromfile_ex(
|
||||
{
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
if (privatekey == NULL)
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
|
||||
"libssh2 script function 'libssh2_userauth_publickey_fromfile_ex', expects privatekey to be not NULL");
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
|
@ -25,19 +25,31 @@ libssh2 authorization constants
|
||||
#define KEYPUB STRDEF("/home/" TEST_USER "/.ssh/id_rsa.pub")
|
||||
#define KEYPRIV_CSTR "/home/" TEST_USER "/.ssh/id_rsa"
|
||||
#define KEYPUB_CSTR "/home/" TEST_USER "/.ssh/id_rsa.pub"
|
||||
#define KNOWNHOSTS_FILE_CSTR "/home/" TEST_USER "/.ssh/known_hosts"
|
||||
#define KNOWNHOSTS2_FILE_CSTR "/home/" TEST_USER "/.ssh/known_hosts2"
|
||||
#define ETC_KNOWNHOSTS_FILE_CSTR "/etc/ssh/ssh_known_hosts"
|
||||
#define ETC_KNOWNHOSTS2_FILE_CSTR "/etc/ssh/ssh_known_hosts2"
|
||||
#define HOSTKEY "12345678901234567890"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Function constants
|
||||
***********************************************************************************************************************************/
|
||||
#define HRNLIBSSH2_HOSTKEY_HASH "libssh2_hostkey_hash"
|
||||
#define HRNLIBSSH2_INIT "libssh2_init"
|
||||
#define HRNLIBSSH2_SESSION_DISCONNECT_EX "libssh2_session_disconnect_ex"
|
||||
#define HRNLIBSSH2_KNOWNHOST_ADDC "libssh2_knownhost_addc"
|
||||
#define HRNLIBSSH2_KNOWNHOST_CHECKP "libssh2_knownhost_checkp"
|
||||
#define HRNLIBSSH2_KNOWNHOST_FREE "libssh2_knownhost_free"
|
||||
#define HRNLIBSSH2_KNOWNHOST_INIT "libssh2_knownhost_init"
|
||||
#define HRNLIBSSH2_KNOWNHOST_READFILE "libssh2_knownhost_readfile"
|
||||
#define HRNLIBSSH2_KNOWNHOST_WRITEFILE "libssh2_knownhost_writefile"
|
||||
#define HRNLIBSSH2_SESSION_BLOCK_DIRECTIONS "libssh2_session_block_directions"
|
||||
#define HRNLIBSSH2_SESSION_DISCONNECT_EX "libssh2_session_disconnect_ex"
|
||||
#define HRNLIBSSH2_SESSION_FREE "libssh2_session_free"
|
||||
#define HRNLIBSSH2_SESSION_HANDSHAKE "libssh2_session_handshake"
|
||||
#define HRNLIBSSH2_SESSION_HOSTKEY "libssh2_session_hostkey"
|
||||
#define HRNLIBSSH2_SESSION_INIT_EX "libssh2_session_init_ex"
|
||||
#define HRNLIBSSH2_SESSION_LAST_ERRNO "libssh2_session_last_errno"
|
||||
#define HRNLIBSSH2_SESSION_SET_BLOCKING "libssh2_session_set_blocking"
|
||||
#define HRNLIBSSH2_SESSION_LAST_ERROR "libssh2_session_last_error"
|
||||
#define HRNLIBSSH2_SFTP_CLOSE_HANDLE "libssh2_sftp_close_handle"
|
||||
#define HRNLIBSSH2_SFTP_FSYNC "libssh2_sftp_fsync"
|
||||
#define HRNLIBSSH2_SFTP_INIT "libssh2_sftp_init"
|
||||
@ -64,7 +76,11 @@ Macros for defining groups of functions that implement commands
|
||||
{.function = HRNLIBSSH2_INIT, .param = "[0]", .resultInt = 0}, \
|
||||
{.function = HRNLIBSSH2_SESSION_INIT_EX, .param = "[null,null,null,null]"}, \
|
||||
{.function = HRNLIBSSH2_SESSION_HANDSHAKE, .param = HANDSHAKE_PARAM, .resultInt = 0}, \
|
||||
{.function = HRNLIBSSH2_HOSTKEY_HASH, .param = "[2]", .resultZ = "12345678910123456789"}, \
|
||||
{.function = HRNLIBSSH2_KNOWNHOST_INIT}, \
|
||||
{.function = HRNLIBSSH2_KNOWNHOST_READFILE, .param = "[\"" KNOWNHOSTS_FILE_CSTR "\",1]", .resultInt = 5}, \
|
||||
{.function = HRNLIBSSH2_SESSION_HOSTKEY, .len = 20, .type = LIBSSH2_HOSTKEY_TYPE_RSA, .resultZ = HOSTKEY}, \
|
||||
{.function = HRNLIBSSH2_KNOWNHOST_CHECKP, .param = "[\"localhost\",22,\"" HOSTKEY "\",20,65537]", \
|
||||
.resultInt = LIBSSH2_KNOWNHOST_CHECK_MATCH}, \
|
||||
{.function = HRNLIBSSH2_USERAUTH_PUBLICKEY_FROMFILE_EX, \
|
||||
.param = "[\"" TEST_USER "\"," TEST_USER_LEN ",\"" KEYPUB_CSTR "\",\"" KEYPRIV_CSTR "\",null]", \
|
||||
.resultInt = 0}, \
|
||||
@ -77,7 +93,7 @@ Macros for defining groups of functions that implement commands
|
||||
{.function = HRNLIBSSH2_SESSION_FREE, .resultInt = 0}, \
|
||||
{.function = NULL} \
|
||||
|
||||
// older systems do not support LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
// Older systems do not support LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
#define HOSTKEY_HASH_ENTRY() \
|
||||
{.function = HRNLIBSSH2_HOSTKEY_HASH, .param = "[3]", .resultZ = "12345678910123456789"}
|
||||
@ -108,6 +124,9 @@ typedef struct HrnLibSsh2
|
||||
const String *fileName; // libssh2_readdir* libssh2_stat* filename
|
||||
const String *readBuffer; // what to copy into read buffer
|
||||
TimeMSec sleep; // Sleep specified milliseconds before returning from function
|
||||
size_t len; // libssh2_session_hostkey len
|
||||
int type; // libssh2_session_hostkey type
|
||||
char *errMsg; // libssh2_session_last_error error msg
|
||||
} HrnLibSsh2;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
|
@ -319,9 +319,11 @@ testRun(void)
|
||||
" --repo-s3-uri-style S3 URI Style [default=host]\n"
|
||||
" --repo-sftp-host SFTP repository host\n"
|
||||
" --repo-sftp-host-fingerprint SFTP repository host fingerprint\n"
|
||||
" --repo-sftp-host-key-check-type SFTP host key check type [default=strict]\n"
|
||||
" --repo-sftp-host-key-hash-type SFTP repository host key hash type\n"
|
||||
" --repo-sftp-host-port SFTP repository host port [default=22]\n"
|
||||
" --repo-sftp-host-user SFTP repository host user\n"
|
||||
" --repo-sftp-known-host SFTP known hosts file\n"
|
||||
" --repo-sftp-private-key-file SFTP private key file\n"
|
||||
" --repo-sftp-private-key-passphrase SFTP private key passphrase\n"
|
||||
" --repo-sftp-public-key-file SFTP public key file\n"
|
||||
|
@ -640,6 +640,102 @@ testRun(void)
|
||||
|
||||
hrnCfgEnvKeyRemoveRaw(cfgOptRepoAzureAccount, 1);
|
||||
hrnCfgEnvKeyRemoveRaw(cfgOptRepoAzureKey, 1);
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error on missing SFTP fingerprint");
|
||||
|
||||
argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
|
||||
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/pg");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoType, 1, "sftp");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, "/repo");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHost, "localhost");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyHashType, "sha1");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostUser, TEST_USER);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpPrivateKeyFile, "/privatekey");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyCheckType, "fingerprint");
|
||||
|
||||
TEST_ERROR(
|
||||
hrnCfgLoadP(cfgCmdArchivePush, argList), OptionRequiredError,
|
||||
"archive-push command requires option: repo1-sftp-host-fingerprint");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("success on SFTP fingerprint not needed");
|
||||
|
||||
argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
|
||||
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/pg");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoType, 1, "sftp");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, "/repo");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHost, "localhost");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyHashType, "sha1");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostUser, TEST_USER);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpPrivateKeyFile, "/privatekey");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyCheckType, "accept-new");
|
||||
HRN_CFG_LOAD(cfgCmdArchivePush, argList);
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error on incorrect SFTP fingerprint check type");
|
||||
|
||||
argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
|
||||
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/pg");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoType, 1, "sftp");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, "/repo");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHost, "localhost");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyHashType, "sha1");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostUser, TEST_USER);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpPrivateKeyFile, "/privatekey");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostFingerprint, "xxx");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyCheckType, "strict");
|
||||
|
||||
TEST_ERROR(
|
||||
hrnCfgLoadP(cfgCmdArchivePush, argList), OptionInvalidError,
|
||||
"option 'repo1-sftp-host-fingerprint' not valid without option 'repo1-sftp-host-key-check-type' = 'fingerprint'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("warn on default SFTP fingerprint check type");
|
||||
|
||||
argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
|
||||
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/pg");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoType, 1, "sftp");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, "/repo");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHost, "localhost");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyHashType, "sha1");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostUser, TEST_USER);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpPrivateKeyFile, "/privatekey");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostFingerprint, "xxx");
|
||||
HRN_CFG_LOAD(cfgCmdArchivePush, argList);
|
||||
|
||||
TEST_RESULT_LOG(
|
||||
"P00 WARN: option 'repo1-sftp-host-fingerprint' without option 'repo1-sftp-host-key-check-type' = 'fingerprint' is"
|
||||
" deprecated\n"
|
||||
" HINT: set option 'repo1-sftp-host-key-check-type=fingerprint'");
|
||||
|
||||
TEST_RESULT_UINT(
|
||||
cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, 0), CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT,
|
||||
"check type updated");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("success on correct SFTP fingerprint check type");
|
||||
|
||||
argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
|
||||
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/pg");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoType, 1, "sftp");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, "/repo");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHost, "localhost");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyHashType, "sha1");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostUser, TEST_USER);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpPrivateKeyFile, "/privatekey");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostFingerprint, "xxx");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyCheckType, "fingerprint");
|
||||
HRN_CFG_LOAD(cfgCmdArchivePush, argList);
|
||||
|
||||
TEST_RESULT_UINT(
|
||||
cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, 0), CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT,
|
||||
"check type");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user