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#if OPENSSL_VERSION_NUMBER < 0x10100000L
58static 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
64static 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
76namespace XrdSecProtection
77{
79{
81 unsigned char secSig; // The encrypted hash follows starting here
82};
83}
84
85using 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
91namespace
92{
93class XrdSecVec
94{
95public:
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
121namespace
122{
123
124XrdSecVec secTable(0,
125// Compatible Standard Intense Pedantic
1570);
158}
159
160/******************************************************************************/
161/* Private: G e t S H A 2 */
162/******************************************************************************/
163
164bool 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
196bool 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
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
ServerResponseSVec_Protocol secvec
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
#define kXR_signIgnore
#define kXR_secPedantic
@ kXR_nodata
Definition XProtocol.hh:738
#define kXR_secver_0
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
@ 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:99
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)
virtual int Decrypt(const char *inbuff, int inlen, XrdSecBuffer **outbuff)
virtual int Encrypt(const char *inbuff, int inlen, XrdSecBuffer **outbuff)
Generic structure to pass security information back and forth.