CernVM-FS  2.12.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
authz_curl.cc
Go to the documentation of this file.
1 
4 #define __STDC_FORMAT_MACROS
5 #include "authz_curl.h"
6 
7 #include <openssl/err.h>
8 #include <openssl/ssl.h>
9 #include <pthread.h>
10 
11 #include <cassert>
12 
13 #include "authz/authz_session.h"
14 #include "crypto/openssl_version.h"
15 #include "duplex_curl.h"
16 #include "util/concurrency.h"
17 #include "util/logging.h"
18 #include "util/pointer.h"
19 
20 using namespace std; // NOLINT
21 
22 
23 namespace {
24 
25 struct sslctx_info {
26  sslctx_info() : chain(NULL), pkey(NULL) {}
27 
28  STACK_OF(X509) *chain;
29  EVP_PKEY *pkey;
30 };
31 
32 struct bearer_info {
37  struct curl_slist *list;
38 
42  char* token;
43 };
44 } // anonymous namespace
45 
46 
48 
49 
51  : authz_session_manager_(sm)
52 {
53  // Required for logging OpenSSL errors
54  SSL_load_error_strings();
55  ssl_strings_loaded_ = true;
56 }
57 
58 
60  CURL *curl,
61  void *sslctx,
62  void *parm)
63 {
64  sslctx_info *p = reinterpret_cast<sslctx_info *>(parm);
65  SSL_CTX *ctx = reinterpret_cast<SSL_CTX *>(sslctx);
66 
67  if (parm == NULL)
68  return CURLE_OK;
69 
70  STACK_OF(X509) *chain = p->chain;
71  EVP_PKEY *pkey = p->pkey;
72 
73  LogCvmfs(kLogAuthz, kLogDebug, "Customizing OpenSSL context.");
74 
75  int cert_count = sk_X509_num(chain);
76  if (cert_count == 0) {
77  LogOpenSSLErrors("No certificate found in chain.");
78  }
79  X509 *cert = sk_X509_value(chain, 0);
80 
81  // NOTE: SSL_CTX_use_certificate and _user_PrivateKey increase the ref count.
82  if (!SSL_CTX_use_certificate(ctx, cert)) {
83  LogOpenSSLErrors("Failed to set the user certificate in the SSL "
84  "connection");
85  return CURLE_SSL_CERTPROBLEM;
86  }
87 
88  if (!SSL_CTX_use_PrivateKey(ctx, pkey)) {
89  LogOpenSSLErrors("Failed to set the private key in the SSL connection");
90  return CURLE_SSL_CERTPROBLEM;
91  }
92 
93  if (!SSL_CTX_check_private_key(ctx)) {
94  LogOpenSSLErrors("Provided certificate and key do not match");
95  return CURLE_SSL_CERTPROBLEM;
96  } else {
97  LogCvmfs(kLogAuthz, kLogDebug, "Client certificate and key match.");
98  }
99 
100  // NOTE: SSL_CTX_add_extra_chain_cert DOES NOT increase the ref count
101  // Instead, it now owns the pointer. THIS IS DIFFERENT FROM ABOVE.
102  for (int idx = 1; idx < cert_count; idx++) {
103  cert = sk_X509_value(chain, idx);
104  if (!SSL_CTX_add_extra_chain_cert(ctx, X509_dup(cert))) {
105  LogOpenSSLErrors("Failed to add client cert to chain");
106  }
107  }
108 
109  return CURLE_OK;
110 }
111 
112 
114  CURL *curl_handle,
115  const AuthzToken &token,
116  void **info_data)
117 {
118  if (*info_data == NULL) {
119  AuthzToken* saved_token = new AuthzToken();
120  saved_token->type = kTokenBearer;
121  saved_token->data = new bearer_info;
122  bearer_info* bearer = static_cast<bearer_info*>(saved_token->data);
123  bearer->list = NULL;
124  bearer->token = static_cast<char*>(smalloc((sizeof(char) * token.size)+ 1));
125  memcpy(bearer->token, token.data, token.size);
126  static_cast<char*>(bearer->token)[token.size] = 0;
127  *info_data = saved_token;
128  }
129 
130  AuthzToken* tmp_token = static_cast<AuthzToken*>(*info_data);
131  bearer_info* bearer = static_cast<bearer_info*>(tmp_token->data);
132 
133  LogCvmfs(kLogAuthz, kLogDebug, "Setting OAUTH bearer token to: %s",
134  static_cast<char*>(bearer->token));
135 
136  // Create the Bearer token
137  // The CURLOPT_XOAUTH2_BEARER option only works "IMAP, POP3 and SMTP"
138  // protocols. Not HTTPS
139  std::string auth_preamble = "Authorization: Bearer ";
140  std::string auth_header = auth_preamble + static_cast<char*>(bearer->token);
141  bearer->list = curl_slist_append(bearer->list, auth_header.c_str());
142  int retval = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, bearer->list);
143 
144  if (retval != CURLE_OK) {
145  LogCvmfs(kLogAuthz, kLogSyslogErr, "Failed to set Oauth2 Bearer Token");
146  return false;
147  }
148 
149  return true;
150 }
151 
152 
153 
155  CURL *curl_handle,
156  pid_t pid,
157  void **info_data)
158 {
159  assert(info_data);
160 
161  // File catalog has no membership requirement, no tokens to attach
162  if (membership_.empty())
163  return false;
164 
165  // We cannot rely on libcurl to pipeline (yet), as cvmfs may
166  // bounce between different auth handles.
167  curl_easy_setopt(curl_handle, CURLOPT_FRESH_CONNECT, 1);
168  curl_easy_setopt(curl_handle, CURLOPT_FORBID_REUSE, 1);
169  curl_easy_setopt(curl_handle, CURLOPT_SSL_SESSIONID_CACHE, 0);
170 
171  UniquePtr<AuthzToken> token(
173  if (!token.IsValid()) {
174  LogCvmfs(kLogAuthz, kLogDebug, "failed to get authz token for pid %d", pid);
175  return false;
176  }
177 
178  switch (token->type) {
179  case kTokenBearer:
180  // If it's a scitoken, then just go to the private
181  // ConfigureSciTokenCurl function
182  return ConfigureSciTokenCurl(curl_handle, *token, info_data);
183 
184  case kTokenX509:
185  // The x509 code is below, so just break and go.
186  break;
187 
188  default:
189  // Oh no, don't know the the token type, throw error and return
190  LogCvmfs(kLogAuthz, kLogDebug, "unknown token type: %d", token->type);
191  return false;
192  }
193 
194  curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_DATA, NULL);
195 
196  // The calling layer is reusing data;
197  if (*info_data) {
198  curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_DATA,
199  static_cast<AuthzToken*>(*info_data)->data);
200  return true;
201  }
202 
203 
204  int retval = curl_easy_setopt(curl_handle,
205  CURLOPT_SSL_CTX_FUNCTION,
207  if (retval != CURLE_OK) {
208  LogCvmfs(kLogAuthz, kLogDebug, "cannot configure curl ssl callback");
209  return false;
210  }
211 
212  UniquePtr<sslctx_info> parm(new sslctx_info);
213 
214  STACK_OF(X509_INFO) *sk = NULL;
215  STACK_OF(X509) *certstack = sk_X509_new_null();
216  parm->chain = certstack;
217  if (certstack == NULL) {
218  LogCvmfs(kLogAuthz, kLogSyslogErr, "Failed to allocate new X509 chain.");
219  return false;
220  }
221 
222  BIO *bio_token = BIO_new_mem_buf(token->data, token->size);
223  assert(bio_token != NULL);
224  sk = PEM_X509_INFO_read_bio(bio_token, NULL, NULL, NULL);
225  BIO_free(bio_token);
226  if (!sk) {
227  LogOpenSSLErrors("Failed to load credential file.");
228  sk_X509_INFO_free(sk);
229  sk_X509_free(certstack);
230  return false;
231  }
232 
233  while (sk_X509_INFO_num(sk)) {
234  X509_INFO *xi = sk_X509_INFO_shift(sk);
235  if (xi == NULL) {continue;}
236  if (xi->x509 != NULL) {
237 #ifdef OPENSSL_API_INTERFACE_V11
238  retval = X509_up_ref(xi->x509);
239  assert(retval == 1);
240 #else
241  CRYPTO_add(&xi->x509->references, 1, CRYPTO_LOCK_X509);
242 #endif
243  sk_X509_push(certstack, xi->x509);
244  }
245  if ((xi->x_pkey != NULL) && (xi->x_pkey->dec_pkey != NULL)) {
246  parm->pkey = xi->x_pkey->dec_pkey;
247 #ifdef OPENSSL_API_INTERFACE_V11
248  retval = EVP_PKEY_up_ref(parm->pkey);
249  assert(retval == 1);
250 #else
251  CRYPTO_add(&parm->pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
252 #endif
253  }
254  X509_INFO_free(xi);
255  }
256  sk_X509_INFO_free(sk);
257 
258  if (parm->pkey == NULL) {
259  // Sigh - PEM_X509_INFO_read doesn't understand old key encodings.
260  // Try a more general-purpose function.
261  BIO *bio_token = BIO_new_mem_buf(token->data, token->size);
262  assert(bio_token != NULL);
263  EVP_PKEY *old_pkey = PEM_read_bio_PrivateKey(bio_token, NULL, NULL, NULL);
264  BIO_free(bio_token);
265  if (old_pkey) {
266  parm->pkey = old_pkey;
267  } else {
268  sk_X509_free(certstack);
270  "credential did not contain a decrypted private key.");
271  return false;
272  }
273  }
274 
275  if (!sk_X509_num(certstack)) {
276  EVP_PKEY_free(parm->pkey);
277  sk_X509_free(certstack);
279  "Credential file did not contain any actual credentials.");
280  return false;
281  } else {
282  LogCvmfs(kLogAuthz, kLogDebug, "Certificate stack contains %d entries.",
283  sk_X509_num(certstack));
284  }
285 
286  AuthzToken* to_return = new AuthzToken();
287  to_return->type = kTokenX509;
288  to_return->data = static_cast<void*>(parm.Release());
289  curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_DATA,
290  static_cast<sslctx_info*>(to_return->data));
291  *info_data = to_return;
292  return true;
293 }
294 
295 
296 void AuthzAttachment::LogOpenSSLErrors(const char *top_message) {
298  char error_buf[1024];
299  LogCvmfs(kLogAuthz, kLogSyslogWarn, "%s", top_message);
300  unsigned long next_err; // NOLINT; this is the type expected by OpenSSL
301  while ((next_err = ERR_get_error())) {
302  ERR_error_string_n(next_err, error_buf, 1024);
303  LogCvmfs(kLogAuthz, kLogSyslogErr, "%s", error_buf);
304  }
305 }
306 
307 
308 void AuthzAttachment::ReleaseCurlHandle(CURL *curl_handle, void *info_data) {
309  assert(info_data);
310 
311  AuthzToken* token = static_cast<AuthzToken*>(info_data);
312  if (token->type == kTokenBearer) {
313  // Compiler complains if we delete a void*
314  bearer_info* bearer = static_cast<bearer_info*>(token->data);
315  delete static_cast<char*>(bearer->token);
316  curl_slist_free_all(bearer->list);
317  delete static_cast<bearer_info*>(token->data);
318  token->data = NULL;
319  delete token;
320 
321  } else if (token->type == kTokenX509) {
322  sslctx_info *p = static_cast<sslctx_info *>(token->data);
323  STACK_OF(X509) *chain = p->chain;
324  EVP_PKEY *pkey = p->pkey;
325  p->chain = NULL;
326  p->pkey = NULL;
327  delete p;
328 
329  // Calls X509_free on each element, then frees the stack itself
330  sk_X509_pop_free(chain, X509_free);
331  EVP_PKEY_free(pkey);
332 
333  // Make sure that if CVMFS reuses this curl handle, curl doesn't try
334  // to reuse cert chain we just freed.
335  curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_DATA, 0);
336  }
337 }
struct cvmcache_context * ctx
std::string membership_
Definition: authz_curl.h:44
virtual bool ConfigureCurlHandle(CURL *curl_handle, pid_t pid, void **info_data)
Definition: authz_curl.cc:154
void * data
Definition: authz.h:33
static CURLcode CallbackSslCtx(CURL *curl, void *sslctx, void *parm)
Definition: authz_curl.cc:59
assert((mem||(size==0))&&"Out Of Memory")
AuthzToken * GetTokenCopy(const pid_t pid, const std::string &membership)
static bool ssl_strings_loaded_
Definition: authz_curl.h:34
bool ConfigureSciTokenCurl(CURL *curl_handle, const AuthzToken &token, void **info_data)
Definition: authz_curl.cc:113
AuthzAttachment(AuthzSessionManager *sm)
Definition: authz_curl.cc:50
AuthzSessionManager * authz_session_manager_
Definition: authz_curl.h:39
static void LogOpenSSLErrors(const char *top_message)
Definition: authz_curl.cc:296
AuthzTokenType type
Definition: authz.h:32
unsigned size
Definition: authz.h:34
virtual void ReleaseCurlHandle(CURL *curl_handle, void *info_data)
Definition: authz_curl.cc:308
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:528