00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 const char *XrdSecsssKTCVSID = "$Id: XrdSecsssKT.cc 38011 2011-02-08 18:35:57Z ganis $";
00014
00015 #include <fcntl.h>
00016 #include <stdio.h>
00017 #include <stddef.h>
00018 #include <stdlib.h>
00019 #include <string.h>
00020 #include <sys/time.h>
00021 #include <sys/types.h>
00022 #include <sys/stat.h>
00023
00024 #include "XrdSecsss/XrdSecsssKT.hh"
00025
00026 #include "XrdOuc/XrdOucErrInfo.hh"
00027 #include "XrdOuc/XrdOucStream.hh"
00028 #include "XrdOuc/XrdOucUtils.hh"
00029 #include "XrdSys/XrdSysHeaders.hh"
00030
00031
00032
00033
00034
00035 int XrdSecsssKT::randFD = -1;
00036
00037
00038
00039
00040
00041 void *XrdSecsssKTRefresh(void *Data)
00042 {
00043 XrdSecsssKT *theKT = (XrdSecsssKT *)Data;
00044 struct timespec naptime = {theKT->RefrTime(), 0};
00045
00046
00047
00048 while(1) {nanosleep(&naptime, 0); theKT->Refresh();}
00049
00050 return (void *)0;
00051 }
00052
00053
00054
00055
00056
00057 XrdSecsssKT::XrdSecsssKT(XrdOucErrInfo *eInfo, const char *kPath,
00058 xMode oMode, int refrInt)
00059 {
00060 static const char *eText = "Unable to start keytab refresh thread";
00061 const char *devRand = "/dev/urandom";
00062 struct stat sbuf;
00063 pthread_t pid;
00064 int retc;
00065
00066
00067
00068 ktPath = (kPath ? strdup(kPath) : 0);
00069 ktList = 0; kthiID = 0; ktMode = oMode; ktRefT = (time_t)refrInt;
00070 if (eInfo) eInfo->setErrCode(0);
00071
00072
00073
00074 if (stat(devRand, &sbuf)) devRand = "/dev/random";
00075 if ((randFD = open(devRand, O_RDONLY)) < 0
00076 && oMode != isClient && errno != ENOENT)
00077 eMsg("sssKT",errno,"Unable to generate random key"," opening ",devRand);
00078
00079
00080
00081 if (!kPath)
00082 {if (oMode != isAdmin)
00083 {eMsg("sssKT", -1, "Keytable path not specified.");
00084 if (eInfo) eInfo->setErrInfo(EINVAL, "Keytable path missing.");
00085 return;
00086 }
00087 sbuf.st_mtime = 0; sbuf.st_mode = S_IRWXU;
00088 } else if (stat(kPath, &sbuf))
00089 {if (eInfo) eInfo->setErrInfo(errno, "Keytable not found");
00090 if (errno != ENOENT || oMode != isAdmin)
00091 eMsg("sssKT",errno,"Unable process keytable ",kPath);
00092 return;
00093 }
00094
00095
00096
00097 if ((ktList = getKeyTab(eInfo, sbuf.st_mtime, sbuf.st_mode))
00098 && (oMode != isAdmin) && (!eInfo || eInfo->getErrInfo() == 0))
00099 {if ((retc = XrdSysThread::Run(&pid,XrdSecsssKTRefresh,(void *)this)))
00100 {eMsg("sssKT", errno, eText); eInfo->setErrInfo(-1, eText);}
00101 }
00102 }
00103
00104
00105
00106
00107
00108 XrdSecsssKT::~XrdSecsssKT()
00109 {
00110 ktEnt *ktP;
00111
00112 myMutex.Lock();
00113 if (ktPath) {free(ktPath); ktPath = 0;}
00114
00115 while((ktP = ktList)) {ktList = ktList->Next; delete ktP;}
00116 myMutex.UnLock();
00117 }
00118
00119
00120
00121
00122
00123 void XrdSecsssKT::addKey(ktEnt &ktNew)
00124 {
00125 ktEnt *ktPP = 0, *ktP;
00126
00127
00128
00129 genKey(ktNew.Data.Val, ktNew.Data.Len);
00130 ktNew.Data.Crt = time(0);
00131 ktNew.Data.ID = static_cast<long long>(ktNew.Data.Crt & 0x7fffffff) << 32L
00132 | static_cast<long long>(++kthiID);
00133
00134
00135
00136 ktP = ktList;
00137 while(ktP && !isKey(*ktP, &ktNew, 0)) {ktPP = ktP; ktP = ktP->Next;}
00138
00139
00140
00141 if (ktPP) ktPP->Next = &ktNew;
00142 else ktList = &ktNew;
00143 ktNew.Next = ktP;
00144 }
00145
00146
00147
00148
00149
00150 int XrdSecsssKT::delKey(ktEnt &ktDel)
00151 {
00152 ktEnt *ktN, *ktPP = 0, *ktP = ktList;
00153 int nDel = 0;
00154
00155
00156
00157 while(ktP)
00158 {if (isKey(ktDel, ktP))
00159 {if (ktPP) ktPP->Next = ktP->Next;
00160 else ktList = ktP->Next;
00161 ktN = ktP; ktP = ktP->Next; delete ktN; nDel++;
00162 } else {ktPP = ktP; ktP = ktP->Next;}
00163 }
00164
00165 return nDel;
00166 }
00167
00168
00169
00170
00171
00172 int XrdSecsssKT::getKey(ktEnt &theEnt)
00173 {
00174 ktEnt *ktP, *ktN;
00175
00176
00177
00178 myMutex.Lock();
00179 ktP = ktList;
00180
00181
00182
00183 if (!*theEnt.Data.Name)
00184 {if (theEnt.Data.ID >= 0)
00185 while(ktP && ktP->Data.ID != theEnt.Data.ID) ktP = ktP->Next;
00186 }
00187 else {while(ktP && strcmp(ktP->Data.Name,theEnt.Data.Name)) ktP=ktP->Next;
00188 while(ktP && ktP->Data.Exp <= time(0))
00189 {if (!(ktN=ktP->Next)
00190 || strcmp(ktN->Data.Name,theEnt.Data.Name)) break;
00191 ktP = ktN;
00192 }
00193 }
00194
00195
00196
00197 if (ktP) theEnt = *ktP;
00198 myMutex.UnLock();
00199
00200
00201
00202 if (!ktP) return ENOENT;
00203 return (theEnt.Data.Exp && theEnt.Data.Exp <= time(0) ? -1 : 0);
00204 }
00205
00206
00207
00208
00209
00210 char *XrdSecsssKT::genFN()
00211 {
00212 static char fnbuff[1040];
00213 const char *pfx;
00214
00215
00216
00217 if (!(pfx = getenv("HOME")) || !*pfx) pfx = "";
00218
00219
00220
00221 snprintf(fnbuff, sizeof(fnbuff), "%s/.xrd/sss.keytab", pfx);
00222 return fnbuff;
00223 }
00224
00225
00226
00227
00228
00229 void XrdSecsssKT::genKey(char *kBP, int kLen)
00230 {
00231 struct timeval tval;
00232 int kTemp;
00233
00234
00235
00236
00237 if (randFD >= 0)
00238 {char *buffP = kBP;
00239 int i, Got, Want = kLen, zcnt = 0, maxZ = kLen*25/100;
00240 while(Want)
00241 do { {do {Got = read(randFD, buffP, Want);}
00242 while(Got < 0 && errno == EINTR);
00243 if (Got > 0) {buffP += Got; Want -= Got;}
00244 }
00245 } while(Got > 0 && Want);
00246 if (!Want)
00247 {for (i = 0; i < kLen; i++) if (!kBP[i]) zcnt++;
00248 if (zcnt <= maxZ) return;
00249 }
00250 }
00251
00252
00253
00254 gettimeofday(&tval, 0);
00255 if (tval.tv_usec == 0) tval.tv_usec = tval.tv_sec;
00256 tval.tv_usec = tval.tv_usec ^ getpid();
00257 srand48(static_cast<long>(tval.tv_usec));
00258
00259
00260
00261 while(kLen > 0)
00262 {kTemp = mrand48();
00263 memcpy(kBP, &kTemp, (4 > kLen ? kLen : 4));
00264 kBP += 4; kLen -= 4;
00265 }
00266 }
00267
00268
00269
00270
00271
00272 void XrdSecsssKT::Refresh()
00273 {
00274 XrdOucErrInfo eInfo;
00275 ktEnt *ktNew, *ktOld, *ktNext;
00276 struct stat sbuf;
00277 int retc = 0;
00278
00279
00280
00281 if (stat(ktPath, &sbuf) == 0)
00282 {if (sbuf.st_mtime == ktMtime) return;
00283 if ((ktNew = getKeyTab(&eInfo, sbuf.st_mtime, sbuf.st_mode))
00284 && eInfo.getErrInfo() == 0)
00285 {myMutex.Lock(); ktOld = ktList; ktList = ktNew; myMutex.UnLock();
00286 } else ktOld = ktNew;
00287 while(ktOld) {ktNext = ktOld->Next; delete ktOld; ktOld = ktNext;}
00288 if ((retc == eInfo.getErrInfo()) == 0) return;
00289 } else retc = errno;
00290
00291
00292
00293 eMsg("Refresh",retc,"Unable to refresh keytable",ktPath);
00294 }
00295
00296
00297
00298
00299
00300 int XrdSecsssKT::Rewrite(int Keep, int &numKeys, int &numTot, int &numExp)
00301 {
00302 char tmpFN[1024], buff[2048], kbuff[4096], *Slash;
00303 int ktFD, numID = 0, n, retc = 0;
00304 ktEnt ktCurr, *ktP, *ktN;
00305 mode_t theMode = fileMode(ktPath);
00306
00307
00308
00309 strcpy(tmpFN, ktPath);
00310 if ((Slash = rindex(tmpFN, '/'))) *Slash = '\0';
00311 retc = XrdOucUtils::makePath(tmpFN,S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
00312 if (retc) return (retc < 0 ? -retc : retc);
00313 if (Slash) *Slash = '/';
00314
00315
00316
00317 sprintf(buff, ".%d", static_cast<int>(getpid()));
00318 strcat(tmpFN, buff);
00319
00320
00321
00322 if ((ktFD = open(tmpFN, O_WRONLY|O_CREAT|O_TRUNC, theMode)) < 0)
00323 return errno;
00324
00325
00326
00327 ktCurr.Data.Name[0] = ktCurr.Data.User[0] = ktCurr.Data.Grup[0] = 3;
00328 ktN = ktList; numKeys = numTot = numExp = 0;
00329 while((ktP = ktN))
00330 {ktN = ktN->Next; numTot++;
00331 if (ktP->Data.Name[0] == '\0') continue;
00332 if (ktP->Data.Exp && ktP->Data.Exp <= time(0)) {numExp++; continue;}
00333 if (!isKey(ktCurr, ktP, 0)) {ktCurr.NUG(ktP); numID = 0;}
00334 else if (Keep && numID >= Keep) continue;
00335 n = sprintf(buff, "%s0 u:%s g:%s n:%s N:%lld c:%ld e:%ld k:",
00336 (numKeys ? "\n" : ""),
00337 ktP->Data.User,ktP->Data.Grup,ktP->Data.Name,ktP->Data.ID,
00338 ktP->Data.Crt, ktP->Data.Exp);
00339 numID++; numKeys++; keyB2X(ktP, kbuff);
00340 if (write(ktFD, buff, n) < 0
00341 || write(ktFD, kbuff, ktP->Data.Len*2) < 0) break;
00342 }
00343
00344
00345
00346 if (ktP) retc = errno;
00347 else if (!numKeys) retc = ENODATA;
00348
00349
00350
00351 close(ktFD);
00352 if (!retc && rename(tmpFN, ktPath) < 0) retc = errno;
00353
00354
00355
00356 unlink(tmpFN);
00357 return retc;
00358 }
00359
00360
00361
00362
00363
00364
00365
00366
00367 int XrdSecsssKT::eMsg(const char *epname, int rc,
00368 const char *txt1, const char *txt2,
00369 const char *txt3, const char *txt4)
00370 {
00371 cerr <<"Secsss (" << epname <<"): ";
00372 cerr <<txt1;
00373 if (txt2) cerr <<txt2;
00374 if (txt3) cerr <<txt3;
00375 if (txt4) cerr <<txt4;
00376 if (rc>0) cerr <<"; " <<strerror(rc);
00377 cerr <<endl;
00378
00379 return (rc ? (rc < 0 ? rc : -rc) : -1);
00380 }
00381
00382
00383
00384
00385
00386 XrdSecsssKT::ktEnt* XrdSecsssKT::getKeyTab(XrdOucErrInfo *eInfo,
00387 time_t Mtime, mode_t Amode)
00388 {
00389 static const int altMode = S_IRWXG | S_IRWXO;
00390 XrdOucStream myKT;
00391 int ktFD, retc, tmpID, recno = 0, NoGo = 0;
00392 const char *What = 0, *ktFN;
00393 char *lp, *tp, rbuff[64];
00394 ktEnt *ktP, *ktPP, *ktNew, *ktBase = 0;
00395
00396
00397
00398 ktMtime = Mtime;
00399 if ((Amode & altMode) & ~fileMode(ktPath))
00400 {if (eInfo) eInfo->setErrInfo(EACCES, "Keytab file is not secure!");
00401 eMsg("getKeyTab",-1,"Unable to process ",ktPath,"; file is not secure!");
00402 return 0;
00403 }
00404
00405
00406
00407 if (ktPath)
00408 {if ((ktFD = open(ktPath, O_RDONLY)) < 0)
00409 {if (eInfo) eInfo->setErrInfo(errno, "Unable to open keytab file.");
00410 eMsg("getKeyTab", errno, "Unable to open ", ktPath);
00411 return 0;
00412 } else ktFN = ktPath;
00413 } else {ktFD = dup(STDIN_FILENO); ktFN = "stdin";}
00414
00415
00416
00417 myKT.Attach(ktFD);
00418
00419
00420
00421
00422
00423 do{while((lp = myKT.GetLine()))
00424 {recno++; What = 0;
00425 if (!*lp) continue;
00426 if (!(tp = myKT.GetToken()) || strcmp("0", tp))
00427 {What = "keytable format missing or unsupported"; break;}
00428 if (!(ktNew = ktDecode0(myKT, eInfo)))
00429 {What = (eInfo ? eInfo->getErrText(): "invalid data"); break;}
00430 if (ktMode!=isAdmin && ktNew->Data.Exp && ktNew->Data.Exp <= time(0))
00431 {delete ktNew; continue;}
00432 tmpID = static_cast<int>(ktNew->Data.ID & 0x7fffffff);
00433 if (tmpID > kthiID) kthiID = tmpID;
00434
00435 ktP = ktBase; ktPP = 0;
00436 while(ktP && !isKey(*ktP, ktNew, 0)) {ktPP=ktP; ktP=ktP->Next;}
00437 if (!ktP) {ktNew->Next = ktBase; ktBase = ktNew;}
00438 else {if (ktMode == isClient)
00439 {if ((ktNew->Data.Exp == 0 && ktP->Data.Exp != 0)
00440 || (ktP->Data.Exp!=0 && ktP->Data.Exp < ktNew->Data.Exp))
00441 ktP->Set(*ktNew);
00442 delete ktNew;
00443 } else {
00444 while(ktNew->Data.Crt < ktP->Data.Crt)
00445 {ktPP = ktP; ktP = ktP->Next;
00446 if (!ktP || !isKey(*ktP, ktNew, 0)) break;
00447 }
00448 if (ktPP) {ktPP->Next = ktNew; ktNew->Next = ktP;}
00449 else {ktNew->Next= ktBase; ktBase = ktNew;}
00450 }
00451 }
00452 }
00453 if (What)
00454 {sprintf(rbuff, "; line %d in ", recno);
00455 NoGo = eMsg("getKeyTab", -1, What, rbuff, ktFN);
00456 }
00457 } while(lp);
00458
00459
00460
00461 if (NoGo) {if (eInfo) eInfo->setErrInfo(EINVAL,"Invalid keytab file.");}
00462 else if ((retc = myKT.LastError()))
00463 {if (eInfo) eInfo->setErrInfo(retc,"Unable to read keytab file.");
00464 NoGo = eMsg("getKeyTab", retc, "Unable to read keytab ",ktFN);
00465 }
00466 else if (!ktBase)
00467 {if (eInfo) eInfo->setErrInfo(ESRCH,"Keytable is empty.");
00468 NoGo = eMsg("getKeyTab",-1,"No keys found in ",ktFN);
00469 }
00470
00471
00472
00473
00474 if (!NoGo) eInfo->setErrCode(0);
00475
00476
00477
00478 myKT.Close();
00479 return ktBase;
00480 }
00481
00482
00483
00484
00485
00486 mode_t XrdSecsssKT::fileMode(const char *Path)
00487 {
00488 int n;
00489
00490 return (!Path || (n = strlen(Path)) < 5 || strcmp(".grp", &Path[n-4])
00491 ? S_IRUSR|S_IWUSR : S_IRUSR|S_IWUSR|S_IRGRP);
00492 }
00493
00494
00495
00496
00497
00498 int XrdSecsssKT::isKey(ktEnt &ktRef, ktEnt *ktP, int Full)
00499 {
00500 if (*ktRef.Data.Name && strcmp(ktP->Data.Name, ktRef.Data.Name)) return 0;
00501 if (*ktRef.Data.User && strcmp(ktP->Data.User, ktRef.Data.User)) return 0;
00502 if (*ktRef.Data.Grup && strcmp(ktP->Data.Grup, ktRef.Data.Grup)) return 0;
00503 if (Full && ktRef.Data.ID > 0
00504 && (ktP->Data.ID & 0x7fffffff) != ktRef.Data.ID) return 0;
00505 return 1;
00506 }
00507
00508
00509
00510
00511
00512 void XrdSecsssKT::keyB2X(ktEnt *theKT, char *buff)
00513 {
00514 static const char xTab[] = "0123456789abcdef";
00515 int kLen = theKT->Data.Len;
00516 char *kP = theKT->Data.Val, Val;
00517
00518
00519
00520 while(kLen--)
00521 {Val = *kP++;
00522 *buff++ = xTab[(Val>>4) & 0x0f];
00523 *buff++ = xTab[ Val & 0x0f];
00524 }
00525 *buff = '\0';
00526 }
00527
00528
00529
00530
00531
00532 void XrdSecsssKT::keyX2B(ktEnt *theKT, char *xKey)
00533 {
00534
00535 static const char xtab[] = {10, 10, 11, 12, 13, 14, 15, 15};
00536 int n = strlen(xKey);
00537 char *kp, kByte;
00538
00539
00540
00541 n = (n%2 ? (n+1)/2 : n/2);
00542 if (n > ktEnt::maxKLen) n = ktEnt::maxKLen;
00543 kp = theKT->Data.Val;
00544 theKT->Data.Val[n-1] = 0;
00545
00546
00547
00548 while(*xKey)
00549 {if (*xKey <= '9') kByte = (*xKey & 0x0f) << 4;
00550 else kByte = xtab[*xKey & 0x07] << 4;
00551 xKey++;
00552 if (*xKey <= '9') kByte |= (*xKey & 0x0f);
00553 else kByte |= xtab[*xKey & 0x07];
00554 *kp++ = kByte; xKey++;
00555 }
00556
00557
00558
00559 theKT->Data.Len = n;
00560 }
00561
00562
00563
00564
00565
00566 XrdSecsssKT::ktEnt *XrdSecsssKT::ktDecode0(XrdOucStream &kTab,
00567 XrdOucErrInfo *eInfo)
00568 {
00569 static const short haveCRT = 0x0001;
00570 static const short haveEXP = 0x0002;
00571 static const short isTIMET = 0x0003;
00572 static const short haveGRP = 0x0004;
00573 static const short haveKEY = 0x0008;
00574 static const short haveNAM = 0x0010;
00575 static const short haveNUM = 0x0020;
00576 static const short haveUSR = 0x0040;
00577
00578 static struct
00579 {const char *Name; size_t Offset; int Ctl; short What; char Tag;}
00580 ktDesc[] = {
00581 {"crtdt", offsetof(ktEnt::ktData,Crt), 0, haveCRT, 'c'},
00582 {"expdt", offsetof(ktEnt::ktData,Exp), 0, haveEXP, 'e'},
00583 {"group", offsetof(ktEnt::ktData,Grup), ktEnt::GrupSZ, haveGRP, 'g'},
00584 {"keyval", offsetof(ktEnt::ktData,Val), ktEnt::maxKLen*2, haveKEY, 'k'},
00585 {"keyname", offsetof(ktEnt::ktData,Name), ktEnt::NameSZ, haveNAM, 'n'},
00586 {"keynum", offsetof(ktEnt::ktData,ID), 0, haveNUM, 'N'},
00587 {"user", offsetof(ktEnt::ktData,User), ktEnt::UserSZ, haveUSR, 'u'}
00588 };
00589 static const int ktDnum = sizeof(ktDesc)/sizeof(ktDesc[0]);
00590
00591 ktEnt *ktNew = new ktEnt;
00592 const char *Prob = 0, *What = "Whatever";
00593 char Tag, *Dest, *ep, *tp;
00594 long long nVal;
00595 short Have = 0;
00596 int i = 0;
00597
00598
00599
00600 while((tp = kTab.GetToken()) && !Prob)
00601 {Tag = *tp++;
00602 if (*tp++ == ':')
00603 for (i = 0; i < ktDnum; i++)
00604 if (ktDesc[i].Tag == Tag)
00605 {Dest = (char *)&(ktNew->Data) + ktDesc[i].Offset;
00606 Have |= ktDesc[i].What; What = ktDesc[i].Name;
00607 if (ktDesc[i].Ctl)
00608 {if ((int)strlen(tp) >= ktDesc[i].Ctl) Prob=" is too long";
00609 else if (Tag == 'k') keyX2B(ktNew, tp);
00610 else strcpy(Dest, tp);
00611 } else {
00612 nVal = strtoll(tp, &ep, 10);
00613 if (ep && *ep) Prob = " has invalid value";
00614 else if (ktDesc[i].What & isTIMET)
00615 *(time_t *)Dest = static_cast<time_t>(nVal);
00616 else *(long long *)Dest = nVal;
00617 }
00618 }
00619 }
00620
00621
00622
00623 if (!Prob)
00624 {if (!(Have & haveGRP)) strcpy(ktNew->Data.Grup, "nogroup");
00625 if (!(Have & haveNAM)) strcpy(ktNew->Data.Name, "nowhere");
00626 if (!(Have & haveUSR)) strcpy(ktNew->Data.User, "nobody");
00627 if (!(Have & haveKEY)) {What = "keyval"; Prob = " not found";}
00628 else if (!(Have & haveNUM)) {What = "keynum"; Prob = " not found";}
00629 }
00630
00631
00632
00633 if (Prob)
00634 {const char *eVec[] = {What, Prob};
00635 if (eInfo) eInfo->setErrInfo(-1, eVec, 2);
00636 delete ktNew;
00637 return 0;
00638 }
00639
00640
00641
00642 if (!strcmp(ktNew->Data.Grup, "anygroup"))
00643 ktNew->Data.Opts|=ktEnt::anyGRP;
00644 else if (!strcmp(ktNew->Data.Grup, "usrgroup"))
00645 ktNew->Data.Opts|=ktEnt::usrGRP;
00646 if (!strcmp(ktNew->Data.User, "anybody"))
00647 ktNew->Data.Opts|=ktEnt::anyUSR;
00648
00649
00650
00651 return ktNew;
00652 }