XRootD
Loading...
Searching...
No Matches
XrdDigConfig.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d D i g C o n f i g . c c */
4/* */
5/* (C) 2013 by the Board of Trustees of the Leland Stanford, Jr., University */
6/* All Rights Reserved */
7/* Produced by Andrew Hanushevsky for Stanford University under contract */
8/* DE-AC02-76-SFO0515 with the Deprtment of Energy */
9/* */
10/* This file is part of the XRootD software suite. */
11/* */
12/* XRootD is free software: you can redistribute it and/or modify it under */
13/* the terms of the GNU Lesser General Public License as published by the */
14/* Free Software Foundation, either version 3 of the License, or (at your */
15/* option) any later version. */
16/* */
17/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20/* License for more details. */
21/* */
22/* You should have received a copy of the GNU Lesser General Public License */
23/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25/* */
26/* The copyright holder's institutional names and contributor's names may not */
27/* be used to endorse or promote products derived from this software without */
28/* specific prior written permission of the institution or contributor. */
29/******************************************************************************/
30
31#include <cctype>
32#include <dirent.h>
33#include <fcntl.h>
34#include <cstdlib>
35#include <strings.h>
36#include <cstdio>
37#include <unistd.h>
38#include <sys/param.h>
39#include <sys/stat.h>
40#include <sys/types.h>
41
42#include "XrdOuc/XrdOucEnv.hh"
46#include "XrdOuc/XrdOucUtils.hh"
47#include "XrdNet/XrdNetAddr.hh"
48#include "XrdNet/XrdNetUtils.hh"
49#include "XrdSys/XrdSysE2T.hh"
50#include "XrdSys/XrdSysError.hh"
52
53#include "XrdDig/XrdDigAuth.hh"
55
56/******************************************************************************/
57/* d e f i n e s */
58/******************************************************************************/
59
60#define TS_Xeq(x,m) if (!strcmp(x,var)) return m(cFile);
61
62/******************************************************************************/
63/* S t a t i c G l o b a l O b j e c t s */
64/******************************************************************************/
65
66namespace XrdDig
67{
69
70 extern XrdDigAuth Auth;
71
73};
74
75using namespace XrdDig;
76
77namespace
78{
79 struct pTV {const char *pfx;
81 const char pfxlen;
82 char isOK;
83 } pTab[] = {{"conf", XrdDigAuthEnt::aConf, 4, 0},
84 {"core", XrdDigAuthEnt::aCore, 4, 0},
85 {"logs", XrdDigAuthEnt::aLogs, 4, 0},
86 {"proc", XrdDigAuthEnt::aProc, 4, 0}
87 };
88 static const int pNum = 4;
89
90 struct stat rootStat;
91};
92
93/******************************************************************************/
94/* C o n f i g u r e */
95/******************************************************************************/
96
97bool XrdDigConfig::Configure(const char *cFN, const char *parms)
98{
99/*
100 Function: Establish default values using configuration parameters.
101
102 Input: None.
103
104 Output: true upon success or false otherwise.
105*/
106 char buff[4096], *afile, *var;
107 XrdOucTokenizer cParms(buff);
108 struct stat Stat;
109 int n;
110 bool isOK = true;
111
112// Get the adminpath (this better succeed).
113//
114 if (!(var = getenv("XRDADMINPATH")) || (n = strlen(var)) >= MAXPATHLEN)
115 {eDest->Emsg("Config", "Unable to deterine adminpath!");
116 return false;
117 }
118
119// Create a template for file remapping
120//
121 strcpy(buff, var);
122 if (buff[n-1] != '/') {buff[n] = '/'; n++;}
123 strcpy(buff+n, ".xrd/=/%s");
124 fnTmplt = strdup(buff);
125
126// Make sure that conf/etc no longer exists as a previous start may have
127// exported something that we no longer wish to export.
128//
129 if (snprintf(buff, sizeof(buff), fnTmplt, "conf/etc") < (int)sizeof(buff))
130 Empty(buff);
131
132// Pake sure there are parameters here
133//
134 if(!parms || !*parms)
135 {eDest->Emsg("Config", "DigFS parameters not specified.");
136 return false;
137 }
138
139// Copy the parms as they will be altered and attach it to the tokenizer
140//
141 n = strlen(parms);
142 if (n >= (int)sizeof(buff))
143 {eDest->Emsg("Config", "DigFS parm string is too long.");
144 return false;
145 }
146 strcpy(buff, parms);
147
148// First token is the authfile
149//
150 cParms.GetLine();
151 if (!(afile = cParms.GetToken()) || !afile[0])
152 {eDest->Emsg("Config", "DigFS authfile not specified.");
153 return false;
154 }
155
156// If we have a config file, process it now
157//
158 if (cFN && *cFN) isOK = ConfigProc(cFN);
159
160// Config authorization. The config may have failed but we want to generate
161// all of the rror messages in one go.
162//
163 if (!Auth.Configure(afile)) isOK = false;
164
165// Setup locate response
166//
167 SetLocResp();
168
169// Get a valid stat structure for the root directory
170//
171 stat("/", &rootStat);
172
173// Validate base entries
174//
175 for (n = 0; n < pNum; n++)
176 {sprintf(buff, fnTmplt, pTab[n].pfx);
177 pTab[n].isOK = stat(buff, &Stat) == 0;
178 }
179
180// All done
181//
182 return isOK;
183}
184
185/******************************************************************************/
186/* G e n A c c e s s */
187/******************************************************************************/
188
190 const char *aList[],
191 int aMax
192 )
193{
194 bool aOK[XrdDigAuthEnt::aNum], hasAcc = false;
195 int i, n = 0;
196
197// Validate aMax
198//
199 if (aMax < 1) return -1;
200
201// Get access right for this client
202//
203 Auth.Authorize(client, XrdDigAuthEnt::aNum, aOK);
204
205// Return entries that are allowed
206//
207 for (i = (int)sizeof(aOK)-1; i >= 0 && n < aMax; i--)
208 {hasAcc |= aOK[i];
209 if (aOK[i] && pTab[i].isOK) aList[n++] = pTab[i].pfx;
210 }
211
212// Return permission denied if no access allowed
213//
214 if (!hasAcc) return -1;
215
216// Return something if we had an error setting up as empty dirs cause problems.
217//
218 if (!n) {aList[0] = "."; n = 1;}
219 return n;
220}
221
222/******************************************************************************/
223/* G e n P a t h */
224/******************************************************************************/
225
226char *XrdDigConfig::GenPath(int &rc, const XrdSecEntity *client,
227 const char *opname,
228 const char *fname,
229 XrdDigConfig::pType lfnType)
230
231{
232 char path[2048];
233 int i, n;
234
235// First we better have a client object
236//
237 if (!client) {rc = EPERM; return 0;}
238
239// Translate the fname to the right file type
240//
241 for (i = 0; i < pNum; i++)
242 {if (!strncmp(pTab[i].pfx, fname, pTab[i].pfxlen)
243 && (*(fname+pTab[i].pfxlen) == '/' || !*(fname+pTab[i].pfxlen))) break;
244 }
245
246// Make sure we found a valid entry
247//
248 if (i >= pNum || !pTab[i].isOK) {rc = ENOENT; return 0;}
249
250// Authorize this access
251//
252 if (!Auth.Authorize(client, pTab[i].aType))
253 {if (lfnType == isFile && logRej) Audit(client, "denied", opname, fname);
254 rc = EACCES;
255 return 0;
256 }
257
258// If the entry is being suffixed and it's proc, make sure we are not trying
259// to gain access to something outside of the proc directory tree
260//
261 if (pTab[i].aType == XrdDigAuthEnt::aProc && (rc = ValProc(fname)))
262 {if (logRej && rc == EPERM) Audit(client, "denied", opname, fname);
263 return 0;
264 }
265
266// Log this access if so wanted
267//
268 if (lfnType == isFile && logAcc) Audit(client, "allowed", opname, fname);
269
270// Construct the name to be returned
271//
272 i = (lfnType == isDir ? 1 : 0);
273 n = snprintf(path, sizeof(path), fnTmplt, fname);
274 if (n >= (int)sizeof(path)-1) {rc = ENAMETOOLONG; return 0;}
275
276// Attach a trailing slash if there is none if this is a directory
277//
278 if (lfnType == isDir && path[n-1] != '/') {path[n] = '/'; path[n+1] = 0;}
279
280// Return the composite name
281//
282 rc = 0;
283 return strdup(path);
284}
285
286/******************************************************************************/
287/* G e t L o c R e s p */
288/******************************************************************************/
289
291{
292
293// Return desired value
294//
295 if (nameok)
296 eInfo.setErrInfo(locRlenHP, locRespHP);
297 else if (eInfo.getUCap() & XrdOucEI::uIPv4)
298 eInfo.setErrInfo(locRlenV4, locRespV4);
299 else eInfo.setErrInfo(locRlenV6, locRespV6);
300}
301
302/******************************************************************************/
303/* S t a t R o o t */
304/******************************************************************************/
305
307{
308 memcpy(sP, &rootStat, sizeof(struct stat));
309}
310
311/******************************************************************************/
312/* p r i v a t e f u n c t i o n s */
313/******************************************************************************/
314/******************************************************************************/
315/* Private: A d d P a t h */
316/******************************************************************************/
317
318const char *XrdDigConfig::AddPath(XrdDigConfig::pType sType, const char *src,
319 const char *tpd, const char *tfn)
320{
321 struct stat Stat;
322 char *pP, tBuff[MAXPATHLEN], pBuff[MAXPATHLEN], nBuff[MAXNAMELEN];
323 int fd, rc;
324
325// Make sure the source path is absolute and is readable and is of proper type
326//
327 if (*src != '/' || *(src+1) == 0) return "not absolute path";
328 if ((fd = open(src, O_RDONLY)) < 0) return XrdSysE2T(errno);
329 rc = (fstat(fd, &Stat) ? errno : 0); close(fd);
330 if (rc) return XrdSysE2T(rc);
331 switch(sType)
332 {case isFile: if (!S_ISREG(Stat.st_mode)) return "not a file";
333 break;
334 case isDir: if (!S_ISDIR(Stat.st_mode)) return "not a directory";
335 break;
336 default: break;
337 }
338
339// If no target name specified it becomes the last components of src
340//
341 if (!tfn)
342 {const char *tbeg = (strncmp(src, "/etc/", 5) ? src+1 : src+5);
343 tfn = src;
344 while(strlen(tbeg) >= sizeof(nBuff)/2 && (tfn = index(tbeg,'/')))
345 tbeg = tfn + 1;
346 if (!tfn) tfn = rindex(src, '/')+1;
347 else {strcpy(nBuff, tbeg); tfn = pP = nBuff;
348 while((pP = index(pP, '/'))) *pP++ = '.';
349 }
350 }
351 if (!(*tfn)) return "invalid derived target name";
352
353// Construct the target path
354//
355 if (snprintf(tBuff, sizeof(tBuff), "%s%s", tpd, tfn) > (int)sizeof(tBuff))
356 return "target name too long";
357 if (snprintf(pBuff, sizeof(pBuff), fnTmplt, tBuff) > (int)sizeof(pBuff))
358 return "target path too long";
359
360// Create the link and return
361//
362 if ((rc = XrdOucUtils::ReLink(pBuff, src))) return XrdSysE2T(rc);
363 return 0;
364}
365
366/******************************************************************************/
367/* Private: A u d i t */
368/******************************************************************************/
369
370void XrdDigConfig::Audit(const XrdSecEntity *client, const char *what,
371 const char *opn, const char *trg)
372{
373 const char *name = (client->name ? client->name : "anon");
374 char hbuff[512], buff[1024];
375
376// Get the hostname
377//
378 client->addrInfo->Format(hbuff, sizeof(hbuff), XrdNetAddrInfo::fmtName,
380
381// Format the message and print it
382//
383 snprintf(buff, sizeof(buff), "%s@%s %s", name, hbuff, what);
384 eDest->Emsg(opn, client->tident, buff, trg);
385}
386
387/******************************************************************************/
388/* C o n f i g P r o c */
389/******************************************************************************/
390
391bool XrdDigConfig::ConfigProc(const char *ConfigFN)
392{
393 char *var;
394 int cfgFD, retc, NoGo = 0;
395 XrdOucEnv myEnv;
396 XrdOucStream cFile(eDest, getenv("XRDINSTANCE"), &myEnv, "=====> ");
397
398// Try to open the configuration file.
399//
400 if ( (cfgFD = open(ConfigFN, O_RDONLY, 0)) < 0)
401 {eDest->Emsg("Config", errno, "open config file", ConfigFN);
402 return 1;
403 }
404 cFile.Attach(cfgFD);
405 static const char *cvec[] = { "*** digfs plugin config:", 0 };
406 cFile.Capture(cvec);
407
408// Now start reading records until eof.
409//
410 while((var = cFile.GetMyFirstWord()))
411 {if (!strncmp(var, "dig.", 4))
412 if (!ConfigXeq(var+4, cFile)) {cFile.Echo(); NoGo = 1;}
413 }
414
415// Now check if any errors occurred during file i/o
416//
417 if ((retc = cFile.LastError()))
418 NoGo = eDest->Emsg("Config", retc, "read config file", ConfigFN);
419 cFile.Close();
420
421// Return final return code
422//
423 return !NoGo;
424}
425
426/******************************************************************************/
427/* Private: C o n f i g X e q */
428/******************************************************************************/
429
430bool XrdDigConfig::ConfigXeq(char *var, XrdOucStream &cFile)
431{
432
433// Process items. for either a local or a remote configuration
434//
435 TS_Xeq("addconf", xacf);
436 TS_Xeq("log", xlog);
437 return true;
438}
439
440/******************************************************************************/
441/* Private: E m p t y */
442/******************************************************************************/
443
444void XrdDigConfig::Empty(const char *path)
445{
446 DIR *dh;
447 struct dirent *dP;
448 char pBuff[MAXPATHLEN+8], *pB;
449 int n, bLeft;
450
451// Copy the path. We will need it for deletions. This should never fail.
452//
453 if ((n = snprintf(pBuff,sizeof(pBuff),"%s/",path)) >= (int)sizeof(pBuff))
454 return;
455 bLeft = sizeof(pBuff) - n - 1;
456 pB = pBuff + n;
457
458// Open the directory
459//
460 if (!(dh = opendir(path))) return;
461
462// Delete each entry in this directory (no need to be thread safe here)
463//
464 while((dP = readdir(dh)))
465 {if (bLeft > (int)strlen(dP->d_name))
466 {strcpy(pB, dP->d_name);
467 unlink(pBuff);
468 }
469 }
470
471// Now remove the actual directory
472//
473 rmdir(path);
474}
475
476/******************************************************************************/
477/* Private: S e t L o c R e s p */
478/******************************************************************************/
479
480void XrdDigConfig::SetLocResp()
481{
482 static const int fmtopts = XrdNetAddr::old6Map4;
483 XrdNetAddr myAddr(0);
484 char *pP, buff[512], *bp = buff+2;
485 int myPort, bsz = sizeof(buff)-2;
486
487// Obtain port number we will be using. Note that the constructor must occur
488// after the port number is known (i.e., this cannot be a global static).
489//
490 myPort = (pP = getenv("XRDPORT")) ? strtol(pP, (char **)NULL, 10) : 0;
491 strcpy(buff, "Sr");
492
493// Establish hostname locate response
494//
495 myAddr.Port(myPort);
496 myAddr.Format(bp, bsz, XrdNetAddr::fmtName);
497 locRespHP = strdup(buff); locRlenHP = strlen(buff)+1;
498
499// Extablish IPV6 locate response
500//
501 myAddr.Format(bp, bsz, XrdNetAddr::fmtAdv6, fmtopts);
502 locRespV6 = locRespV4 = strdup(buff); locRlenV6 = locRlenV4 = strlen(buff)+1;
503
504// If we are truly IPv6 then see if we also have an IPv4 address
505//
506 if (myAddr.isIPType(XrdNetAddrInfo::IPv6) && !myAddr.isMapped())
507 {XrdNetAddr *iP;
508 int iN;
509 if (!XrdNetUtils::GetAddrs(myAddr.Name(""), &iP, iN,
510 XrdNetUtils::onlyIPv4, 0) && iN)
511 {iP[0].Port(myPort);
512 iP[0].Format(bp, bsz, XrdNetAddr::fmtAdv6, fmtopts);
513 locRespV4 = strdup(buff); locRlenV4 = strlen(buff)+1;
514 delete [] iP;
515 }
516 }
517}
518
519/******************************************************************************/
520/* V a l P r o c */
521/******************************************************************************/
522
523int XrdDigConfig::ValProc(const char *path)
524{
525 struct stat Stat;
526 char *Slash, cpath[1040], ppath[1040];
527 int n;
528
529// Copy the path so we can modify it and make sure it ends with a slash
530//
531 n = snprintf(ppath, sizeof(ppath), "%s", path);
532 if (n >= (int)sizeof(ppath)-2) return ENAMETOOLONG;
533 if (ppath[n-1] != '/') {ppath[n] = '/'; ppath[n+1] = 0;}
534
535// We accept proc/x/y where y and any other path suffix is not a symlink
536//
537 if (!(Slash = index(ppath, '/')) || !(Slash = index(Slash+1, '/'))
538 || !(Slash = index(Slash+1, '/'))) return 0;
539
540// Now check each component
541//
542 while(Slash)
543 {*Slash = 0;
544 n = snprintf(cpath, sizeof(cpath), fnTmplt, ppath);
545 if (n >= (int)sizeof(cpath)) return ENAMETOOLONG;
546 if (lstat(cpath, &Stat)) return errno;
547 if (!S_ISDIR(Stat.st_mode) && !S_ISREG(Stat.st_mode)) return EPERM;
548 *Slash = '/';
549 Slash = index(Slash+1, '/');
550 }
551
552// It's OK to use proc
553//
554 return 0;
555}
556
557/******************************************************************************/
558/* Private: x a c f */
559/******************************************************************************/
560
561/* Function: xacf
562
563
564 Purpose: Parse the directive: addconf <path> [<fname>]
565
566 <path> path to a configuration file
567 <fname> the file name it is to have in "/=/conf/etc/"
568
569 Output: true upon success or false upon failure.
570*/
571
572bool XrdDigConfig::xacf(XrdOucStream &cFile)
573{
574 const char *eTxt;
575 char *val, src[MAXPATHLEN+8];
576
577// Check out first token
578//
579 if (!(val = cFile.GetWord()) || !val[0])
580 {eDest->Emsg("Config", "addconf path not specified."); return false;}
581
582// Copy the path
583//
584 if (strlen(val) >= sizeof(src))
585 {eDest->Emsg("Config", "addconf path is too long."); return false;}
586 strcpy(src, val);
587
588// Check if we have a special filename here
589//
590 if (!(val = cFile.GetWord()) || !val[0]) val = 0;
591 else {if (index(val, '/'))
592 {eDest->Emsg("Config", "invalid addconf fname -", val);
593 return false;
594 }
595 }
596
597// Now add the file path
598//
599 if ((eTxt = AddPath(isFile, src, "conf/etc/", val)))
600 {char eBuff[256];
601 snprintf(eBuff, sizeof(eBuff), "- %s", eTxt);
602 eDest->Emsg("Config", "Unable to addconf" , src, eBuff);
603 return false;
604 }
605
606// All done
607//
608 return true;
609}
610
611/******************************************************************************/
612/* Private: x l o g */
613/******************************************************************************/
614
615/* Function: xlog
616
617
618 Purpose: Parse the directive: log [grant] [deny] | none
619
620 grant log successful access to information
621 deny log unsuccessful access to information
622 none do not log anything
623
624 Output: true upon success or false upon failure.
625*/
626
627bool XrdDigConfig::xlog(XrdOucStream &cFile)
628{
629 char *val;
630
631// Check out first token
632//
633 if (!(val = cFile.GetWord()) || !val[0])
634 {eDest->Emsg("Config", "log parameter not specified"); return false;}
635
636// Check for appropriate words
637//
638 logAcc = logRej = false;
639 do { if (!strcmp("grant", val)) logAcc = true;
640 else if (!strcmp("deny", val)) logRej = true;
641 else if (!strcmp("none", val)) logRej = logAcc = false;
642 else {eDest->Emsg("Config","invalid log option -",val); return false;}
643 val = cFile.GetWord();
644 } while(val && *val);
645
646// All done
647//
648 return true;
649}
struct stat Stat
Definition XrdCks.cc:49
#define TS_Xeq(x, m)
Definition XrdConfig.cc:156
static XrdSysError eDest(0,"crypto_")
int lstat(const char *path, struct stat *buf)
#define close(a)
Definition XrdPosix.hh:43
#define rmdir(a)
Definition XrdPosix.hh:92
#define fstat(a, b)
Definition XrdPosix.hh:57
#define opendir(a)
Definition XrdPosix.hh:73
#define open
Definition XrdPosix.hh:71
#define unlink(a)
Definition XrdPosix.hh:108
#define stat(a, b)
Definition XrdPosix.hh:96
#define readdir(a)
Definition XrdPosix.hh:81
const char * XrdSysE2T(int errcode)
Definition XrdSysE2T.cc:104
int GenAccess(const XrdSecEntity *client, const char *aList[], int aMax)
char * GenPath(int &rc, const XrdSecEntity *client, const char *opname, const char *lfn, pType lfnType=isAny)
static void StatRoot(struct stat *sP)
void GetLocResp(XrdOucErrInfo &eInfo, bool nameok)
bool Configure(const char *cFN, const char *parms)
static const int noPort
Do not add port number.
static const int old6Map4
Use deprecated IPV6 mapped format.
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
@ fmtName
Hostname if it is resolvable o/w use fmtAddr.
int Port(int pNum=-1)
static const char * GetAddrs(const char *hSpec, XrdNetAddr *aListP[], int &aListN, AddrOpts opts=allIPMap, int pNum=PortInSpec)
int setErrInfo(int code, const char *emsg)
char * GetWord(int lowcase=0)
char * GetToken(char **rest=0, int lowcase=0)
static int ReLink(const char *path, const char *target, mode_t mode=0)
XrdNetAddrInfo * addrInfo
Entity's connection details.
const char * tident
Trace identifier always preset.
char * name
Entity's name.
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
XrdDigAuth Auth
Definition XrdDigAuth.cc:63
XrdSysError * eDest
XrdDigConfig Config
static const int uIPv4
ucap: Supports read redirects