38 #include <sys/types.h>
49 #define ENODATA ENOATTR
56 int XrdSecsssKT::randFD = -1;
65 struct timespec naptime = {theKT->
RefrTime(), 0};
69 while(1) {nanosleep(&naptime, 0); theKT->
Refresh();}
79 xMode oMode,
int refrInt)
81 static const char *eText =
"Unable to start keytab refresh thread";
82 const char *devRand =
"/dev/urandom";
89 ktPath = (kPath ? strdup(kPath) : 0);
90 ktList = 0; kthiID = 0; ktMode = oMode; ktRefT = (time_t)refrInt;
95 if (
stat(devRand, &sbuf)) devRand =
"/dev/random";
96 if ((randFD =
open(devRand, O_RDONLY)) < 0
97 && oMode !=
isClient && errno != ENOENT)
98 eMsg(
"sssKT",errno,
"Unable to generate random key",
" opening ",devRand);
104 {
eMsg(
"sssKT", -1,
"Keytable path not specified.");
105 if (eInfo) eInfo->
setErrInfo(EINVAL,
"Keytable path missing.");
108 sbuf.st_mtime = 0; sbuf.st_mode = S_IRWXU;
109 }
else if (
stat(kPath, &sbuf))
110 {
if (eInfo) eInfo->
setErrInfo(errno,
"Keytable not found");
111 if (errno != ENOENT || oMode !=
isAdmin)
112 eMsg(
"sssKT",errno,
"Unable process keytable ",kPath);
118 if ((ktList = getKeyTab(eInfo, sbuf.st_mtime, sbuf.st_mode))
147 if (ktPath) {free(ktPath); ktPath = 0;}
149 while((ktP = ktList)) {ktList = ktList->
Next;
delete ktP;}
160 ktEnt *ktPP = 0, *ktP;
166 ktNew.
Data.
ID =
static_cast<long long>(ktNew.
Data.
Crt & 0x7fffffff) << 32L
167 |
static_cast<long long>(++kthiID);
172 while(ktP && !isKey(*ktP, &ktNew, 0)) {ktPP = ktP; ktP = ktP->
Next;}
176 if (ktPP) ktPP->
Next = &ktNew;
177 else ktList = &ktNew;
187 ktEnt *ktN, *ktPP = 0, *ktP = ktList;
193 {
if (isKey(ktDel, ktP))
195 else ktList = ktP->
Next;
196 ktN = ktP; ktP = ktP->
Next;
delete ktN; nDel++;
197 }
else {ktPP = ktP; ktP = ktP->
Next;}
227 while(ktP && ktP->
Data.
Exp <= time(0))
228 {
if (!(ktN=ktP->
Next)
236 if (ktP) theEnt = *ktP;
241 if (!ktP)
return ENOENT;
251 static char fnbuff[1040];
256 if (!(pfx = getenv(
"HOME")) || !*pfx) pfx =
"";
260 snprintf(fnbuff,
sizeof(fnbuff),
"%s/.xrd/sss.keytab", pfx);
278 int i, Got, Want = kLen, zcnt = 0, maxZ = kLen*25/100;
280 do { {
do {Got =
read(randFD, buffP, Want);}
281 while(Got < 0 && errno == EINTR);
282 if (Got > 0) {buffP += Got; Want -= Got;}
284 }
while(Got > 0 && Want);
286 {
for (i = 0; i < kLen; i++)
if (!kBP[i]) zcnt++;
287 if (zcnt <= maxZ)
return;
293 gettimeofday(&tval, 0);
294 if (tval.tv_usec == 0) tval.tv_usec = tval.tv_sec;
295 tval.tv_usec = tval.tv_usec ^ getpid();
296 srand48(
static_cast<long>(tval.tv_usec));
302 memcpy(kBP, &kTemp, (4 > kLen ? kLen : 4));
314 ktEnt *ktNew, *ktOld, *ktNext;
320 if (
stat(ktPath, &sbuf) == 0)
321 {
if (sbuf.st_mtime == ktMtime)
return;
322 if ((ktNew = getKeyTab(&eInfo, sbuf.st_mtime, sbuf.st_mode))
324 {myMutex.
Lock(); ktOld = ktList; ktList = ktNew; myMutex.
UnLock();
325 }
else ktOld = ktNew;
326 while(ktOld) {ktNext = ktOld->
Next;
delete ktOld; ktOld = ktNext;}
327 if ((retc == eInfo.
getErrInfo()) == 0)
return;
332 eMsg(
"Refresh",retc,
"Unable to refresh keytable",ktPath);
341 char tmpFN[2048], buff[2048], kbuff[4096], *Slash;
342 int ktFD, numID = 0, n, retc = 0;
343 ktEnt ktCurr, *ktP, *ktN;
344 mode_t theMode = fileMode(ktPath);
348 strcpy(tmpFN, ktPath);
349 if ((Slash = rindex(tmpFN,
'/'))) *Slash =
'\0';
351 if (retc)
return (retc < 0 ? -retc : retc);
352 if (Slash) *Slash =
'/';
356 sprintf(buff,
".%d",
static_cast<int>(getpid()));
361 if ((ktFD =
open(tmpFN, O_WRONLY|O_CREAT|O_TRUNC, theMode)) < 0)
367 ktN = ktList; numKeys = numTot = numExp = 0;
369 {ktN = ktN->
Next; numTot++;
370 if (ktP->
Data.
Name[0] ==
'\0')
continue;
371 if (ktP->
Data.
Exp && ktP->
Data.
Exp <= time(0)) {numExp++;
continue;}
372 if (!isKey(ktCurr, ktP, 0)) {ktCurr.
NUG(ktP); numID = 0;}
373 else if (Keep && numID >= Keep)
continue;
374 n = sprintf(buff,
"%s0 u:%s g:%s n:%s N:%lld c:%lld e:%lld f:%lld k:",
375 (numKeys ?
"\n" :
""),
379 numID++; numKeys++; keyB2X(ktP, kbuff);
380 if (
write(ktFD, buff, n) < 0
386 if (ktP) retc = errno;
387 else if (!numKeys) retc =
ENODATA;
392 if (!retc &&
rename(tmpFN, ktPath) < 0) retc = errno;
407 int XrdSecsssKT::eMsg(
const char *epname,
int rc,
408 const char *txt1,
const char *txt2,
409 const char *txt3,
const char *txt4)
411 std::cerr <<
"Secsss (" << epname <<
"): ";
413 if (txt2) std::cerr <<txt2;
414 if (txt3) std::cerr <<txt3;
415 if (txt4) std::cerr <<txt4;
416 {
if (rc>0) {std::cerr <<
"; " <<
XrdSysE2T(rc);}}
417 std::cerr <<
"\n" <<std::endl;
419 return (rc ? (rc < 0 ? rc : -rc) : -1);
427 time_t Mtime, mode_t Amode)
429 static const int altMode = S_IRWXG | S_IRWXO;
431 int ktFD, retc, tmpID, recno = 0, NoGo = 0;
432 const char *What = 0, *ktFN;
433 char *lp, *tp, rbuff[64];
434 ktEnt *ktP, *ktPP, *ktNew, *ktBase = 0;
439 if ((Amode & altMode) & ~fileMode(ktPath))
440 {
if (eInfo) eInfo->
setErrInfo(EACCES,
"Keytab file is not secure!");
441 eMsg(
"getKeyTab",-1,
"Unable to process ",ktPath,
"; file is not secure!");
448 {
if ((ktFD =
open(ktPath, O_RDONLY)) < 0)
449 {
if (eInfo) eInfo->
setErrInfo(errno,
"Unable to open keytab file.");
450 eMsg(
"getKeyTab", errno,
"Unable to open ", ktPath);
452 }
else ktFN = ktPath;
453 }
else {ktFD = dup(STDIN_FILENO); ktFN =
"stdin";}
463 do{
while((lp = myKT.
GetLine()))
466 if (!(tp = myKT.
GetToken()) || (strcmp(
"0", tp) && strcmp(
"1", tp)))
467 {What =
"keytable format missing or unsupported";
break;}
468 if (!(ktNew = ktDecode0(myKT, eInfo)))
469 {What = (eInfo ? eInfo->
getErrText():
"invalid data");
break;}
470 if (ktMode!=
isAdmin && ktNew->Data.Exp && ktNew->Data.Exp <= time(0))
471 {
delete ktNew;
continue;}
472 tmpID =
static_cast<int>(ktNew->Data.ID & 0x7fffffff);
473 if (tmpID > kthiID) kthiID = tmpID;
475 ktP = ktBase; ktPP = 0;
476 while(ktP && !isKey(*ktP, ktNew, 0)) {ktPP=ktP; ktP=ktP->Next;}
477 if (!ktP) {ktNew->Next = ktBase; ktBase = ktNew;}
479 {
if ((ktNew->Data.Exp == 0 && ktP->Data.Exp != 0)
480 || (ktP->Data.Exp!=0 && ktP->Data.Exp < ktNew->Data.Exp))
484 while(ktNew->Data.Crt < ktP->Data.Crt)
485 {ktPP = ktP; ktP = ktP->Next;
486 if (!ktP || !isKey(*ktP, ktNew, 0))
break;
488 if (ktPP) {ktPP->Next = ktNew; ktNew->Next = ktP;}
489 else {ktNew->Next= ktBase; ktBase = ktNew;}
494 {sprintf(rbuff,
"; line %d in ", recno);
495 NoGo =
eMsg(
"getKeyTab", -1, What, rbuff, ktFN);
501 if (NoGo) {
if (eInfo) eInfo->
setErrInfo(EINVAL,
"Invalid keytab file.");}
503 {
if (eInfo) eInfo->
setErrInfo(retc,
"Unable to read keytab file.");
504 NoGo =
eMsg(
"getKeyTab", retc,
"Unable to read keytab ",ktFN);
507 {
if (eInfo) eInfo->
setErrInfo(ESRCH,
"Keytable is empty.");
508 NoGo =
eMsg(
"getKeyTab",-1,
"No keys found in ",ktFN);
526 mode_t XrdSecsssKT::fileMode(
const char *
Path)
530 return (!
Path || (n = strlen(
Path)) < 5 || strcmp(
".grp", &
Path[n-4])
531 ? S_IRUSR|S_IWUSR : S_IRUSR|S_IWUSR|S_IRGRP);
538 int XrdSecsssKT::isKey(ktEnt &ktRef, ktEnt *ktP,
int Full)
540 if (*ktRef.Data.Name && strcmp(ktP->Data.Name, ktRef.Data.Name))
return 0;
541 if (*ktRef.Data.User && strcmp(ktP->Data.User, ktRef.Data.User))
return 0;
542 if (*ktRef.Data.Grup && strcmp(ktP->Data.Grup, ktRef.Data.Grup))
return 0;
543 if (Full && ktRef.Data.ID > 0
544 && (ktP->Data.ID & 0x7fffffff) != ktRef.Data.ID)
return 0;
552 void XrdSecsssKT::keyB2X(ktEnt *theKT,
char *buff)
554 static const char xTab[] =
"0123456789abcdef";
555 int kLen = theKT->Data.Len;
556 char *kP = theKT->Data.Val, Val;
562 *buff++ = xTab[(Val>>4) & 0x0f];
563 *buff++ = xTab[ Val & 0x0f];
572 void XrdSecsssKT::keyX2B(ktEnt *theKT,
char *xKey)
575 static const char xtab[] = {10, 10, 11, 12, 13, 14, 15, 15};
576 int n = strlen(xKey);
581 n = (n%2 ? (n+1)/2 : n/2);
583 kp = theKT->Data.Val;
584 theKT->Data.Val[n-1] = 0;
589 {
if (*xKey <=
'9') kByte = (*xKey & 0x0f) << 4;
590 else kByte = xtab[*xKey & 0x07] << 4;
592 if (*xKey <=
'9') kByte |= (*xKey & 0x0f);
593 else kByte |= xtab[*xKey & 0x07];
594 *kp++ = kByte; xKey++;
609 static const short haveCRT = 0x0001;
610 static const short haveEXP = 0x0002;
611 static const short isTIMET = 0x0003;
612 static const short haveGRP = 0x0004;
613 static const short haveKEY = 0x0008;
614 static const short haveNAM = 0x0010;
615 static const short haveNUM = 0x0020;
616 static const short haveUSR = 0x0040;
617 static const short haveFLG = 0x0080;
620 {
const char *Name;
size_t Offset;
int Ctl;
short What;
char Tag;}
622 {
"crtdt", offsetof(ktEnt::ktData,Crt), 0, haveCRT,
'c'},
623 {
"expdt", offsetof(ktEnt::ktData,Exp), 0, haveEXP,
'e'},
624 {
"flags", offsetof(ktEnt::ktData,Flags),0, haveFLG,
'f'},
625 {
"group", offsetof(ktEnt::ktData,Grup),
ktEnt::GrupSZ, haveGRP,
'g'},
626 {
"keyval", offsetof(ktEnt::ktData,Val),
ktEnt::maxKLen*2, haveKEY,
'k'},
627 {
"keyname", offsetof(ktEnt::ktData,Name),
ktEnt::NameSZ, haveNAM,
'n'},
628 {
"keynum", offsetof(ktEnt::ktData,
ID), 0, haveNUM,
'N'},
629 {
"user", offsetof(ktEnt::ktData,User),
ktEnt::UserSZ, haveUSR,
'u'}
631 static const int ktDnum =
sizeof(ktDesc)/
sizeof(ktDesc[0]);
633 ktEnt *ktNew =
new ktEnt;
634 const char *Prob = 0, *What =
"Whatever";
635 char Tag, *Dest, *ep, *tp;
642 while((tp = kTab.
GetToken()) && !Prob)
645 for (i = 0; i < ktDnum; i++)
646 if (ktDesc[i].Tag == Tag)
647 {Dest = (
char *)&(ktNew->Data) + ktDesc[i].Offset;
648 Have |= ktDesc[i].What; What = ktDesc[i].Name;
650 {
if ((
int)strlen(tp) > ktDesc[i].Ctl) Prob=
" is too long";
651 else if (Tag ==
'k') keyX2B(ktNew, tp);
652 else strcpy(Dest, tp);
654 nVal = strtoll(tp, &ep, 10);
655 if (ep && *ep) Prob =
" has invalid value";
656 else if (ktDesc[i].What & isTIMET)
657 *(time_t *)Dest =
static_cast<time_t
>(nVal);
658 else *(
long long *)Dest = nVal;
666 {
if (!(Have & haveGRP)) strcpy(ktNew->Data.Grup,
"nogroup");
667 if (!(Have & haveNAM)) strcpy(ktNew->Data.Name,
"nowhere");
668 else {
int n = strlen(ktNew->Data.Name);
669 if (ktNew->Data.Name[n-1] ==
'+')
672 if (!(Have & haveUSR)) strcpy(ktNew->Data.User,
"nobody");
673 if (!(Have & haveKEY)) {What =
"keyval"; Prob =
" not found";}
674 else if (!(Have & haveNUM)) {What =
"keynum"; Prob =
" not found";}
680 {
const char *eVec[] = {What, Prob};
688 if (!strcmp(ktNew->Data.Grup,
"anygroup"))
690 else if (!strcmp(ktNew->Data.Grup,
"usrgroup"))
692 if (!strcmp(ktNew->Data.User,
"anybody"))
694 else if (!strcmp(ktNew->Data.User,
"allusers"))
int stat(const char *path, struct stat *buf)
int open(const char *path, int oflag,...)
int unlink(const char *path)
int rename(const char *oldpath, const char *newpath)
ssize_t write(int fildes, const void *buf, size_t nbyte)
ssize_t read(int fildes, void *buf, size_t nbyte)
void * XrdSecsssKTRefresh(void *Data)
const char * XrdSysE2T(int errcode)
#define XRDSYSTHREAD_HOLD
const char * getErrText()
int setErrInfo(int code, const char *emsg)
int Attach(int FileDescriptor, int bsz=2047)
char * GetToken(int lowcase=0)
static int makePath(char *path, mode_t mode, bool reset=false)
struct XrdSecsssKT::ktEnt::ktData Data
int Rewrite(int Keep, int &numKeys, int &numTot, int &numExp)
int getKey(ktEnt &ktEql, bool andKeyID=false)
static void genKey(char *Buff, int blen)
void addKey(ktEnt &ktNew)
XrdSecsssKT(XrdOucErrInfo *, const char *, xMode, int refr=60 *60)
static int Join(pthread_t tid, void **ret)
static int Run(pthread_t *, void *(*proc)(void *), void *arg, int opts=0, const char *desc=0)
static int Kill(pthread_t tid)