XRootD
Loading...
Searching...
No Matches
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"
23#include "XrdCl/XrdClLog.hh"
25
26#include "XrdTls/XrdTls.hh"
28#include "XrdOuc/XrdOucUtils.hh"
29
30#include <mutex>
31#include <string>
32#include <stdexcept>
33
34static std::unique_ptr<XrdTlsContext> tlsContext = nullptr;
35
36namespace
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
94namespace 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(
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.
@ ReadyToRead
New data has arrived.
A network socket.
XRootDStatus Uncork()
int GetFD()
Get the file descriptor.
bool IsCorked() const
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.
@ TLS_HS_NOBLK
Do not block during handshake.
@ TLS_RNB_WNB
Non-blocking read non-blocking write.
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
const uint16_t errTlsError
const uint16_t stFatal
Fatal error, it's still an error.
const uint16_t stError
An error occurred that could potentially be retried.
const uint16_t stOK
Everything went OK.
const uint64_t TlsMsg
const uint16_t errSocketError
bool InitTLS()
Definition XrdClTls.cc:96
none object for initializing empty Optional
bool IsOK() const
We're fine.