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")
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:
"<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.
s3fs is always using DNS cache, this option make DNS cache disable.
.TP

View File

@ -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:
// <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)
{
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;

View File

@ -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);

View File

@ -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
//

View File

@ -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"
" \"<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"
" - s3fs is always using DNS cache, this option make DNS cache disable.\n"
"\n"