diff --git a/doc/xml/release.xml b/doc/xml/release.xml index 9c0145cd9..e228900e4 100644 --- a/doc/xml/release.xml +++ b/doc/xml/release.xml @@ -28,6 +28,17 @@

Multi-stanza check command.

+ + + + + + + + +

Accept leading tilde in paths for SFTP public/private keys.

+
+ diff --git a/src/common/user.c b/src/common/user.c index e10030e4e..3051787e9 100644 --- a/src/common/user.c +++ b/src/common/user.c @@ -24,6 +24,9 @@ static struct gid_t groupId; // Real group id of the calling process from getgid() const String *groupName; // Group name if it exists +#ifdef HAVE_LIBSSH2 + const String *userHome; // User home directory +#endif // HAVE_LIBSSH2 } userLocalData; /**********************************************************************************************************************************/ @@ -40,6 +43,9 @@ userInitInternal(void) userLocalData.userId = getuid(); userLocalData.userName = userNameFromId(userLocalData.userId); +#ifdef HAVE_LIBSSH2 + userLocalData.userHome = userHomeFromId(userLocalData.userId); +#endif // HAVE_LIBSSH2 userLocalData.userRoot = userLocalData.userId == 0; userLocalData.groupId = getgid(); @@ -114,6 +120,35 @@ groupNameFromId(gid_t groupId) FUNCTION_TEST_RETURN(STRING, NULL); } +/**********************************************************************************************************************************/ +// Currently userHome() and userHomeFromId() are only used when building with libssh2 +#ifdef HAVE_LIBSSH2 + +FN_EXTERN const String * +userHome(void) +{ + FUNCTION_TEST_VOID(); + FUNCTION_TEST_RETURN_CONST(STRING, userLocalData.userHome); +} + +/**********************************************************************************************************************************/ +FN_EXTERN String * +userHomeFromId(uid_t userId) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(UINT, userId); + FUNCTION_TEST_END(); + + struct passwd *userData = getpwuid(userId); + + if (userData != NULL) + FUNCTION_TEST_RETURN(STRING, strNewZ(userData->pw_dir)); + + FUNCTION_TEST_RETURN(STRING, NULL); +} + +#endif // HAVE_LIBSSH2 + /**********************************************************************************************************************************/ FN_EXTERN uid_t userId(void) diff --git a/src/common/user.h b/src/common/user.h index 11248668b..61dfb85e5 100644 --- a/src/common/user.h +++ b/src/common/user.h @@ -26,6 +26,16 @@ FN_EXTERN const String *groupName(void); // Get the group name from a group id. Returns NULL if the group id is invalid or there is no mapping. FN_EXTERN String *groupNameFromId(gid_t groupId); +#ifdef HAVE_LIBSSH2 + +// Get the home directory of the current user. Returns NULL if there is no mapping. +FN_EXTERN const String *userHome(void); + +// Get the user home directory from a user id. Returns NULL if the user id is invalid or there is no mapping. +FN_EXTERN String *userHomeFromId(uid_t userId); + +#endif // HAVE_LIBSSH2 + // Get the id of the current user FN_EXTERN uid_t userId(void); diff --git a/src/storage/sftp/storage.c b/src/storage/sftp/storage.c index 99610a03d..876540fd4 100644 --- a/src/storage/sftp/storage.c +++ b/src/storage/sftp/storage.c @@ -10,6 +10,7 @@ SFTP Storage #include "common/io/fd.h" #include "common/io/socket/client.h" #include "common/log.h" +#include "common/regExp.h" #include "common/user.h" #include "storage/sftp/read.h" #include "storage/sftp/storage.intern.h" @@ -306,6 +307,26 @@ storageSftpInfo(THIS_VOID, const String *const file, const StorageInfoLevel leve FUNCTION_LOG_RETURN(STORAGE_INFO, result); } +/**********************************************************************************************************************************/ +static String * +storageSftpExpandTildePath(const String *const tildePath) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(STRING, tildePath); + FUNCTION_TEST_END(); + + String *const result = strNew(); + + // Append to user home directory path substring after the tilde + MEM_CONTEXT_TEMP_BEGIN() + { + strCatFmt(result, "%s%s", strZ(userHome()), strZ(strSub(tildePath, (size_t)strChr(tildePath, '~') + 1))); + } + MEM_CONTEXT_TEMP_END(); + + FUNCTION_TEST_RETURN(STRING, result); +} + /**********************************************************************************************************************************/ // Helper function to get info for a file if it exists. This logic can't live directly in storageSftpList() because there is a race // condition where a file might exist while listing the directory but it is gone before stat() can be called. In order to get @@ -877,13 +898,22 @@ storageSftpNew( } } + // Perform public key authorization, expand leading tilde key file paths if needed + String *const privKeyPath = regExpMatchOne(STRDEF("^ *~"), keyPriv) ? storageSftpExpandTildePath(keyPriv) : strDup(keyPriv); + String *const pubKeyPath = + param.keyPub != NULL && regExpMatchOne(STRDEF("^ *~"), param.keyPub) ? + storageSftpExpandTildePath(param.keyPub) : strDup(param.keyPub); + do { rc = libssh2_userauth_publickey_fromfile( - this->session, strZ(user), strZNull(param.keyPub), strZ(keyPriv), strZNull(param.keyPassphrase)); + this->session, strZ(user), strZNull(pubKeyPath), strZ(privKeyPath), strZNull(param.keyPassphrase)); } while (storageSftpWaitFd(this, rc)); + strFree(privKeyPath); + strFree(pubKeyPath); + if (rc != 0) { if (rc == LIBSSH2_ERROR_EAGAIN) diff --git a/test/src/module/common/userTest.c b/test/src/module/common/userTest.c index 6b450cc56..b7c90f244 100644 --- a/test/src/module/common/userTest.c +++ b/test/src/module/common/userTest.c @@ -30,6 +30,10 @@ testRun(void) TEST_RESULT_UINT(groupIdFromName(STRDEF("bogus")), (uid_t)-1, "get bogus group id"); TEST_RESULT_STR(groupName(), TEST_GROUP_STR, "check name name"); TEST_RESULT_STR_Z(groupNameFromId(77777), NULL, "invalid group name by id"); + + TEST_RESULT_STR(userHome(), STRDEF("/home/" TEST_USER), "check user name"); + TEST_RESULT_STR_Z(userHomeFromId(userId()), "/home/" TEST_USER, "user home by id"); + TEST_RESULT_STR_Z(userHomeFromId(77777), NULL, "invalid user home by id"); } FUNCTION_HARNESS_RETURN_VOID(); diff --git a/test/src/module/storage/sftpTest.c b/test/src/module/storage/sftpTest.c index 03f02380a..da94cf7b1 100644 --- a/test/src/module/storage/sftpTest.c +++ b/test/src/module/storage/sftpTest.c @@ -192,7 +192,7 @@ testRun(void) ServiceError, "requested ssh2 hostkey hash type (aes-256-cbc) not available"); // ------------------------------------------------------------------------------------------------------------------------- - TEST_TITLE("public key from file auth failure"); + TEST_TITLE("public key from file auth failure leading - tilde key paths"); hrnLibSsh2ScriptSet((HrnLibSsh2 []) { @@ -212,7 +212,8 @@ testRun(void) TEST_ERROR( storageSftpNewP( - TEST_PATH_STR, STRDEF("localhost"), 22, TEST_USER_STR, 1000, KEYPRIV, hashTypeSha1, .keyPub = KEYPUB, + TEST_PATH_STR, STRDEF("localhost"), 22, TEST_USER_STR, 1000, STRDEF("~/.ssh/id_rsa"), hashTypeSha1, + .keyPub = STRDEF("~/.ssh/id_rsa.pub"), .hostFingerprint = STRDEF("3132333435363738393039383736353433323130")), ServiceError, "public key authentication failed: libssh2 error [-16]\n" @@ -4577,8 +4578,8 @@ testRun(void) hrnCfgArgRawZ(argList, cfgOptRepoType, "sftp"); hrnCfgArgRawZ(argList, cfgOptRepoSftpHost, "localhost"); hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyHashType, "sha1"); - hrnCfgArgRawZ(argList, cfgOptRepoSftpPrivateKeyFile, KEYPRIV_CSTR); - hrnCfgArgRawZ(argList, cfgOptRepoSftpPublicKeyFile, KEYPUB_CSTR); + hrnCfgArgRawZ(argList, cfgOptRepoSftpPrivateKeyFile, " ~/.ssh/id_rsa"); + hrnCfgArgRawZ(argList, cfgOptRepoSftpPublicKeyFile, " ~/.ssh/id_rsa.pub"); HRN_CFG_LOAD(cfgCmdArchiveGet, argList); const Storage *storage = NULL;