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 /******************************************************************************/
58 /* S t r u c t X r d S e c R e q */
59 /******************************************************************************/
60 
61 namespace XrdSecProtection
62 {
63 struct XrdSecReq
64 {
66  unsigned char secSig; // The encrypted hash follows starting here
67 };
68 }
69 
70 using namespace XrdSecProtection; // Fix warnings from slc5 compiler!
71 
72 /******************************************************************************/
73 /* C l a s s X r d S e c V e c */
74 /******************************************************************************/
75 
76 namespace
77 {
78 class XrdSecVec
79 {
80 public:
81 
83 
84  XrdSecVec(int arg, ...)
85  {va_list ap;
86  int reqCode, sVal;
87  memset(Vec, 0, sizeof(Vec));
88  va_start(ap, arg);
89  reqCode = va_arg(ap, int);
90  while(reqCode >= kXR_auth && reqCode < kXR_REQFENCE)
91  {for (int i=0; i < (int)XrdSecProtectParms::secFence-1; i++)
92  {sVal = va_arg(ap, int);
93  Vec[i][reqCode-kXR_auth] = static_cast<char>(sVal);
94  }
95  reqCode = va_arg(ap, int);
96  }
97  }
98  ~XrdSecVec() {}
99 };
100 }
101 
102 /******************************************************************************/
103 /* S e c u r i t y T a b l e */
104 /******************************************************************************/
105 
106 namespace
107 {
108 
109 XrdSecVec secTable(0,
110 // Compatible Standard Intense Pedantic
142 0);
143 }
144 
145 /******************************************************************************/
146 /* Private: G e t S H A 2 */
147 /******************************************************************************/
148 
149 bool XrdSecProtect::GetSHA2(unsigned char *hBuff, struct iovec *iovP, int iovN)
150 {
151  bool ret = false;
152  EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
153  const EVP_MD *md = EVP_get_digestbyname("sha256");
154 
155 // Initialize the hash calculattion
156 //
157  if (1 != EVP_DigestInit_ex(mdctx, md, 0)) goto err;
158 
159 // Go through the iovec updating the hash
160 //
161  for (int i = 0; i < iovN; i++)
162  {
163  if (1 != EVP_DigestUpdate(mdctx, iovP[i].iov_base, iovP[i].iov_len))
164  goto err;
165  }
166 
167 // Compute final hash and return result
168 //
169  if (1 != EVP_DigestFinal_ex(mdctx, hBuff, 0)) goto err;
170 
171  ret = true;
172  err:
173  EVP_MD_CTX_free (mdctx);
174  return ret;
175 }
176 
177 /******************************************************************************/
178 /* Private: S c r e e n */
179 /******************************************************************************/
180 
181 bool XrdSecProtect::Screen(ClientRequest &thereq)
182 {
183  static const int rwOpen = kXR_delete|kXR_new|kXR_open_apnd|kXR_open_updt;
184 
185  kXR_unt16 reqCode = ntohs(thereq.header.requestid);
186  char theLvl;
187 
188 // Validate the request code. Invalid codes are never secured
189 //
190  if (reqCode < kXR_auth || reqCode >= kXR_REQFENCE || !secVec) return false;
191 
192 // Get the security level
193 //
194  theLvl = secVec[reqCode-kXR_auth];
195 
196 // If we need not secure this or we definitely do then return result
197 //
198  if (theLvl == kXR_signIgnore) return false;
199  if (theLvl != kXR_signLikely) return true;
200 
201 // Security is conditional based on open() trying to modify something.
202 //
203  if (reqCode == kXR_open)
204  {kXR_int16 opts = ntohs(thereq.open.options);
205  return (opts & rwOpen) != 0;
206  }
207 
208 // Security is conditional based on query() trying to modify something.
209 //
210  if (reqCode == kXR_query)
211  {short qopt = (short)ntohs(thereq.query.infotype);
212  switch(qopt)
213  {case kXR_QStats: return false;
214  case kXR_Qcksum: return false;
215  case kXR_Qckscan: return false;
216  case kXR_Qconfig: return false;
217  case kXR_Qspace: return false;
218  case kXR_Qxattr: return false;
219  case kXR_Qopaque:
220  case kXR_Qopaquf: return true;
221  case kXR_Qopaqug: return true;
222  default: return false;
223  }
224  }
225 
226 // Security is conditional based on set() trying to modify something.
227 //
228  if (reqCode == kXR_set) return thereq.set.modifier != 0;
229 
230 // At this point we force security as we don't understand this code
231 //
232  return true;
233 }
234 
235 /******************************************************************************/
236 /* S e c u r e */
237 /******************************************************************************/
238 
240  ClientRequest &thereq,
241  const char *thedata)
242 {
243  static const ClientSigverRequest initSigVer = {{0,0}, htons(kXR_sigver),
244  0, kXR_secver_0, 0, 0,
245  kXR_SHA256, {0, 0, 0}, 0
246  };
247  struct buffHold {XrdSecReq *P;
248  XrdSecBuffer *bP;
249  buffHold() : P(0), bP(0) {}
250  ~buffHold() {if (P) free(P); if (bP) delete bP;}
251  };
252  static const int iovNum = 3;
253  struct iovec iov[iovNum];
254  buffHold myReq;
255  kXR_unt64 mySeq;
256  const char *sigBuff, *payload = thedata;
257  unsigned char secHash[SHA256_DIGEST_LENGTH];
258  int sigSize, n, newSize, rc, paysize = 0;
259  bool nodata = false;
260 
261 // Generate a new sequence number
262 //
263  mySeq = nextSeqno++;
264  mySeq = htonll(mySeq);
265 
266 // Determine if we are going to sign the payload and its location
267 //
268  if (thereq.header.dlen)
269  {kXR_unt16 reqid = htons(thereq.header.requestid);
270  paysize = ntohl(thereq.header.dlen);
271  if (!payload) payload = ((char *)&thereq) + sizeof(ClientRequest);
272  if (reqid == kXR_write || reqid == kXR_pgwrite) n = (secVerData ? 3 : 2);
273  else n = 3;
274  } else n = 2;
275 
276 // Fill out the iovec
277 //
278  iov[0].iov_base = (char *)&mySeq;
279  iov[0].iov_len = sizeof(mySeq);
280  iov[1].iov_base = (char *)&thereq;
281  iov[1].iov_len = sizeof(ClientRequest);
282  if (n < 3) nodata = true;
283  else {iov[2].iov_base = (char *)payload;
284  iov[2].iov_len = paysize;
285  }
286 
287 // Compute the hash
288 //
289  if (!GetSHA2(secHash, iov, n)) return -EDOM;
290 
291 // Now encrypt the hash
292 //
293  if (edOK)
294  {rc = authProt->Encrypt((const char *)secHash,sizeof(secHash),&myReq.bP);
295  if (rc < 0) return rc;
296  sigSize = myReq.bP->size;
297  sigBuff = myReq.bP->buffer;
298  } else {
299  sigSize = sizeof(secHash);
300  sigBuff = (char *)secHash;
301  }
302 
303 // Allocate a new request object
304 //
305  newSize = sizeof(SecurityRequest) + sigSize;
306  myReq.P = (XrdSecReq *)malloc(newSize);
307  if (!myReq.P) return -ENOMEM;
308 
309 // Setup the security request (we only support signing)
310 //
311  memcpy(&(myReq.P->secReq), &initSigVer, sizeof(ClientSigverRequest));
312  memcpy(&(myReq.P->secReq.header.streamid ), thereq.header.streamid,
313  sizeof(myReq.P->secReq.header.streamid));
314  memcpy(&(myReq.P->secReq.sigver.expectrid),&thereq.header.requestid,
315  sizeof(myReq.P->secReq.sigver.expectrid));
316  myReq.P->secReq.sigver.seqno = mySeq;
317  if (nodata) myReq.P->secReq.sigver.flags |= kXR_nodata;
318  myReq.P->secReq.sigver.dlen = htonl(sigSize);
319 
320 // Append the signature to the request
321 //
322  memcpy(&(myReq.P->secSig), sigBuff, sigSize);
323 
324 // Return pointer to he security request and its size
325 //
326  newreq = &(myReq.P->secReq); myReq.P = 0;
327  return newSize;
328 }
329 
330 /******************************************************************************/
331 /* Private: S e t P r o t e c t i o n */
332 /******************************************************************************/
333 
335 {
336  unsigned int lvl, vsz;
337 
338 // Check for no security, the simlplest case
339 //
340  if (inReqs.secvsz == 0 && inReqs.seclvl == 0)
341  {memset(&myReqs, 0, sizeof(myReqs));
342  secVec = 0;
343  secVerData = false;
344  return;
345  }
346 
347 // Precheck the security level
348 //
349  lvl = inReqs.seclvl;
350  if (lvl > kXR_secPedantic) lvl = kXR_secPedantic;
351 
352 // Perform the default setup (the usual case)
353 //
354  secVec = secTable.Vec[lvl-1];
355  myReqs.seclvl = lvl;
356  myReqs.secvsz = 0;
357  myReqs.secver = kXR_secver_0;
358  myReqs.secopt = inReqs.secopt;
359 
360 // Set options
361 //
362  secVerData = (inReqs.secopt & kXR_secOData) != 0;
363 
364 // Create a modified vectr if there are overrides
365 //
366  if (inReqs.secvsz != 0)
367  {const ServerResponseSVec_Protocol *urVec = &inReqs.secvec;
368  memcpy(myVec, secVec, maxRIX);
369  vsz = inReqs.secvsz;
370  for (unsigned int i = 0; i < vsz; i++, urVec++)
371  {if (urVec->reqindx < maxRIX)
372  {if (urVec->reqsreq > kXR_signNeeded)
373  myVec[urVec->reqindx] = kXR_signNeeded;
374  else myVec[urVec->reqindx] = urVec->reqsreq;
375  }
376  }
377  secVec = myVec;
378  }
379 }
380 
381 /******************************************************************************/
382 /* V e r i f y */
383 /******************************************************************************/
384 
386  ClientRequest &thereq,
387  const char *thedata
388  )
389 {
390  struct buffHold {XrdSecBuffer *bP;
391  buffHold() : bP(0) {}
392  ~buffHold() {if (bP) delete bP;}
393  };
394  static const int iovNum = 3;
395  struct iovec iov[iovNum];
396  buffHold myReq;
397  unsigned char *inHash, secHash[SHA256_DIGEST_LENGTH];
398  int dlen, n, rc;
399 
400 // First check for replay attacks. The incoming sequence number must be greater
401 // the previous one we have seen. Since it is in network byte order we can use
402 // a simple byte for byte compare (no need for byte swapping).
403 //
404  if (memcmp(&lastSeqno, &secreq.sigver.seqno, sizeof(lastSeqno)) >= 0)
405  return "Incorrect signature sequence";
406 
407 // Do basic verification for this request
408 //
409  if (memcmp(secreq.header.streamid, thereq.header.streamid,
410  sizeof(secreq.header.streamid)))
411  return "Signature streamid mismatch";
412  if (secreq.sigver.expectrid != thereq.header.requestid)
413  return "Signature requestid mismatch";
414  if (secreq.sigver.version != kXR_secver_0)
415  return "Unsupported signature version";
416  if ((secreq.sigver.crypto & kXR_HashMask) != kXR_SHA256)
417  return "Unsupported signature hash";
418  if (secreq.sigver.crypto & kXR_rsaKey)
419  return "Unsupported signature key";
420 
421 // Now get the hash information
422 //
423  dlen = ntohl(secreq.header.dlen);
424  inHash = ((unsigned char *)&secreq)+sizeof(SecurityRequest);
425 
426 // Now decrypt the hash
427 //
428  if (edOK)
429  {rc = authProt->Decrypt((const char *)inHash, dlen, &myReq.bP);
430  if (rc < 0) return XrdSysE2T(-rc);
431  if (myReq.bP->size != (int)sizeof(secHash))
432  return "Invalid signature hash length";
433  inHash = (unsigned char *)myReq.bP->buffer;
434  } else {
435  if (dlen != (int)sizeof(secHash))
436  return "Invalid signature hash length";
437  }
438 
439 // Fill out the iovec to recompute the hash
440 //
441  iov[0].iov_base = (char *)&secreq.sigver.seqno;
442  iov[0].iov_len = sizeof(secreq.sigver.seqno);
443  iov[1].iov_base = (char *)&thereq;
444  iov[1].iov_len = sizeof(ClientRequest);
445  if (thereq.header.dlen == 0 || secreq.sigver.flags & kXR_nodata) n = 2;
446  else {iov[2].iov_base = (char *)thedata;
447  iov[2].iov_len = ntohl(thereq.header.dlen);
448  n = 3;
449  }
450 
451 // Compute the hash
452 //
453  if (!GetSHA2(secHash, iov, n))
454  return "Signature hash computation failed";
455 
456 // Compare this hash with the hash we were given
457 //
458  if (memcmp(secHash, inHash, sizeof(secHash)))
459  return "Signature hash mismatch";
460 
461 // This request has been verified (update the seqno)
462 //
463  lastSeqno = secreq.sigver.seqno;
464  return 0;
465 }
#define kXR_signLikely
Definition: XProtocol.hh:1182
struct ClientRequestHdr header
Definition: XProtocol.hh:923
struct ClientSetRequest set
Definition: XProtocol.hh:913
kXR_char streamid[2]
Definition: XProtocol.hh:158
kXR_unt16 options
Definition: XProtocol.hh:513
@ kXR_delete
Definition: XProtocol.hh:483
@ kXR_open_updt
Definition: XProtocol.hh:487
@ kXR_new
Definition: XProtocol.hh:485
@ kXR_open_apnd
Definition: XProtocol.hh:492
struct ClientOpenRequest open
Definition: XProtocol.hh:902
struct ClientRequestHdr header
Definition: XProtocol.hh:887
kXR_unt16 infotype
Definition: XProtocol.hh:667
ServerResponseSVec_Protocol secvec
Definition: XProtocol.hh:1156
kXR_unt16 requestid
Definition: XProtocol.hh:159
@ kXR_read
Definition: XProtocol.hh:126
@ kXR_open
Definition: XProtocol.hh:123
@ kXR_readv
Definition: XProtocol.hh:138
@ kXR_mkdir
Definition: XProtocol.hh:121
@ kXR_sync
Definition: XProtocol.hh:129
@ kXR_REQFENCE
Definition: XProtocol.hh:146
@ kXR_chmod
Definition: XProtocol.hh:115
@ kXR_bind
Definition: XProtocol.hh:137
@ kXR_dirlist
Definition: XProtocol.hh:117
@ kXR_sigver
Definition: XProtocol.hh:142
@ kXR_fattr
Definition: XProtocol.hh:133
@ kXR_rm
Definition: XProtocol.hh:127
@ kXR_query
Definition: XProtocol.hh:114
@ kXR_write
Definition: XProtocol.hh:132
@ kXR_gpfile
Definition: XProtocol.hh:118
@ kXR_login
Definition: XProtocol.hh:120
@ kXR_auth
Definition: XProtocol.hh:113
@ kXR_endsess
Definition: XProtocol.hh:136
@ kXR_set
Definition: XProtocol.hh:131
@ kXR_rmdir
Definition: XProtocol.hh:128
@ kXR_statx
Definition: XProtocol.hh:135
@ kXR_truncate
Definition: XProtocol.hh:141
@ kXR_protocol
Definition: XProtocol.hh:119
@ kXR_mv
Definition: XProtocol.hh:122
@ kXR_ping
Definition: XProtocol.hh:124
@ kXR_stat
Definition: XProtocol.hh:130
@ kXR_pgread
Definition: XProtocol.hh:143
@ kXR_chkpoint
Definition: XProtocol.hh:125
@ kXR_locate
Definition: XProtocol.hh:140
@ kXR_close
Definition: XProtocol.hh:116
@ kXR_pgwrite
Definition: XProtocol.hh:139
@ kXR_prepare
Definition: XProtocol.hh:134
struct ClientSigverRequest sigver
Definition: XProtocol.hh:924
struct ClientQueryRequest query
Definition: XProtocol.hh:908
#define kXR_secOData
Definition: XProtocol.hh:1168
#define kXR_signIgnore
Definition: XProtocol.hh:1181
#define kXR_secPedantic
Definition: XProtocol.hh:1177
@ kXR_nodata
Definition: XProtocol.hh:774
kXR_unt16 expectrid
Definition: XProtocol.hh:785
#define kXR_secver_0
Definition: XProtocol.hh:1188
kXR_char modifier
Definition: XProtocol.hh:757
@ kXR_Qopaqug
Definition: XProtocol.hh:661
@ kXR_Qconfig
Definition: XProtocol.hh:655
@ kXR_Qopaquf
Definition: XProtocol.hh:660
@ kXR_Qckscan
Definition: XProtocol.hh:654
@ kXR_Qxattr
Definition: XProtocol.hh:652
@ kXR_Qspace
Definition: XProtocol.hh:653
@ kXR_QStats
Definition: XProtocol.hh:649
@ kXR_Qcksum
Definition: XProtocol.hh:651
@ kXR_Qopaque
Definition: XProtocol.hh:659
#define kXR_signNeeded
Definition: XProtocol.hh:1183
kXR_int32 dlen
Definition: XProtocol.hh:161
@ kXR_SHA256
Definition: XProtocol.hh:767
@ kXR_HashMask
Definition: XProtocol.hh:768
@ kXR_rsaKey
Definition: XProtocol.hh:769
unsigned long long kXR_unt64
Definition: XPtypes.hh:99
short kXR_int16
Definition: XPtypes.hh:66
unsigned short kXR_unt16
Definition: XPtypes.hh:67
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.