CernVM-FS  2.10.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 "duplex_curl.h"
15 #include "duplex_ssl.h"
16 #include "logging.h"
17 #include "util/pointer.h"
18 #include "util_concurrency.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  // We cannot rely on libcurl to pipeline (yet), as cvmfs may
162  // bounce between different auth handles.
163  curl_easy_setopt(curl_handle, CURLOPT_FRESH_CONNECT, 1);
164  curl_easy_setopt(curl_handle, CURLOPT_FORBID_REUSE, 1);
165  curl_easy_setopt(curl_handle, CURLOPT_SSL_SESSIONID_CACHE, 0);
166 
167  UniquePtr<AuthzToken> token(
169  if (!token.IsValid()) {
170  LogCvmfs(kLogAuthz, kLogDebug, "failed to get authz token for pid %d", pid);
171  return false;
172  }
173 
174  switch (token->type) {
175  case kTokenBearer:
176  // If it's a scitoken, then just go to the private
177  // ConfigureSciTokenCurl function
178  return ConfigureSciTokenCurl(curl_handle, *token, info_data);
179 
180  case kTokenX509:
181  // The x509 code is below, so just break and go.
182  break;
183 
184  default:
185  // Oh no, don't know the the token type, throw error and return
186  LogCvmfs(kLogAuthz, kLogDebug, "unknown token type: %d", token->type);
187  return false;
188  }
189 
190  curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_DATA, NULL);
191 
192  // The calling layer is reusing data;
193  if (*info_data) {
194  curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_DATA,
195  static_cast<AuthzToken*>(*info_data)->data);
196  return true;
197  }
198 
199 
200  int retval = curl_easy_setopt(curl_handle,
201  CURLOPT_SSL_CTX_FUNCTION,
203  if (retval != CURLE_OK) {
204  LogCvmfs(kLogAuthz, kLogDebug, "cannot configure curl ssl callback");
205  return false;
206  }
207 
208  UniquePtr<sslctx_info> parm(new sslctx_info);
209 
210  STACK_OF(X509_INFO) *sk = NULL;
211  STACK_OF(X509) *certstack = sk_X509_new_null();
212  parm->chain = certstack;
213  if (certstack == NULL) {
214  LogCvmfs(kLogAuthz, kLogSyslogErr, "Failed to allocate new X509 chain.");
215  return false;
216  }
217 
218  BIO *bio_token = BIO_new_mem_buf(token->data, token->size);
219  assert(bio_token != NULL);
220  sk = PEM_X509_INFO_read_bio(bio_token, NULL, NULL, NULL);
221  BIO_free(bio_token);
222  if (!sk) {
223  LogOpenSSLErrors("Failed to load credential file.");
224  sk_X509_INFO_free(sk);
225  sk_X509_free(certstack);
226  return false;
227  }
228 
229  while (sk_X509_INFO_num(sk)) {
230  X509_INFO *xi = sk_X509_INFO_shift(sk);
231  if (xi == NULL) {continue;}
232  if (xi->x509 != NULL) {
233 #ifdef OPENSSL_API_INTERFACE_V11
234  retval = X509_up_ref(xi->x509);
235  assert(retval == 1);
236 #else
237  CRYPTO_add(&xi->x509->references, 1, CRYPTO_LOCK_X509);
238 #endif
239  sk_X509_push(certstack, xi->x509);
240  }
241  if ((xi->x_pkey != NULL) && (xi->x_pkey->dec_pkey != NULL)) {
242  parm->pkey = xi->x_pkey->dec_pkey;
243 #ifdef OPENSSL_API_INTERFACE_V11
244  retval = EVP_PKEY_up_ref(parm->pkey);
245  assert(retval == 1);
246 #else
247  CRYPTO_add(&parm->pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
248 #endif
249  }
250  X509_INFO_free(xi);
251  }
252  sk_X509_INFO_free(sk);
253 
254  if (parm->pkey == NULL) {
255  // Sigh - PEM_X509_INFO_read doesn't understand old key encodings.
256  // Try a more general-purpose funciton.
257  BIO *bio_token = BIO_new_mem_buf(token->data, token->size);
258  assert(bio_token != NULL);
259  EVP_PKEY *old_pkey = PEM_read_bio_PrivateKey(bio_token, NULL, NULL, NULL);
260  BIO_free(bio_token);
261  if (old_pkey) {
262  parm->pkey = old_pkey;
263  } else {
264  sk_X509_free(certstack);
266  "credential did not contain a decrypted private key.");
267  return false;
268  }
269  }
270 
271  if (!sk_X509_num(certstack)) {
272  EVP_PKEY_free(parm->pkey);
273  sk_X509_free(certstack);
275  "Credential file did not contain any actual credentials.");
276  return false;
277  } else {
278  LogCvmfs(kLogAuthz, kLogDebug, "Certificate stack contains %d entries.",
279  sk_X509_num(certstack));
280  }
281 
282  AuthzToken* to_return = new AuthzToken();
283  to_return->type = kTokenX509;
284  to_return->data = static_cast<void*>(parm.Release());
285  curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_DATA,
286  static_cast<sslctx_info*>(to_return->data));
287  *info_data = to_return;
288  return true;
289 }
290 
291 
292 void AuthzAttachment::LogOpenSSLErrors(const char *top_message) {
294  char error_buf[1024];
295  LogCvmfs(kLogAuthz, kLogSyslogWarn, "%s", top_message);
296  unsigned long next_err; // NOLINT; this is the type expected by OpenSSL
297  while ((next_err = ERR_get_error())) {
298  ERR_error_string_n(next_err, error_buf, 1024);
299  LogCvmfs(kLogAuthz, kLogSyslogErr, "%s", error_buf);
300  }
301 }
302 
303 
304 void AuthzAttachment::ReleaseCurlHandle(CURL *curl_handle, void *info_data) {
305  assert(info_data);
306 
307  AuthzToken* token = static_cast<AuthzToken*>(info_data);
308  if (token->type == kTokenBearer) {
309  // Compiler complains if we delete a void*
310  bearer_info* bearer = static_cast<bearer_info*>(token->data);
311  delete static_cast<char*>(bearer->token);
312  curl_slist_free_all(bearer->list);
313  delete static_cast<bearer_info*>(token->data);
314  token->data = NULL;
315  delete token;
316 
317  } else if (token->type == kTokenX509) {
318  sslctx_info *p = static_cast<sslctx_info *>(token->data);
319  STACK_OF(X509) *chain = p->chain;
320  EVP_PKEY *pkey = p->pkey;
321  p->chain = NULL;
322  p->pkey = NULL;
323  delete p;
324 
325  // Calls X509_free on each element, then frees the stack itself
326  sk_X509_pop_free(chain, X509_free);
327  EVP_PKEY_free(pkey);
328 
329  // Make sure that if CVMFS reuses this curl handle, curl doesn't try
330  // to reuse cert chain we just freed.
331  curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_DATA, 0);
332  }
333 }
#define LogCvmfs(source, mask,...)
Definition: logging.h:20
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:292
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:304