XRootD
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"
43 #include "XrdOuc/XrdOucErrInfo.hh"
44 #include "XrdOuc/XrdOucStream.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"
51 #include "XrdSys/XrdSysHeaders.hh"
52 
53 #include "XrdDig/XrdDigAuth.hh"
54 #include "XrdDig/XrdDigConfig.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 
66 namespace XrdDig
67 {
68  extern XrdSysError *eDest;
69 
70  extern XrdDigAuth Auth;
71 
73 };
74 
75 using namespace XrdDig;
76 
77 namespace
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 
97 bool 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 
226 char *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 
290 void XrdDigConfig::GetLocResp(XrdOucErrInfo &eInfo, bool nameok)
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 
306 void XrdDigConfig::StatRoot(struct stat *sP)
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 
318 const 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 
370 void 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 
391 bool 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 
430 bool 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 
444 void 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 
480 void 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 
523 int 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 
572 bool 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 
627 bool 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: XrdDigConfig.cc:60
int stat(const char *path, struct stat *buf)
struct dirent * readdir(DIR *dirp)
int open(const char *path, int oflag,...)
int fstat(int fildes, struct stat *buf)
int lstat(const char *path, struct stat *buf)
int unlink(const char *path)
int rmdir(const char *path)
DIR * opendir(const char *path)
#define close(a)
Definition: XrdPosix.hh:43
const char * XrdSysE2T(int errcode)
Definition: XrdSysE2T.cc:104
bool Authorize(const XrdSecEntity *client, XrdDigAuthEnt::aType aType, bool aVec[XrdDigAuthEnt::aNum]=0)
Definition: XrdDigAuth.cc:89
bool Configure(const char *aFN)
Definition: XrdDigAuth.cc:163
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)
Definition: XrdDigConfig.cc:97
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)
Definition: XrdNetAddr.cc:156
static const char * GetAddrs(const char *hSpec, XrdNetAddr *aListP[], int &aListN, AddrOpts opts=allIPMap, int pNum=PortInSpec)
Definition: XrdNetUtils.cc:239
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.
Definition: XrdSecEntity.hh:80
const char * tident
Trace identifier always preset.
Definition: XrdSecEntity.hh:81
char * name
Entity's name.
Definition: XrdSecEntity.hh:69
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
XrdDigAuth Auth
Definition: XrdDigAuth.cc:63
XrdSysError * eDest
Definition: XrdDigConfig.cc:68
XrdDigConfig Config
Definition: XrdDigConfig.cc:72
static const int uIPv4
ucap: Supports read redirects