diff --git a/doc/man/s3fs.1.in b/doc/man/s3fs.1.in index 30631a0..d6056e1 100644 --- a/doc/man/s3fs.1.in +++ b/doc/man/s3fs.1.in @@ -179,6 +179,18 @@ server certificate won't be checked against the available certificate authoritie \fB\-o\fR ssl_verify_hostname (default="2") When 0, do not verify the SSL certificate against the hostname. .TP +\fB\-o\fR ssl_client_cert (default="") +Specify an SSL client certificate. +Specify this optional parameter in the following format: + "[:[:[: + [:]]]]" + : Client certificate. + Specify the file path or NickName(for NSS, etc.). + : Type of certificate, default is "PEM"(optional). + : Certificate's private key file(optional). + : Type of private key, default is "PEM"(optional). + : Passphrase of the private key(optional). It is also possible to omit this value and specify it using the environment variable "S3FS_SSL_PRIVKEY_PASSWORD". +.TP \fB\-o\fR nodnscache - disable DNS cache. s3fs is always using DNS cache, this option make DNS cache disable. .TP diff --git a/src/curl.cpp b/src/curl.cpp index 55d16e7..d93d0e7 100644 --- a/src/curl.cpp +++ b/src/curl.cpp @@ -83,6 +83,7 @@ static constexpr char SPECIAL_DARWIN_MIME_FILE[] = "/etc/apache2/mime.typ //------------------------------------------------------------------- // Class S3fsCurl //------------------------------------------------------------------- +constexpr char S3fsCurl::S3FS_SSL_PRIVKEY_PASSWORD[]; pthread_mutex_t S3fsCurl::curl_warnings_lock; pthread_mutex_t S3fsCurl::curl_handles_lock; S3fsCurl::callback_locks_t S3fsCurl::callback_locks; @@ -107,6 +108,12 @@ bool S3fsCurl::is_verbose = false; bool S3fsCurl::is_dump_body = false; S3fsCred* S3fsCurl::ps3fscred = nullptr; long S3fsCurl::ssl_verify_hostname = 1; // default(original code...) +// SSL client cert options +std::string S3fsCurl::client_cert; +std::string S3fsCurl::client_cert_type; +std::string S3fsCurl::client_priv_key; +std::string S3fsCurl::client_priv_key_type; +std::string S3fsCurl::client_key_password; // protected by curl_warnings_lock bool S3fsCurl::curl_warnings_once = false; @@ -1013,6 +1020,75 @@ long S3fsCurl::SetSslVerifyHostname(long value) return old; } +bool S3fsCurl::SetSSLClientCertOptions(const std::string& values) +{ + // Parse values: + // = "::::" + // + if(values.empty()){ + return false; + } + + std::list valarr; + std::string::size_type start_pos = 0; + std::string::size_type pos; + do{ + if(std::string::npos == (pos = values.find(':', start_pos))){ + valarr.push_back(values.substr(start_pos)); + start_pos = pos; + }else{ + if(0 < (pos - start_pos)){ + valarr.push_back(values.substr(start_pos, (pos - start_pos))); + }else{ + valarr.emplace_back(""); + } + start_pos = ++pos; + } + }while(std::string::npos != start_pos); + + // set client cert + if(!valarr.empty() && !valarr.front().empty()){ + S3fsCurl::client_cert = valarr.front(); + valarr.pop_front(); + + // set client cert type + if(!valarr.empty()){ + S3fsCurl::client_cert_type = valarr.front(); // allow empty(default: PEM) + valarr.pop_front(); + + // set client private key + if(!valarr.empty()){ + S3fsCurl::client_priv_key = valarr.front(); // allow empty + valarr.pop_front(); + + // set client private key type + if(!valarr.empty()){ + S3fsCurl::client_priv_key_type = valarr.front(); // allow empty(default: PEM) + valarr.pop_front(); + + // set key password + if(!valarr.empty()){ + S3fsCurl::client_key_password = valarr.front(); // allow empty + } + } + } + } + } + + // [NOTE] + // If the private key is set but the password is not set, + // check the environment variables. + // + if(!S3fsCurl::client_priv_key.empty() && S3fsCurl::client_key_password.empty()){ + const char* pass = std::getenv(S3fsCurl::S3FS_SSL_PRIVKEY_PASSWORD); + if(pass != nullptr){ + S3fsCurl::client_key_password = pass; + } + } + + return true; +} + bool S3fsCurl::SetMultipartSize(off_t size) { size = size * 1024 * 1024; @@ -1985,6 +2061,36 @@ bool S3fsCurl::ResetHandle(AutoLock::Type locktype) } } } + // SSL Client Cert + if(!S3fsCurl::client_cert.empty()){ + if(CURLE_OK != curl_easy_setopt(hCurl, CURLOPT_SSLCERT, S3fsCurl::client_cert.c_str())){ + return false; + } + if(!S3fsCurl::client_cert_type.empty() && 0 != strcasecmp(S3fsCurl::client_cert_type.c_str(), "PEM")){ // "PEM" is default + if(CURLE_OK != curl_easy_setopt(hCurl, CURLOPT_SSLCERTTYPE, S3fsCurl::client_cert_type.c_str())){ + return false; + } + } + + // Private key + if(!S3fsCurl::client_priv_key.empty()){ + if(CURLE_OK != curl_easy_setopt(hCurl, CURLOPT_SSLKEY, S3fsCurl::client_priv_key.c_str())){ + return false; + } + if(!S3fsCurl::client_priv_key_type.empty() && 0 != strcasecmp(S3fsCurl::client_priv_key_type.c_str(), "PEM")){ // "PEM" is default + if(CURLE_OK != curl_easy_setopt(hCurl, CURLOPT_SSLKEYTYPE, S3fsCurl::client_priv_key_type.c_str())){ + return false; + } + } + // Password + if(!S3fsCurl::client_key_password.empty()){ + if(CURLE_OK != curl_easy_setopt(hCurl, CURLOPT_KEYPASSWD, S3fsCurl::client_key_password.c_str())){ + return false; + } + } + } + } + if((S3fsCurl::is_dns_cache || S3fsCurl::is_ssl_session_cache) && S3fsCurl::hCurlShare){ if(CURLE_OK != curl_easy_setopt(hCurl, CURLOPT_SHARE, S3fsCurl::hCurlShare)){ return false; diff --git a/src/curl.h b/src/curl.h index e288c85..74d6958 100644 --- a/src/curl.h +++ b/src/curl.h @@ -110,6 +110,9 @@ class S3fsCurl IAMROLE }; + // Environment name + static constexpr char S3FS_SSL_PRIVKEY_PASSWORD[] = "S3FS_SSL_PRIVKEY_PASSWORD"; + // class variables static pthread_mutex_t curl_warnings_lock; static bool curl_warnings_once; // emit older curl warnings only once @@ -139,6 +142,11 @@ class S3fsCurl static bool is_dump_body; static S3fsCred* ps3fscred; static long ssl_verify_hostname; + static std::string client_cert; + static std::string client_cert_type; + static std::string client_priv_key; + static std::string client_priv_key_type; + static std::string client_key_password; static curltime_t curl_times; static curlprogress_t curl_progress; static std::string curl_ca_bundle; @@ -317,6 +325,7 @@ class S3fsCurl static bool IsDumpBody() { return S3fsCurl::is_dump_body; } static long SetSslVerifyHostname(long value); static long GetSslVerifyHostname() { return S3fsCurl::ssl_verify_hostname; } + static bool SetSSLClientCertOptions(const std::string& values); static void ResetOffset(S3fsCurl* pCurl); // maximum parallel GET and PUT requests static int SetMaxParallelCount(int value); diff --git a/src/s3fs.cpp b/src/s3fs.cpp index 1e633a2..3a2c6cf 100644 --- a/src/s3fs.cpp +++ b/src/s3fs.cpp @@ -5085,6 +5085,14 @@ static int my_fuse_opt_proc(void* data, const char* arg, int key, struct fuse_ar } return 0; } + else if(is_prefix(arg, "ssl_client_cert=")){ + std::string values = strchr(arg, '=') + sizeof(char); + if(!S3fsCurl::SetSSLClientCertOptions(values)){ + S3FS_PRN_EXIT("failed to set SSL client certification options."); + return -1; + } + return 0; + } // // Detect options for credential // diff --git a/src/s3fs_help.cpp b/src/s3fs_help.cpp index 735c485..500ba06 100644 --- a/src/s3fs_help.cpp +++ b/src/s3fs_help.cpp @@ -217,6 +217,21 @@ static constexpr char help_string[] = " ssl_verify_hostname (default=\"2\")\n" " - When 0, do not verify the SSL certificate against the hostname.\n" "\n" + " ssl_client_cert (default=\"\")\n" + " - Specify an SSL client certificate.\n" + " Specify this optional parameter in the following format:\n" + " \"[:[:[:\n" + " [:]]]]\"\n" + " : Client certificate.\n" + " Specify the file path or NickName(for NSS, etc.).\n" + " : Type of certificate, default is \"PEM\"(optional).\n" + " : Certificate's private key file(optional).\n" + " : Type of private key, default is \"PEM\"(optional).\n" + " : Passphrase of the private key(optional).\n" + " It is also possible to omit this value and specify\n" + " it using the environment variable\n" + " \"S3FS_SSL_PRIVKEY_PASSWORD\".\n" + "\n" " nodnscache (disable DNS cache)\n" " - s3fs is always using DNS cache, this option make DNS cache disable.\n" "\n"