XRootD
XrdClTls.cc
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN)
3 // Author: Michal Simon <simonm@cern.ch>
4 //------------------------------------------------------------------------------
5 // XRootD is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // XRootD is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with XRootD. If not, see <http://www.gnu.org/licenses/>.
17 //------------------------------------------------------------------------------
18 
19 #include "XrdCl/XrdClTls.hh"
20 #include "XrdCl/XrdClPoller.hh"
21 #include "XrdCl/XrdClSocket.hh"
22 #include "XrdCl/XrdClDefaultEnv.hh"
23 #include "XrdCl/XrdClLog.hh"
24 #include "XrdCl/XrdClConstants.hh"
25 
26 #include "XrdTls/XrdTls.hh"
27 #include "XrdTls/XrdTlsContext.hh"
28 #include "XrdOuc/XrdOucUtils.hh"
29 
30 #include <mutex>
31 #include <string>
32 #include <stdexcept>
33 
34 static std::unique_ptr<XrdTlsContext> tlsContext = nullptr;
35 
36 namespace
37 {
38  //------------------------------------------------------------------------
39  // Helper class for setting the message callback for the TLS layer for
40  // logging purposes
41  //------------------------------------------------------------------------
42  struct SetTlsMsgCB
43  {
44  //----------------------------------------------------------------------
45  // The message callback
46  //----------------------------------------------------------------------
47  static void MsgCallBack(const char *tid, const char *msg, bool sslmsg)
48  {
50  if( sslmsg )
51  log->Debug( XrdCl::TlsMsg, "[%s] %s", tid, msg );
52  else
53  log->Error( XrdCl::TlsMsg, "[%s] %s", tid, msg );
54  }
55 
56  inline static void Once()
57  {
58  static SetTlsMsgCB instance;
59  }
60 
61  private:
62 
63  //--------------------------------------------------------------------
64  // Constructor. Sets the callback, there should be only one static
65  // instance
66  //--------------------------------------------------------------------
67  inline SetTlsMsgCB()
68  {
69  XrdTls::SetMsgCB( MsgCallBack );
70  XrdTls::SetDebug( TlsDbgLvl(), MsgCallBack );
71  }
72 
73  //--------------------------------------------------------------------
74  // Get TLS debug level
75  //--------------------------------------------------------------------
76  static int TlsDbgLvl()
77  {
79  std::string tlsDbgLvl;
80  env->GetString( "TlsDbgLvl", tlsDbgLvl );
81 
82  if( tlsDbgLvl == "OFF" ) return XrdTls::dbgOFF;
83  if( tlsDbgLvl == "CTX" ) return XrdTls::dbgCTX;
84  if( tlsDbgLvl == "SOK" ) return XrdTls::dbgSOK;
85  if( tlsDbgLvl == "SIO" ) return XrdTls::dbgSIO;
86  if( tlsDbgLvl == "ALL" ) return XrdTls::dbgALL;
87  if( tlsDbgLvl == "OUT" ) return XrdTls::dbgOUT;
88 
89  return XrdTls::dbgOFF;
90  }
91  };
92 }
93 
94 namespace XrdCl
95 {
96  bool InitTLS()
97  {
98  static std::mutex tls_mutex;
99  std::lock_guard<std::mutex> tls_lock(tls_mutex);
100 
101  if (tlsContext)
102  return true;
103 
106 
107  int notls = false;
108  env->GetInt("NoTlsOK", notls);
109 
110  if (notls)
111  return false;
112 
113  const char *cadir = getenv("X509_CERT_DIR");
114  const char *cafile = getenv("X509_CERT_FILE");
115 
116  if (!cadir && !cafile)
117  cadir = "/etc/grid-security/certificates";
118 
119  const char *msg;
120  const mode_t camode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
121 
122  if (cadir && (msg = XrdOucUtils::ValPath(cadir, camode, true))) {
123  log->Error(XrdCl::TlsMsg, "Failed to initialize TLS context: CA directory %s", msg);
124  env->PutInt("NoTlsOK", 1);
125  return false;
126  }
127 
128  std::string emsg = "unknown error";
129  tlsContext = std::make_unique<XrdTlsContext>(nullptr, nullptr, cadir, cafile, 0ul, &emsg);
130 
131  if (!tlsContext || !tlsContext->isOK()) {
132  tlsContext.reset(nullptr);
133  log->Error(XrdCl::TlsMsg, "Failed to initialize TLS context: %s", emsg.c_str());
134  env->PutInt("NoTlsOK", 1);
135  return false;
136  }
137 
138  return true;
139  }
140 
141  //------------------------------------------------------------------------
142  // Constructor
143  //------------------------------------------------------------------------
144  Tls::Tls( Socket *socket, AsyncSocketHandler *socketHandler ) : pSocket( socket ), pTlsHSRevert( None ), pSocketHandler( socketHandler )
145  {
146  //----------------------------------------------------------------------
147  // Set the message callback for TLS layer
148  //----------------------------------------------------------------------
149  SetTlsMsgCB::Once();
150 
151  if( !InitTLS() )
152  throw std::runtime_error( "Failed to initialize TLS" );
153 
154  pTls.reset(
156  XrdTlsSocket::TLS_HS_NOBLK, true ) );
157  }
158 
159  //------------------------------------------------------------------------
160  // Establish a TLS/SSL session and perform host verification.
161  //------------------------------------------------------------------------
162  XRootDStatus Tls::Connect( const std::string &thehost, XrdNetAddrInfo *netInfo )
163  {
164  std::string errmsg;
165  const char *verhost = 0;
166  if( thehost != "localhost" && thehost != "127.0.0.1" && thehost != "[::1]" )
167  verhost = thehost.c_str();
168  XrdTls::RC error = pTls->Connect( verhost, &errmsg );
169  XRootDStatus status = ToStatus( error );
170  if( !status.IsOK() )
171  status.SetErrorMessage( errmsg );
172 
173  //--------------------------------------------------------------------------
174  // There's no follow up if the read simply failed
175  //--------------------------------------------------------------------------
176  if( !status.IsOK() )
177  {
179  log->Error( XrdCl::TlsMsg, "Failed to do TLS connect: %s", errmsg.c_str() );
180  return status;
181  }
182 
183 
184  if( pTls->NeedHandShake() )
185  {
186  //------------------------------------------------------------------------
187  // Make sure the socket is uncorked so the TLS hand-shake can go through
188  //------------------------------------------------------------------------
189  if( pSocket->IsCorked() )
190  {
191  XRootDStatus st = pSocket->Uncork();
192  if( !st.IsOK() ) return st;
193  }
194 
195  //----------------------------------------------------------------------
196  // Check if TLS hand-shake wants to write something
197  //----------------------------------------------------------------------
198  if( error == XrdTls::TLS_WantWrite )
199  {
200  XRootDStatus st = pSocketHandler->EnableUplink();
201  if( !st.IsOK() ) return st;
202  }
203  //----------------------------------------------------------------------
204  // Otherwise disable uplink
205  //----------------------------------------------------------------------
206  else if( error == XrdTls::TLS_WantRead )
207  {
208  XRootDStatus st = pSocketHandler->DisableUplink();
209  if( !st.IsOK() ) return st;
210  }
211  }
212 
213  return status;
214  }
215 
216  XRootDStatus Tls::Read( char *buffer, size_t size, int &bytesRead )
217  {
218  //--------------------------------------------------------------------------
219  // If necessary, TLS_read() will negotiate a TLS/SSL session, so we don't
220  // have to explicitly call connect or do_handshake.
221  //--------------------------------------------------------------------------
222  XrdTls::RC error = pTls->Read( buffer, size, bytesRead );
223  XRootDStatus status = ToStatus( error );
224 
225  //--------------------------------------------------------------------------
226  // There's no follow up if the read simply failed
227  //--------------------------------------------------------------------------
228  if( !status.IsOK() ) return status;
229 
230  if( pTls->NeedHandShake() )
231  {
232  //------------------------------------------------------------------------
233  // Make sure the socket is uncorked so the TLS hand-shake can go through
234  //------------------------------------------------------------------------
235  if( pSocket->IsCorked() )
236  {
237  XRootDStatus st = pSocket->Uncork();
238  if( !st.IsOK() ) return st;
239  }
240 
241  //----------------------------------------------------------------------
242  // Check if we need to switch on a revert state
243  //----------------------------------------------------------------------
244  if( error == XrdTls::TLS_WantWrite )
245  {
246  pTlsHSRevert = ReadOnWrite;
247  XRootDStatus st = pSocketHandler->EnableUplink();
248  if( !st.IsOK() ) status = st;
249  //--------------------------------------------------------------------
250  // Return early so the revert state won't get cleared
251  //--------------------------------------------------------------------
252  return status;
253  }
254  }
255 
256  //------------------------------------------------------------------------
257  // If we got up until here we need to clear the revert state
258  //------------------------------------------------------------------------
259  if( pTlsHSRevert == ReadOnWrite )
260  {
261  XRootDStatus st = pSocketHandler->DisableUplink();
262  if( !st.IsOK() ) status = st;
263  }
264  pTlsHSRevert = None;
265 
266  //------------------------------------------------------------------------
267  // If we didn't manage to read any data wait for another read event
268  //------------------------------------------------------------------------
269  if( bytesRead == 0 )
270  return XRootDStatus( stOK, suRetry );
271 
272  return status;
273  }
274 
275  //------------------------------------------------------------------------
278  //------------------------------------------------------------------------
279  XRootDStatus Tls::ReadV( iovec *iov, int iocnt, int &bytesRead )
280  {
281  bytesRead = 0;
282  for( int i = 0; i < iocnt; ++i )
283  {
284  int btsread = 0;
285  auto st = Read( static_cast<char*>( iov[i].iov_base ),
286  iov[i].iov_len, btsread );
287  if( !st.IsOK() ) return st;
288  bytesRead += btsread;
289  if( st.code == suRetry ) return st;
290  }
291  return XRootDStatus();
292  }
293 
294  XRootDStatus Tls::Send( const char *buffer, size_t size, int &bytesWritten )
295  {
296  //--------------------------------------------------------------------------
297  // If necessary, TLS_write() will negotiate a TLS/SSL session, so we don't
298  // have to explicitly call connect or do_handshake.
299  //--------------------------------------------------------------------------
300  XrdTls::RC error = pTls->Write( buffer, size, bytesWritten );
301  XRootDStatus status = ToStatus( error );
302 
303  //--------------------------------------------------------------------------
304  // There's no follow up if the write simply failed
305  //--------------------------------------------------------------------------
306  if( !status.IsOK() ) return status;
307 
308  //--------------------------------------------------------------------------
309  // We are in the middle of a TLS hand-shake
310  //--------------------------------------------------------------------------
311  if( pTls->NeedHandShake() )
312  {
313  //------------------------------------------------------------------------
314  // Make sure the socket is uncorked so the TLS hand-shake can go through
315  //------------------------------------------------------------------------
316  if( pSocket->IsCorked() )
317  {
318  XRootDStatus st = pSocket->Uncork();
319  if( !st.IsOK() ) return st;
320  }
321 
322  //------------------------------------------------------------------------
323  // Check if we need to switch on a revert state
324  //------------------------------------------------------------------------
325  if( error == XrdTls::TLS_WantRead )
326  {
327  pTlsHSRevert = WriteOnRead;
328  XRootDStatus st = pSocketHandler->DisableUplink();
329  if( !st.IsOK() ) status = st;
330  //----------------------------------------------------------------------
331  // Return early so the revert state won't get cleared
332  //----------------------------------------------------------------------
333  return status;
334  }
335  }
336 
337  //--------------------------------------------------------------------------
338  // If we got up until here we need to clear the revert state
339  //--------------------------------------------------------------------------
340  if( pTlsHSRevert == WriteOnRead )
341  {
342  XRootDStatus st = pSocketHandler->EnableUplink();
343  if( !st.IsOK() ) status = st;
344  }
345  pTlsHSRevert = None;
346 
347  //------------------------------------------------------------------------
348  // If we didn't manage to read any data wait for another write event
349  //
350  // Adding this by symmetry, never actually experienced this (for reads
351  // it has been experienced)
352  //------------------------------------------------------------------------
353  if( bytesWritten == 0 )
354  return XRootDStatus( stOK, suRetry );
355 
356  return status;
357  }
358 
359  //------------------------------------------------------------------------
360  // Shutdown the TLS/SSL connection
361  //------------------------------------------------------------------------
363  {
364  pTls->Shutdown();
365  }
366 
367  XRootDStatus Tls::ToStatus( XrdTls::RC rc )
368  {
369  std::string msg = XrdTls::RC2Text( rc, true );
370 
371  switch( rc )
372  {
373  case XrdTls::TLS_AOK: return XRootDStatus();
374 
377  case XrdTls::TLS_WantRead: return XRootDStatus( stOK, suRetry, 0, msg );
378 
380  case XrdTls::TLS_SYS_Error: return XRootDStatus( stError, errTlsError, 0, msg );
381 
382  case XrdTls::TLS_SSL_Error: return XRootDStatus( stFatal, errTlsError, EAGAIN, msg );
383 
385  case XrdTls::TLS_HNV_Error: return XRootDStatus( stFatal, errTlsError, 0, msg );
386 
387  // the connection was closed by the server, treat this as a socket error
389 
390  default:
391  return XRootDStatus( stError, errTlsError, 0, msg );
392  }
393  }
394 
395  //------------------------------------------------------------------------
396  // Map:
397  // * in case the TLS layer requested reads on writes map
398  // ReadyToWrite to ReadyToRead
399  // * in case the TLS layer requested writes on reads map
400  // ReadyToRead to ReadyToWrite
401  //------------------------------------------------------------------------
402  uint8_t Tls::MapEvent( uint8_t event )
403  {
404  if( pTlsHSRevert == ReadOnWrite )
405  {
406  //------------------------------------------------------------------------
407  // In this case we would like to call the OnRead routine on the Write event
408  //------------------------------------------------------------------------
410  }
411  else if( pTlsHSRevert == WriteOnRead )
412  {
413  //------------------------------------------------------------------------
414  // In this case we would like to call the OnWrite routine on the Read event
415  //------------------------------------------------------------------------
417  }
418 
419  return event;
420  }
421 
423  {
425  }
426 }
static std::unique_ptr< XrdTlsContext > tlsContext
Definition: XrdClTls.cc:34
int emsg(int rc, char *msg)
XRootDStatus EnableUplink()
Enable uplink.
XRootDStatus DisableUplink()
Disable uplink.
static Log * GetLog()
Get default log.
static Env * GetEnv()
Get default client environment.
bool PutInt(const std::string &key, int value)
Definition: XrdClEnv.cc:110
bool GetString(const std::string &key, std::string &value)
Definition: XrdClEnv.cc:31
bool GetInt(const std::string &key, int &value)
Definition: XrdClEnv.cc:89
Handle diagnostics.
Definition: XrdClLog.hh:101
void Error(uint64_t topic, const char *format,...)
Report an error.
Definition: XrdClLog.cc:231
void Debug(uint64_t topic, const char *format,...)
Print a debug message.
Definition: XrdClLog.cc:282
@ ReadyToWrite
Writing won't block.
Definition: XrdClPoller.hh:43
@ ReadyToRead
New data has arrived.
Definition: XrdClPoller.hh:41
A network socket.
Definition: XrdClSocket.hh:43
XRootDStatus Uncork()
Definition: XrdClSocket.cc:800
int GetFD()
Get the file descriptor.
Definition: XrdClSocket.hh:214
bool IsCorked() const
Definition: XrdClSocket.hh:286
Tls(Socket *socket, AsyncSocketHandler *socketHandler)
Constructor - creates async TLS layer for given socker file descriptor.
Definition: XrdClTls.cc:144
XRootDStatus ReadV(iovec *iov, int iocnt, int &bytesRead)
Definition: XrdClTls.cc:279
XRootDStatus Read(char *buffer, size_t size, int &bytesRead)
Definition: XrdClTls.cc:216
uint8_t MapEvent(uint8_t event)
Definition: XrdClTls.cc:402
static void ClearErrorQueue()
Clear the error queue for the calling thread.
Definition: XrdClTls.cc:422
XRootDStatus Send(const char *buffer, size_t size, int &bytesWritten)
Definition: XrdClTls.cc:294
void Shutdown()
Shutdown the TLS/SSL connection.
Definition: XrdClTls.cc:362
XRootDStatus Connect(const std::string &thehost, XrdNetAddrInfo *netInfo)
Establish a TLS/SSL session and perform host verification.
Definition: XrdClTls.cc:162
void SetErrorMessage(const std::string &message)
Set the error message.
static const char * ValPath(const char *path, mode_t allow, bool isdir)
Socket wrapper for TLS I/O.
Definition: XrdTlsSocket.hh:40
@ TLS_HS_NOBLK
Do not block during handshake.
Definition: XrdTlsSocket.hh:54
@ TLS_RNB_WNB
Non-blocking read non-blocking write.
Definition: XrdTlsSocket.hh:45
static void SetMsgCB(msgCB_t cbP)
Definition: XrdTls.cc:196
static std::string RC2Text(XrdTls::RC rc, bool dbg=false)
Definition: XrdTls.cc:127
static const int dbgSIO
Turn debugging in for socket I/O.
Definition: XrdTls.hh:102
static const int dbgSOK
Turn debugging in for socket operations.
Definition: XrdTls.hh:101
static const int dbgOUT
Force msgs to stderr for easier client debug.
Definition: XrdTls.hh:104
static void ClearErrorQueue()
Clear the SSL error queue for the calling thread.
Definition: XrdTls.cc:265
static const int dbgALL
Turn debugging for everything.
Definition: XrdTls.hh:103
static const int dbgOFF
Turn debugging off (initial deault)
Definition: XrdTls.hh:99
@ TLS_AOK
All went well, will always be zero.
Definition: XrdTls.hh:40
@ TLS_WantWrite
Reissue call when writes do not block.
Definition: XrdTls.hh:52
@ TLS_HNV_Error
A hostname validation error occuured.
Definition: XrdTls.hh:44
@ TLS_CON_Closed
TLS connection has been closed.
Definition: XrdTls.hh:41
@ TLS_WantRead
Reissue call when reads do not block.
Definition: XrdTls.hh:51
@ TLS_VER_Error
Certificate verification failed.
Definition: XrdTls.hh:48
@ TLS_UNK_Error
An unknown error occurred.
Definition: XrdTls.hh:47
@ TLS_SYS_Error
A system call error occurred.
Definition: XrdTls.hh:46
@ TLS_WantConnect
Reissue call when Connect() completes.
Definition: XrdTls.hh:50
@ TLS_SSL_Error
An SSL error occurred.
Definition: XrdTls.hh:45
static const int dbgCTX
Turn debugging in for context operations.
Definition: XrdTls.hh:100
static void SetDebug(int opts, XrdSysLogger *logP=0)
Definition: XrdTls.cc:177
const uint16_t suRetry
Definition: XrdClStatus.hh:40
const uint16_t errTlsError
Definition: XrdClStatus.hh:80
const uint16_t stFatal
Fatal error, it's still an error.
Definition: XrdClStatus.hh:33
const uint16_t stError
An error occurred that could potentially be retried.
Definition: XrdClStatus.hh:32
const uint16_t stOK
Everything went OK.
Definition: XrdClStatus.hh:31
const uint64_t TlsMsg
const uint16_t errSocketError
Definition: XrdClStatus.hh:72
bool InitTLS()
Definition: XrdClTls.cc:96
none object for initializing empty Optional
bool IsOK() const
We're fine.
Definition: XrdClStatus.hh:124