/******************************************************************************/
/* */
/* X r d D i g A u t h . c c */
/* */
/* (C) 2013 by the Board of Trustees of the Leland Stanford, Jr., University */
/* All Rights Reserved */
/* Produced by Andrew Hanushevsky for Stanford University under contract */
/* DE-AC02-76-SFO0515 with the Deprtment of Energy */
/* */
/* This file is part of the XRootD software suite. */
/* */
/* XRootD is free software: you can redistribute it and/or modify it under */
/* the terms of the GNU Lesser General Public License as published by the */
/* Free Software Foundation, either version 3 of the License, or (at your */
/* option) any later version. */
/* */
/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
/* License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
/* COPYING (GPL license). If not, see . */
/* */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without */
/* specific prior written permission of the institution or contributor. */
/******************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "XrdDig/XrdDigAuth.hh"
#include "XrdNet/XrdNetAddrInfo.hh"
#include "XrdOuc/XrdOucStream.hh"
#include "XrdSys/XrdSysError.hh"
/******************************************************************************/
/* d e f i n e s */
/******************************************************************************/
#define TS_Xeq(x,m) if (!strcmp(x,var)) return m(Config);
/******************************************************************************/
/* G l o b a l S t a t i c O b j e c t s */
/******************************************************************************/
namespace XrdDig
{
extern XrdSysError *eDest;
XrdDigAuth Auth;
};
using namespace XrdDig;
/******************************************************************************/
/* S t a t i c L o c a l V a l u e s */
/******************************************************************************/
namespace
{
const char eVec[] = "nhorg";
struct aToks {const char *aTok; XrdDigAuthEnt::aType aRef;} aTab[] =
{{"conf", XrdDigAuthEnt::aConf},
{"core", XrdDigAuthEnt::aCore},
{"logs", XrdDigAuthEnt::aLogs},
{"proc", XrdDigAuthEnt::aProc}
};
};
/******************************************************************************/
/* A u t h o r i z e */
/******************************************************************************/
bool XrdDigAuth::Authorize(const XrdSecEntity *client,
XrdDigAuthEnt::aType aType,
bool aVec[XrdDigAuthEnt::aNum]
)
{
XrdSysMutexHelper mHelp(&authMutex);
time_t tNow = time(0);
XrdDigAuthEnt *aP;
int rc;
// Check if we need to refresh the auth list
//
if (tNow >= authCHK)
{struct stat Stat;
if ((rc = stat(authFN, &Stat)) && errno != ENOENT)
{eDest->Emsg("Config",errno,"stat dig auth file", authFN);
authCHK = tNow + 30;
} else {
if (rc) {if (authList) {if (!Refresh()) authCHK = tNow + 30;}
else authCHK = tNow + 60;
}
else if (authTOD == Stat.st_mtime) authCHK = tNow + 5;
else if (!Refresh()) authCHK = tNow + 30;
}
}
// Clear aVec if so supplied (client's auth mask)
//
if (aVec) memset(aVec, false, XrdDigAuthEnt::aNum);
// Check if we have anything to authorize with
//
if (!authList) return false;
// Check if we are granting access to this resouce at all
//
if (aType != XrdDigAuthEnt::aNum && !accOK[aType]) return false;
// Go through the access list and try to match the client
//
aP = authList;
while(aP)
{do {if (strcmp(client->prot, aP->prot)) break;
if (aP->eChk[XrdDigAuthEnt::eName] && (!client->name ||
strcmp(client->name, aP->eChk[XrdDigAuthEnt::eName]))) break;
if (aP->eChk[XrdDigAuthEnt::eHost]
&& strcmp(client->addrInfo->Name(""),
aP->eChk[XrdDigAuthEnt::eHost])) break;
if (aP->eChk[XrdDigAuthEnt::eVorg] && (!client->vorg ||
strcmp(client->vorg, aP->eChk[XrdDigAuthEnt::eVorg]))) break;
if (aP->eChk[XrdDigAuthEnt::eRole] && (!client->role ||
strcmp(client->role, aP->eChk[XrdDigAuthEnt::eRole]))) break;
if (aP->eChk[XrdDigAuthEnt::eGrp ] && (!client->grps ||
!OkGrp(client->grps, aP->eChk[XrdDigAuthEnt::eGrp ]))) break;
if (aVec) memcpy(aVec, aP->accOK, XrdDigAuthEnt::aNum);
return (aType == XrdDigAuthEnt::aNum ? false : aP->accOK[aType]);
} while(1);
aP = aP->next;
}
// Client failed the test
//
return false;
}
/******************************************************************************/
/* C o n f i g u r e */
/******************************************************************************/
bool XrdDigAuth::Configure(const char *aFN)
{
/*
Function: Configure authorization (one time call).
Input: None.
Output: true upon success or false otherwise.
*/
// Establish the location of the auth file (stable string do not copy)
//
if (!aFN || !(*aFN))
{eDest->Emsg("Config", "Dig authorization file not specified.");
return false;
}
// Initialize authorization
//
authFN = strdup(aFN);
SetupAuth(false);
return true;
}
/******************************************************************************/
/* Private: F a i l u r e */
/******************************************************************************/
bool XrdDigAuth::Failure(int lNum, const char *txt1, const char *txt2)
{
char buff[256];
sprintf(buff, "Error in dig authfile line %d:", lNum);
eDest->Emsg("Auth", buff, txt1, txt2);
return false;
}
/******************************************************************************/
/* Private: O k G r p */
/******************************************************************************/
bool XrdDigAuth::OkGrp(const char *glist, const char *gname)
{
const char *ghit;
int glen = strlen(gname);
// Attempt to find a match in the list
//
do {if (!(ghit = strstr(glist, gname))) return false;
ghit += glen;
if (!(*ghit) || *ghit == ' ') return true;
glist = ghit;
} while(1);
return false;
}
/******************************************************************************/
/* Private: P a r s e */
/******************************************************************************/
bool XrdDigAuth::Parse(XrdOucStream &aFile, int lNum)
{
struct aEntHelper
{XrdDigAuthEnt *eP;
aEntHelper() {eP = new XrdDigAuthEnt;}
~aEntHelper() {if (eP) delete eP;}
} aEnt;
static const char *eCode;
char buff[4096];
char *var, *rec, *bP = buff;
int k, n, bLeft = sizeof(buff);
bool aOK = false, tfVal;
// Get the record type tokens first
//
while((var = aFile.GetToken()) && *var)
{ if (!strcmp(var, "all"))
{for (k = 0; k < (int)XrdDigAuthEnt::aNum; k++)
aEnt.eP->accOK[k] = true;
aOK = true; continue;
}
else if (!strcmp(var, "allow")) break;
else{if (*var == '-') {tfVal = false; var++;}
else tfVal = true;
for (n = 0; n < (int)XrdDigAuthEnt::aNum; n++)
if (!strcmp(var, aTab[n].aTok))
{aEnt.eP->accOK[aTab[n].aRef] = tfVal; aOK = true; break;}
if (n >= (int)XrdDigAuthEnt::aNum)
return Failure(lNum, "Invalid token -", var);
}
}
// Make sure a type has been specified
//
if (!aOK) return Failure(lNum, "Information type not specified.");
// Now scan for the security protocol
//
if (!(var = aFile.GetToken()) || !(*var))
return Failure(lNum, "Auth protocol not specified.");
// Make sure it is not too big
//
if (strlen(var) >= sizeof(aEnt.eP->prot))
return Failure(lNum, "Invalid auth protocol -", var);
strcpy(aEnt.eP->prot, var);
// Now start getting the auth values
//
aOK = false;
while((var = aFile.GetToken()) && *var)
{if (!(eCode = index(eVec, *var))) // "nhorg" lookup
return Failure(lNum, "Invalid entity type -", var);
if (*(var+1) != '=' || !*(var+2))
return Failure(lNum, "Badly formed entity value in", var);
n = snprintf(bP, bLeft, "%s", var+2) + 1;
if ((bLeft -= n) <= 0) break;
if ((var = index(bP, '\\'))) Squash(var);
aEnt.eP->eChk[eCode-eVec] = bP; bP += n;
aOK = true;
}
// Check if we over-ran the buffer
//
if (bLeft <= 0) return Failure(lNum, "Too many auth values.");
// Make sure we have somthing here
//
if (!aOK) return Failure(lNum, "No entity values specified.");
// Create composite mask (we assume no memory failures)
//
aOK = false;
for (n = 0; n < (int)XrdDigAuthEnt::aNum; n++)
if (aEnt.eP->accOK[n]) accOK[n] = aOK = true;
if(!aOK) return Failure(lNum, "Entity has no effective access.");
// Allocate a new value record
//
if (!(rec = (char *)malloc(bP-buff)))
return Failure(lNum, "Insufficient memory.");
memcpy(rec, buff, bP-buff);
aEnt.eP->rec = rec;
// Relocate pointers
//
for (k = (int)XrdDigAuthEnt::eName; k < (int)XrdDigAuthEnt::eNum; k++)
{if (aEnt.eP->eChk[k])
aEnt.eP->eChk[k] = rec + (aEnt.eP->eChk[k] - buff);
}
// Chain this record into the record list and return success
//
aEnt.eP->next = authList;
authList = aEnt.eP;
aEnt.eP = 0;
return true;
}
/******************************************************************************/
/* Private: R e f r e s h */
/******************************************************************************/
bool XrdDigAuth::Refresh() // authMutex must be locked!
{
XrdDigAuthEnt *aP, *nP = authList;
// Delete the current auth list
//
while((aP = nP)) {nP = aP->next; delete aP;}
authList = 0;
// Resetup the auth list
//
return SetupAuth(true);
}
/******************************************************************************/
/* Private: S e t u p A u t h */
/******************************************************************************/
bool XrdDigAuth::SetupAuth(bool isRefresh)
{
XrdOucStream aFile(eDest);
struct stat Stat;
char *line;
int authFD, retc, lNum = 1;
bool NoGo = false;
// Clear summary flags
//
memset(accOK, 0, sizeof(accOK));
// Print message
//
eDest->Say("++++++ Dig ", (isRefresh ? "refreshing" : "initializing"),
" from ", authFN);
// Try to open the configuration file.
//
if ( (authFD = open(authFN, O_RDONLY, 0)) < 0)
{NoGo = errno != ENOENT;
eDest->Say("Config ",strerror(errno)," opening dig auth file ", authFN);
return SetupAuth(isRefresh, !NoGo);
}
aFile.Attach(authFD, 4096);
// Get the time the file was ctreated
//
if (fstat(authFD, &Stat))
{eDest->Say("Config ",strerror(errno)," stating dig auth file ", authFN);
close(authFD);
return SetupAuth(isRefresh, false);
}
authTOD = Stat.st_mtime;
// Now start reading records until eof.
//
while((line = aFile.GetLine()))
{if (*line && *line != '#') NoGo |= !Parse(aFile, lNum);
lNum++;
}
// Now check if any errors occured during file i/o
//
if ((retc = aFile.LastError()))
{eDest->Say("Config ",strerror(-retc)," reading config file ", authFN);
NoGo = true;
}
aFile.Close();
// All done
//
return SetupAuth(isRefresh, !NoGo);
}
/******************************************************************************/
bool XrdDigAuth::SetupAuth(bool isRefresh, bool aOK)
{
// Indicate whether we are active or not
//
if (!authList) eDest->Say("Config ","No users authorized to access digFS; "
"access suspended.");
// All done
//
eDest->Say("------ Dig auth ", (isRefresh ? "refresh" : "initialization"),
(aOK ? " succeeded." : " encountered errors."));
return aOK;
}
/******************************************************************************/
/* Private: S q u a s h */
/******************************************************************************/
void XrdDigAuth::Squash(char *bP)
{
// Insert spaces where needed
//
do {if (*(bP+1) == 's') {*bP = ' '; strcpy(bP+1, bP+2);}
} while((bP = index(bP+1, '\\')));
}