<commit_before>// @(#)root/proofd:$Id$
// Author: <NAME> 2008

/*************************************************************************
 * Copyright (C) 1995-2005, <NAME> and <NAME>.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// XrdProofdClientMgr                                                   //
//                                                                      //
// Author: <NAME>, CERN, 2008                                         //
//                                                                      //
// Class managing clients.                                              //
//                                                                      //
//////////////////////////////////////////////////////////////////////////
#include "XrdProofdPlatform.h"

#include "XrdProofdXrdVers.h"
#if ROOTXRDVERS < ROOT_OldXrdOuc
#  define XPD_LOG_01 OUC_LOG_01
#else
#  define XPD_LOG_01 SYS_LOG_01
#endif

#include "XpdSysError.h"

#include "Xrd/XrdBuffer.hh"
#include "XrdOuc/XrdOucErrInfo.hh"
#include "XrdOuc/XrdOucStream.hh"
#include "XrdSec/XrdSecInterface.hh"
#include "XrdSys/XrdSysPlugin.hh"

#include "XrdProofdClient.h"
#include "XrdProofdClientMgr.h"
#include "XrdProofdManager.h"
#include "XrdProofdProtocol.h"
#include "XrdProofGroup.h"
#include "XrdProofdProofServ.h"
#include "XrdProofdProofServMgr.h"
#include "XrdROOT.h"

// Tracing utilities
#include "XrdProofdTrace.h"

static XpdManagerCron_t fManagerCron;

// Security handle
typedef XrdSecService *(*XrdSecServLoader_t)(XrdSysLogger *, const char *cfn);

//--------------------------------------------------------------------------
//
// XrdProofdClientCron
//
// Client manager thread
//
//--------------------------------------------------------------------------
void *XrdProofdClientCron(void *p)
{
   // This is an endless loop to check the system periodically or when
   // triggered via a message in a dedicated pipe
   XPDLOC(CMGR, "ClientCron")

   XpdManagerCron_t *mc = (XpdManagerCron_t *)p;
   XrdProofdClientMgr *mgr = mc->fClientMgr;
   if (!(mgr)) {
      TRACE(REQ, "undefined client manager: cannot start");
      return (void *)0;
   }
   XrdProofdProofServMgr *smgr = mc->fSessionMgr;
   if (!(smgr)) {
      TRACE(REQ, "undefined session manager: cannot start");
      return (void *)0;
   }

   // Time of last session check
   int lastcheck = time(0), ckfreq = mgr->CheckFrequency(), deltat = 0;
   while(1) {
      // We wait for processes to communicate a session status change
      if ((deltat = ckfreq - (time(0) - lastcheck)) <= 0)
         deltat = ckfreq;
      int pollRet = mgr->Pipe()->Poll(deltat);

      if (pollRet > 0) {
         // Read message
         XpdMsg msg;
         int rc = 0;
         if ((rc = mgr->Pipe()->Recv(msg)) != 0) {
            XPDERR("problems receiving message; errno: "<<-rc);
            continue;
         }
         // Parse type
         //XrdOucString buf;
         if (msg.Type() == XrdProofdClientMgr::kClientDisconnect) {
            // obsolete
	    TRACE(XERR, "obsolete type: XrdProofdClientMgr::kClientDisconnect");
         } else {
            TRACE(XERR, "unknown type: "<<msg.Type());
            continue;
         }
      } else {
         // Run regular checks
         mgr->CheckClients();
         // Remember when ...
         lastcheck = time(0);
      }
   }

   // Should never come here
   return (void *)0;
}

//______________________________________________________________________________
XrdProofdClientMgr::XrdProofdClientMgr(XrdProofdManager *mgr,
                                       XrdProtocol_Config *pi, XrdSysError *e)
                  : XrdProofdConfig(pi->ConfigFN, e), fSecPlugin(0)
{
   // Constructor
   XPDLOC(CMGR, "XrdProofdClientMgr")

   fMutex = new XrdSysRecMutex;
   fMgr = mgr;
   fCIA = 0;
   fNDisconnected = 0;
   fReconnectTimeOut = 300;
   // Defaults can be changed via 'clientmgr'
   fActivityTimeOut = 1200;
   fCheckFrequency = 60;

   // Init pipe for manager thread
   if (!fPipe.IsValid()) {
      TRACE(XERR, "unable to generate the pipe");
      return;
   }

   // Configuration directives
   RegisterDirectives();
}

//__________________________________________________________________________
void XrdProofdClientMgr::RegisterDirectives()
{
   // Register directives for configuration

   Register("clientmgr", new XrdProofdDirective("clientmgr", this, &DoDirectiveClass));
   Register("seclib", new XrdProofdDirective("seclib",
                                   (void *)&fSecLib, &DoDirectiveString, 0));
   Register("reconnto", new XrdProofdDirective("reconnto",
                               (void *)&fReconnectTimeOut, &DoDirectiveInt));
}

//______________________________________________________________________________
int XrdProofdClientMgr::DoDirective(XrdProofdDirective *d,
                                    char *val, XrdOucStream *cfg, bool rcf)
{
   // Update the priorities of the active sessions.
   XPDLOC(SMGR, "ClientMgr::DoDirective")

   if (!d)
      // undefined inputs
      return -1;

   if (d->fName == "clientmgr") {
      return DoDirectiveClientMgr(val, cfg, rcf);
   }
   TRACE(XERR,"unknown directive: "<<d->fName);
   return -1;
}

//______________________________________________________________________________
int XrdProofdClientMgr::DoDirectiveClientMgr(char *val, XrdOucStream *cfg, bool)
{
   // Process 'clientmgr' directive
   // eg: xpd.clientmgr checkfq:120 activityto:600
   XPDLOC(SMGR, "ClientMgr::DoDirectiveClientMgr")

   if (!val || !cfg)
      // undefined inputs
      return -1;

   int checkfq = -1;
   int activityto = -1;

   while (val) {
      XrdOucString tok(val);
      if (tok.beginswith("checkfq:")) {
         tok.replace("checkfq:", "");
         checkfq = strtol(tok.c_str(), 0, 10);
      } else if (tok.beginswith("activityto:")) {
         tok.replace("activityto:", "");
         activityto = strtol(tok.c_str(), 0, 10);
      }
      // Get next
      val = cfg->GetWord();
   }

   // Check deprecated 'if' directive
   if (fMgr->Host() && cfg)
      if (XrdProofdAux::CheckIf(cfg, fMgr->Host()) == 0)
         return 0;

   // Set the values
   fCheckFrequency = (XPD_LONGOK(checkfq) && checkfq > 0) ? checkfq : fCheckFrequency;
   fActivityTimeOut = (XPD_LONGOK(activityto) && activityto > 0) ? activityto : fActivityTimeOut;

   XrdOucString msg;
   XPDFORM(msg, "checkfq: %d s, activityto: %d s", fCheckFrequency, fActivityTimeOut);
   TRACE(ALL, msg);

   return 0;
}

//__________________________________________________________________________
int XrdProofdClientMgr::Config(bool rcf)
{
   // Run configuration and parse the entered config directives.
   // Return 0 on success, -1 on error
   XPDLOC(CMGR, "ClientMgr::Config")

   // Run first the configurator
   if (XrdProofdConfig::Config(rcf) != 0) {
      XPDERR("problems parsing file ");
      return -1;
   }

   XrdOucString msg;
   msg = (rcf) ? "re-configuring" : "configuring";
   TRACE(ALL, msg.c_str());

   // Admin paths
   fClntAdminPath = fMgr->AdminPath();
   fClntAdminPath += "/clients";

   // Make sure they exist
   XrdProofUI ui;
   XrdProofdAux::GetUserInfo(fMgr->EffectiveUser(), ui);
   if (XrdProofdAux::AssertDir(fClntAdminPath.c_str(), ui, 1) != 0) {
      XPDERR("unable to assert the clients admin path: "<<fClntAdminPath);
      fClntAdminPath = "";
      return -1;
   }
   TRACE(ALL, "clients admin path set to: "<<fClntAdminPath);

   // Init place holders for previous active clients, if any
   if (ParsePreviousClients(msg) != 0) {
      XPDERR("problems parsing previous active clients: "<<msg);
   }

   // Initialize the security system if this is wanted
   if (!rcf) {
      if (fSecLib.length() <= 0) {
         TRACE(ALL, "XRD seclib not specified; strong authentication disabled");
      } else {
         if (!(fCIA = LoadSecurity())) {
            XPDERR("unable to load security system.");
            return -1;
         }
         TRACE(ALL, "security library loaded");
      }
   }

   if (rcf) {
      // Re-assign groups
      if (fMgr->GroupsMgr() && fMgr->GroupsMgr()->Num() > 0) {
         std::list<XrdProofdClient *>::iterator pci;
         for (pci = fProofdClients.begin(); pci != fProofdClients.end(); ++pci)
            (*pci)->SetGroup(fMgr->GroupsMgr()->GetUserGroup((*pci)->User())->Name());
      }
   }

   if (!rcf) {
      // Start cron thread
      pthread_t tid;
      // Fill manager pointers structure
      fManagerCron.fClientMgr = this;
      fManagerCron.fSessionMgr = fMgr->SessionMgr();
      if (XrdSysThread::Run(&tid, XrdProofdClientCron,
                           (void *)&fManagerCron, 0, "ClientMgr cron thread") != 0) {
         XPDERR("could not start cron thread");
         return 0;
      }
      TRACE(ALL, "cron thread started");
   }

   // Done
   return 0;
}

//______________________________________________________________________________
int XrdProofdClientMgr::Login(XrdProofdProtocol *p)
{
   // Process a login request
   XPDLOC(CMGR, "ClientMgr::Login")

   int rc = 0;
   XPD_SETRESP(p, "Login");

   TRACEP(p, HDBG, "enter");

   // If this server is explicitely required to be a worker node or a
   // submaster, check whether the requesting host is allowed to connect
   if (p->Request()->login.role[0] != 'i' &&
      (fMgr->SrvType() == kXPD_MasterWorker || fMgr->SrvType() == kXPD_Master)) {
      if (!fMgr->CheckMaster(p->Link()->Host())) {
         TRACEP(p, XERR,"master not allowed to connect - "
                        "ignoring request ("<<p->Link()->Host()<<")");
         response->Send(kXR_InvalidRequest,
                            "master not allowed to connect - request ignored");
         return 0;
      }
   }

   // Get user and group names for the entity requiring to login
   int i, pid;
   XrdOucString uname, gname, emsg;

   // If this is the second call (after authentication) we just need mapping
   if (p->Status() == XPD_NEED_MAP) {

      // Finalize the login, checking the if username is allowed to use the facility.
      // The username could have been set as part of the authentication process (for
      // example via a user mapping funtion or a grid-map file), so these checks have
      // to be done at this level.
      // (The XrdProofdClient instance is created in here, if everything else goes well)
      int rccc = 0;
      if ((rccc = CheckClient(p, 0, emsg)) != 0) {
         TRACEP(p, XERR, emsg);
         XErrorCode rcode = (rccc == -2) ? (XErrorCode) kXR_NotAuthorized
                                         : (XErrorCode) kXR_InvalidRequest;
         response->Send(rcode, emsg.c_str());
         response->Send(kXR_InvalidRequest, emsg.c_str());
         return 0;
      }

      // Acknowledge the client
      response->Send();
      p->SetStatus(XPD_LOGGEDIN);
      return MapClient(p, 0);
   }

   // Make sure the user is not already logged in
   if ((p->Status() & XPD_LOGGEDIN)) {
      response->Send(kXR_InvalidRequest, "duplicate login; already logged in");
      return 0;
   }

   // Find out the connection type: 'i', internal, means this is a proofsrv calling back.
   bool needauth = 0;
   bool ismaster = (fMgr->SrvType() == kXPD_TopMaster || fMgr->SrvType() == kXPD_Master) ? 1 : 0;
   switch (p->Request()->login.role[0]) {
   case 'A':
      p->SetConnType(kXPD_Admin);
      response->SetTag("adm");
      break;
   case 'i':
      p->SetConnType(kXPD_Internal);
      response->SetTag("int");
      break;
   case 'M':
      if (fMgr->SrvType() == kXPD_AnyServer || ismaster) {
         p->SetConnType(kXPD_ClientMaster);
         needauth = 1;
         response->SetTag("m2c");
      } else {
         TRACEP(p, XERR,"top master mode not allowed - ignoring request");
         response->Send(kXR_InvalidRequest,
                            "Server not allowed to be top master - ignoring request");
         return 0;
      }
      break;
   case 'm':
      if (fMgr->SrvType() == kXPD_AnyServer || ismaster) {
         p->SetConnType(kXPD_MasterMaster);
         needauth = 1;
         response->SetTag("m2m");
      } else {
         TRACEP(p, XERR,"submaster mode not allowed - ignoring request");
         response->Send(kXR_InvalidRequest,
                             "Server not allowed to be submaster - ignoring request");
         return 0;
      }
      break;
   case 's':
      if (fMgr->SrvType() == kXPD_AnyServer || fMgr->SrvType() == kXPD_MasterWorker) {
         p->SetConnType(kXPD_MasterWorker);
         needauth = 1;
         response->SetTag("w2m");
      } else {
         TRACEP(p, XERR,"worker mode not allowed - ignoring request");
         response->Send(kXR_InvalidRequest,
                        "Server not allowed to be worker - ignoring request");
         return 0;
      }
      break;
   default:
      TRACEP(p, XERR, "unknown mode: '" << p->Request()->login.role[0] <<"'");
      response->Send(kXR_InvalidRequest, "Server type: invalide mode");
      return rc;
   }
   response->SetTraceID();

   // Unmarshall the data: process ID
   pid = (int)ntohl(p->Request()->login.pid);
   p->SetPid(pid);

   // Username
   char un[9];
   for (i = 0; i < (int)sizeof(un)-1; i++) {
      if (p->Request()->login.username[i] == '\0' || p->Request()->login.username[i] == ' ')
         break;
      un[i] = p->Request()->login.username[i];
   }
   un[i] = '\0';
   uname = un;

   // Longer usernames are in the attached buffer
   if (uname == "?>buf") {
      // Attach to buffer
      char *buf = p->Argp()->buff;
      int   len = p->Request()->login.dlen;
      // Extract username
      uname.assign(buf,0,len-1);
      int iusr = uname.find("|usr:");
      if (iusr == -1) {
         TRACEP(p, XERR,"long user name not found");
         response->Send(kXR_InvalidRequest, "long user name not found");
         return 0;
      }
      uname.erase(0,iusr+5);
      uname.erase(uname.find("|"));
   }

   // Extract group name, if specified (syntax is uname[:gname])
   int ig = uname.find(":");
   if (ig != -1) {
      gname.assign(uname, ig+1);
      uname.erase(ig);
      TRACEP(p, DBG, "requested group: "<<gname);
      // Save the requested group info in the protocol instance
      p->SetGroupIn(gname.c_str());
   }

   // Save the incoming username setting in the protocol instance
   p->SetUserIn(uname.c_str());

   // Establish IDs for this link
   p->Link()->setID(uname.c_str(), pid);
   p->SetTraceID();
   response->SetTraceID();
   p->SetClntCapVer(p->Request()->login.capver[0]);

   // Get the security token for this link. We will either get a token, a null
   // string indicating host-only authentication, or a null indicating no
   // authentication. We can then optimize of each case.
   if (needauth && fCIA) {
      const char *pp = fCIA->getParms(i, p->Link()->Name());
      if (pp && i ) {
         response->SendI((kXR_int32)XPROOFD_VERSBIN, (void *)pp, i);
         p->SetStatus((XPD_NEED_MAP | XPD_NEED_AUTH));
         return 0;
      } else {
         response->SendI((kXR_int32)XPROOFD_VERSBIN);
         p->SetStatus(XPD_LOGGEDIN);
         if (pp)
            p->SetAuthEntity();
      }
   } else {
      // Check the client at theis point; the XrdProofdClient instance is created
      // in here, if everything else goes well
      int rccc = 0;
      if ((rccc = CheckClient(p, p->UserIn(), emsg)) != 0) {
         TRACEP(p, XERR, emsg);
         XErrorCode rcode = (rccc == -2) ? (XErrorCode) kXR_NotAuthorized
                                         : (XErrorCode) kXR_InvalidRequest;
         response->Send(rcode, emsg.c_str());
         return 0;
      }
      rc = response->SendI((kXR_int32)XPROOFD_VERSBIN);
      p->SetStatus(XPD_LOGGEDIN);
   }

   // Map the client
   return MapClient(p, 1);
}

//______________________________________________________________________________
int XrdProofdClientMgr::CheckClient(XrdProofdProtocol *p,
                                    const char *user, XrdOucString &emsg)
{
   // Perform checks on the client username. In case authentication is required
   // this is called afetr authentication.
   // Return 0 on success; on error, return -1 .
   XPDLOC(CMGR, "ClientMgr::CheckClient")<commit_msg>Import patch #47833: Make sure the XrdProofClient instance is always defined when calling MapClient<commit_after>// @(#)root/proofd:$Id$
// Author: <NAME> 2008

/*************************************************************************
 * Copyright (C) 1995-2005, <NAME> and <NAME>.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// XrdProofdClientMgr                                                   //
//                                                                      //
// Author: <NAME>, CERN, 2008                                         //
//                                                                      //
// Class managing clients.                                              //
//                                                                      //
//////////////////////////////////////////////////////////////////////////
#include "XrdProofdPlatform.h"

#include "XrdProofdXrdVers.h"
#if ROOTXRDVERS < ROOT_OldXrdOuc
#  define XPD_LOG_01 OUC_LOG_01
#else
#  define XPD_LOG_01 SYS_LOG_01
#endif

#include "XpdSysError.h"

#include "Xrd/XrdBuffer.hh"
#include "XrdOuc/XrdOucErrInfo.hh"
#include "XrdOuc/XrdOucStream.hh"
#include "XrdSec/XrdSecInterface.hh"
#include "XrdSys/XrdSysPlugin.hh"

#include "XrdProofdClient.h"
#include "XrdProofdClientMgr.h"
#include "XrdProofdManager.h"
#include "XrdProofdProtocol.h"
#include "XrdProofGroup.h"
#include "XrdProofdProofServ.h"
#include "XrdProofdProofServMgr.h"
#include "XrdROOT.h"

// Tracing utilities
#include "XrdProofdTrace.h"

static XpdManagerCron_t fManagerCron;

// Security handle
typedef XrdSecService *(*XrdSecServLoader_t)(XrdSysLogger *, const char *cfn);

//--------------------------------------------------------------------------
//
// XrdProofdClientCron
//
// Client manager thread
//
//--------------------------------------------------------------------------
void *XrdProofdClientCron(void *p)
{
   // This is an endless loop to check the system periodically or when
   // triggered via a message in a dedicated pipe
   XPDLOC(CMGR, "ClientCron")

   XpdManagerCron_t *mc = (XpdManagerCron_t *)p;
   XrdProofdClientMgr *mgr = mc->fClientMgr;
   if (!(mgr)) {
      TRACE(REQ, "undefined client manager: cannot start");
      return (void *)0;
   }
   XrdProofdProofServMgr *smgr = mc->fSessionMgr;
   if (!(smgr)) {
      TRACE(REQ, "undefined session manager: cannot start");
      return (void *)0;
   }

   // Time of last session check
   int lastcheck = time(0), ckfreq = mgr->CheckFrequency(), deltat = 0;
   while(1) {
      // We wait for processes to communicate a session status change
      if ((deltat = ckfreq - (time(0) - lastcheck)) <= 0)
         deltat = ckfreq;
      int pollRet = mgr->Pipe()->Poll(deltat);

      if (pollRet > 0) {
         // Read message
         XpdMsg msg;
         int rc = 0;
         if ((rc = mgr->Pipe()->Recv(msg)) != 0) {
            XPDERR("problems receiving message; errno: "<<-rc);
            continue;
         }
         // Parse type
         if (msg.Type() == XrdProofdClientMgr::kClientDisconnect) {
            // Obsolete
            TRACE(XERR, "obsolete type: XrdProofdClientMgr::kClientDisconnect");
         } else {
            TRACE(XERR, "unknown type: "<<msg.Type());
            continue;
         }
      } else {
         // Run regular checks
         mgr->CheckClients();
         // Remember when ...
         lastcheck = time(0);
      }
   }

   // Should never come here
   return (void *)0;
}

//______________________________________________________________________________
XrdProofdClientMgr::XrdProofdClientMgr(XrdProofdManager *mgr,
                                       XrdProtocol_Config *pi, XrdSysError *e)
                  : XrdProofdConfig(pi->ConfigFN, e), fSecPlugin(0)
{
   // Constructor
   XPDLOC(CMGR, "XrdProofdClientMgr")

   fMutex = new XrdSysRecMutex;
   fMgr = mgr;
   fCIA = 0;
   fNDisconnected = 0;
   fReconnectTimeOut = 300;
   // Defaults can be changed via 'clientmgr'
   fActivityTimeOut = 1200;
   fCheckFrequency = 60;

   // Init pipe for manager thread
   if (!fPipe.IsValid()) {
      TRACE(XERR, "unable to generate the pipe");
      return;
   }

   // Configuration directives
   RegisterDirectives();
}

//__________________________________________________________________________
void XrdProofdClientMgr::RegisterDirectives()
{
   // Register directives for configuration

   Register("clientmgr", new XrdProofdDirective("clientmgr", this, &DoDirectiveClass));
   Register("seclib", new XrdProofdDirective("seclib",
                                   (void *)&fSecLib, &DoDirectiveString, 0));
   Register("reconnto", new XrdProofdDirective("reconnto",
                               (void *)&fReconnectTimeOut, &DoDirectiveInt));
}

//______________________________________________________________________________
int XrdProofdClientMgr::DoDirective(XrdProofdDirective *d,
                                    char *val, XrdOucStream *cfg, bool rcf)
{
   // Update the priorities of the active sessions.
   XPDLOC(SMGR, "ClientMgr::DoDirective")

   if (!d)
      // undefined inputs
      return -1;

   if (d->fName == "clientmgr") {
      return DoDirectiveClientMgr(val, cfg, rcf);
   }
   TRACE(XERR,"unknown directive: "<<d->fName);
   return -1;
}

//______________________________________________________________________________
int XrdProofdClientMgr::DoDirectiveClientMgr(char *val, XrdOucStream *cfg, bool)
{
   // Process 'clientmgr' directive
   // eg: xpd.clientmgr checkfq:120 activityto:600
   XPDLOC(SMGR, "ClientMgr::DoDirectiveClientMgr")

   if (!val || !cfg)
      // undefined inputs
      return -1;

   int checkfq = -1;
   int activityto = -1;

   while (val) {
      XrdOucString tok(val);
      if (tok.beginswith("checkfq:")) {
         tok.replace("checkfq:", "");
         checkfq = strtol(tok.c_str(), 0, 10);
      } else if (tok.beginswith("activityto:")) {
         tok.replace("activityto:", "");
         activityto = strtol(tok.c_str(), 0, 10);
      }
      // Get next
      val = cfg->GetWord();
   }

   // Check deprecated 'if' directive
   if (fMgr->Host() && cfg)
      if (XrdProofdAux::CheckIf(cfg, fMgr->Host()) == 0)
         return 0;

   // Set the values
   fCheckFrequency = (XPD_LONGOK(checkfq) && checkfq > 0) ? checkfq : fCheckFrequency;
   fActivityTimeOut = (XPD_LONGOK(activityto) && activityto > 0) ? activityto : fActivityTimeOut;

   XrdOucString msg;
   XPDFORM(msg, "checkfq: %d s, activityto: %d s", fCheckFrequency, fActivityTimeOut);
   TRACE(ALL, msg);

   return 0;
}

//__________________________________________________________________________
int XrdProofdClientMgr::Config(bool rcf)
{
   // Run configuration and parse the entered config directives.
   // Return 0 on success, -1 on error
   XPDLOC(CMGR, "ClientMgr::Config")

   // Run first the configurator
   if (XrdProofdConfig::Config(rcf) != 0) {
      XPDERR("problems parsing file ");
      return -1;
   }

   XrdOucString msg;
   msg = (rcf) ? "re-configuring" : "configuring";
   TRACE(ALL, msg.c_str());

   // Admin paths
   fClntAdminPath = fMgr->AdminPath();
   fClntAdminPath += "/clients";

   // Make sure they exist
   XrdProofUI ui;
   XrdProofdAux::GetUserInfo(fMgr->EffectiveUser(), ui);
   if (XrdProofdAux::AssertDir(fClntAdminPath.c_str(), ui, 1) != 0) {
      XPDERR("unable to assert the clients admin path: "<<fClntAdminPath);
      fClntAdminPath = "";
      return -1;
   }
   TRACE(ALL, "clients admin path set to: "<<fClntAdminPath);

   // Init place holders for previous active clients, if any
   if (ParsePreviousClients(msg) != 0) {
      XPDERR("problems parsing previous active clients: "<<msg);
   }

   // Initialize the security system if this is wanted
   if (!rcf) {
      if (fSecLib.length() <= 0) {
         TRACE(ALL, "XRD seclib not specified; strong authentication disabled");
      } else {
         if (!(fCIA = LoadSecurity())) {
            XPDERR("unable to load security system.");
            return -1;
         }
         TRACE(ALL, "security library loaded");
      }
   }

   if (rcf) {
      // Re-assign groups
      if (fMgr->GroupsMgr() && fMgr->GroupsMgr()->Num() > 0) {
         std::list<XrdProofdClient *>::iterator pci;
         for (pci = fProofdClients.begin(); pci != fProofdClients.end(); ++pci)
            (*pci)->SetGroup(fMgr->GroupsMgr()->GetUserGroup((*pci)->User())->Name());
      }
   }

   if (!rcf) {
      // Start cron thread
      pthread_t tid;
      // Fill manager pointers structure
      fManagerCron.fClientMgr = this;
      fManagerCron.fSessionMgr = fMgr->SessionMgr();
      if (XrdSysThread::Run(&tid, XrdProofdClientCron,
                           (void *)&fManagerCron, 0, "ClientMgr cron thread") != 0) {
         XPDERR("could not start cron thread");
         return 0;
      }
      TRACE(ALL, "cron thread started");
   }

   // Done
   return 0;
}

//______________________________________________________________________________
int XrdProofdClientMgr::Login(XrdProofdProtocol *p)
{
   // Process a login request
   XPDLOC(CMGR, "ClientMgr::Login")

   int rc = 0;
   XPD_SETRESP(p, "Login");

   TRACEP(p, HDBG, "enter");

   // If this server is explicitely required to be a worker node or a
   // submaster, check whether the requesting host is allowed to connect
   if (p->Request()->login.role[0] != 'i' &&
      (fMgr->SrvType() == kXPD_MasterWorker || fMgr->SrvType() == kXPD_Master)) {
      if (!fMgr->CheckMaster(p->Link()->Host())) {
         TRACEP(p, XERR,"master not allowed to connect - "
                        "ignoring request ("<<p->Link()->Host()<<")");
         response->Send(kXR_InvalidRequest,
                            "master not allowed to connect - request ignored");
         return 0;
      }
   }

   // Get user and group names for the entity requiring to login
   int i, pid;
   XrdOucString uname, gname, emsg;

   // If this is the second call (after authentication) we just need mapping
   if (p->Status() == XPD_NEED_MAP) {

      // Finalize the login, checking the if username is allowed to use the facility.
      // The username could have been set as part of the authentication process (for
      // example via a user mapping funtion or a grid-map file), so these checks have
      // to be done at this level.
      // (The XrdProofdClient instance is created in here, if everything else goes well)
      int rccc = 0;
      if ((rccc = CheckClient(p, 0, emsg)) != 0) {
         TRACEP(p, XERR, emsg);
         XErrorCode rcode = (rccc == -2) ? (XErrorCode) kXR_NotAuthorized
                                         : (XErrorCode) kXR_InvalidRequest;
         response->Send(rcode, emsg.c_str());
         response->Send(kXR_InvalidRequest, emsg.c_str());
         return 0;
      }

      // Acknowledge the client
      response->Send();
      p->SetStatus(XPD_LOGGEDIN);
      return MapClient(p, 0);
   }

   // Make sure the user is not already logged in
   if ((p->Status() & XPD_LOGGEDIN)) {
      response->Send(kXR_InvalidRequest, "duplicate login; already logged in");
      return 0;
   }

   // Find out the connection type: 'i', internal, means this is a proofsrv calling back.
   bool needauth = 0;
   bool ismaster = (fMgr->SrvType() == kXPD_TopMaster || fMgr->SrvType() == kXPD_Master) ? 1 : 0;
   switch (p->Request()->login.role[0]) {
   case 'A':
      p->SetConnType(kXPD_Admin);
      response->SetTag("adm");
      break;
   case 'i':
      p->SetConnType(kXPD_Internal);
      response->SetTag("int");
      break;
   case 'M':
      if (fMgr->SrvType() == kXPD_AnyServer || ismaster) {
         p->SetConnType(kXPD_ClientMaster);
         needauth = 1;
         response->SetTag("m2c");
      } else {
         TRACEP(p, XERR,"top master mode not allowed - ignoring request");
         response->Send(kXR_InvalidRequest,
                            "Server not allowed to be top master - ignoring request");
         return 0;
      }
      break;
   case 'm':
      if (fMgr->SrvType() == kXPD_AnyServer || ismaster) {
         p->SetConnType(kXPD_MasterMaster);
         needauth = 1;
         response->SetTag("m2m");
      } else {
         TRACEP(p, XERR,"submaster mode not allowed - ignoring request");
         response->Send(kXR_InvalidRequest,
                             "Server not allowed to be submaster - ignoring request");
         return 0;
      }
      break;
   case 's':
      if (fMgr->SrvType() == kXPD_AnyServer || fMgr->SrvType() == kXPD_MasterWorker) {
         p->SetConnType(kXPD_MasterWorker);
         needauth = 1;
         response->SetTag("w2m");
      } else {
         TRACEP(p, XERR,"worker mode not allowed - ignoring request");
         response->Send(kXR_InvalidRequest,
                        "Server not allowed to be worker - ignoring request");
         return 0;
      }
      break;
   default:
      TRACEP(p, XERR, "unknown mode: '" << p->Request()->login.role[0] <<"'");
      response->Send(kXR_InvalidRequest, "Server type: invalide mode");
      return rc;
   }
   response->SetTraceID();

   // Unmarshall the data: process ID
   pid = (int)ntohl(p->Request()->login.pid);
   p->SetPid(pid);

   // Username
   char un[9];
   for (i = 0; i < (int)sizeof(un)-1; i++) {
      if (p->Request()->login.username[i] == '\0' || p->Request()->login.username[i] == ' ')
         break;
      un[i] = p->Request()->login.username[i];
   }
   un[i] = '\0';
   uname = un;

   // Longer usernames are in the attached buffer
   if (uname == "?>buf") {
      // Attach to buffer
      char *buf = p->Argp()->buff;
      int   len = p->Request()->login.dlen;
      // Extract username
      uname.assign(buf,0,len-1);
      int iusr = uname.find("|usr:");
      if (iusr == -1) {
         TRACEP(p, XERR,"long user name not found");
         response->Send(kXR_InvalidRequest, "long user name not found");
         return 0;
      }
      uname.erase(0,iusr+5);
      uname.erase(uname.find("|"));
   }

   // Extract group name, if specified (syntax is uname[:gname])
   int ig = uname.find(":");
   if (ig != -1) {
      gname.assign(uname, ig+1);
      uname.erase(ig);
      TRACEP(p, DBG, "requested group: "<<gname);
      // Save the requested group info in the protocol instance
      p->SetGroupIn(gname.c_str());
   }

   // Save the incoming username setting in the protocol instance
   p->SetUserIn(uname.c_str());

   // Establish IDs for this link
   p->Link()->setID(uname.c_str(), pid);
   p->SetTraceID();
   response->SetTraceID();
   p->SetClntCapVer(p->Request()->login.capver[0]);

   // Get the security token for this link. We will either get a token, a null
   // string indicating host-only authentication, or a null indicating no
   // authentication. We can then optimize of each case.
   if (needauth && fCIA) {
      const char *pp = fCIA->getParms(i, p->Link()->Name());
      if (pp && i ) {
         response->SendI((kXR_int32)XPROOFD_VERSBIN, (void *)pp, i);
         p->SetStatus((XPD_NEED_MAP | XPD_NEED_AUTH));
         return 0;
      } else if (pp) {
         p->SetAuthEntity();
      }
   }
   // Check the client at this point; the XrdProofdClient instance is created
   // in here, if everything else goes well
   int rccc = 0;
   if ((rccc = CheckClient(p, p->UserIn(), emsg)) != 0) {
      TRACEP(p, XERR, emsg);
      XErrorCode rcode = (rccc == -2) ? (XErrorCode) kXR_NotAuthorized
                                       : (XErrorCode) kXR_InvalidRequest;
      response->Send(rcode, emsg.c_str());
      return 0;
   }
   rc = response->SendI((kXR_int32)XPROOFD_VERSBIN);
   p->SetStatus(XPD_LOGGEDIN);

   // Map the client
   return MapClient(p, 1);
}

//______________________________________________________________________________
int XrdProofdClientMgr::CheckClient(XrdProofdProtocol *p,
                                    const char *user, XrdOucString &emsg)
{
   // Perform checks on the client username. In case authentication is required
   // this is called afetr authentication.
   // Return 0 on success; on error, return -1 .
   XPDLOC(CMGR, "ClientMgr::CheckClient")
{'fraction_non_alphanumeric': 0.15275230736854764, 'fraction_numerical': 0.008387698042870456, 'mean_word_length': 2.1755608591885442, 'pattern_counts': {'":': 2, '<': 65, '<?xml version=': 0, '>': 225, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 64, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7044998', 'n_tokens_mistral': 10354, 'n_tokens_neox': 10059, 'n_words': 2527}