Support SSL client cert and added ssl_client_cert option

This commit is contained in:
Takeshi Nakatani 2024-03-24 07:30:40 +00:00 committed by Andrew Gaul
parent 9ab5a2ea73
commit 95026804e9
5 changed files with 150 additions and 0 deletions

View File

@ -179,6 +179,18 @@ server certificate won't be checked against the available certificate authoritie
\fB\-o\fR ssl_verify_hostname (default="2") \fB\-o\fR ssl_verify_hostname (default="2")
When 0, do not verify the SSL certificate against the hostname. When 0, do not verify the SSL certificate against the hostname.
.TP .TP
\fB\-o\fR ssl_client_cert (default="")
Specify an SSL client certificate.
Specify this optional parameter in the following format:
"<SSL Cert>[:<Cert Type>[:<Private Key>[:<Key Type>
[:<Password>]]]]"
<SSL Cert>: Client certificate.
Specify the file path or NickName(for NSS, etc.).
<Cert Type>: Type of certificate, default is "PEM"(optional).
<Private Key>: Certificate's private key file(optional).
<Key Type>: Type of private key, default is "PEM"(optional).
<Password>: 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. \fB\-o\fR nodnscache - disable DNS cache.
s3fs is always using DNS cache, this option make DNS cache disable. s3fs is always using DNS cache, this option make DNS cache disable.
.TP .TP

View File

@ -83,6 +83,7 @@ static constexpr char SPECIAL_DARWIN_MIME_FILE[] = "/etc/apache2/mime.typ
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Class S3fsCurl // Class S3fsCurl
//------------------------------------------------------------------- //-------------------------------------------------------------------
constexpr char S3fsCurl::S3FS_SSL_PRIVKEY_PASSWORD[];
pthread_mutex_t S3fsCurl::curl_warnings_lock; pthread_mutex_t S3fsCurl::curl_warnings_lock;
pthread_mutex_t S3fsCurl::curl_handles_lock; pthread_mutex_t S3fsCurl::curl_handles_lock;
S3fsCurl::callback_locks_t S3fsCurl::callback_locks; S3fsCurl::callback_locks_t S3fsCurl::callback_locks;
@ -107,6 +108,12 @@ bool S3fsCurl::is_verbose = false;
bool S3fsCurl::is_dump_body = false; bool S3fsCurl::is_dump_body = false;
S3fsCred* S3fsCurl::ps3fscred = nullptr; S3fsCred* S3fsCurl::ps3fscred = nullptr;
long S3fsCurl::ssl_verify_hostname = 1; // default(original code...) 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 // protected by curl_warnings_lock
bool S3fsCurl::curl_warnings_once = false; bool S3fsCurl::curl_warnings_once = false;
@ -1013,6 +1020,75 @@ long S3fsCurl::SetSslVerifyHostname(long value)
return old; return old;
} }
bool S3fsCurl::SetSSLClientCertOptions(const std::string& values)
{
// Parse values:
// <values> = "<SSL Client Cert>:<SSL Cert Type>:<SSL Cert Private Key>:<SSL Cert Private Type>:<Key Password>"
//
if(values.empty()){
return false;
}
std::list<std::string> 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) bool S3fsCurl::SetMultipartSize(off_t size)
{ {
size = size * 1024 * 1024; 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((S3fsCurl::is_dns_cache || S3fsCurl::is_ssl_session_cache) && S3fsCurl::hCurlShare){
if(CURLE_OK != curl_easy_setopt(hCurl, CURLOPT_SHARE, S3fsCurl::hCurlShare)){ if(CURLE_OK != curl_easy_setopt(hCurl, CURLOPT_SHARE, S3fsCurl::hCurlShare)){
return false; return false;

View File

@ -110,6 +110,9 @@ class S3fsCurl
IAMROLE IAMROLE
}; };
// Environment name
static constexpr char S3FS_SSL_PRIVKEY_PASSWORD[] = "S3FS_SSL_PRIVKEY_PASSWORD";
// class variables // class variables
static pthread_mutex_t curl_warnings_lock; static pthread_mutex_t curl_warnings_lock;
static bool curl_warnings_once; // emit older curl warnings only once static bool curl_warnings_once; // emit older curl warnings only once
@ -139,6 +142,11 @@ class S3fsCurl
static bool is_dump_body; static bool is_dump_body;
static S3fsCred* ps3fscred; static S3fsCred* ps3fscred;
static long ssl_verify_hostname; 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 curltime_t curl_times;
static curlprogress_t curl_progress; static curlprogress_t curl_progress;
static std::string curl_ca_bundle; static std::string curl_ca_bundle;
@ -317,6 +325,7 @@ class S3fsCurl
static bool IsDumpBody() { return S3fsCurl::is_dump_body; } static bool IsDumpBody() { return S3fsCurl::is_dump_body; }
static long SetSslVerifyHostname(long value); static long SetSslVerifyHostname(long value);
static long GetSslVerifyHostname() { return S3fsCurl::ssl_verify_hostname; } static long GetSslVerifyHostname() { return S3fsCurl::ssl_verify_hostname; }
static bool SetSSLClientCertOptions(const std::string& values);
static void ResetOffset(S3fsCurl* pCurl); static void ResetOffset(S3fsCurl* pCurl);
// maximum parallel GET and PUT requests // maximum parallel GET and PUT requests
static int SetMaxParallelCount(int value); static int SetMaxParallelCount(int value);

View File

@ -5085,6 +5085,14 @@ static int my_fuse_opt_proc(void* data, const char* arg, int key, struct fuse_ar
} }
return 0; 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 // Detect options for credential
// //

View File

@ -217,6 +217,21 @@ static constexpr char help_string[] =
" ssl_verify_hostname (default=\"2\")\n" " ssl_verify_hostname (default=\"2\")\n"
" - When 0, do not verify the SSL certificate against the hostname.\n" " - When 0, do not verify the SSL certificate against the hostname.\n"
"\n" "\n"
" ssl_client_cert (default=\"\")\n"
" - Specify an SSL client certificate.\n"
" Specify this optional parameter in the following format:\n"
" \"<SSL Cert>[:<Cert Type>[:<Private Key>[:<Key Type>\n"
" [:<Password>]]]]\"\n"
" <SSL Cert>: Client certificate.\n"
" Specify the file path or NickName(for NSS, etc.).\n"
" <Cert Type>: Type of certificate, default is \"PEM\"(optional).\n"
" <Private Key>: Certificate's private key file(optional).\n"
" <Key Type>: Type of private key, default is \"PEM\"(optional).\n"
" <Password>: 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" " nodnscache (disable DNS cache)\n"
" - s3fs is always using DNS cache, this option make DNS cache disable.\n" " - s3fs is always using DNS cache, this option make DNS cache disable.\n"
"\n" "\n"