XRootD
Loading...
Searching...
No Matches
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
53#include "XrdSys/XrdSysE2T.hh"
56
57/******************************************************************************/
58/* S t r u c t X r d S e c R e q */
59/******************************************************************************/
60
61namespace XrdSecProtection
62{
64{
66 unsigned char secSig; // The encrypted hash follows starting here
67};
68}
69
70using 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
76namespace
77{
78class XrdSecVec
79{
80public:
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
106namespace
107{
108
109XrdSecVec secTable(0,
110// Compatible Standard Intense Pedantic
1420);
143}
144
145/******************************************************************************/
146/* Private: G e t S H A 2 */
147/******************************************************************************/
148
149bool 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
181bool 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
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
ServerResponseSVec_Protocol secvec
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
#define kXR_signIgnore
#define kXR_secPedantic
@ kXR_nodata
Definition XProtocol.hh:774
#define kXR_secver_0
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
@ 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.