XRootD
XrdSecProtect.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d S e c P r o t e c t . c c */
4 /* */
5 /* (c) 2016 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* Produced by Andrew Hanushevsky for Stanford University under contract */
7 /* DE-AC02-76-SFO0515 with the Department of Energy */
8 /* */
9 /* This file is part of the XRootD software suite. */
10 /* */
11 /* XRootD is free software: you can redistribute it and/or modify it under */
12 /* the terms of the GNU Lesser General Public License as published by the */
13 /* Free Software Foundation, either version 3 of the License, or (at your */
14 /* option) any later version. */
15 /* */
16 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
17 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
18 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
19 /* License for more details. */
20 /* */
21 /* You should have received a copy of the GNU Lesser General Public License */
22 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
23 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
24 /* */
25 /* The copyright holder's institutional names and contributor's names may not */
26 /* be used to endorse or promote products derived from this software without */
27 /* specific prior written permission of the institution or contributor. */
28 /******************************************************************************/
29 
30 #include <cinttypes>
31 #include <netinet/in.h>
32 #include <cstdarg>
33 #include <cstring>
34 #include <sys/types.h>
35 #include <sys/uio.h>
36 
37 #ifdef __APPLE__
38 #define COMMON_DIGEST_FOR_OPENSSL
39 #include "CommonCrypto/CommonDigest.h"
40 #else
41 #include <openssl/sha.h>
42 #endif
43 
44 #include <openssl/evp.h>
45 
46 #include "XrdVersion.hh"
47 
48 #include "XProtocol/XProtocol.hh"
50 #include "XrdSec/XrdSecProtect.hh"
52 #include "XrdSys/XrdSysAtomics.hh"
53 #include "XrdSys/XrdSysE2T.hh"
54 #include "XrdSys/XrdSysPlatform.hh"
55 #include "XrdSys/XrdSysPthread.hh"
56 
57 #if OPENSSL_VERSION_NUMBER < 0x10100000L
58 static EVP_MD_CTX* EVP_MD_CTX_new() {
59  EVP_MD_CTX *ctx = (EVP_MD_CTX *)OPENSSL_malloc(sizeof(EVP_MD_CTX));
60  if (ctx) EVP_MD_CTX_init(ctx);
61  return ctx;
62 }
63 
64 static void EVP_MD_CTX_free(EVP_MD_CTX *ctx) {
65  if (ctx) {
66  EVP_MD_CTX_cleanup(ctx);
67  OPENSSL_free(ctx);
68  }
69 }
70 #endif
71 
72 /******************************************************************************/
73 /* S t r u c t X r d S e c R e q */
74 /******************************************************************************/
75 
76 namespace XrdSecProtection
77 {
78 struct XrdSecReq
79 {
81  unsigned char secSig; // The encrypted hash follows starting here
82 };
83 }
84 
85 using namespace XrdSecProtection; // Fix warnings from slc5 compiler!
86 
87 /******************************************************************************/
88 /* C l a s s X r d S e c V e c */
89 /******************************************************************************/
90 
91 namespace
92 {
93 class XrdSecVec
94 {
95 public:
96 
98 
99  XrdSecVec(int arg, ...)
100  {va_list ap;
101  int reqCode, sVal;
102  memset(Vec, 0, sizeof(Vec));
103  va_start(ap, arg);
104  reqCode = va_arg(ap, int);
105  while(reqCode >= kXR_auth && reqCode < kXR_REQFENCE)
106  {for (int i=0; i < (int)XrdSecProtectParms::secFence-1; i++)
107  {sVal = va_arg(ap, int);
108  Vec[i][reqCode-kXR_auth] = static_cast<char>(sVal);
109  }
110  reqCode = va_arg(ap, int);
111  }
112  }
113  ~XrdSecVec() {}
114 };
115 }
116 
117 /******************************************************************************/
118 /* S e c u r i t y T a b l e */
119 /******************************************************************************/
120 
121 namespace
122 {
123 
124 XrdSecVec secTable(0,
125 // Compatible Standard Intense Pedantic
157 0);
158 }
159 
160 /******************************************************************************/
161 /* Private: G e t S H A 2 */
162 /******************************************************************************/
163 
164 bool XrdSecProtect::GetSHA2(unsigned char *hBuff, struct iovec *iovP, int iovN)
165 {
166  bool ret = false;
167  EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
168  const EVP_MD *md = EVP_get_digestbyname("sha256");
169 
170 // Initialize the hash calculattion
171 //
172  if (1 != EVP_DigestInit_ex(mdctx, md, 0)) goto err;
173 
174 // Go through the iovec updating the hash
175 //
176  for (int i = 0; i < iovN; i++)
177  {
178  if (1 != EVP_DigestUpdate(mdctx, iovP[i].iov_base, iovP[i].iov_len))
179  goto err;
180  }
181 
182 // Compute final hash and return result
183 //
184  if (1 != EVP_DigestFinal_ex(mdctx, hBuff, 0)) goto err;
185 
186  ret = true;
187  err:
188  EVP_MD_CTX_free (mdctx);
189  return ret;
190 }
191 
192 /******************************************************************************/
193 /* Private: S c r e e n */
194 /******************************************************************************/
195 
196 bool XrdSecProtect::Screen(ClientRequest &thereq)
197 {
198  static const int rwOpen = kXR_delete|kXR_new|kXR_open_apnd|kXR_open_updt;
199 
200  kXR_unt16 reqCode = ntohs(thereq.header.requestid);
201  char theLvl;
202 
203 // Validate the request code. Invalid codes are never secured
204 //
205  if (reqCode < kXR_auth || reqCode >= kXR_REQFENCE || !secVec) return false;
206 
207 // Get the security level
208 //
209  theLvl = secVec[reqCode-kXR_auth];
210 
211 // If we need not secure this or we definitely do then return result
212 //
213  if (theLvl == kXR_signIgnore) return false;
214  if (theLvl != kXR_signLikely) return true;
215 
216 // Security is conditional based on open() trying to modify something.
217 //
218  if (reqCode == kXR_open)
219  {kXR_int16 opts = ntohs(thereq.open.options);
220  return (opts & rwOpen) != 0;
221  }
222 
223 // Security is conditional based on query() trying to modify something.
224 //
225  if (reqCode == kXR_query)
226  {short qopt = (short)ntohs(thereq.query.infotype);
227  switch(qopt)
228  {case kXR_QStats: return false;
229  case kXR_Qcksum: return false;
230  case kXR_Qckscan: return false;
231  case kXR_Qconfig: return false;
232  case kXR_Qspace: return false;
233  case kXR_Qxattr: return false;
234  case kXR_Qopaque:
235  case kXR_Qopaquf: return true;
236  case kXR_Qopaqug: return true;
237  default: return false;
238  }
239  }
240 
241 // Security is conditional based on set() trying to modify something.
242 //
243  if (reqCode == kXR_set) return thereq.set.modifier != 0;
244 
245 // At this point we force security as we don't understand this code
246 //
247  return true;
248 }
249 
250 /******************************************************************************/
251 /* S e c u r e */
252 /******************************************************************************/
253 
255  ClientRequest &thereq,
256  const char *thedata)
257 {
258  static const ClientSigverRequest initSigVer = {{0,0}, htons(kXR_sigver),
259  0, kXR_secver_0, 0, 0,
260  kXR_SHA256, {0, 0, 0}, 0
261  };
262  struct buffHold {XrdSecReq *P;
263  XrdSecBuffer *bP;
264  buffHold() : P(0), bP(0) {}
265  ~buffHold() {if (P) free(P); if (bP) delete bP;}
266  };
267  static const int iovNum = 3;
268  struct iovec iov[iovNum];
269  buffHold myReq;
270  kXR_unt64 mySeq;
271  const char *sigBuff, *payload = thedata;
272  unsigned char secHash[SHA256_DIGEST_LENGTH];
273  int sigSize, n, newSize, rc, paysize = 0;
274  bool nodata = false;
275 
276 // Generate a new sequence number
277 //
278  mySeq = nextSeqno++;
279  mySeq = htonll(mySeq);
280 
281 // Determine if we are going to sign the payload and its location
282 //
283  if (thereq.header.dlen)
284  {kXR_unt16 reqid = htons(thereq.header.requestid);
285  paysize = ntohl(thereq.header.dlen);
286  if (!payload) payload = ((char *)&thereq) + sizeof(ClientRequest);
287  if (reqid == kXR_write || reqid == kXR_pgwrite) n = (secVerData ? 3 : 2);
288  else n = 3;
289  } else n = 2;
290 
291 // Fill out the iovec
292 //
293  iov[0].iov_base = (char *)&mySeq;
294  iov[0].iov_len = sizeof(mySeq);
295  iov[1].iov_base = (char *)&thereq;
296  iov[1].iov_len = sizeof(ClientRequest);
297  if (n < 3) nodata = true;
298  else {iov[2].iov_base = (char *)payload;
299  iov[2].iov_len = paysize;
300  }
301 
302 // Compute the hash
303 //
304  if (!GetSHA2(secHash, iov, n)) return -EDOM;
305 
306 // Now encrypt the hash
307 //
308  if (edOK)
309  {rc = authProt->Encrypt((const char *)secHash,sizeof(secHash),&myReq.bP);
310  if (rc < 0) return rc;
311  sigSize = myReq.bP->size;
312  sigBuff = myReq.bP->buffer;
313  } else {
314  sigSize = sizeof(secHash);
315  sigBuff = (char *)secHash;
316  }
317 
318 // Allocate a new request object
319 //
320  newSize = sizeof(SecurityRequest) + sigSize;
321  myReq.P = (XrdSecReq *)malloc(newSize);
322  if (!myReq.P) return -ENOMEM;
323 
324 // Setup the security request (we only support signing)
325 //
326  memcpy(&(myReq.P->secReq), &initSigVer, sizeof(ClientSigverRequest));
327  memcpy(&(myReq.P->secReq.header.streamid ), thereq.header.streamid,
328  sizeof(myReq.P->secReq.header.streamid));
329  memcpy(&(myReq.P->secReq.sigver.expectrid),&thereq.header.requestid,
330  sizeof(myReq.P->secReq.sigver.expectrid));
331  myReq.P->secReq.sigver.seqno = mySeq;
332  if (nodata) myReq.P->secReq.sigver.flags |= kXR_nodata;
333  myReq.P->secReq.sigver.dlen = htonl(sigSize);
334 
335 // Append the signature to the request
336 //
337  memcpy(&(myReq.P->secSig), sigBuff, sigSize);
338 
339 // Return pointer to he security request and its size
340 //
341  newreq = &(myReq.P->secReq); myReq.P = 0;
342  return newSize;
343 }
344 
345 /******************************************************************************/
346 /* Private: S e t P r o t e c t i o n */
347 /******************************************************************************/
348 
350 {
351  unsigned int lvl, vsz;
352 
353 // Check for no security, the simlplest case
354 //
355  if (inReqs.secvsz == 0 && inReqs.seclvl == 0)
356  {memset(&myReqs, 0, sizeof(myReqs));
357  secVec = 0;
358  secVerData = false;
359  return;
360  }
361 
362 // Precheck the security level
363 //
364  lvl = inReqs.seclvl;
365  if (lvl > kXR_secPedantic) lvl = kXR_secPedantic;
366 
367 // Perform the default setup (the usual case)
368 //
369  secVec = secTable.Vec[lvl-1];
370  myReqs.seclvl = lvl;
371  myReqs.secvsz = 0;
372  myReqs.secver = kXR_secver_0;
373  myReqs.secopt = inReqs.secopt;
374 
375 // Set options
376 //
377  secVerData = (inReqs.secopt & kXR_secOData) != 0;
378 
379 // Create a modified vectr if there are overrides
380 //
381  if (inReqs.secvsz != 0)
382  {const ServerResponseSVec_Protocol *urVec = &inReqs.secvec;
383  memcpy(myVec, secVec, maxRIX);
384  vsz = inReqs.secvsz;
385  for (unsigned int i = 0; i < vsz; i++, urVec++)
386  {if (urVec->reqindx < maxRIX)
387  {if (urVec->reqsreq > kXR_signNeeded)
388  myVec[urVec->reqindx] = kXR_signNeeded;
389  else myVec[urVec->reqindx] = urVec->reqsreq;
390  }
391  }
392  secVec = myVec;
393  }
394 }
395 
396 /******************************************************************************/
397 /* V e r i f y */
398 /******************************************************************************/
399 
401  ClientRequest &thereq,
402  const char *thedata
403  )
404 {
405  struct buffHold {XrdSecBuffer *bP;
406  buffHold() : bP(0) {}
407  ~buffHold() {if (bP) delete bP;}
408  };
409  static const int iovNum = 3;
410  struct iovec iov[iovNum];
411  buffHold myReq;
412  unsigned char *inHash, secHash[SHA256_DIGEST_LENGTH];
413  int dlen, n, rc;
414 
415 // First check for replay attacks. The incoming sequence number must be greater
416 // the previous one we have seen. Since it is in network byte order we can use
417 // a simple byte for byte compare (no need for byte swapping).
418 //
419  if (memcmp(&lastSeqno, &secreq.sigver.seqno, sizeof(lastSeqno)) >= 0)
420  return "Incorrect signature sequence";
421 
422 // Do basic verification for this request
423 //
424  if (memcmp(secreq.header.streamid, thereq.header.streamid,
425  sizeof(secreq.header.streamid)))
426  return "Signature streamid mismatch";
427  if (secreq.sigver.expectrid != thereq.header.requestid)
428  return "Signature requestid mismatch";
429  if (secreq.sigver.version != kXR_secver_0)
430  return "Unsupported signature version";
431  if ((secreq.sigver.crypto & kXR_HashMask) != kXR_SHA256)
432  return "Unsupported signature hash";
433  if (secreq.sigver.crypto & kXR_rsaKey)
434  return "Unsupported signature key";
435 
436 // Now get the hash information
437 //
438  dlen = ntohl(secreq.header.dlen);
439  inHash = ((unsigned char *)&secreq)+sizeof(SecurityRequest);
440 
441 // Now decrypt the hash
442 //
443  if (edOK)
444  {rc = authProt->Decrypt((const char *)inHash, dlen, &myReq.bP);
445  if (rc < 0) return XrdSysE2T(-rc);
446  if (myReq.bP->size != (int)sizeof(secHash))
447  return "Invalid signature hash length";
448  inHash = (unsigned char *)myReq.bP->buffer;
449  } else {
450  if (dlen != (int)sizeof(secHash))
451  return "Invalid signature hash length";
452  }
453 
454 // Fill out the iovec to recompute the hash
455 //
456  iov[0].iov_base = (char *)&secreq.sigver.seqno;
457  iov[0].iov_len = sizeof(secreq.sigver.seqno);
458  iov[1].iov_base = (char *)&thereq;
459  iov[1].iov_len = sizeof(ClientRequest);
460  if (thereq.header.dlen == 0 || secreq.sigver.flags & kXR_nodata) n = 2;
461  else {iov[2].iov_base = (char *)thedata;
462  iov[2].iov_len = ntohl(thereq.header.dlen);
463  n = 3;
464  }
465 
466 // Compute the hash
467 //
468  if (!GetSHA2(secHash, iov, n))
469  return "Signature hash computation failed";
470 
471 // Compare this hash with the hash we were given
472 //
473  if (memcmp(secHash, inHash, sizeof(secHash)))
474  return "Signature hash mismatch";
475 
476 // This request has been verified (update the seqno)
477 //
478  lastSeqno = secreq.sigver.seqno;
479  return 0;
480 }
#define kXR_signLikely
Definition: XProtocol.hh:1140
struct ClientRequestHdr header
Definition: XProtocol.hh:881
struct ClientSetRequest set
Definition: XProtocol.hh:871
kXR_char streamid[2]
Definition: XProtocol.hh:156
kXR_unt16 options
Definition: XProtocol.hh:481
@ kXR_delete
Definition: XProtocol.hh:453
@ kXR_open_updt
Definition: XProtocol.hh:457
@ kXR_new
Definition: XProtocol.hh:455
@ kXR_open_apnd
Definition: XProtocol.hh:462
struct ClientOpenRequest open
Definition: XProtocol.hh:860
struct ClientRequestHdr header
Definition: XProtocol.hh:846
kXR_unt16 infotype
Definition: XProtocol.hh:631
ServerResponseSVec_Protocol secvec
Definition: XProtocol.hh:1114
kXR_unt16 requestid
Definition: XProtocol.hh:157
@ kXR_read
Definition: XProtocol.hh:125
@ kXR_open
Definition: XProtocol.hh:122
@ kXR_readv
Definition: XProtocol.hh:137
@ kXR_mkdir
Definition: XProtocol.hh:120
@ kXR_sync
Definition: XProtocol.hh:128
@ kXR_REQFENCE
Definition: XProtocol.hh:144
@ kXR_chmod
Definition: XProtocol.hh:114
@ kXR_bind
Definition: XProtocol.hh:136
@ kXR_dirlist
Definition: XProtocol.hh:116
@ kXR_sigver
Definition: XProtocol.hh:141
@ kXR_fattr
Definition: XProtocol.hh:132
@ kXR_rm
Definition: XProtocol.hh:126
@ kXR_query
Definition: XProtocol.hh:113
@ kXR_write
Definition: XProtocol.hh:131
@ kXR_gpfile
Definition: XProtocol.hh:117
@ kXR_login
Definition: XProtocol.hh:119
@ kXR_auth
Definition: XProtocol.hh:112
@ kXR_endsess
Definition: XProtocol.hh:135
@ kXR_set
Definition: XProtocol.hh:130
@ kXR_rmdir
Definition: XProtocol.hh:127
@ kXR_statx
Definition: XProtocol.hh:134
@ kXR_truncate
Definition: XProtocol.hh:140
@ kXR_protocol
Definition: XProtocol.hh:118
@ kXR_mv
Definition: XProtocol.hh:121
@ kXR_ping
Definition: XProtocol.hh:123
@ kXR_stat
Definition: XProtocol.hh:129
@ kXR_pgread
Definition: XProtocol.hh:142
@ kXR_chkpoint
Definition: XProtocol.hh:124
@ kXR_locate
Definition: XProtocol.hh:139
@ kXR_close
Definition: XProtocol.hh:115
@ kXR_pgwrite
Definition: XProtocol.hh:138
@ kXR_prepare
Definition: XProtocol.hh:133
struct ClientSigverRequest sigver
Definition: XProtocol.hh:882
struct ClientQueryRequest query
Definition: XProtocol.hh:866
#define kXR_secOData
Definition: XProtocol.hh:1126
#define kXR_signIgnore
Definition: XProtocol.hh:1139
#define kXR_secPedantic
Definition: XProtocol.hh:1135
@ kXR_nodata
Definition: XProtocol.hh:738
kXR_unt16 expectrid
Definition: XProtocol.hh:749
#define kXR_secver_0
Definition: XProtocol.hh:1146
kXR_char modifier
Definition: XProtocol.hh:721
@ kXR_Qopaqug
Definition: XProtocol.hh:625
@ kXR_Qconfig
Definition: XProtocol.hh:621
@ kXR_Qopaquf
Definition: XProtocol.hh:624
@ kXR_Qckscan
Definition: XProtocol.hh:620
@ kXR_Qxattr
Definition: XProtocol.hh:618
@ kXR_Qspace
Definition: XProtocol.hh:619
@ kXR_QStats
Definition: XProtocol.hh:615
@ kXR_Qcksum
Definition: XProtocol.hh:617
@ kXR_Qopaque
Definition: XProtocol.hh:623
#define kXR_signNeeded
Definition: XProtocol.hh:1141
kXR_int32 dlen
Definition: XProtocol.hh:159
@ kXR_SHA256
Definition: XProtocol.hh:731
@ kXR_HashMask
Definition: XProtocol.hh:732
@ kXR_rsaKey
Definition: XProtocol.hh:733
unsigned long long kXR_unt64
Definition: XPtypes.hh:99
short kXR_int16
Definition: XPtypes.hh:66
unsigned short kXR_unt16
Definition: XPtypes.hh:67
static EVP_MD_CTX * EVP_MD_CTX_new()
static void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
struct myOpts opts
const char * XrdSysE2T(int errcode)
Definition: XrdSysE2T.cc:104
virtual const char * Verify(SecurityRequest &secreq, ClientRequest &thereq, const char *thedata)
void SetProtection(const ServerResponseReqs_Protocol &inReqs)
virtual int Secure(SecurityRequest *&newreq, ClientRequest &thereq, const char *thedata)
Generic structure to pass security information back and forth.