// @(#)root/rpdutils:$Id$ // Author: Gerardo Ganis 7/4/2003 /************************************************************************* * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ ////////////////////////////////////////////////////////////////////////// // // // rpdutils // // // // Set of utilities for rootd/proofd daemon authentication. // // // ////////////////////////////////////////////////////////////////////////// #include "RConfigure.h" #include "RConfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snprintf.h" #if defined(__CYGWIN__) && defined(__GNUC__) # define cygwingcc #endif #if defined(linux) || defined(__sun) || defined(__sgi) || \ defined(_AIX) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ defined(__APPLE__) || defined(__MACH__) || defined(cygwingcc) #include #include #include #endif #ifdef _AIX extern "C" int ruserok(char *, int, char *, char *); #endif #if defined(__alpha) && !defined(linux) # ifdef _XOPEN_SOURCE # if _XOPEN_SOURCE+0 > 0 # define R__TRUE64 # endif # endif #include #ifndef R__TRUE64 extern "C" int fstatfs(int file_descriptor, struct statfs *buffer); extern "C" int ruserok(const char *, int, const char *, const char *); #endif #elif defined(__APPLE__) #include extern "C" int fstatfs(int file_descriptor, struct statfs *buffer); #elif defined(linux) || defined(__hpux) #include #elif defined(__FreeBSD__) || defined(__OpenBSD__) #include #include #else #include #endif #if defined(linux) # include # if __GNU_LIBRARY__ == 6 # ifndef R__GLIBC # define R__GLIBC # endif # endif #endif #if defined(cygwingcc) || (defined(__MACH__) && !defined(__APPLE__)) # define R__GLIBC #endif #if (defined(__FreeBSD__) && (__FreeBSD__ < 4)) || defined(__OpenBSD__) || \ (defined(__APPLE__) && (!defined(MAC_OS_X_VERSION_10_3) || \ (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_3))) #include #define lockf(fd, op, sz) flock((fd), (op)) #ifndef F_LOCK #define F_LOCK (LOCK_EX | LOCK_NB) #endif #ifndef F_ULOCK #define F_ULOCK LOCK_UN #endif #endif #if defined(cygwingcc) #define F_LOCK F_WRLCK #define F_ULOCK F_UNLCK int ruserok(const char *, int, const char *, const char *) { return 0; } static int fcntl_lockf(int fd, int op, off_t off) { flock fl; fl.l_whence = SEEK_SET; fl.l_start = off; fl.l_len = 0; // whole file fl.l_pid = getpid(); fl.l_type = op; return fcntl(fd, F_SETLK, &fl); } #define lockf fcntl_lockf #endif #if defined(__sun) || defined(R__GLIBC) #include #endif #if defined(__osf__) || defined(__sgi) || defined(R__MACOSX) extern "C" char *crypt(const char *, const char *); #endif #ifdef R__WIN32 #define R__NOCRYPT #endif #ifdef R__NOCRYPT static std::string gRndmSalt = std::string("ABCDEFGH"); #endif #if defined(__sun) #ifndef R__SHADOWPW #define R__SHADOWPW #endif #endif #ifdef R__SHADOWPW #include #endif #ifdef R__AFS #include "AFSAuth.h" #endif #ifdef R__SRP extern "C" { #include #include } #endif #ifdef R__KRB5 #include "Krb5Auth.h" #include extern krb5_deltat krb5_clockskew; #endif #ifdef R__SSL // SSL specific headers for RSA keys #include #include #include #include #include #include #include #endif #include "rpdp.h" #include "rsadef.h" #include "rsalib.h" // // To improve error logging for UsrPwd on the client side static ERootdErrors gUsrPwdErr[4][4] = { {kErrNoPasswd, kErrNoPassHEquNoFiles, kErrNoPassHEquBadFiles, kErrNoPassHEquFailed}, {kErrBadPasswd, kErrBadPassHEquNoFiles, kErrBadPassHEquBadFiles, kErrBadPassHEquFailed}, {kErrBadRtag, kErrBadRtagHEquNoFiles, kErrBadRtagHEquBadFiles, kErrBadRtagHEquFailed}, {kErrBadPwdFile, kErrBadPwdFileHEquNoFiles, kErrBadPwdFileHEquBadFiles, kErrBadPwdFileHEquFailed}}; //--- Machine specific routines ------------------------------------------------ #if defined(__alpha) && !defined(linux) && !defined(__FreeBSD__) && \ !defined(__OpenBSD__) extern "C" int initgroups(const char *name, int basegid); #endif #if defined(__sgi) && !defined(__GNUG__) && (SGI_REL<62) extern "C" { int seteuid(int euid); int setegid(int egid); } #endif #if defined(_AIX) extern "C" { int seteuid(uid_t euid); int setegid(gid_t egid); } #endif #if !defined(__hpux) && !defined(linux) && !defined(__FreeBSD__) && \ !defined(__OpenBSD__) || defined(cygwingcc) static int setresgid(gid_t r, gid_t e, gid_t) { if (setgid(r) == -1) return -1; return setegid(e); } static int setresuid(uid_t r, uid_t e, uid_t) { if (setuid(r) == -1) return -1; return seteuid(e); } #else #if defined(linux) && !defined(R__HAS_SETRESUID) extern "C" { int setresgid(gid_t r, gid_t e, gid_t s); int setresuid(uid_t r, uid_t e, uid_t s); } #endif #endif #if defined(__sun) #if defined(R__SUNGCC3) extern "C" int gethostname(char *, unsigned int); #endif #endif extern int gDebug; namespace ROOT { // // rpdutils module globals ErrorHandler_t gErrSys = 0; ErrorHandler_t gErrFatal = 0; ErrorHandler_t gErr = 0; bool gSysLog = 0; std::string gServName[3] = { "sockd", "rootd", "proofd" }; // // Local global consts static const int gAUTH_CLR_MSK = 0x1; // Masks for authentication methods static const int gAUTH_SRP_MSK = 0x2; static const int gAUTH_KRB_MSK = 0x4; static const int gAUTH_GLB_MSK = 0x8; static const int gAUTH_SSH_MSK = 0x10; static const int gMAXTABSIZE = 50000000; static const std::string gAuthMeth[kMAXSEC] = { "UsrPwd", "SRP", "Krb5", "Globus", "SSH", "UidGid" }; static const std::string gAuthTab = "/rpdauthtab"; // auth table static const std::string gDaemonRc = ".rootdaemonrc"; // daemon access rules static const std::string gRootdPass = ".rootdpass"; // special rootd passwd static const std::string gSRootdPass = "/.srootdpass"; // SRP passwd static const std::string gKeyRoot = "/rpk."; // Root for key files // // RW dir for temporary files (needed by gRpdAuthTab: do not move) static std::string gTmpDir = "/tmp"; // // Local global vars static int gAuthProtocol = -1; // Protocol used fro a successful authentication static char gBufOld[kMAXRECVBUF] = {0}; // msg sync for old client (<=3.05/07) static bool gCheckHostsEquiv = 1; static int gClientOld = 0; // msg sync for old client (<=3.05/07) static int gClientProtocol = -1; static int gCryptRequired = -1; static std::string gCryptToken; static int gAllowMeth[kMAXSEC]; static std::string gAltSRPPass; static int gAnon = 0; static int gExistingAuth = 0; static int gAuthListSent = 0; static int gHaveMeth[kMAXSEC]; static EMessageTypes gKindOld; // msg sync for old client (<=3.05/07) static int gMethInit = 0; static int gNumAllow = -1; static int gNumLeft = -1; static int gOffSet = -1; static std::string gOpenHost = "????"; static int gParentId = -1; static char gPasswd[kMAXUSERLEN] = { 0 }; static char gPubKey[kMAXPATHLEN] = { 0 }; static int gPubKeyLen = 0; static int gRandInit = 0; static int gRemPid = -1; static bool gRequireAuth = 1; static int gReUseAllow = 0x1F; // define methods for which tokens can be asked static int gReUseRequired = -1; static int gDoLogin = 0; // perform login static std::string gRpdAuthTab = std::string(gTmpDir).append(gAuthTab); static std::string gRpdKeyRoot = std::string(gTmpDir).append(gKeyRoot); static rsa_NUMBER gRSA_d; static rsa_NUMBER gRSA_n; static int gRSAInit = 0; static int gRSAKey = 0; static rsa_KEY gRSAPriKey; static rsa_KEY_export gRSAPubExport[2] = {{0,0},{0,0}}; static rsa_KEY gRSAPubKey; #ifdef R__SSL static BF_KEY gBFKey; // Session symmetric key static RSA *gRSASSLKey = 0; // Local RSA SSL key #endif static int gSaltRequired = -1; static int gSec = -1; static int gServerProtocol = -1; static EService gService = kROOTD; static int gSshdPort = 22; static int gTriedMeth[kMAXSEC]; static char gUser[64] = { 0 }; static char *gUserAllow[kMAXSEC] = { 0 }; // User access control static unsigned int gUserAlwLen[kMAXSEC] = { 0 }; static unsigned int gUserIgnLen[kMAXSEC] = { 0 }; static char *gUserIgnore[kMAXSEC] = { 0 }; // // Kerberos stuff #ifdef R__KRB5 static krb5_context gKcontext; static krb5_keytab gKeytab = 0; // default Keytab file can be changed static std::string gKeytabFile = ""; // via RpdSetKeytabFile #endif // // Globus stuff #ifdef R__GLBS static int gShmIdCred = -1; static gss_cred_id_t gGlbCredHandle = GSS_C_NO_CREDENTIAL; static bool gHaveGlobus = 1; static std::string gGlobusSubjName; #endif //______________________________________________________________________________ static int rpd_rand() { // rand() implementation using /udev/random or /dev/random, if available #ifndef WIN32 int frnd = open("/dev/urandom", O_RDONLY); if (frnd < 0) frnd = open("/dev/random", O_RDONLY); int r; if (frnd >= 0) { ssize_t rs = read(frnd, (void *) &r, sizeof(int)); close(frnd); if (r < 0) r = -r; if (rs == sizeof(int)) return r; } ErrorInfo("+++ERROR+++ : rpd_rand: neither /dev/urandom nor /dev/random are available or readable!"); struct timeval tv; if (gettimeofday(&tv,0) == 0) { int t1, t2; memcpy((void *)&t1, (void *)&tv.tv_sec, sizeof(int)); memcpy((void *)&t2, (void *)&tv.tv_usec, sizeof(int)); r = t1 + t2; if (r < 0) r = -r; return r; } return -1; #else // No special random device available: use rand() return rand(); #endif } //______________________________________________________________________________ static int reads(int fd, char *buf, int len) { // reads in at most one less than len characters from open // descriptor fd and stores them into the buffer pointed to by buf. // Reading stops after an EOF or a newline. If a newline is // read, it is stored into the buffer. // A '\0' is stored after the last character in the buffer. // The number of characters read is returned (newline included). // Returns < 0 in case of error. int k = 0; int nread = -1; int nr = read(fd,buf,1); while (nr > 0 && buf[k] != '\n' && k < (len-1)) { k++; nr = read(fd,buf+k,1); } if (k >= len-1) { buf[k] = 0; nread = len-1; } else if (buf[k] == '\n'){ if (k <= len-2) { buf[k+1] = 0; nread = k+1; } else { buf[k] = 0; nread = k; } } else if (nr == 0) { if (k > 0) { buf[k-1] = 0; nread = k-1; } else { buf[0] = 0; nread = 0; } } else if (nr < 0) { if (k > 0) { buf[k] = 0; nread = -(k-1); } else { buf[0] = 0; nread = -1; } } // Fix the lengths if (nread >= 0) buf[nread] = 0; return nread; } //______________________________________________________________________________ static int rpdstrncasecmp(const char *str1, const char *str2, int n) { // Case insensitive string compare of n characters. while (n > 0) { int c1 = *str1; int c2 = *str2; if (isupper(c1)) c1 = tolower(c1); if (isupper(c2)) c2 = tolower(c2); if (c1 != c2) return c1 - c2; str1++; str2++; n--; } return 0; } //______________________________________________________________________________ static int rpdstrcasecmp(const char *str1, const char *str2) { // Case insensitive string compare. return rpdstrncasecmp(str1, str2, strlen(str2) + 1); } //______________________________________________________________________________ static volatile void *rpdmemset(volatile void *dst, int c, int len) { // To avoid problems due to compiler optmization // Taken from Viega&Messier, "Secure Programming Cookbook", O'Really, #13.2 // (see discussion there) volatile char *buf; for (buf = (volatile char *)dst; len; (buf[--len] = c)) { } return dst; } #ifdef R__NOCRYPT //______________________________________________________________________________ char *rpdcrypt(const char *pw, const char *sa) { // This applies simple nor encryption with sa to the first 64 bytes // pw. Returns the hex of the result (max length 128). // This is foreseen for systms where crypt is not available // (on windows ...), to provide some protection of tokens. static char buf[129]; char tbuf[64]; int np = (strlen(pw) < 64) ? strlen(pw) : 64; int ns = strlen(sa); char c; int i = 0; for (i=0; i> 4; if (j < 10) c = 48 + j; else c = 55 + j; k = 2*i + 1; buf[k] = c; } // Null termination buf[np*2] = 0; return buf; } #endif //______________________________________________________________________________ void RpdSetSysLogFlag(int syslog) { // Change the value of the static gSysLog to syslog. // Recognized values: // 0 log to syslog (for root started daemons) // 1 log to stderr (for user started daemons) gSysLog = syslog; if (gDebug > 2) ErrorInfo("RpdSetSysLogFlag: gSysLog set to %d", gSysLog); } //______________________________________________________________________________ void RpdSetMethInitFlag(int methinit) { // Change the value of the static gMethInit to methinit. // Recognized values: // 0 reset // 1 initialized already gMethInit = methinit; if (gDebug > 2) ErrorInfo("RpdSetMethInitFlag: gMethInit set to %d", gMethInit); } //______________________________________________________________________________ const char *RpdGetKeyRoot() { // Return pointer to the root string for key files // Used by proofd. return (const char *)gRpdKeyRoot.c_str(); } //______________________________________________________________________________ int RpdGetClientProtocol() { // Return protocol version run by the client. // Used by proofd. return gClientProtocol; } //______________________________________________________________________________ int RpdGetAuthProtocol() { // Return authentication protocol used for the handshake. // Used by proofd. return gAuthProtocol; } //______________________________________________________________________________ int RpdGetOffSet() { // Return offset in the authtab file. // Used by proofd. return gOffSet; } #ifdef R__KRB5 //______________________________________________________________________________ void RpdSetKeytabFile(const char *keytabfile) { // Change the value of the static gKeytab to keytab. gKeytabFile = std::string(keytabfile); if (gDebug > 2) ErrorInfo("RpdSetKeytabFile: using keytab file %s", gKeytabFile.c_str()); } //______________________________________________________________________________ void RpdFreeKrb5Vars(krb5_context context, krb5_principal principal, krb5_ticket *ticket, krb5_auth_context auth_context, krb5_creds **creds) { // Free allocated quantities for Krb stuff if (context) { // free creds if (creds) krb5_free_tgt_creds(context,creds); // free auth_context if (auth_context) krb5_auth_con_free(context, auth_context); // free ticket if (ticket) krb5_free_ticket(context,ticket); // free principal if (principal) krb5_free_principal(context, principal); // free context krb5_free_context(context); } } #endif //______________________________________________________________________________ int RpdGetAuthMethod(int kind) { int method = -1; if (kind == kROOTD_USER) method = 0; if (kind == kROOTD_SRPUSER) method = 1; if (kind == kROOTD_KRB5) method = 2; if (kind == kROOTD_GLOBUS) method = 3; if (kind == kROOTD_SSH) method = 4; if (kind == kROOTD_RFIO) method = 5; return method; } //______________________________________________________________________________ int RpdDeleteKeyFile(int ofs) { // Delete Public Key file // Returns: 0 if ok // 1 if error unlinking (check errno); int retval = 0; std::string pukfile = gRpdKeyRoot; pukfile.append(ItoA(ofs)); // Some debug info if (gDebug > 2) { ErrorInfo("RpdDeleteKeyFile: proc uid:%d gid:%d", getuid(),getgid()); } // Unlink if (unlink(pukfile.c_str()) == -1) { if (gDebug > 0 && GetErrno() != ENOENT) { ErrorInfo("RpdDeleteKeyFile: problems unlinking pub" " key file '%s' (errno: %d)", pukfile.c_str(),GetErrno()); } retval = 1; } return retval; } //______________________________________________________________________________ int RpdUpdateAuthTab(int opt, const char *line, char **token, int ilck) { // Update tab file. // If ilck <= 0 open and lock the file; if ilck > 0, use file // descriptor ilck, which should correspond to an open and locked file. // If opt = -1 : delete file (backup saved in .bak); // If opt = 0 : eliminate all inactive entries // (if line="size" act only if size > gMAXTABSIZE) // if opt = 1 : append 'line'. // Returns -1 in case of error. // Returns offset for 'line' and token for opt = 1. // Returns new file size for opt = 0. int retval = -1; int itab = 0; char fbuf[kMAXPATHLEN]; if (gDebug > 2) ErrorInfo("RpdUpdateAuthTab: analyzing: opt: %d, line: %s, ilck: %d", opt, line, ilck); if (ilck <= 0) { // Open file for reading/ writing itab = open(gRpdAuthTab.c_str(), O_RDWR); if (itab == -1) { if (opt == 1 && GetErrno() == ENOENT) { // Try creating the file itab = open(gRpdAuthTab.c_str(), O_RDWR | O_CREAT, 0600); if (itab == -1) { ErrorInfo("RpdUpdateAuthTab: opt=%d: error opening %s" "(errno: %d)", opt, gRpdAuthTab.c_str(), GetErrno()); return retval; } } else { ErrorInfo("RpdUpdateAuthTab: opt=%d: error opening %s" " (errno: %d)", opt, gRpdAuthTab.c_str(), GetErrno()); return retval; } } // lock tab file if (lockf(itab, F_LOCK, (off_t) 1) == -1) { ErrorInfo("RpdUpdateAuthTab: opt=%d: error locking %s" " (errno: %d)", opt, gRpdAuthTab.c_str(), GetErrno()); close(itab); return retval; } if (gDebug > 0) ErrorInfo("RpdUpdateAuthTab: opt= %d - file LOCKED", opt); } else { itab = ilck; } // File size int fsize = 0; if ((fsize = lseek(itab, 0, SEEK_END)) == -1) { ErrorInfo("RpdUpdateAuthTab: opt=%d: lseek error (errno: %d)", opt, GetErrno()); goto goingout; } // Set indicator to beginning if (lseek(itab, 0, SEEK_SET) == -1) { ErrorInfo("RpdUpdateAuthTab: opt=%d: lseek error (errno: %d)", opt, GetErrno()); goto goingout; } if (opt == -1) { // // Save file in .bak and delete its content // Open backup file std::string bak = std::string(gRpdAuthTab).append(".bak"); int ibak = open(bak.c_str(), O_RDWR | O_CREAT, 0600); if (ibak == -1) { ErrorInfo("RpdUpdateAuthTab: opt=%d: error opening/creating %s" " (errno: %d)", opt, bak.c_str(), GetErrno()); goto goingout; } // Truncate file to new length if (ftruncate(ibak, 0) == -1) ErrorInfo("RpdUpdateAuthTab: opt=%d: ftruncate error (%s)" " (errno: %d)", opt, bak.c_str(), GetErrno()); // Copy the content char buf[kMAXPATHLEN]; int ofs = 0, nr = 0; while ((nr = reads(itab, buf, sizeof(buf)))) { int slen = strlen(buf); // Make sure there is a '\n' before writing if (buf[slen-1] != '\n') { if (slen >= kMAXPATHLEN -1) buf[slen-1] = '\n'; else { buf[slen] = '\n'; buf[slen+1] = '\0'; } } if (slen) { while (write(ibak, buf, slen) < 0 && GetErrno() == EINTR) ResetErrno(); } // Delete Public Key file RpdDeleteKeyFile(ofs); // Next OffSet ofs += slen; } close(ibak); // Truncate file to new length if (ftruncate(itab, 0) == -1) ErrorInfo("RpdUpdateAuthTab: opt=%d: ftruncate error (%s)" " (errno: %d)", opt, gRpdAuthTab.c_str(), GetErrno()); retval = 0; } else if (opt == 0) { // // Cleanup the file (remove inactive entries) // Now scan over entries int pr = 0, pw = 0; int lsec, act, oldofs = 0, bytesread = 0; char ln[kMAXPATHLEN], dumm[kMAXPATHLEN]; bool fwr = 0; while ((bytesread = reads(itab, ln, sizeof(ln)))) { bool ok = 1; // Current position if ((pr = lseek(itab,0,SEEK_CUR)) < 0) { ErrorInfo("RpdUpdateAuthTab: opt=%d: problems lseeking file %s" " (errno: %d)", opt, gRpdAuthTab.c_str(), errno); fwr = 1; ok = 0; } // Check file corruption: length and number of items int slen = bytesread; if (ok && slen < 1) { ErrorInfo("RpdUpdateAuthTab: opt=%d: file %s seems corrupted" " (slen: %d)", opt, gRpdAuthTab.c_str(), slen); fwr = 1; ok = 0; } if (ok) { // Check file corruption: number of items int ns = sscanf(ln, "%d %d %4095s", &lsec, &act, dumm); if (ns < 3 ) { ErrorInfo("RpdUpdateAuthTab: opt=%d: file %s seems corrupted" " (ns: %d)", opt, gRpdAuthTab.c_str(), ns); fwr = 1; ok = 0; } } if (ok && act > 0) { if (fwr) { // We have to update the key file name int nr = 0; if ((nr = RpdRenameKeyFile(oldofs,pw)) == 0) { // Write the entry at new position lseek(itab, pw, SEEK_SET); if (ln[slen-1] != '\n') { if (slen >= kMAXPATHLEN -1) ln[slen-1] = '\n'; else { ln[slen] = '\n'; ln[slen+1] = '\0'; } } while (write(itab, ln, strlen(ln)) < 0 && GetErrno() == EINTR) ResetErrno(); pw += strlen(ln); } else RpdDeleteKeyFile(oldofs); lseek(itab, pr, SEEK_SET); } else pw += strlen(ln); } else { fwr = 1; } // Set old offset oldofs = pr; } // Truncate file to new length if (ftruncate(itab, pw) == -1) ErrorInfo("RpdUpdateAuthTab: opt=%d: ftruncate error (errno: %d)", opt, GetErrno()); // Return new file size retval = pw; } else if (opt == 1) { // // Add 'line' at the end // (check size and cleanup/truncate if needed) // Check size ... if ((int)(fsize+strlen(line)) > gMAXTABSIZE) { // If it is going to be too big, cleanup or truncate first fsize = RpdUpdateAuthTab(0,(const char *)0,0,itab); // If still too big: delete everything if ((int)(fsize+strlen(line)) > gMAXTABSIZE) fsize = RpdUpdateAuthTab(-1,(const char *)0,0,itab); } // We are going to write at the end retval = lseek(itab, 0, SEEK_END); // Save first RSA public key into file for later use by the // same or other rootd/proofd; we will update the tab file // only if this operation is successful int ntry = 10; int rs = 0; while ((rs = RpdSavePubKey(gPubKey, retval, gUser)) == 2 && ntry--) { // We are here if a file with the same name exists already // and can not be deleted: we shift the offset with a // dummy entry char ltmp[256]; SPrintf(ltmp, 256, "0 0 %d %d %s error: pubkey file in use: shift offset\n", gRSAKey, gRemPid, gOpenHost.c_str()); // adds line while (write(itab, ltmp, strlen(ltmp)) < 0 && GetErrno() == EINTR) ResetErrno(); // Set to the new end retval = lseek(itab, 0, SEEK_END); } if (rs > 0) { // Something wrong retval = -1; if (gDebug > 0) ErrorInfo("RpdUpdateAuthTab: pub key could not be saved (%d)",rs); } else { // Generate token *token = RpdGetRandString(3, 8); // 8 crypt-like chars #ifndef R__NOCRYPT char *cryptToken = crypt(*token, *token); #else char *cryptToken = rpdcrypt(*token,gRndmSalt.c_str()); #endif SPrintf(fbuf, kMAXPATHLEN, "%s %s\n", line, cryptToken); if (gDebug > 2) ErrorInfo("RpdUpdateAuthTab: token: '%s'", cryptToken); // Save it for later use in kSOCKD servers gCryptToken = std::string(cryptToken); // adds line while (write(itab, fbuf, strlen(fbuf)) < 0 && GetErrno() == EINTR) ResetErrno(); } } else { // // Unknown option ErrorInfo("RpdUpdateAuthTab: unrecognized option (opt= %d)", opt); } goingout: if (itab != ilck) { // unlock the file lseek(itab, 0, SEEK_SET); if (lockf(itab, F_ULOCK, (off_t) 1) == -1) { ErrorInfo("RpdUpdateAuthTab: error unlocking %s", gRpdAuthTab.c_str()); } // closing file ... close(itab); } return retval; } //______________________________________________________________________________ int RpdCleanupAuthTab(const char *crypttoken) { // De-activates entry related to token with crypt crypttoken. // Returns: 0 if successful // -4 if entry not found or inactive // -1 problems opening auth tab file // -2 problems locking auth tab file // -3 auth tab file does not exists int retval = -4; if (gDebug > 2) ErrorInfo("RpdCleanupAuthTab: Crypt-token: '%s'",crypttoken); // Open file for update int itab = -1; if ((itab = open(gRpdAuthTab.c_str(), O_RDWR)) == -1) { if (GetErrno() == ENOENT) { if (gDebug > 0) ErrorInfo("RpdCleanupAuthTab: file %s does not exist", gRpdAuthTab.c_str()); return -3; } else { ErrorInfo("RpdCleanupAuthTab: error opening %s (errno: %d)", gRpdAuthTab.c_str(), GetErrno()); return -1; } } // lock tab file if (lockf(itab, F_LOCK, (off_t) 1) == -1) { ErrorInfo("RpdCleanupAuthTab: error locking %s (errno: %d)", gRpdAuthTab.c_str(), GetErrno()); close(itab); return -2; } if (gDebug > 0) ErrorInfo("RpdCleanupAuthTab: file LOCKED (ctkn: '%s')",crypttoken); // Now access entry or scan over entries int pr = 0, pw = 0; int nw, lsec, act, remid, pkey; char line[kMAXPATHLEN]; // Set indicators if ((pr = lseek(itab, 0, SEEK_SET)) < 0) { ErrorInfo("RpdCleanupAuthTab: error lseeking %s (errno: %d)", gRpdAuthTab.c_str(), GetErrno()); close(itab); return -2; } pw = pr; while (reads(itab,line, sizeof(line))) { pr += strlen(line); if (gDebug > 2) ErrorInfo("RpdCleanupAuthTab: pr:%d pw:%d (line:%s) (pId:%d)", pr, pw, line, gParentId); char dum1[kMAXPATHLEN] = {0}, host[kMAXUSERLEN] = {0}, user[kMAXUSERLEN] = {0}, ctkn[30] = {0}, dum2[30] = {0}; nw = sscanf(line, "%d %d %d %d %127s %127s %29s %4095s %29s", &lsec, &act, &pkey, &remid, host, user, ctkn, dum1, dum2); int deactivate = 0; if (act > 0) { if (lsec == 3 && nw == 9) { if (!strncmp(dum2,crypttoken,strlen(crypttoken))) deactivate = 1; } else if (nw == 7) { if (!strncmp(ctkn,crypttoken,strlen(crypttoken))) deactivate = 1; } // Deactivate active entries: remote client has gone ... if (deactivate) { retval = 0; // Delete Public Key file RpdDeleteKeyFile(pw); #ifdef R__GLBS if (lsec == 3) { int shmid = atoi(ctkn); struct shmid_ds shm_ds; if (shmctl(shmid, IPC_RMID, &shm_ds) == -1) { if (GetErrno() != EIDRM) { ErrorInfo("RpdCleanupAuthTab: unable to mark shared" " memory segment %d (buf:%s)", shmid, ctkn); ErrorInfo("RpdCleanupAuthTab: for destruction" " (errno: %d)", GetErrno()); retval++; } } } #endif // Locate 'act' ... skeep initial spaces, if any int slen = (int)strlen(line); int ka = 0; while (ka < slen && line[ka] == 32) ka++; // skeep method while (ka < slen && line[ka] != 32) ka++; // skeep spaces before 'act' while (ka < slen && line[ka] == 32) ka++; // This is 'act' line[ka] = '0'; // Make sure there is a '\n' before writing int sl = strlen(line); if (line[sl-1] != '\n') { if (sl >= kMAXPATHLEN -1) line[sl-1] = '\n'; else { line[sl] = '\n'; line[sl+1] = '\0'; } } // Write it now lseek(itab, pw, SEEK_SET); while (write(itab, line, strlen(line)) < 0 && GetErrno() == EINTR) ResetErrno(); // We are done lseek(itab, 0, SEEK_END); } } pw = pr; } // unlock the file lseek(itab, 0, SEEK_SET); if (lockf(itab, F_ULOCK, (off_t) 1) == -1) { ErrorInfo("RpdCleanupAuthTab: error unlocking %s", gRpdAuthTab.c_str()); } // closing file ... close(itab); return retval; } //______________________________________________________________________________ int RpdCleanupAuthTab(const char *Host, int RemId, int OffSet) { // In tab file, cleanup (set inactive) entry at offset // 'OffSet' from remote PiD 'RemId' at 'Host'. // If Host="all" or RemId=0 discard all entries. // Return number of entries not cleaned properly ... int retval = 0; if (gDebug > 2) ErrorInfo("RpdCleanupAuthTab: Host: '%s', RemId:%d, OffSet: %d", Host, RemId, OffSet); // Open file for update int itab = -1; if ((itab = open(gRpdAuthTab.c_str(), O_RDWR)) == -1) { if (GetErrno() == ENOENT) { if (gDebug > 0) ErrorInfo("RpdCleanupAuthTab: file %s does not exist", gRpdAuthTab.c_str()); return -3; } else { ErrorInfo("RpdCleanupAuthTab: error opening %s (errno: %d)", gRpdAuthTab.c_str(), GetErrno()); return -1; } } // lock tab file if (lockf(itab, F_LOCK, (off_t) 1) == -1) { ErrorInfo("RpdCleanupAuthTab: error locking %s (errno: %d)", gRpdAuthTab.c_str(), GetErrno()); close(itab); // return retval; return -2; } if (gDebug > 0) ErrorInfo("RpdCleanupAuthTab: file LOCKED" " (Host: '%s', RemId:%d, OffSet: %d)", Host, RemId, OffSet); // Now access entry or scan over entries int pr = 0, pw = 0; int nw, lsec, act, remid, pkey; char line[kMAXPATHLEN]; // Clean all flag int all = (!strcmp(Host, "all") || RemId == 0); // Set indicator if (all || OffSet < 0) pr = lseek(itab, 0, SEEK_SET); else pr = lseek(itab, OffSet, SEEK_SET); if (pr < 0) { ErrorInfo("RpdCleanupAuthTab: error lseeking %s (errno: %d)", gRpdAuthTab.c_str(), GetErrno()); close(itab); // return retval; return -2; } pw = pr; while (reads(itab,line, sizeof(line))) { pr += strlen(line); if (gDebug > 2) ErrorInfo("RpdCleanupAuthTab: pr:%d pw:%d (line:%s) (pId:%d)", pr, pw, line, gParentId); char dumm[kMAXPATHLEN], host[kMAXUSERLEN], user[kMAXUSERLEN], shmbuf[30]; nw = sscanf(line, "%d %d %d %d %127s %127s %29s %4095s", &lsec, &act, &pkey, &remid, host, user, shmbuf, dumm); if (nw > 5) { if (all || OffSet > -1 || (strstr(line,Host) && (RemId == remid))) { // Delete Public Key file RpdDeleteKeyFile(pw); #ifdef R__GLBS if (lsec == 3 && act > 0) { int shmid = atoi(shmbuf); struct shmid_ds shm_ds; if (shmctl(shmid, IPC_RMID, &shm_ds) == -1) { if (GetErrno() != EIDRM) { ErrorInfo("RpdCleanupAuthTab: unable to mark shared" " memory segment %d (buf:%s)", shmid, shmbuf); ErrorInfo("RpdCleanupAuthTab: for destruction" " (errno: %d)", GetErrno()); retval++; } } } #endif // Deactivate active entries: remote client has gone ... if (act > 0) { // Locate 'act' ... skeep initial spaces, if any int slen = (int)strlen(line); int ka = 0; while (ka < slen && line[ka] == 32) ka++; // skeep method while (ka < slen && line[ka] != 32) ka++; // skeep spaces before 'act' while (ka < slen && line[ka] == 32) ka++; // This is 'act' line[ka] = '0'; // Make sure there is a '\n' before writing int sl = strlen(line); if (line[sl-1] != '\n') { if (sl >= kMAXPATHLEN -1) line[sl-1] = '\n'; else { line[sl] = '\n'; line[sl+1] = '\0'; } } // Write it now lseek(itab, pw, SEEK_SET); while (write(itab, line, strlen(line)) < 0 && GetErrno() == EINTR) ResetErrno(); if (all || OffSet < 0) lseek(itab, pr, SEEK_SET); else lseek(itab, 0, SEEK_END); } } } pw = pr; } // unlock the file lseek(itab, 0, SEEK_SET); if (lockf(itab, F_ULOCK, (off_t) 1) == -1) { ErrorInfo("RpdCleanupAuthTab: error unlocking %s", gRpdAuthTab.c_str()); } // closing file ... close(itab); return retval; } //______________________________________________________________________________ int RpdCheckAuthTab(int Sec, const char *User, const char *Host, int RemId, int *OffSet) { // Check authentication entry in tab file. int retval = 0; if (gDebug > 2) ErrorInfo("RpdCheckAuthTab: analyzing: %d %s %s %d %d", Sec, User, Host, RemId, *OffSet); // Check OffSet first char *tkn = 0, *user =0; int shmid; bool goodOfs = RpdCheckOffSet(Sec,User,Host,RemId, OffSet,&tkn,&shmid,&user); if (gDebug > 2) ErrorInfo("RpdCheckAuthTab: goodOfs: %d", goodOfs); // Notify the result of the check int tag = 0; if (gClientProtocol >= 10) { if (goodOfs) { if (gClientProtocol > 11) { // Generate tag RpdInitRand(); while ((tag = rpd_rand()) == 1) ; // .ne.1 for backward comptibility // We will receive the user token next NetSend(tag, kROOTD_AUTH); } else // We will receive the user token next NetSend(1, kROOTD_AUTH); } else { // No authentication available for re-use NetSend(0, kROOTD_AUTH); // Cleanup and return: we need a new one ... if (tkn) delete[] tkn; if (user) delete[] user; // ... no need to continue receiving the old token return retval; } } // Now Receive Token int ofs = *OffSet; char *token = 0; if (gRSAKey > 0) { if (RpdSecureRecv(&token) == -1) { ErrorInfo("RpdCheckAuthTab: problems secure-" "receiving token %s", "- may result in authentication failure "); } } else { EMessageTypes kind; int lenToken = 9; token = new char[lenToken]; NetRecv(token, lenToken, kind); if (kind != kMESS_STRING) ErrorInfo ("RpdCheckAuthTab: got msg kind: %d instead of %d (kMESS_STRING)", kind, kMESS_STRING); // Invert Token for (int i = 0; i < (int) strlen(token); i++) { token[i] = ~token[i]; } } if (gDebug > 2) ErrorInfo ("RpdCheckAuthTab: received from client: token: '%s' ", token); // Check tag, if there if (token && strlen(token) > 8) { // Create hex from tag char tagref[9] = {0}; SPrintf(tagref,9,"%08x",tag); if (strncmp(token+8,tagref,8)) { ErrorInfo("RpdCheckAuthTab: token tag does not match - failure"); goodOfs = 0; } else // Drop tag token[8] = 0; } // Now check Token validity if (goodOfs && token && RpdCheckToken(token, tkn)) { if (Sec == 3) { #ifdef R__GLBS // kGlobus: if (GlbsToolCheckContext(shmid)) { retval = 1; strlcpy(gUser, user, sizeof(gUser)); } else { // set entry inactive RpdCleanupAuthTab(Host,RemId,*OffSet); } #else ErrorInfo ("RpdCheckAuthTab: compiled without Globus support:%s", " you shouldn't have got here!"); #endif } else { retval = 1; } // Comunicate new offset to remote client if (retval) *OffSet = ofs; } if (tkn) delete[] tkn; if (token) delete[] token; if (user) delete[] user; return retval; } //______________________________________________________________________________ int RpdCheckOffSet(int Sec, const char *User, const char *Host, int RemId, int *OffSet, char **Token, int *ShmId, char **GlbsUser) { // Check offset received from client entry in tab file. int retval = 0; bool goodOfs = 0; int ofs = *OffSet >= 0 ? *OffSet : 0; if (gDebug > 2) ErrorInfo("RpdCheckOffSet: analyzing: %d %s %s %d %d", Sec, User, Host, RemId, *OffSet); // Open file int itab = open(gRpdAuthTab.c_str(), O_RDWR); if (itab == -1) { if (GetErrno() == ENOENT) ErrorInfo("RpcCheckOffSet: file %s does not exist", gRpdAuthTab.c_str()); else ErrorInfo("RpcCheckOffSet: error opening %s (errno: %d)", gRpdAuthTab.c_str(), GetErrno()); return retval; } // lock tab file if (lockf(itab, F_LOCK, (off_t) 1) == -1) { ErrorInfo("RpcCheckOffSet: error locking %s (errno: %d)", gRpdAuthTab.c_str(), GetErrno()); close(itab); return retval; } if (gDebug > 0) ErrorInfo("RpdCheckOffSet: file LOCKED"); // File is open: set position at wanted location if (lseek(itab, ofs, SEEK_SET) < 0) { ErrorInfo("RpcCheckOffSet: error lseeking %s (errno: %d)", gRpdAuthTab.c_str(), GetErrno()); close(itab); return retval; } // Now read the entry char line[kMAXPATHLEN]; if (reads(itab,line, sizeof(line)) < 0) { ErrorInfo("RpcCheckOffSet: error reading %d bytes from %s (errno: %d)", sizeof(line), gRpdAuthTab.c_str(), GetErrno()); close(itab); return retval; } // and parse its content according to auth method int lsec, act, remid, shmid = -1; char host[kMAXPATHLEN], usr[kMAXPATHLEN], subj[kMAXPATHLEN], dumm[kMAXPATHLEN], tkn[20]; int nw = sscanf(line, "%d %d %d %d %4095s %4095s %19s %4095s", &lsec, &act, &gRSAKey, &remid, host, usr, tkn, dumm); if (gDebug > 2) ErrorInfo("RpdCheckOffSet: found line: %s", line); if (nw > 5 && act > 0) { if (lsec == Sec) { if (lsec == 3) { sscanf(line, "%d %d %d %d %4095s %4095s %d %4095s %19s %4095s", &lsec, &act, &gRSAKey, &remid, host, usr, &shmid, subj, tkn, dumm); if ((remid == RemId) && !strcmp(host, Host) && !strcmp(subj, User)) goodOfs = 1; } else { if ((remid == RemId) && !strcmp(host, Host) && !strcmp(usr, User)) goodOfs = 1; } } } if (!goodOfs) { // Tab may have been cleaned in the meantime ... try a scan lseek(itab, 0, SEEK_SET); ofs = 0; while (reads(itab, line, sizeof(line))) { nw = sscanf(line, "%d %d %d %d %4095s %4095s %19s %4095s", &lsec, &act, &gRSAKey, &remid, host, usr, tkn, dumm); if (gDebug > 2) ErrorInfo("RpdCheckOffSet: found line: %s", line); if (nw > 5 && act > 0) { if (lsec == Sec) { if (lsec == 3) { sscanf(line, "%d %d %d %d %4095s %4095s %d %4095s %19s %4095s", &lsec, &act, &gRSAKey, &remid, host, usr, &shmid, subj, tkn, dumm); if ((remid == RemId) && !strcmp(host, Host) && !strcmp(subj, User)) { goodOfs = 1; goto found; } } else { if ((remid == RemId) && !strcmp(host, Host) && !strcmp(usr, User)) { goodOfs = 1; goto found; } } } } } } found: // unlock the file lseek(itab, 0, SEEK_SET); if (lockf(itab, F_ULOCK, (off_t) 1) == -1) { ErrorInfo("RpcCheckOffSet: error unlocking %s", gRpdAuthTab.c_str()); } // closing file ... close(itab); // Read public key std::string pukfile = gRpdKeyRoot; pukfile.append(ItoA(*OffSet)); if (gDebug > 2) ErrorInfo("RpdCheckOffSet: RSAKey ofs file: %d %d '%s' ", gRSAKey, ofs, pukfile.c_str()); struct passwd *pw = getpwnam(usr); if (pw) { uid_t fromUid = getuid(); uid_t fromEUid = geteuid(); // The check must be done with 'usr' UIDs to prevent // unauthorized users from forcing the server to read // manipulated key files. if (fromUid == 0) if (setresuid(pw->pw_uid, pw->pw_uid, fromEUid) == -1) // Since we could not set the user IDs, we will // not trust the client goodOfs = 0; // Get the key now if (goodOfs) if (RpdGetRSAKeys(pukfile.c_str(), 1) < 1) goodOfs = 0; // Reset original IDs if (getuid() != fromUid) setresuid(fromUid,fromEUid,pw->pw_uid); } else { // Since we could not set the user IDs, we will // not trust the client goodOfs = 0; if (gDebug > 0) ErrorInfo("RpdCheckOffSet: error in getpwname(%s) (errno: %d)", usr,GetErrno()); } if (gDebug > 2) ErrorInfo("RpdCheckOffSet: goodOfs: %d (active: %d)", goodOfs, act); // Comunicate new offset to remote client if (goodOfs) { // Rename the key file, if needed if (*OffSet > 0 && *OffSet != ofs) { if (RpdRenameKeyFile(*OffSet,ofs) > 0) { goodOfs = 0; // Error: set entry inactive RpdCleanupAuthTab(Host,RemId,ofs); } } *OffSet = ofs; // return token if requested if (Token) { const size_t tokenSize = strlen(tkn)+1; *Token = new char[tokenSize]; strlcpy(*Token,tkn,tokenSize); } if (Sec == 3) { if (GlbsUser) { const size_t glbsUserSize = strlen(usr)+1; *GlbsUser = new char[glbsUserSize]; strlcpy(*GlbsUser,usr,glbsUserSize); } if (ShmId) *ShmId = shmid; } } return goodOfs; } //______________________________________________________________________________ int RpdRenameKeyFile(int oldofs, int newofs) { // Rename public file with new offset // Returns: 0 if OK // 1 if problems renaming int retval = 0; // Old name std::string oldname = gRpdKeyRoot; oldname.append(ItoA(oldofs)); // New name std::string newname = gRpdKeyRoot; newname.append(ItoA(newofs)); if (rename(oldname.c_str(), newname.c_str()) == -1) { if (gDebug > 0) ErrorInfo("RpdRenameKeyFile: error renaming key file" " %s to %s (errno: %d)", oldname.c_str(),newname.c_str(),GetErrno()); retval = 2; } return retval; } //______________________________________________________________________________ bool RpdCheckToken(char *token, char *tknref) { // Check token validity. // Get rid of '\n' char *s = strchr(token, '\n'); if (s) *s = 0; s = strchr(tknref, '\n'); if (s) *s = 0; #ifndef R__NOCRYPT char *tkn_crypt = crypt(token, tknref); int tlen = 13; #else char *tkn_crypt = rpdcrypt(token,gRndmSalt.c_str()); int tlen = 16; #endif if (gDebug > 2) ErrorInfo("RpdCheckToken: ref:'%s' crypt:'%s'", tknref, tkn_crypt); if (!strncmp(tkn_crypt, tknref, tlen)) return 1; else return 0; } //______________________________________________________________________________ int RpdReUseAuth(const char *sstr, int kind) { // Check the requiring subject has already authenticated during this session // and its 'ticket' is still valid. // Not implemented for SRP and Krb5 (yet). int lenU, offset, opt; gOffSet = -1; gExistingAuth = 0; int auth= 0; if (gDebug > 2) ErrorInfo("RpdReUseAuth: analyzing: %s, %d", sstr, kind); char user[64]; // kClear if (kind == kROOTD_USER) { if (!(gReUseAllow & gAUTH_CLR_MSK)) { return 0; // re-authentication required by administrator } gSec = 0; // Decode subject string sscanf(sstr, "%d %d %d %d %63s", &gRemPid, &offset, &opt, &lenU, user); user[lenU] = '\0'; if ((gReUseRequired = (opt & kAUTH_REUSE_MSK))) { gOffSet = offset; if (gRemPid > 0 && gOffSet > -1) { auth = RpdCheckAuthTab(gSec, user, gOpenHost.c_str(), gRemPid, &gOffSet); } if ((auth == 1) && (offset != gOffSet)) auth = 2; // Fill gUser and free allocated memory strlcpy(gUser, user, sizeof(gUser)); } } // kSRP if (kind == kROOTD_SRPUSER) { if (!(gReUseAllow & gAUTH_SRP_MSK)) { return 0; // re-authentication required by administrator } gSec = 1; // Decode subject string sscanf(sstr, "%d %d %d %d %63s", &gRemPid, &offset, &opt, &lenU, user); user[lenU] = '\0'; if ((gReUseRequired = (opt & kAUTH_REUSE_MSK))) { gOffSet = offset; if (gRemPid > 0 && gOffSet > -1) { auth = RpdCheckAuthTab(gSec, user, gOpenHost.c_str(), gRemPid, &gOffSet); } if ((auth == 1) && (offset != gOffSet)) auth = 2; // Fill gUser and free allocated memory strlcpy(gUser, user, sizeof(gUser)); } } // kKrb5 if (kind == kROOTD_KRB5) { if (!(gReUseAllow & gAUTH_KRB_MSK)) { return 0; // re-authentication required by administrator } gSec = 2; // Decode subject string sscanf(sstr, "%d %d %d %d %63s", &gRemPid, &offset, &opt, &lenU, user); user[lenU] = '\0'; if ((gReUseRequired = (opt & kAUTH_REUSE_MSK))) { gOffSet = offset; if (gRemPid > 0 && gOffSet > -1) { auth = RpdCheckAuthTab(gSec, user, gOpenHost.c_str(), gRemPid, &gOffSet); } if ((auth == 1) && (offset != gOffSet)) auth = 2; // Fill gUser and free allocated memory strlcpy(gUser, user, sizeof(gUser)); } } // kGlobus if (kind == kROOTD_GLOBUS) { if (!(gReUseAllow & gAUTH_GLB_MSK)) { return 0; // re-authentication required by administrator } gSec = 3; // Decode subject string int lenS; sscanf(sstr, "%d %d %d %d %63s", &gRemPid, &offset, &opt, &lenS, user); user[lenS] = '\0'; if ((gReUseRequired = (opt & kAUTH_REUSE_MSK))) { gOffSet = offset; if (gRemPid > 0 && gOffSet > -1) { auth = RpdCheckAuthTab(gSec, user, gOpenHost.c_str(), gRemPid, &gOffSet); } if ((auth == 1) && (offset != gOffSet)) auth = 2; } } // kSSH if (kind == kROOTD_SSH) { if (!(gReUseAllow & gAUTH_SSH_MSK)) { return 0; // re-authentication required by administrator } gSec = 4; // Decode subject string char pipe[kMAXPATHLEN]; sscanf(sstr, "%d %d %d %4095s %d %63s", &gRemPid, &offset, &opt, pipe, &lenU, user); user[lenU] = '\0'; if ((gReUseRequired = (opt & kAUTH_REUSE_MSK))) { gOffSet = offset; if (gRemPid > 0 && gOffSet > -1) { auth = RpdCheckAuthTab(gSec, user, gOpenHost.c_str(), gRemPid, &gOffSet); } if ((auth == 1) && (offset != gOffSet)) auth = 2; // Fill gUser and free allocated memory strlcpy(gUser, user, sizeof(gUser)); } } // Flag if existing token has been re-used if (auth > 0) gExistingAuth = 1; // Return value return auth; } //______________________________________________________________________________ int RpdCheckAuthAllow(int Sec, const char *Host) { // Check if required auth method is allowed for 'Host'. // If 'yes', returns 0, if 'no', returns 1, the number of allowed // methods in NumAllow, and the codes of the allowed methods (in order // of preference) in AllowMeth. Memory for AllowMeth must be allocated // outside. Directives read from (in decreasing order of priority): // $ROOTDAEMONRC, $HOME/.rootdaemonrc (privately startd daemons only) // or $ROOTETCDIR/system.rootdaemonrc. int retval = 1, found = 0; #ifdef R__GBLS if (Sec == 3 && !gHaveGlobus) { ErrorInfo("RpdCheckAuthAllow: meth: 3:" " server does not have globus/GSI credentials"); return 1; } #endif std::string theDaemonRc; // Check if a non-standard file has been requested if (getenv("ROOTDAEMONRC")) theDaemonRc = getenv("ROOTDAEMONRC"); if (theDaemonRc.length() <= 0) { if (getuid()) { // Check if user has a private daemon access file ... struct passwd *pw = getpwuid(getuid()); if (pw != 0) { theDaemonRc = std::string(pw->pw_dir).append("/"); theDaemonRc.append(gDaemonRc); } else { if (getenv("ROOTETCDIR")) { theDaemonRc = std::string(getenv("ROOTETCDIR")).append("/system"); theDaemonRc.append(gDaemonRc); } else theDaemonRc = std::string("/etc/root/system").append(gDaemonRc); } } else { // If running as super-user, check system file only if (getenv("ROOTETCDIR")) { theDaemonRc = std::string(getenv("ROOTETCDIR")).append("/system"); theDaemonRc.append(gDaemonRc); } else theDaemonRc = std::string("/etc/root/system").append(gDaemonRc); } } if (gDebug > 2) ErrorInfo("RpdCheckAuthAllow: Checking file: %s for meth:%d" " host:%s (gNumAllow: %d)", theDaemonRc.c_str(), Sec, Host, gNumAllow); // Check if info already loaded (not first call ...) if (gMethInit == 1) { // Look for the method in the allowed list and flag this method // as tried, if found ... int newtry = 0, i; for (i = 0; i < gNumAllow; i++) { if (gTriedMeth[i] == 0 && gAllowMeth[i] == Sec) { newtry = 1; gTriedMeth[i] = 1; gNumLeft--; } } if (newtry == 0) { ErrorInfo ("RpdCheckAuthAllow: new auth method proposed by %s", " client not in the list or already attempted"); return retval; } retval = 0; } else { // Open file FILE *ftab = fopen(theDaemonRc.c_str(), "r"); if (ftab == 0) { if (GetErrno() == ENOENT) ErrorInfo("RpdCheckAuthAllow: file %s does not exist", theDaemonRc.c_str()); else ErrorInfo("RpdCheckAuthAllow: error opening %s (errno: %d)", theDaemonRc.c_str(), GetErrno()); } // Now read the entry char line[kMAXPATHLEN], host[kMAXPATHLEN], rest[kMAXPATHLEN], cmth[kMAXPATHLEN]; int nmet = 0, mth[6] = { 0 }; int cont = 0, jm = -1; while (ftab && fgets(line, sizeof(line), ftab)) { int i; if (line[0] == '#') continue; // skip comment lines if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = '\0'; // get rid of '\n', if any ... // Analyze the line now ... int nw = 0; char *pstr = line; // Check if a continuation line if (cont == 1) { cont = 0; strlcpy(rest, pstr, kMAXPATHLEN); } else { jm = -1; // Get 'host' first ... nw = sscanf(pstr, "%4095s %4095s", host, rest); if (nw < 2) continue; // no method defined for this host pstr = line + strlen(host) + 1; // Check if a service is specified char *pcol = strstr(host, ":"); if (pcol) { if (!strstr(pcol+1, gServName[gService].c_str())) continue; else host[(int)(pcol-host)] = '\0'; } if (strlen(host) == 0) strlcpy(host, "default", kMAXPATHLEN); if (gDebug > 2) ErrorInfo("RpdCheckAuthAllow: found host: %s ", host); if (strcmp(host, "default")) { // now check validity of 'host' format if (!RpdCheckHost(Host,host)) { goto next; } } else { // This is a default entry: ignore it if a host-specific entry was already // found, analyse it otherwise ... if (found == 1) goto next; } // Reset mth[kMAXSEC] nmet = 0; for (i = 0; i < kMAXSEC; i++) { mth[i] = -1; } } // We are at the end and there will be a continuation line ... if (rest[0] == '\\') { cont = 1; continue; } while (pstr != 0) { int tmet = -1; char *pd = 0, *pd2 = 0; cmth[0] = '\0'; rest[0] = '\0'; nw = sscanf(pstr, "%4095s %4095s", cmth, rest); if (!strcmp(cmth, "none")) { nmet = 0; goto nexti; } pd = strchr(cmth, ':'); // Parse the method char tmp[20]; if (pd != 0) { int mlen = pd - cmth; strncpy(tmp, cmth, mlen); tmp[mlen] = '\0'; } else { strlcpy(tmp, cmth, sizeof(tmp)); } if (strlen(tmp) > 1) { for (tmet = 0; tmet < kMAXSEC; tmet++) { if (!rpdstrcasecmp(gAuthMeth[tmet].c_str(), tmp)) break; } if (tmet < kMAXSEC) { if (gDebug > 2) ErrorInfo("RpdCheckAuthAllow: tmet %d", tmet); } else { if (gDebug > 1) ErrorInfo("RpdCheckAuthAllow: unknown methods" " %s - ignore", tmp); goto nexti; } } else { tmet = atoi(tmp); } jm = -1; if (gDebug > 2) ErrorInfo("RpdCheckAuthAllow: found method %d (have?:%d)", tmet, (tmet >= 0 && tmet < kMAXSEC) ? gHaveMeth[tmet] : 0); if (tmet >= 0 && tmet < kMAXSEC) { if (gHaveMeth[tmet] == 1) { int ii; for (ii = 0; ii < nmet; ii++) { if (mth[ii] == tmet) { jm = ii; } } } else goto nexti; } else goto nexti; if (jm == -1) { // New method ... mth[nmet] = tmet; jm = nmet; nmet++; } // Now parse users list, if any ... while (pd != 0 && (int) (pd[1]) != 32) { pd2 = strchr(pd + 1, ':'); if (pd[1] == '-') { pd += 2; // Ignore if (gUserIgnore[mth[jm]] == 0) { gUserIgnLen[mth[jm]] = kMAXPATHLEN; gUserIgnore[mth[jm]] = new char[gUserIgnLen[mth[jm]]]; gUserIgnore[mth[jm]][0] = '\0'; } if (strlen(gUserIgnore[mth[jm]]) > (gUserIgnLen[mth[jm]] - 10)) { char *tmpUI = strdup(gUserIgnore[mth[jm]]); free(gUserIgnore[mth[jm]]); gUserIgnLen[mth[jm]] += kMAXPATHLEN; gUserIgnore[mth[jm]] = new char[gUserIgnLen[mth[jm]]]; strlcpy(gUserIgnore[mth[jm]], tmpUI, sizeof(gUserIgnLen[mth[jm]])); free(tmpUI); } char usr[256]; if (pd2 != 0) { int ulen = pd2 - pd; strncpy(usr, pd, ulen); usr[ulen] = '\0'; } else { strlcpy(usr, pd, sizeof(usr)); } struct passwd *pw = getpwnam(usr); if (pw != 0) SPrintf(gUserIgnore[mth[jm]], gUserIgnLen[mth[jm]], "%s %d", gUserIgnore[mth[jm]], (int)pw->pw_uid); } else { pd += 1; if (pd[1] == '+') pd += 1; // Keep if (gUserAllow[mth[jm]] == 0) { gUserAlwLen[mth[jm]] = kMAXPATHLEN; gUserAllow[mth[jm]] = new char[gUserAlwLen[mth[jm]]]; gUserAllow[mth[jm]][0] = '\0'; } if (strlen(gUserAllow[mth[jm]]) > (gUserAlwLen[mth[jm]] - 10)) { char *tmpUI = strdup(gUserAllow[mth[jm]]); free(gUserAllow[mth[jm]]); gUserAlwLen[mth[jm]] += kMAXPATHLEN; gUserAllow[mth[jm]] = new char[gUserAlwLen[mth[jm]]]; strlcpy(gUserAllow[mth[jm]], tmpUI, sizeof(gUserAlwLen[mth[jm]])); free(tmpUI); } char usr[256]; if (pd2 != 0) { int ulen = pd2 - pd; strncpy(usr, pd, ulen); usr[ulen] = '\0'; } else { strlcpy(usr, pd, sizeof(usr)); } struct passwd *pw = getpwnam(usr); if (pw != 0) SPrintf(gUserAllow[mth[jm]], gUserIgnLen[mth[jm]], "%s %d", gUserAllow[mth[jm]], (int)pw->pw_uid); } pd = pd2; } // Get next item nexti: if (nw > 1 && (int) rest[0] != 92) { pstr = strstr(pstr, rest); } else { if ((int) rest[0] == 92) cont = 1; pstr = 0; } } if (gDebug > 2) { ErrorInfo("RpdCheckAuthAllow: for host %s found %d methods", host, nmet); ErrorInfo("RpdCheckAuthAllow: %d %d %d %d %d %d", mth[0], mth[1], mth[2], mth[3], mth[4], mth[5]); } // Found new entry matching: superseed previous result found = 1; retval = 1; gNumAllow = gNumLeft = nmet; for (i = 0; i < kMAXSEC; i++) { gAllowMeth[i] = -1; gTriedMeth[i] = 0; if (i < gNumAllow) { gAllowMeth[i] = mth[i]; if (Sec == mth[i]) { retval = 0; gNumLeft--; gTriedMeth[i] = 1; } } } next: continue; } // closing file ... if (ftab) fclose(ftab); // Host specific directives have been checked for ... gMethInit = 1; // Use defaults if nothing found if (!found) { if (gDebug > 2) ErrorInfo ("RpdCheckAuthAllow: no specific or 'default' entry found: %s", "using system defaults"); int i; for (i = 0; i < gNumAllow; i++) { if (Sec == gAllowMeth[i]) { retval = 0; gNumLeft--; gTriedMeth[i] = 1; } } } } if (gDebug > 2) { ErrorInfo ("RpdCheckAuthAllow: returning: %d (gNumAllow: %d, gNumLeft:%d)", retval, gNumAllow, gNumLeft); int i, jm; for (i = 0; i < kMAXSEC; i++) { jm = gAllowMeth[i]; if (gUserAlwLen[jm] > 0) ErrorInfo("RpdCheckAuthAllow: users allowed for method %d: %s", jm, gUserAllow[jm]); } for (i = 0; i < kMAXSEC; i++) { jm = gAllowMeth[i]; if (gUserIgnLen[jm] > 0) ErrorInfo("RpdCheckAuthAllow: users ignored for method %d: %s", jm, gUserIgnore[jm]); } } return retval; } //______________________________________________________________________________ int RpdCheckHost(const char *Host, const char *host) { // Checks if 'host' is compatible with 'Host' taking into account // wild cards in the host name // Returns 1 if successful, 0 otherwise ... int rc = 1; // Strings must be both defined if (!Host || !host) return 0; // If host is a just wild card accept it if (!strcmp(host,"*")) return 1; // Try now to understand whether it is an address or a name ... int name = 0, i = 0; for (i = 0; i < (int) strlen(host); i++) { if ((host[i] < 48 || host[i] > 57) && host[i] != '*' && host[i] != '.') { name = 1; break; } } // If ref host is an IP, get IP of Host char *hh; if (!name) { hh = RpdGetIP(Host); if (gDebug > 2) ErrorInfo("RpdCheckHost: Checking Host IP: %s", hh); } else { const size_t hhSize = strlen(Host)+1; hh = new char[hhSize]; strlcpy(hh,Host,hhSize); if (gDebug > 2) ErrorInfo("RpdCheckHost: Checking Host name: %s", hh); } // Check if starts with wild // Starting with '.' defines a full (sub)domain int sos = 0; if (host[0] == '*' || host[0] == '.') sos = 1; // Check if ends with wild // Ending with '.' defines a name int eos = 0, le = strlen(host); if (host[le-1] == '*' || host[le-1] == '.') eos = 1; int first= 1; int ends= 0; int starts= 0; const size_t hSize = strlen(host)+1; char *h = new char[hSize]; strlcpy(h,host,hSize); char *tk = strtok(h,"*"); while (tk) { char *ps = strstr(hh,tk); if (!ps) { rc = 0; break; } if (!sos && first && ps == hh) starts = 1; first = 0; if (ps == hh + strlen(hh) - strlen(tk)) ends = 1; tk = strtok(0,"*"); } delete[] h; delete[] hh; if ((!sos || !eos) && !starts && !ends) rc = 0; return rc; } //______________________________________________________________________________ char *RpdGetIP(const char *host) { // Get IP address of 'host' as a string. String must be deleted by // the user. struct hostent *h; unsigned long ip; unsigned char ip_fld[4]; // Check server name if ((h = gethostbyname(host)) == 0) { ErrorInfo("RpdGetIP: unknown host %s", host); return 0; } // Decode ... ip = ntohl(*(unsigned long *) h->h_addr_list[0]); ip_fld[0] = (unsigned char) ((0xFF000000 & ip) >> 24); ip_fld[1] = (unsigned char) ((0x00FF0000 & ip) >> 16); ip_fld[2] = (unsigned char) ((0x0000FF00 & ip) >> 8); ip_fld[3] = (unsigned char) ((0x000000FF & ip)); // Prepare output char *output = new char[20]; SPrintf(output, 20, "%d.%d.%d.%d", ip_fld[0], ip_fld[1], ip_fld[2], ip_fld[3]); // return return output; } //______________________________________________________________________________ void RpdSendAuthList() { // Send list of authentication methods not yet tried. if (gDebug > 2) ErrorInfo("RpdSendAuthList: analyzing (gNumLeft: %d)", gNumLeft); // Send Number of methods left NetSend(gNumLeft, kROOTD_NEGOTIA); if (gNumLeft > 0) { int i = 0; std::string alist; char cm[5]; for (i = 0; i < gNumAllow; i++) { if (gDebug > 2) ErrorInfo("RpdSendAuthList: gTriedMeth[%d]: %d", i, gTriedMeth[i]); if (gTriedMeth[i] == 0) { SPrintf(cm, 5, " %d",gAllowMeth[i]); alist.append(cm); } } NetSend(alist.c_str(), alist.length() + 1, kMESS_STRING); if (gDebug > 2) ErrorInfo("RpdSendAuthList: sent list: %s", alist.c_str()); } } //______________________________________________________________________________ int RpdSshAuth(const char *sstr) { // Authenitcation via ssh. int auth = 0; if (gDebug > 2) ErrorInfo("RpdSshAuth: contacted by host: %s for user %s", gOpenHost.c_str(),sstr); // Decode subject string char user[kMAXUSERLEN]; char pipeId[10] = {0}; int lenU, ofs, opt; char rproto[20] = {0}; sscanf(sstr, "%d %d %d %9s %d %127s %19s", &gRemPid, &ofs, &opt, pipeId, &lenU, user, rproto); user[lenU] = '\0'; gReUseRequired = (opt & kAUTH_REUSE_MSK); #ifdef R__SSL if (gRSASSLKey) { // Determine type of RSA key required gRSAKey = (opt & kAUTH_RSATY_MSK) ? 2 : 1; } else gRSAKey = 1; #else gRSAKey = 1; #endif // Check if we have been called to notify failure ... if (gRemPid < 0) { if (gDebug > 2) ErrorInfo ("RpdSshAuth: this is a failure notification (%s,%s,%d,%s)", user, gOpenHost.c_str(), gRemPid, pipeId); struct passwd *pw = getpwnam(user); if (pw) { std::string pipeFile = std::string(pw->pw_dir) + std::string("/RootSshPipe.") + pipeId; FILE *fpipe = fopen(pipeFile.c_str(), "r"); if (!fpipe) { pipeFile= gTmpDir + std::string("/RootSshPipe.") + pipeId; fpipe = fopen(pipeFile.c_str(), "r"); } char pipe[kMAXPATHLEN]; if (fpipe) { while (fgets(pipe, sizeof(pipe), fpipe)) { if (pipe[strlen(pipe)-1] == '\n') pipe[strlen(pipe)-1] = 0; } fclose(fpipe); // Remove the temporary file unlink(pipeFile.c_str()); if (SshToolNotifyFailure(pipe)) ErrorInfo("RpdSshAuth: failure notification may have" " failed "); } else { if (GetErrno() == ENOENT) ErrorInfo("RpdSshAuth: pipe file %s does not exists", pipeFile.c_str()); else ErrorInfo("RpdSshAuth: cannot open pipe file %s" " (errno= %d)",pipeFile.c_str(),GetErrno()); } } else ErrorInfo("RpdSshAuth: unable to get user info for '%s'" " (errno: %d)",user,GetErrno()); gClientProtocol = atoi(rproto); // Improved diagnostics, check if there is something listening on // port gSshdPort char buf[20] = {0}; #if defined(linux) if (!RpdCheckSshd(0)) { // Nothing found by netstat ... try opening a socket if (!RpdCheckSshd(1)) { if (gDebug > 2) ErrorInfo("RpdSshAuth: sshd not found - return"); if (gClientProtocol > 9) { SPrintf(buf, 20, "%d",gSshdPort); NetSend(strlen(buf), kROOTD_SSH); NetSend(buf, strlen(buf), kMESS_STRING); } return auth; } } #else if (!RpdCheckSshd(1)) { if (gDebug > 2) ErrorInfo("RpdSshAuth: sshd not found - return"); if (gClientProtocol > 9) { SPrintf(buf, 20,"%d",gSshdPort); NetSend(strlen(buf), kROOTD_SSH); NetSend(buf, strlen(buf), kMESS_STRING); } return auth; } #endif if (gClientProtocol > 9) { SPrintf(buf,20,"OK"); NetSend(strlen(buf), kROOTD_SSH); NetSend(buf, strlen(buf), kMESS_STRING); ErrorInfo("RpdSshAuth: failure notified"); } else { NetSend(kErrAuthNotOK,kROOTD_ERR); ErrorInfo("RpdSshAuth: failure notified"); } return auth; } // Protocol of our ssh implementation int sshproto = atoi(rproto); // Check user existence and get its environment struct passwd *pw = getpwnam(user); if (!pw) { ErrorInfo("RpdSshAuth: entry for user % not found in /etc/passwd", user); NetSend(-2, kROOTD_SSH); return auth; } // Method cannot be attempted for anonymous users ... (ie data servers )... if (!strcmp(pw->pw_shell, "/bin/false")) { ErrorInfo("RpdSshAuth: no SSH for anonymous user '%s' ", user); NetSend(-2, kROOTD_SSH); return auth; } // Some useful variables char *pipeFile = 0; char *authFile = 0; char *uniquePipe = 0; std::string cmdInfo = ""; int unixFd = -1; struct stat st0, st1; if (sshproto == 0) { // Now we create an internal (UNIX) socket to listen to the // result of sshd from ssh2rpd. // Path will be /tmp/rootdSSH_ if ((unixFd = SshToolAllocateSocket(pw->pw_uid, pw->pw_gid, &uniquePipe)) < 0) { ErrorInfo ("RpdSshAuth: can't allocate UNIX socket for authentication"); NetSend(0, kROOTD_SSH); delete[] uniquePipe; return auth; } // Open a file to put the pipe to be read by ssh2rpd int itmp = 0; pipeFile = new char[strlen(pw->pw_dir) + 25]; SPrintf(pipeFile, strlen(pw->pw_dir) + 25, "%s/RootSshPipe.XXXXXX", pw->pw_dir); mode_t oldumask = umask(0700); int ipipe = mkstemp(pipeFile); if (ipipe == -1) { delete[] pipeFile; pipeFile = new char[gTmpDir.length() + 25]; SPrintf(pipeFile, gTmpDir.length() + 25, "%s/RootSshPipe.XXXXXX", gTmpDir.c_str()); ipipe = mkstemp(pipeFile); itmp = 1; } umask(oldumask); FILE *fpipe = 0; if (ipipe == -1 || !(fpipe = fdopen(ipipe,"w")) ) { ErrorInfo("RpdSshAuth: failure creating pipe file %s (errno: %d)", pipeFile,GetErrno()); // Could not open the file: notify failure and close // properly everything if (SshToolNotifyFailure(uniquePipe)) ErrorInfo("RpdSshAuth: failure notification perhaps" " unsuccessful ... "); NetSend(kErrNoPipeInfo, kROOTD_ERR); delete[] uniquePipe; delete[] pipeFile; close(unixFd); return auth; } else { // File open: fill it fprintf(fpipe,"%s\n",uniquePipe); fclose(fpipe); // Set strict protections chmod(pipeFile, 0600); // Set ownership of the pipe file to the user if (getuid() == 0) if (chown(pipeFile,pw->pw_uid,pw->pw_gid) == -1) ErrorInfo("RpdSshAuth: cannot change ownership of %s (errno: %d)", pipeFile,GetErrno()); } // Get ID char *pId = (char *)strstr(pipeFile,"SshPipe.")+strlen("SshPipe."); strlcpy(pipeId, pId, sizeof(pipeId)); // Communicate command to be executed via ssh ... std::string rootbindir; if (getenv("ROOTBINDIR")) rootbindir = getenv("ROOTBINDIR"); char dbgstr[4] = {0}; snprintf(dbgstr,3,"%d ",gDebug); cmdInfo = std::string(rootbindir).append("/ssh2rpd "); cmdInfo.append(dbgstr); cmdInfo.append(" "); cmdInfo.append(pipeId); // Add Tmp dir, if used if (itmp) { cmdInfo.append(" "); cmdInfo.append(gTmpDir); } } else { // New protocol using scp // Allocate a file to be overwritten by the client authFile = new char[strlen(pw->pw_dir) + 25]; SPrintf(authFile, strlen(pw->pw_dir) + 25, "%s/RootSshAuth.XXXXXX", pw->pw_dir); mode_t oldumask = umask(0700); int iauth = mkstemp(authFile); if (iauth == -1) { if (gDebug > 2) ErrorInfo("RpdSshAuth: failure creating Auth file %s (errno: %d)", authFile,GetErrno()); delete[] authFile; authFile = new char[gTmpDir.length() + 25]; SPrintf(authFile, gTmpDir.length() + 25, "%s/RootSshAuth.XXXXXX", gTmpDir.c_str()); if ((iauth = mkstemp(authFile)) == -1) { ErrorInfo("RpdSshAuth: failure creating Auth file %s (errno: %d)", authFile,GetErrno()); NetSend(kErrFileOpen, kROOTD_ERR); delete[] authFile; umask(oldumask); return auth; } } umask(oldumask); // Store stat result to check changes if (fstat(iauth, &st0) == -1) ErrorInfo("RpdSshAuth: cannot stat %s",authFile); // Make sure the permissions are the rigth ones if (fchmod(iauth,0600)) { if (gDebug > 0) { ErrorInfo("RpdSshAuth: chmod: could not change" " '%s' permission (errno= %d)",authFile, errno); ErrorInfo("RpdSshAuth: path (uid,gid) are: %d %d", st0.st_uid, st0.st_gid); NetSend(kErrNoChangePermission, kROOTD_ERR); delete[] authFile; return auth; } } if ((unsigned int)st0.st_uid != pw->pw_uid || (unsigned int)st0.st_gid != pw->pw_gid) { if (fchown(iauth, pw->pw_uid, pw->pw_gid)) { if (gDebug > 0) { ErrorInfo("RpdSshAuth: chown: could not change file" " '%s' ownership (errno= %d)",authFile, errno); ErrorInfo("RpdSshAuth: path (uid,gid) are: %d %d", st0.st_uid, st0.st_gid); ErrorInfo("RpdSshAuth: may follow authentication problems"); } } } // Reset reference time if (fstat(iauth, &st0) == -1) ErrorInfo("RpdSshAuth: cannot stat %s",authFile); // Send Back the name of the file if (gClientProtocol > 13) { // Add the full coordinates, so that it works in all cases, // included SSH tunnelling ... char hostname[64]; gethostname(hostname, sizeof(hostname)); char *cmd = new char[strlen(authFile) + strlen(user) + strlen(hostname) + 5]; SPrintf(cmd, strlen(authFile) + strlen(user) + strlen(hostname) + 5, " %s@%s:%s ", user, hostname, authFile); cmdInfo.append(cmd); delete[] cmd; } else { cmdInfo = std::string(authFile); } } // Add non-standard port, if so if (gSshdPort != 22) { char sshp[10]; snprintf(sshp,10," p:%d",gSshdPort); cmdInfo.append(sshp); } // Add key type, if SSL if (gRSAKey == 2) { char key[10]; snprintf(key,10," k:%d",gRSAKey); cmdInfo.append(key); } if (gDebug > 2) ErrorInfo("RpdSshAuth: sending cmdInfo (%d) %s", cmdInfo.length(), cmdInfo.c_str()); NetSend(cmdInfo.length(), kROOTD_SSH); NetSend(cmdInfo.c_str(), cmdInfo.length(), kROOTD_SSH); if (sshproto == 0) { // Old protocol: // Wait for verdict from sshd (via ssh2rpd ...) // Additional check on the username ... auth = SshToolGetAuth(unixFd, user); // Close socket SshToolDiscardSocket(uniquePipe, unixFd); } else { auth = 0; // New Protocol: // Get result from client and check it locally EMessageTypes kind; char res[5]; NetRecv(res, 5, kind); if (kind != kROOTD_SSH) { ErrorInfo("RpdSshAuth: expecting message kind: %d" " - received: %d", (int)kROOTD_SSH, kind); if (!strncmp(res,"1",1)) NetSend(kErrBadOp, kROOTD_ERR); if (unlink(authFile) == -1) if (GetErrno() != ENOENT) ErrorInfo("RpdSshAuth: cannot unlink file %s (errno: %d)", authFile,GetErrno()); delete[] authFile; // Set to Auth failed auth = 0; return auth; } if (!strncmp(res,"0",1)) { // Failure if (unlink(authFile) == -1) if (GetErrno() != ENOENT) ErrorInfo("RpdSshAuth: cannot unlink file %s (errno: %d)", authFile,GetErrno()); delete[] authFile; // Set to Auth failed auth = 0; return auth; } if (!strncmp(res,"1",1)) { // Client pretends success: lets check it locally FILE *floc = fopen(authFile,"r"); if (!floc) { ErrorInfo("RpdSshAuth: cannot open auth file:" " %s (errno: %d)", authFile, GetErrno()); NetSend(kErrFileOpen, kROOTD_ERR); if (unlink(authFile) == -1) if (GetErrno() != ENOENT) ErrorInfo("RpdSshAuth: cannot unlink file %s (errno: %d)", authFile,GetErrno()); delete[] authFile; // Set to Auth failed auth = 0; return auth; } // Stat again file to check for modification if (fstat(fileno(floc), &st1) == -1) { ErrorInfo("RpdSshAuth: cannot fstat descriptor %d", fileno(floc)); fclose(floc); delete[] authFile; // Set to Auth failed auth = 0; return auth; } char line[kMAXPATHLEN]; while (fgets(line, sizeof(line), floc) != 0) { // Get rid of '\n' if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = '\0'; if (gDebug > 2) ErrorInfo("RpdSshAuth: read line ... '%s'", line); if (!strncmp(line,"k:",2)) { // The file contains some meaningful info ... auth = 1; // Get the key, if there char key[4], val[10]; int nw = sscanf(line,"%3s %9s",key,val); if (nw >= 2 && strncmp(val,"-1",2)) { gPubKeyLen = fread((void *)gPubKey,1,sizeof(gPubKey),floc); // Import Key and Determine key type gRSAKey = RpdGetRSAKeys(gPubKey, 0); if (gRSAKey == 0) { ErrorInfo("RpdSshAuth: could not import a valid key"); gReUseRequired = 0; } } } } fclose(floc); // If the file is still empty or scrappy return if (auth == 0) { // Send error only if the client really got in // otherwise it already quit, and sending error // would screw up negotiation if (gDebug > 2) ErrorInfo("RpdSshAuth: %d %d",st1.st_ctime,st0.st_ctime); if (st1.st_ctime != st0.st_ctime) // the file has been overwritten: the client got in // but something went wrong NetSend(kErrAuthNotOK, kROOTD_ERR); if (unlink(authFile) == -1) if (GetErrno() != ENOENT) ErrorInfo("RpdSshAuth: cannot unlink file %s (errno: %d)", authFile, GetErrno()); delete[] authFile; return auth; } } else { ErrorInfo("RpdSshAuth: got unknown reply: %s", res); NetSend(kErrBadMess, kROOTD_ERR); if (unlink(authFile) == -1) if (GetErrno() != ENOENT) ErrorInfo("RpdSshAuth: cannot unlink file %s (errno: %d)", authFile,GetErrno()); delete[] authFile; // Set to Auth failed auth = 0; return auth; } // Remove the file if (unlink(authFile) == -1) if (GetErrno() != ENOENT) ErrorInfo("RpdSshAuth: cannot unlink file %s (errno: %d)", authFile,GetErrno()); } // If failure, notify and return ... if (auth <= 0) { if (auth == -1) NetSend(kErrWrongUser, kROOTD_ERR); // Send message length first else NetSend(kErrAuthNotOK, kROOTD_ERR); // Send message length first delete[] uniquePipe; delete[] pipeFile; // Set to Auth failed auth = 0; return auth; } // notify the client if (gDebug > 0 && auth == 1) ErrorInfo("RpdSshAuth: user %s authenticated by sshd", user); gSec = 4; // Save username ... strlcpy(gUser, user, sizeof(gUser)); char line[kMAXPATHLEN]; if ((gReUseAllow & gAUTH_SSH_MSK) && gReUseRequired) { if (sshproto == 0) { // Ask for the RSA key NetSend(gRSAKey, kROOTD_RSAKEY); // Receive the key securely if (RpdRecvClientRSAKey()) { ErrorInfo("RpdSshAuth: could not import a valid key" " - switch off reuse for this session"); gReUseRequired = 0; } } // Set an entry in the auth tab file for later (re)use, if required ... int offset = -1; char *token = 0; if (gReUseRequired) { SPrintf(line, kMAXPATHLEN, "4 1 %d %d %s %s", gRSAKey, gRemPid, gOpenHost.c_str(), gUser); offset = RpdUpdateAuthTab(1, line, &token); } // Comunicate login user name to client SPrintf(line, kMAXPATHLEN, "%s %d", gUser, offset); NetSend(strlen(line), kROOTD_SSH); // Send message length first NetSend(line, kMESS_STRING); if (gReUseRequired && offset > -1) { // Send over the token if (!token || (token && RpdSecureSend(token) == -1)) { ErrorInfo ("RpdSshAuth: problems secure-sending token" " - may result in corrupted token"); } if (token) delete[] token; } gOffSet = offset; } else { // Comunicate login user name to client SPrintf(line, kMAXPATHLEN, "%s -1", gUser); NetSend(strlen(line), kROOTD_SSH); // Send message length first NetSend(line, kMESS_STRING); } // Release allocated memory delete[] uniquePipe; delete[] pipeFile; delete[] authFile; return auth; } //______________________________________________________________________________ int RpdKrb5Auth(const char *sstr) { // Authenticate via Kerberos. int auth = 0; #ifdef R__KRB5 NetSend(1, kROOTD_KRB5); // TAuthenticate will respond to our encouragement by sending krb5 // authentication through the socket int retval; if (gDebug > 2) ErrorInfo("RpdKrb5Auth: analyzing ... %s", sstr); if (gClientProtocol > 8) { int lenU, ofs, opt; char dumm[256]; // Decode subject string sscanf(sstr, "%d %d %d %d %255s", &gRemPid, &ofs, &opt, &lenU, dumm); gReUseRequired = (opt & kAUTH_REUSE_MSK); #ifdef R__SSL if (gRSASSLKey) { // Determine type of RSA key required gRSAKey = (opt & kAUTH_RSATY_MSK) ? 2 : 1; } else gRSAKey = 1; #else gRSAKey = 1; #endif } // Init context retval = krb5_init_context(&gKcontext); if (retval) { ErrorInfo("RpdKrb5Auth: %s while initializing krb5", error_message(retval)); return auth; } // Use special Keytab file, if specified if (gKeytabFile.length()) { if ((retval = krb5_kt_resolve(gKcontext, gKeytabFile.c_str(), &gKeytab))) ErrorInfo("RpdKrb5Auth: %s while resolving keytab file %s", error_message(retval),gKeytabFile.c_str()); } // get service principal const char *service = "host"; if (gDebug > 2) ErrorInfo("RpdKrb5Auth: using service: %s ",service); krb5_principal server; if ((retval = krb5_sname_to_principal(gKcontext, 0, service, KRB5_NT_SRV_HST, &server))) { ErrorInfo("RpdKrb5Auth: while generating service name (%s): %d %s", service, retval, error_message(retval)); RpdFreeKrb5Vars(gKcontext, 0, 0, 0, 0); return auth; } // listen for authentication from the client krb5_auth_context auth_context = 0; krb5_ticket *ticket; char proto_version[100] = "krootd_v_1"; int sock = NetGetSockFd(); if (gDebug > 2) ErrorInfo("RpdKrb5Auth: recvauth ... "); if ((retval = krb5_recvauth(gKcontext, &auth_context, (krb5_pointer) &sock, proto_version, server, 0, gKeytab, // default gKeytab is 0 &ticket))) { ErrorInfo("RpdKrb5Auth: recvauth failed--%s", error_message(retval)); RpdFreeKrb5Vars(gKcontext, server, 0, 0, 0); return auth; } // get client name char *cname; if ((retval = krb5_unparse_name(gKcontext, ticket->enc_part2->client, &cname))) { ErrorInfo("RpdKrb5Auth: unparse failed: %s", error_message(retval)); RpdFreeKrb5Vars(gKcontext, server, ticket, auth_context, 0); return auth; } if (gDebug > 2) ErrorInfo("RpdKrb5Auth: name in ticket is: %s",cname); using std::string; std::string user = std::string(cname); free(cname); std::string reply = std::string("authenticated as ").append(user); // set user name // avoid using 'erase' (it is buggy with some compilers) snprintf(gUser,64,"%s",user.c_str()); char *pc = 0; // cut off realm if ((pc = (char *)strstr(gUser,"@"))) *pc = '\0'; // drop instances, if any if ((pc = (char *)strstr(gUser,"/"))) *pc = '\0'; std::string targetUser = std::string(gUser); if (gClientProtocol >= 9) { // Receive target name if (gDebug > 2) ErrorInfo("RpdKrb5Auth: receiving target user ... "); EMessageTypes kind; char buffer[66]; NetRecv(buffer, 65, kind); if (kind != kROOTD_KRB5) { ErrorInfo("RpdKrb5Auth: protocol error, received message" " of type %d instead of %d\n",kind,kROOTD_KRB5); } buffer[65] = 0; targetUser = std::string(buffer); if (gDebug > 2) ErrorInfo("RpdKrb5Auth: received target user %s ",buffer); } if (gDebug > 2) ErrorInfo("RpdKrb5Auth: using ticket file: %s ... ",getenv("KRB5CCNAME")); // If the target user is not the owner of the principal // check if the user is authorized by the target user if (targetUser != gUser) { if (krb5_kuserok(gKcontext, ticket->enc_part2->client, targetUser.c_str())) { if (gDebug > 2) ErrorInfo("RpdKrb5Auth: change user from %s to %s successful", gUser,targetUser.c_str()); snprintf(gUser,64,"%s",targetUser.c_str()); reply = std::string("authenticated as ").append(gUser); } else { ErrorInfo("RpdKrb5Auth: could not change user from %s to %s", gUser,targetUser.c_str()); ErrorInfo("RpdKrb5Auth: continuing with user: %s",gUser); } } // Get credentials if in a PROOF session if (gClientProtocol >= 9 && (gService == kPROOFD || gClientProtocol < 11)) { char *data = 0; int size = 0; if (gDebug > 2) ErrorInfo("RpdKrb5Auth: receiving forward cred ... "); { EMessageTypes kind; char bufLen[20]; NetRecv(bufLen, 20, kind); if (kind != kROOTD_KRB5) { ErrorInfo("RpdKrb5Auth: protocol error, received" " message of type %d instead of %d\n", kind, kROOTD_KRB5); } size = atoi(bufLen); if (gDebug > 3) ErrorInfo("RpdKrb5Auth: got len '%s' %d ", bufLen, size); data = new char[size+1]; // Receive and decode encoded public key int Nrec = NetRecvRaw(data, size); if (gDebug > 3) ErrorInfo("RpdKrb5Auth: received %d ", Nrec); } krb5_data forwardCreds; forwardCreds.data = data; forwardCreds.length = size; if (gDebug > 2) ErrorInfo("RpdKrb5Auth: received forward cred ... %d %d %d", data[0], data[1], data[2]); int net = sock; retval = krb5_auth_con_genaddrs(gKcontext, auth_context, net, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR); if (retval) { ErrorInfo("RpdKrb5Auth: failed auth_con_genaddrs is: %s\n", error_message(retval)); } bool forwarding = true; krb5_creds **creds = 0; if ((retval = krb5_rd_cred(gKcontext, auth_context, &forwardCreds, &creds, 0))) { ErrorInfo("RpdKrb5Auth: rd_cred failed--%s", error_message(retval)); forwarding = false; } if (data) delete[] data; struct passwd *pw = getpwnam(gUser); if (forwarding && pw) { Int_t fromUid = getuid(); Int_t fromEUid = geteuid(); if (setresuid(pw->pw_uid, pw->pw_uid, fromEUid) == -1) { ErrorInfo("RpdKrb5Auth: can't setuid for user %s", gUser); NetSend(kErrNotAllowed, kROOTD_ERR); RpdFreeKrb5Vars(gKcontext, server, ticket, auth_context, creds); return auth; } if (gDebug>5) ErrorInfo("RpdKrb5Auth: saving ticket to cache ..."); krb5_context context; // Init context retval = krb5_init_context(&context); if (retval) { ErrorInfo("RpdKrb5Auth: %s while initializing second krb5", error_message(retval)); NetSend(kErrNotAllowed, kROOTD_ERR); RpdFreeKrb5Vars(gKcontext, server, ticket, auth_context, creds); return auth; } krb5_ccache cache = 0; char ccacheName[256]; SPrintf(ccacheName,256,"%240s_root_%d",krb5_cc_default_name(context),getpid()); if ((retval = krb5_cc_resolve(context, ccacheName, &cache))) { ErrorInfo("RpdKrb5Auth: cc_default failed--%s", error_message(retval)); NetSend(kErrNotAllowed, kROOTD_ERR); krb5_free_context(context); RpdFreeKrb5Vars(gKcontext, server, ticket, auth_context, creds); return auth; } { char *ccname = new char[strlen("KRB5CCNAME")+strlen(ccacheName)+2]; SPrintf(ccname, strlen("KRB5CCNAME")+strlen(ccacheName)+2, "KRB5CCNAME=%.*s", strlen(ccacheName), ccacheName); putenv(ccname); } if (gDebug > 5) ErrorInfo("RpdKrb5Auth: working (1) on ticket to cache (%s) ... ", krb5_cc_get_name(context,cache)); // this is not working (why?) // this would mean that if a second user comes in, it will tremple // the existing one :( // if ((retval = krb5_cc_gen_new(context,&cache))) { // ErrorInfo("RpdKrb5Auth: cc_gen_new failed--%s", // error_message(retval)); // return auth; // } const char *cacheName = krb5_cc_get_name(context,cache); if (gDebug>5) ErrorInfo("RpdKrb5Auth: working (2) on ticket" " to cache (%s) ... ",cacheName); if ((retval = krb5_cc_initialize(context,cache, ticket->enc_part2->client))) { ErrorInfo("RpdKrb5Auth: cc_initialize failed--%s", error_message(retval)); RpdFreeKrb5Vars(gKcontext, server, ticket, auth_context, creds); krb5_free_context(context); NetSend(kErrNotAllowed, kROOTD_ERR); return auth; } if ((retval = krb5_cc_store_cred(context,cache, *creds))) { ErrorInfo("RpdKrb5Auth: cc_store_cred failed--%s", error_message(retval)); NetSend(kErrNotAllowed, kROOTD_ERR); krb5_free_context(context); RpdFreeKrb5Vars(gKcontext, server, ticket, auth_context, creds); return auth; } if (gDebug>5) ErrorInfo("RpdKrb5Auth: done ticket to cache (%s) ... ", cacheName); if ((retval = krb5_cc_close(context,cache))) { ErrorInfo("RpdKrb5Auth: cc_close failed--%s", error_message(retval)); NetSend(kErrNotAllowed, kROOTD_ERR); krb5_free_context(context); RpdFreeKrb5Vars(gKcontext, server, ticket, auth_context, creds); return auth; } // free context krb5_free_context(context); // if ( chown( cacheName, pw->pw_uid, pw->pw_gid) != 0 ) { // ErrorInfo("RpdKrb5Auth: could not change the owner" // " ship of the cache file %s",cacheName); // } if (setresuid(fromUid,fromEUid,pw->pw_uid) == -1) { ErrorInfo("RpdKrb5Auth: can't setuid back to original uid"); NetSend(kErrNotAllowed, kROOTD_ERR); RpdFreeKrb5Vars(gKcontext, server, ticket, auth_context, creds); return auth; } } // free creds krb5_free_tgt_creds(gKcontext,creds); } NetSend(reply.c_str(), kMESS_STRING); // free allocated stuff RpdFreeKrb5Vars(gKcontext, server, ticket, auth_context, (krb5_creds **)0); // Authentication was successfull auth = 1; gSec = 2; if (gClientProtocol > 8) { char line[kMAXPATHLEN]; if ((gReUseAllow & gAUTH_KRB_MSK) && gReUseRequired) { // Ask for the RSA key NetSend(gRSAKey, kROOTD_RSAKEY); // Receive the key securely if (RpdRecvClientRSAKey()) { ErrorInfo("RpdKrb5Auth: could not import a valid key" " - switch off reuse for this session"); gReUseRequired = 0; } // Set an entry in the auth tab file for later (re)use, // if required ... int offset = -1; char *token = 0; if (gReUseRequired) { SPrintf(line, kMAXPATHLEN, "2 1 %d %d %s %s", gRSAKey, gRemPid, gOpenHost.c_str(), gUser); offset = RpdUpdateAuthTab(1, line, &token); if (gDebug > 2) ErrorInfo("RpdKrb5Auth: line:%s offset:%d", line, offset); } // Comunicate login user name to client SPrintf(line, kMAXPATHLEN, "%s %d", gUser, offset); NetSend(strlen(line), kROOTD_KRB5); // Send message length first NetSend(line, kMESS_STRING); // Send Token if (gReUseRequired && offset > -1) { if (!token || (token && RpdSecureSend(token) == -1)) { ErrorInfo("RpdKrb5Auth: problems secure-sending token" " - may result in corrupted token"); } if (token) delete[] token; } gOffSet = offset; } else { // Comunicate login user name to client SPrintf(line, kMAXPATHLEN, "%s -1", gUser); NetSend(strlen(line), kROOTD_KRB5); // Send message length first NetSend(line, kMESS_STRING); } } else { NetSend(user.c_str(), kMESS_STRING); } if (gDebug > 0) ErrorInfo("RpdKrb5Auth: user %s authenticated", gUser); #else // no krb5 support if (sstr) { } // remove compiler warning NetSend(0, kROOTD_KRB5); #endif return auth; } //______________________________________________________________________________ int RpdSRPUser(const char *sstr) { // Use Secure Remote Password protocol. // Check user id in $HOME/.srootdpass file. int auth = 0; if (!*sstr) { NetSend(kErrBadUser, kROOTD_ERR); ErrorInfo("RpdSRPUser: bad user name"); return auth; } #ifdef R__SRP // Decode subject string char user[kMAXUSERLEN] = { 0 }; if (gClientProtocol > 8) { int lenU, ofs, opt; char dumm[20]; sscanf(sstr, "%d %d %d %d %127s %19s", &gRemPid, &ofs, &opt, &lenU, user, dumm); lenU = (lenU > kMAXUSERLEN-1) ? kMAXUSERLEN-1 : lenU; user[lenU] = '\0'; gReUseRequired = (opt & kAUTH_REUSE_MSK); #ifdef R__SSL if (gRSASSLKey) { // Determine type of RSA key required gRSAKey = (opt & kAUTH_RSATY_MSK) ? 2 : 1; } else gRSAKey = 1; #else gRSAKey = 1; #endif } else { SPrintf(user,kMAXUSERLEN,"%s",sstr); } struct passwd *pw = getpwnam(user); if (!pw) { NetSend(kErrNoUser, kROOTD_ERR); ErrorInfo("RpdSRPUser: user %s unknown", user); return auth; } // Method cannot be attempted for anonymous users ... (ie data servers )... if (!strcmp(pw->pw_shell, "/bin/false")) { NetSend(kErrNotAllowed, kROOTD_ERR); ErrorInfo("RpdSRPUser: no SRP for anonymous user '%s' ", user); return auth; } // If server is not started as root and user is not same as the // one who started rootd then authetication is not ok. uid_t uid = getuid(); if (uid && uid != pw->pw_uid) { NetSend(kErrBadUser, kROOTD_ERR); ErrorInfo("RpdSRPUser: user not same as effective user of rootd"); return auth; } NetSend(auth, kROOTD_AUTH); strlcpy(gUser, user, sizeof(gUser)); std::string srootdpass, srootdconf; if (gAltSRPPass.length()) { srootdpass = gAltSRPPass; } else { srootdpass = std::string(pw->pw_dir).append(gSRootdPass); } srootdconf = srootdpass + std::string(".conf"); FILE *fp1 = fopen(srootdpass.c_str(), "r"); if (!fp1) { NetSend(kErrFileOpen, kROOTD_ERR); ErrorInfo("RpdSRPUser: error opening %s", srootdpass.c_str()); return auth; } FILE *fp2 = fopen(srootdconf.c_str(), "r"); if (!fp2) { NetSend(kErrFileOpen, kROOTD_ERR); ErrorInfo("RpdSRPUser: error opening %s", srootdconf.c_str()); if (fp1) fclose(fp1); return auth; } struct t_pw *tpw = t_openpw(fp1); if (!tpw) { NetSend(kErrFileOpen, kROOTD_ERR); ErrorInfo("RpdSRPUser: unable to open password file %s", srootdpass.c_str()); fclose(fp1); fclose(fp2); return auth; } struct t_conf *tcnf = t_openconf(fp2); if (!tcnf) { NetSend(kErrFileOpen, kROOTD_ERR); ErrorInfo("RpdSRPUser: unable to open configuration file %s", srootdconf.c_str()); t_closepw(tpw); fclose(fp1); fclose(fp2); return auth; } #if R__SRP_1_1 struct t_server *ts = t_serveropen(gUser, tpw, tcnf); #else struct t_server *ts = t_serveropenfromfiles(gUser, tpw, tcnf); #endif if (tcnf) t_closeconf(tcnf); if (tpw) t_closepw(tpw); if (fp2) fclose(fp2); if (fp1) fclose(fp1); if (!ts) { NetSend(kErrNoUser, kROOTD_ERR); ErrorInfo("RpdSRPUser: user %s not found SRP password file", gUser); return auth; } char hexbuf[MAXHEXPARAMLEN]; // send n to client NetSend(t_tob64(hexbuf, (char *) ts->n.data, ts->n.len), kROOTD_SRPN); // send g to client NetSend(t_tob64(hexbuf, (char *) ts->g.data, ts->g.len), kROOTD_SRPG); // send salt to client NetSend(t_tob64(hexbuf, (char *) ts->s.data, ts->s.len), kROOTD_SRPSALT); struct t_num *B = t_servergenexp(ts); // receive A from client EMessageTypes kind; if (NetRecv(hexbuf, MAXHEXPARAMLEN, kind) < 0) { NetSend(kErrFatal, kROOTD_ERR); ErrorInfo("RpdSRPUser: error receiving A from client"); return auth; } if (kind != kROOTD_SRPA) { NetSend(kErrFatal, kROOTD_ERR); ErrorInfo("RpdSRPUser: expected kROOTD_SRPA message"); return auth; } unsigned char buf[MAXPARAMLEN]; struct t_num A; A.data = buf; A.len = t_fromb64((char *) A.data, hexbuf); // send B to client NetSend(t_tob64(hexbuf, (char *) B->data, B->len), kROOTD_SRPB); t_servergetkey(ts, &A); // receive response from client if (NetRecv(hexbuf, MAXHEXPARAMLEN, kind) < 0) { NetSend(kErrFatal, kROOTD_ERR); ErrorInfo("RpdSRPUser: error receiving response from client"); return auth; } if (kind != kROOTD_SRPRESPONSE) { NetSend(kErrFatal, kROOTD_ERR); ErrorInfo("RpdSRPUser: expected kROOTD_SRPRESPONSE message"); return auth; } unsigned char cbuf[20]; t_fromhex((char *) cbuf, hexbuf); if (!t_serververify(ts, cbuf)) { // authentication successful if (gDebug > 0) ErrorInfo("RpdSRPUser: user %s authenticated", gUser); auth = 1; gSec = 1; if (gClientProtocol > 8) { char line[kMAXPATHLEN]; if ((gReUseAllow & gAUTH_SRP_MSK) && gReUseRequired) { // Ask for the RSA key NetSend(gRSAKey, kROOTD_RSAKEY); // Receive the key securely if (RpdRecvClientRSAKey()) { ErrorInfo ("RpdSRPAuth: could not import a valid key" " - switch off reuse for this session"); gReUseRequired = 0; } // Set an entry in the auth tab file for later (re)use, if required ... int offset = -1; char *token = 0; if (gReUseRequired) { SPrintf(line, kMAXPATHLEN, "1 1 %d %d %s %s", gRSAKey, gRemPid, gOpenHost.c_str(), gUser); offset = RpdUpdateAuthTab(1, line, &token); } // Comunicate login user name to client SPrintf(line, kMAXPATHLEN, "%s %d", gUser, offset); NetSend(strlen(line), kROOTD_SRPUSER); // Send message length first NetSend(line, kMESS_STRING); if (gReUseRequired && offset > -1) { // Send Token if (RpdSecureSend(token) == -1) { ErrorInfo("RpdSRPUser: problems secure-sending token" " - may result in corrupted token"); } if (token) delete[] token; } gOffSet = offset; } else { // Comunicate login user name to client SPrintf(line, kMAXPATHLEN, "%s -1", gUser); NetSend(strlen(line), kROOTD_SRPUSER); // Send message length first NetSend(line, kMESS_STRING); } } } else { if (gClientProtocol > 8) { NetSend(kErrBadPasswd, kROOTD_ERR); ErrorInfo("RpdSRPUser: authentication failed for user %s", gUser); return auth; } } t_serverclose(ts); #else NetSend(0, kROOTD_SRPUSER); #endif return auth; } //______________________________________________________________________________ int RpdCheckHostsEquiv(const char *host, const char *ruser, const char *user, int &errout) { // Check if the requesting {host,user} can be granted immediate // login on the base of the information found in /etc/hosts.equiv // and/or $HOME/.rhosts. The two files must be trustable, i.e. owned // and modifiable only by 'root' and by 'user', respectively (0600). // Returns 1 in case access can be granted, 0 in any other case // (errout contains a code for error logging on the client side) // // NB: entries granting access in one of the two files cannot be // overriden in the other file; so, system admins cannot close // access from a host and user cannot stop access to their // account if the administrator has decided so; as an example, // if this entry is found in /etc/hosts.equiv // // remote.host.dom auser // // (allowing user named 'auser' from host 'remote.host.dom' to // login to any non-root local account without specifying a // password) the following entries in $home/.rhosts are ignored // // remote.host.dom -auser // -remote.host.dom // // and access to 'auser' is always granted. This is a "feature" // of ruserok. // int rc = 0; // Effective uid int rootuser = 0; if (!geteuid() && !getegid()) rootuser = 1; // Check the files only if i) at least one exists; ii) those existing // have the right permission settings bool badfiles = 0; int nfiles = 0; // Check system file /etc/hosts.equiv if non-root char hostsequiv[20] = { "/etc/hosts.equiv" }; if (!rootuser) { // Get info about the file ... struct stat st; if (stat(hostsequiv,&st) == -1) { if (GetErrno() != ENOENT) { ErrorInfo("RpdCheckHostsEquiv: cannot stat /etc/hosts.equiv" " (errno: %d)",GetErrno()); badfiles = 1; } else if (gDebug > 1) ErrorInfo("RpdCheckHostsEquiv: %s does not exist", hostsequiv); } else { // Require 'root' ownership if (st.st_uid || st.st_gid) { if (gDebug > 0) ErrorInfo("RpdCheckHostsEquiv: /etc/hosts.equiv not owned by" " system (uid: %d, gid: %d)",st.st_uid,st.st_gid); badfiles = 1; } else { // Require WRITE permission only for owner if ((st.st_mode & S_IWGRP) || (st.st_mode & S_IWOTH)) { if (gDebug > 0) ErrorInfo("RpdCheckHostsEquiv: group or others have write" " permission on /etc/hosts.equiv: do not trust" " it (g: %d, o: %d)", (st.st_mode & S_IWGRP),(st.st_mode & S_IWOTH)); badfiles = 1; } else // Good file nfiles++; } } } // Check local file char rhosts[kMAXPATHLEN] = {0}; if (!badfiles) { struct passwd *pw = getpwnam(user); if (pw) { int ldir = strlen(pw->pw_dir); ldir = (ldir > kMAXPATHLEN - 9) ? (kMAXPATHLEN - 9) : ldir; memcpy(rhosts,pw->pw_dir,ldir); memcpy(rhosts+ldir,"/.rhosts",8); rhosts[ldir+8] = 0; if (gDebug > 2) ErrorInfo("RpdCheckHostsEquiv: checking for user file %s ...",rhosts); } else { if (gDebug > 0) ErrorInfo("RpdCheckHostsEquiv: cannot get user info with getpwnam" " (errno: %d)",GetErrno()); badfiles = 1; } if (!badfiles) { // Check the $HOME/.rhosts file ... ownership and protections struct stat st; if (stat(rhosts,&st) == -1) { if (GetErrno() != ENOENT) { ErrorInfo("RpdCheckHostsEquiv: cannot stat $HOME/.rhosts" " (errno: %d)",GetErrno()); badfiles = 1; } else ErrorInfo("RpdCheckHostsEquiv: %s/.rhosts does not exist", pw->pw_dir); } else { // Only use file when its access rights are 0600 if (!S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || (st.st_mode & 0777) != (S_IRUSR | S_IWUSR)) { if (gDebug > 0) ErrorInfo("RpdCheckHostsEquiv: unsecure permission setting" " found for $HOME/.rhosts: 0%o (must be 0600)", (st.st_mode & 0777)); badfiles = 1; } else // Good file nfiles++; } } } // if files are not available or have wrong permissions or are // not accessible, give up if (!nfiles) { if (gDebug > 0) ErrorInfo("RpdCheckHostsEquiv: no files to check"); errout = 1; if (badfiles) { if (gDebug > 0) ErrorInfo("RpdCheckHostsEquiv: config files cannot be used" " (check permissions)"); errout = 2; } return rc; } // Ok, now use ruserok to find out if {host,ruser,user} // is trusted #if defined(__sgi) || defined(_AIX) || defined(__alpha) if (ruserok((char*)host,rootuser,(char*)ruser,(char*)user) == 0) { #else if (ruserok(host,rootuser,ruser,user) == 0) { #endif if (gDebug > 0) ErrorInfo("RpdCheckHostsEquiv: remote user %s authorized to" " access %s's area",ruser,user); rc = 1; } else { if (gDebug > 0) ErrorInfo("RpdCheckHostsEquiv: no special permission from" " %s or %s",hostsequiv,rhosts); errout = 3; } return rc; } //______________________________________________________________________________ int RpdCheckSpecialPass(const char *passwd) { // Check received user's password against password in $HOME/.rootdpass. // The password is retrieved in RpdUser and temporarly saved in gPasswd. // Returns 1 in case of success authentication, 0 otherwise. // Check inputs if (!passwd) return 0; // and the saved the password if (strlen(gPasswd) <= 0) return 0; // Ok, point to the saved passwd (retrieved in RpdUser) char *rootdpass = gPasswd; int n = 0; if (gClientProtocol > 8 && gSaltRequired > 0) { n = strlen(rootdpass); if (strncmp(passwd, rootdpass, n + 1) != 0) { if (gDebug > 0) ErrorInfo("RpdCheckSpecialPass: wrong password"); rpdmemset((volatile void *)rootdpass,0,n); return 0; } } else { #ifndef R__NOCRYPT char *pass_crypt = crypt(passwd, rootdpass); #else char *pass_crypt = (char *)passwd; #endif n = strlen(rootdpass); if (strncmp(pass_crypt, rootdpass, n+1) != 0) { if (gDebug > 0) ErrorInfo("RpdCheckSpecialPass: wrong password"); rpdmemset((volatile void *)rootdpass,0,n); return 0; } } if (gDebug > 0) ErrorInfo ("RpdCheckSpecialPass: user %s authenticated via ~/.rootdpass", gUser); rpdmemset((volatile void *)rootdpass,0,n); return 1; } //______________________________________________________________________________ int RpdPass(const char *pass, int errheq) { // Check user's password. char passwd[128]; char *passw; char *pass_crypt; struct passwd *pw; #ifdef R__SHADOWPW struct spwd *spw; #endif int afs_auth = 0; #ifdef R__AFS char *reason; #endif if (gDebug > 2) ErrorInfo("RpdPass: Enter (pass length: %d)", (int)strlen(pass)); int auth = 0; errheq = (errheq > -1 && errheq < 4) ? errheq : 0; if (!*gUser) { if (gClientProtocol > 11) NetSend(gUsrPwdErr[0][errheq], kROOTD_ERR); else NetSend(kErrFatal, kROOTD_ERR); if (gDebug > 0) ErrorInfo("RpdPass: user needs to be specified first"); return auth; } if (!pass) { if (gClientProtocol > 11) NetSend(gUsrPwdErr[1][errheq], kROOTD_ERR); else NetSend(kErrNoPasswd, kROOTD_ERR); if (gDebug > 0) ErrorInfo("RpdPass: no password specified"); return auth; } int n = strlen(pass); // Passwd length should be in the correct range ... if (!n) { if (gClientProtocol > 11) NetSend(gUsrPwdErr[1][errheq], kROOTD_ERR); else NetSend(kErrBadPasswd, kROOTD_ERR); if (gDebug > 0) ErrorInfo("RpdPass: null passwd not allowed"); return auth; } if (n > (int) sizeof(passwd)) { if (gClientProtocol > 11) NetSend(gUsrPwdErr[1][errheq], kROOTD_ERR); else NetSend(kErrBadPasswd, kROOTD_ERR); if (gDebug > 0) ErrorInfo("RpdPass: passwd too long"); return auth; } // Inversion is done in RpdUser, if needed strlcpy(passwd, pass, sizeof(passwd)); // Special treatment for anonimous ... if (gAnon) { strlcpy(gPasswd, passwd, sizeof(gPasswd)); goto authok; } // ... and SpecialPass ... if (RpdCheckSpecialPass(passwd)) { goto authok; } // Get local passwd info for gUser if (!(pw = getpwnam(gUser))) { ErrorInfo("RpdPass: getpwnam failed!"); return auth; } #ifdef R__AFS void *tok = GetAFSToken(gUser, passwd, 0, -1, &reason); afs_auth = (tok) ? 1 : 0; // We do not need the token anymore DeleteAFSToken(tok); if (!afs_auth) { if (gDebug > 0) ErrorInfo("RpdPass: AFS login failed for user %s: %s", gUser, reason); // try conventional login... #endif #ifdef R__SHADOWPW // System V Rel 4 style shadow passwords if ((spw = getspnam(gUser)) == 0) { if (gDebug > 0) ErrorInfo("RpdPass: Shadow passwd not available for user %s", gUser); passw = pw->pw_passwd; } else passw = spw->sp_pwdp; #else passw = pw->pw_passwd; #endif #ifndef R__NOCRYPT if (gClientProtocol <= 8 || !gSaltRequired) { char salt[20] = {0}; int lenS = 2; if (!strncmp(passw, "$1$", 3)) { // Shadow passwd char *pd = strstr(passw + 4, "$"); lenS = (int) (pd - passw); strncpy(salt, passw, lenS); } else strncpy(salt, passw, lenS); salt[lenS] = 0; pass_crypt = crypt(passwd, salt); // Comment this } else { pass_crypt = passwd; } #else pass_crypt = passwd; #endif n = strlen(passw); if (strncmp(pass_crypt, passw, n + 1) != 0) { if (gClientProtocol > 11) NetSend(gUsrPwdErr[1][errheq], kROOTD_ERR); else NetSend(kErrBadPasswd, kROOTD_ERR); if (gDebug > 0) ErrorInfo("RpdPass: invalid password for user %s", gUser); return auth; } if (gDebug > 2) ErrorInfo("RpdPass: valid password for user %s", gUser); #ifdef R__AFS } else // afs_auth if (gDebug > 2) ErrorInfo("RpdPass: AFS login successful for user %s", gUser); #endif authok: auth = afs_auth ? 5 : 1; gSec = 0; if (gClientProtocol > 8) { // Set an entry in the auth tab file for later (re)use, if required ... int offset = -1; char *token = 0; char line[kMAXPATHLEN]; if ((gReUseAllow & gAUTH_CLR_MSK) && gReUseRequired) { SPrintf(line, kMAXPATHLEN, "0 1 %d %d %s %s", gRSAKey, gRemPid, gOpenHost.c_str(), gUser); if (!afs_auth || gService == kPROOFD) offset = RpdUpdateAuthTab(1, line, &token); if (gDebug > 2) ErrorInfo("RpdPass: got offset %d", offset); // Comunicate login user name to client SPrintf(line, kMAXPATHLEN, "%s %d", gUser, offset); if (gDebug > 2) ErrorInfo("RpdPass: sending back line %s", line); NetSend(strlen(line), kROOTD_PASS); // Send message length first NetSend(line, kMESS_STRING); if (offset > -1) { if (gDebug > 2) ErrorInfo("RpdPass: sending token %s (Crypt: %d)", token, gCryptRequired); if (gCryptRequired) { // Send over the token if (RpdSecureSend(token) == -1) { if (gDebug > 0) ErrorInfo("RpdPass: problems secure-sending token" " - may result in corrupted token"); } } else { // Send token inverted for (int i = 0; i < (int) strlen(token); i++) { token[i] = ~token[i]; } NetSend(token, kMESS_STRING); } delete[] token; } gOffSet = offset; } else { // Comunicate login user name to client SPrintf(line, kMAXPATHLEN, "%s -1", gUser); if (gDebug > 2) ErrorInfo("RpdPass: sending back line %s", line); NetSend(strlen(line), kROOTD_PASS); // Send message length first NetSend(line, kMESS_STRING); } } return auth; } //______________________________________________________________________________ int RpdGlobusInit() { // Prepare for globus authentication: check hostcer.conf and get // the credential handle. This is run once at daemon start-up #ifdef R__GLBS // Now we open the certificates and we check if we are able to // autheticate the client. In the affirmative case we initialize // our credentials and we send our subject name to the client ... // NB: we look first for a specific certificate for ROOT (default // location under /etc/grid-security/root); if this is does not // work we try to open the host certificate, which however may // require super-user privileges; finally we check if valid proxies // (for the user who started the server) are available. char *subject_name = 0; int certRc = GlbsToolCheckCert(&subject_name); if (certRc) certRc = GlbsToolCheckProxy(&subject_name); if (certRc) { ErrorInfo("RpdGlobusInit: no valid server credentials found: globus disabled"); gHaveGlobus = 0; return 1; } else { // Save the subject name gGlobusSubjName = subject_name; delete [] subject_name; // Inquire Globus credentials: // This is looking to file X509_USER_CERT for valid a X509 cert (default // /etc/grid-security/hostcert.pem) and to dir X509_CERT_DIR for trusted CAs // (default /etc/grid-security/certificates). OM_uint32 majStat = 0; OM_uint32 minStat = 0; if ((majStat = globus_gss_assist_acquire_cred(&minStat, GSS_C_ACCEPT, &gGlbCredHandle)) != GSS_S_COMPLETE) { GlbsToolError("RpdGlobusInit: gss_assist_acquire_cred", majStat, minStat, 0); if (getuid() > 0) ErrorInfo("RpdGlobusInit: non-root: make sure you have" " initialized (manually) your proxies"); return 1; } } #endif // Done return 0; } //______________________________________________________________________________ int RpdGlobusAuth(const char *sstr) { // Authenticate via Globus. int auth = 0; #ifndef R__GLBS if (sstr) { } // use sstr NetSend(0, kROOTD_GLOBUS); return auth; #else if (!gHaveGlobus) { // No valid credentials if (sstr) { } // use sstr return auth; } OM_uint32 MajStat = 0; OM_uint32 MinStat = 0; OM_uint32 GssRetFlags = 0; gss_ctx_id_t GlbContextHandle = GSS_C_NO_CONTEXT; gss_cred_id_t GlbDelCredHandle = GSS_C_NO_CREDENTIAL; int GlbTokenStatus = 0; char *GlbClientName; FILE *FILE_SockFd; char *gridmap_default = "/etc/grid-security/grid-mapfile"; EMessageTypes kind; int lSubj, offset = -1; char *user = 0; int ulen = 0; if (gDebug > 2) ErrorInfo("RpdGlobusAuth: contacted by host: %s", gOpenHost.c_str()); // Tell the remote client that we may accept Globus credentials ... NetSend(1, kROOTD_GLOBUS); // Decode subject string char Subj[kMAXPATHLEN]; int opt; char dumm[20]; sscanf(sstr, "%d %d %d %d %4095s %19s", &gRemPid, &offset, &opt, &lSubj, Subj, dumm); Subj[lSubj] = '\0'; gReUseRequired = (opt & kAUTH_REUSE_MSK); #ifdef R__SSL if (gRSASSLKey) { // Determine type of RSA key required gRSAKey = (opt & kAUTH_RSATY_MSK) ? 2 : 1; } else gRSAKey = 1; #else gRSAKey = 1; #endif if (gDebug > 2) ErrorInfo("RpdGlobusAuth: gRemPid: %d, Subj: %s (%d %d)", gRemPid, Subj, lSubj, strlen(Subj)); if (gClientProtocol < 17) { // GlbClientName will be determined from the security context ... // Now wait for client to communicate the issuer name of the certificate ... char *answer = new char[20]; NetRecv(answer, (int) sizeof(answer), kind); if (kind != kMESS_STRING) { Error(gErr, kErrAuthNotOK, "RpdGlobusAuth: client_issuer_name:received unexpected" " type of message (%d)",kind); if (answer) delete[] answer; return auth; } int client_issuer_name_len = atoi(answer); if (answer) delete[] answer; char *client_issuer_name = new char[client_issuer_name_len + 1]; NetRecv(client_issuer_name, client_issuer_name_len, kind); if (kind != kMESS_STRING) { Error(gErr, kErrAuthNotOK, "RpdGlobusAuth: client_issuer_name:received unexpected" " type of message (%d)",kind); if (client_issuer_name) delete[] client_issuer_name; return auth; } if (gDebug > 2) ErrorInfo("RpdGlobusAuth: client issuer name is: %s", client_issuer_name); } // Send our subject to the clients: it is needed to start // the handshake int sjlen = gGlobusSubjName.length() + 1; int bsnd = NetSend(sjlen, kROOTD_GLOBUS); if (gDebug > 2) ErrorInfo("RpdGlobusAuth: sent: %d (due >=%d))", bsnd, 2 * sizeof(sjlen)); bsnd = NetSend(gGlobusSubjName.c_str(), sjlen, kMESS_STRING); if (gDebug > 2) ErrorInfo("RpdGlobusAuth: sent: %d (due >=%d))", bsnd, sjlen); // We need to associate a FILE* stream with the socket // It will automatically closed when the socket will be closed ... FILE_SockFd = fdopen(NetGetSockFd(), "w+"); // Now we are ready to start negotiating with the Client if ((MajStat = globus_gss_assist_accept_sec_context(&MinStat, &GlbContextHandle, gGlbCredHandle, &GlbClientName, &GssRetFlags, 0, &GlbTokenStatus, &GlbDelCredHandle, globus_gss_assist_token_get_fd, (void *) FILE_SockFd, globus_gss_assist_token_send_fd, (void *) FILE_SockFd)) != GSS_S_COMPLETE) { GlbsToolError("RpdGlobusAuth: gss_assist_accept_sec_context", MajStat, MinStat, GlbTokenStatus); return auth; } else { auth = 1; gSec = 3; if (gDebug > 0) ErrorInfo("RpdGlobusAuth: user: %s \n authenticated", GlbClientName); } // Check if there might be the need of credentials ... if (gService == kPROOFD) { // Check that we got delegation to autheticate the slaves if (GssRetFlags | GSS_C_DELEG_FLAG) { if (gDebug > 2) ErrorInfo("RpdGlobusAuth: Pointer to del cred is %p", GlbDelCredHandle); } else { Error(gErr, kErrAuthNotOK, "RpdGlobusAuth: did not get delegated credentials (RetFlags: 0x%x)", GssRetFlags); return auth; } // Now we have to export these delegated credentials to a shared memory segment // for later use in 'proofserv' ... // credential= (gss_buffer_t)malloc(sizeof(gss_buffer_desc)); gss_buffer_t credential = new gss_buffer_desc; if ((MajStat = gss_export_cred(&MinStat, GlbDelCredHandle, 0, 0, credential)) != GSS_S_COMPLETE) { GlbsToolError("RpdGlobusAuth: gss_export_cred", MajStat, MinStat, 0); return auth; } else if (gDebug > 2) ErrorInfo("RpdGlobusAuth: credentials prepared for export"); // Now store it in shm for later use in proofserv ... int rc; if ((rc = GlbsToolStoreToShm(credential, &gShmIdCred))) { ErrorInfo ("RpdGlobusAuth: credentials not correctly stored in shm (rc: %d)", rc); } if (gDebug > 2) ErrorInfo ("RpdGlobusAuth: credentials stored in shared memory segment %d", gShmIdCred); delete credential; } else { if (gDebug > 2) ErrorInfo("RpdGlobusAuth: no need for delegated credentials (%s)", gServName[gService].c_str()); } // For Now we set the gUser to the certificate owner using the gridmap file ... // Should be understood if this is really necessary ... if (getenv("GRIDMAP") == 0) { // The installation did not specify a special location for the gridmap file // We assume the usual default ... setenv("GRIDMAP", gridmap_default, 1); if (gDebug > 2) ErrorInfo("RpdGlobusAuth: gridmap: using default file (%s)", gridmap_default); } else if (gDebug > 2) ErrorInfo("RpdGlobusAuth: gridmap: using file %s", getenv("GRIDMAP")); // Get local login name for the subject ... char AnonUser[10] = "rootd"; if (globus_gss_assist_gridmap(GlbClientName, &user)) { if (gDebug > 2) ErrorInfo ("RpdGlobusAuth: unable to get local username from gridmap: using: %s", AnonUser); user = strdup(AnonUser); if (gDebug > 2) ErrorInfo("RpdGlobusAuth: user: %s", user); } if (!strcmp(user, "anonymous")) user = strdup(AnonUser); if (!strcmp(user, AnonUser)) gAnon = 1; // No reuse for anonymous users gReUseRequired = (gAnon == 1) ? 0 : gReUseRequired; // Fill gUser and free allocated memory ulen = strlen(user); strncpy(gUser, user, ulen + 1); char line[kMAXPATHLEN]; if ((gReUseAllow & gAUTH_GLB_MSK) && gReUseRequired) { // Ask for the RSA key NetSend(gRSAKey, kROOTD_RSAKEY); // Receive the key securely if (RpdRecvClientRSAKey()) { ErrorInfo ("RpdGlobusAuth: could not import a valid key" " - switch off reuse for this session"); gReUseRequired = 0; } // Store security context and related info for later use ... offset = -1; char *token = 0; if (gReUseRequired) { int ShmId = GlbsToolStoreContext(GlbContextHandle, user); if (ShmId > 0) { SPrintf(line, kMAXPATHLEN, "3 1 %d %d %s %s %d %s", gRSAKey, gRemPid, gOpenHost.c_str(), user, ShmId, GlbClientName); offset = RpdUpdateAuthTab(1, line, &token); } else if (gDebug > 0) ErrorInfo ("RpdGlobusAuth: unable to export context to shm for later use"); } // Comunicate login user name to client (and token) SPrintf(line, kMAXPATHLEN, "%s %d", gUser, offset); NetSend(strlen(line), kROOTD_GLOBUS); // Send message length first NetSend(line, kMESS_STRING); if (gReUseRequired && offset > -1) { // Send Token if (RpdSecureSend(token) == -1) { ErrorInfo("RpdGlobusAuth: problems secure-sending token" " - may result in corrupted token"); } if (token) delete[] token; } gOffSet = offset; } else { // Comunicate login user name to client (and token) SPrintf(line, kMAXPATHLEN, "%s %d", gUser, offset); NetSend(strlen(line), kROOTD_GLOBUS); // Send message length first NetSend(line, kMESS_STRING); } // and free allocated memory free(user); free(GlbClientName); if (gDebug > 0) ErrorInfo("RpdGlobusAuth: client mapped to local user %s ", gUser); return auth; #endif } //______________________________________________________________________________ int RpdRfioAuth(const char *sstr) { // Check if user and group id specified in the request exist in the // passwd file. If they do then grant access. Very insecure: to be used // with care. int auth = 0; if (gDebug > 2) ErrorInfo("RpdRfioAuth: analyzing ... %s", sstr); if (!*sstr) { NetSend(kErrBadUser, kROOTD_ERR); ErrorInfo("RpdRfioAuth: subject string is empty"); return auth; } // Decode subject string unsigned int uid, gid; sscanf(sstr, "%u %u", &uid, &gid); // Now inquire passwd ... struct passwd *pw; if ((pw = getpwuid((uid_t) uid)) == 0) { NetSend(kErrBadUser, kROOTD_ERR); ErrorInfo("RpdRfioAuth: uid %u not found", uid); return auth; } // Check if authorized char cuid[20]; SPrintf(cuid, 20, "%u", uid); if (gUserIgnLen[5] > 0 && strstr(gUserIgnore[5], cuid) != 0) { NetSend(kErrNotAllowed, kROOTD_ERR); ErrorInfo ("RpdRfioAuth: user (%u,%s) not authorized to use (uid:gid) method", uid, pw->pw_name); return auth; } if (gUserAlwLen[5] > 0 && strstr(gUserAllow[5], cuid) == 0) { NetSend(kErrNotAllowed, kROOTD_ERR); ErrorInfo ("RpdRfioAuth: user (%u,%s) not authorized to use (uid:gid) method", uid, pw->pw_name); return auth; } // Now check group id ... if (gid != (unsigned int) pw->pw_gid) { NetSend(kErrBadUser, kROOTD_ERR); ErrorInfo ("RpdRfioAuth: group id does not match (remote:%u,local:%u)", gid, (unsigned int) pw->pw_gid); return auth; } // Set username .... strlcpy(gUser, pw->pw_name, sizeof(gUser)); // Notify, if required ... if (gDebug > 0) ErrorInfo("RpdRfioAuth: user %s authenticated (uid:%u, gid:%u)", gUser, uid, gid); // Set Auth flag auth = 1; gSec = 5; return auth; } //______________________________________________________________________________ void RpdAuthCleanup(const char *sstr, int opt) { // Terminate correctly by cleaning up the auth table (and shared // memories in case of Globus) and closing the file. // Called upon receipt of a kROOTD_CLEANUP and on SIGPIPE. int rpid = 0, sec = -1, offs = -1, nw = 0; char usr[64] = {0}; if (sstr) nw = sscanf(sstr, "%d %d %d %63s", &rpid, &sec, &offs, usr); // Turn back to superuser for cleaning, if the case if (getuid() == 0) { if (setresgid(0, 0, 0) == -1) if (gDebug > 0) ErrorInfo("RpdAuthCleanup: can't setgid to superuser"); if (setresuid(0, 0, 0) == -1) if (gDebug > 0) ErrorInfo("RpdAuthCleanup: can't setuid to superuser"); } if (opt == 0) { RpdCleanupAuthTab("all", 0, -1); // Cleanup everything (SIGPIPE) ErrorInfo("RpdAuthCleanup: cleanup ('all',0) done"); } else if (opt == 1) { if (nw == 1) { // host specific cleanup RpdCleanupAuthTab(gOpenHost.c_str(), rpid, -1); ErrorInfo("RpdAuthCleanup: cleanup ('%s',%d) done", gOpenHost.c_str(), rpid); } else if (nw == 4) { // (host,usr,method) specific cleanup if (RpdCheckOffSet(sec,usr,gOpenHost.c_str(),rpid,&offs,0,0,0)) { RpdCleanupAuthTab(gOpenHost.c_str(), rpid, offs); ErrorInfo("RpdAuthCleanup: cleanup (%s,%d,%d,%d,%s) done", gOpenHost.c_str(), rpid, sec, offs, usr); } else { ErrorInfo("RpdAuthCleanup: cleanup not done: %s", "wrong offset or already cleaned up"); } } } } //______________________________________________________________________________ void RpdInitAuth() { // Size check done in RpdUpdateAuthTab(1,...) // Reset int i; gNumAllow = gNumLeft = 0; for (i = 0; i < kMAXSEC; i++) { gAllowMeth[i] = -1; gHaveMeth[i] = 1; } // List of default authentication methods RpdDefaultAuthAllow(); } //______________________________________________________________________________ void RpdDefaultAuthAllow() { // Check configuration options and running daemons to build a default list // of secure methods. if (gDebug > 2) ErrorInfo("RpdDefaultAuthAllow: Enter"); // UsrPwdClear gAllowMeth[gNumAllow] = 0; gNumAllow++; gNumLeft++; // SSH gAllowMeth[gNumAllow] = 4; gNumAllow++; gNumLeft++; // SRP #ifdef R__SRP gAllowMeth[gNumAllow] = 1; gNumAllow++; gNumLeft++; #else // Don't have this method gHaveMeth[1] = 0; #endif // Kerberos #ifdef R__KRB5 gAllowMeth[gNumAllow] = 2; gNumAllow++; gNumLeft++; #else // Don't have this method gHaveMeth[2] = 0; #endif // Globus #ifdef R__GLBS gAllowMeth[gNumAllow] = 3; gNumAllow++; gNumLeft++; #else // Don't have this method gHaveMeth[3] = 0; #endif if (gDebug > 2) { int i; std::string temp; char cm[5]; if (gNumAllow == 0) temp.append("none"); for (i = 0; i < gNumAllow; i++) { SPrintf(cm, 5, " %3d",gAllowMeth[i]); temp.append(cm); } ErrorInfo ("RpdDefaultAuthAllow: default list of secure methods available: %s", temp.c_str()); } } //______________________________________________________________________________ int RpdCheckDaemon(const char *daemon) { // Check the running of process 'daemon'. // Info got from 'ps ax'. char cmd[kMAXPATHLEN] = { 0 }; int ch, i = 0, cnt = 0; if (gDebug > 2) ErrorInfo("RpdCheckDaemon: Enter ... %s", daemon); // Return if empty if (daemon == 0 || strlen(daemon) == 0) return cnt; // Build command SPrintf(cmd, kMAXPATHLEN, "ps ax | grep %s 2>/dev/null", daemon); // Run it ... FILE *fp = popen(cmd, "r"); if (fp != 0) { for (ch = fgetc(fp); ch != EOF; ch = fgetc(fp)) { if (ch != 10) { cmd[i++] = ch; } else { cmd[i] = '\0'; if (strstr(cmd, "grep") == 0 && strstr(cmd, "rootd") == 0 && strstr(cmd, "proofd") == 0) { cnt++; } i = 0; } } if (i > 0) { cmd[i] = '\0'; cnt++; } pclose(fp); if (gDebug > 2) ErrorInfo("RpdCheckDaemon: found %d instances of daemon %s", cnt, daemon); } else { ErrorInfo("RpdCheckDaemon: problems executing cmd ..."); } return cnt; } //______________________________________________________________________________ int RpdCheckSshd(int opt) { // Tries to connect to sshd daemon on its standard port (22) // Used if RpdCheckDaemon returns a negative result if (gDebug > 2) ErrorInfo("RpdCheckSshd: Enter ... "); int rc = 0; if (opt == 0) { // // Check netstat output ... // // build check string char cs[20]; SPrintf(cs, 20, ":%d",gSshdPort); // Run 'netstat' to check listening processes char cmd[kMAXPATHLEN] = { 0 }; SPrintf(cmd, kMAXPATHLEN, "netstat -apn 2>/dev/null | grep LISTEN | grep -v LISTENING"); FILE *fp= popen(cmd,"r"); if (fp != 0) { while (fgets(cmd, sizeof(cmd), fp) != 0) { if (gDebug > 3) ErrorInfo("RpdCheckSshd: read: %s",cmd); if (strstr(cmd,cs)) { rc = 1; break; } } pclose(fp); } else { ErrorInfo("RpdCheckSshd: Problems executing 'netstat' ..."); } if (gDebug > 2 && rc) { ErrorInfo("RpdCheckSshd: %s: %s %d", "diagnostics report", "something is listening on port", gSshdPort); } if (!rc) { ErrorInfo("RpdCheckSshd: nothing seem to listening on port %d", gSshdPort); } } else if (opt == 1) { // // Open a socket on port gSshdPort ... // // First get local host address struct hostent *h = gethostbyname("localhost"); if (h == 0) { // Make further attempt with HOSTNAME if (getenv("HOSTNAME") == 0) { ErrorInfo("RpdCheckSshd: unable to resolve local host name"); return 0; } else { h = gethostbyname(getenv("HOSTNAME")); if (h == 0) { ErrorInfo ("RpdCheckSshd: local host name is unknown to gethostbyname: '%s'", getenv("HOSTNAME")); return 0; } } } // Fill relevant sockaddr_in structure struct sockaddr_in servAddr; servAddr.sin_zero[0] = 0; servAddr.sin_family = h->h_addrtype; memcpy((char *) &servAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length); servAddr.sin_port = htons(gSshdPort); // create AF_INET socket int sd = socket(AF_INET, SOCK_STREAM, 0); if (sd < 0) { ErrorInfo("RpdCheckSshd: cannot open new AF_INET socket (errno:%d) ", errno); return 0; } /* bind any port number */ struct sockaddr_in localAddr; localAddr.sin_zero[0] = 0; localAddr.sin_family = AF_INET; localAddr.sin_addr.s_addr = htonl(INADDR_ANY); localAddr.sin_port = htons(0); if (bind(sd, (struct sockaddr *) &localAddr, sizeof(localAddr)) < 0) { ErrorInfo("RpdCheckSshd: cannot bind to local port %u", gSshdPort); close(sd); return 0; } // connect to server if (connect(sd, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0) { ErrorInfo("RpdCheckSshd: cannot connect to local port %u", gSshdPort); close(sd); return 0; } close(sd); // Sshd successfully contacted if (gDebug > 2) ErrorInfo("RpdCheckSshd: success!"); rc = 1; } return rc; } //______________________________________________________________________________ int RpdUser(const char *sstr) { // Check user id. If user id is not equal to rootd's effective uid, user // will not be allowed access, unless effective uid = 0 (i.e. root). const int kMaxBuf = 256; char recvbuf[kMaxBuf]; EMessageTypes kind; struct passwd *pw; if (gDebug > 2) ErrorInfo("RpdUser: Enter ... %s", sstr); int auth = 0; // Nothing can be done if empty message if (!*sstr) { NetSend(kErrBadUser, kROOTD_ERR); ErrorInfo("RpdUser: received empty string"); return auth; } // Parse input message char user[kMAXUSERLEN] = {0}; char ruser[kMAXUSERLEN] = {0}; if (gClientProtocol > 8) { int ulen, ofs, opt, rulen; // Decode subject string int nw = sscanf(sstr, "%d %d %d %d %63s %d %63s", &gRemPid, &ofs, &opt, &ulen, user, &rulen, ruser); ulen = (ulen >= kMAXUSERLEN) ? kMAXUSERLEN-1 : ulen; rulen = (rulen >= kMAXUSERLEN) ? kMAXUSERLEN-1 : rulen; user[ulen] = '\0'; if (nw > 5) ruser[rulen] = '\0'; gReUseRequired = (opt & kAUTH_REUSE_MSK); gCryptRequired = (opt & kAUTH_CRYPT_MSK); gSaltRequired = (opt & kAUTH_SSALT_MSK); gOffSet = ofs; #ifdef R__SSL if (gRSASSLKey) { // Determine type of RSA key required gRSAKey = (opt & kAUTH_RSATY_MSK) ? 2 : 1; } else gRSAKey = 1; #else gRSAKey = 1; #endif } else { SPrintf(user,kMAXUSERLEN,"%s",sstr); } if (gDebug > 2) ErrorInfo("RpdUser: gReUseRequired: %d gCryptRequired: %d gRSAKey: %d", gReUseRequired, gCryptRequired, gRSAKey); ERootdErrors err = kErrNoUser; if (gService == kROOTD) { // Default anonymous account ... if (!strcmp(user, "anonymous")) { user[0] = '\0'; strlcpy(user, "rootd", sizeof(user)); } } if ((pw = getpwnam(user)) == 0) { NetSend(err, kROOTD_ERR); ErrorInfo("RpdUser: user %s unknown", user); return auth; } // If server is not started as root and user is not same as the // one who started rootd then authetication is not ok. uid_t uid = getuid(); if (uid && uid != pw->pw_uid) { NetSend(kErrBadUser, kROOTD_ERR); ErrorInfo("RpdUser: user not same as effective user of rootd"); return auth; } // Check if the administrator allows authentication char cuid[20]; SPrintf(cuid, 20, "%d", (int)pw->pw_uid); if (gUserIgnLen[0] > 0 && strstr(gUserIgnore[0], cuid) != 0) { NetSend(kErrNotAllowed, kROOTD_ERR); ErrorInfo ("RpdUser: user (%d,%s) not authorized to use UsrPwd method", uid, pw->pw_name); return auth; } if (gUserAlwLen[0] > 0 && strstr(gUserAllow[0], cuid) == 0) { NetSend(kErrNotAllowed, kROOTD_ERR); ErrorInfo ("RpdUser: user (%d,%s) not authorized to use UsrPwd method", uid, pw->pw_name); return auth; } // Check /etc/hosts.equiv and/or $HOME/.rhosts int errheq = 0; if (gCheckHostsEquiv && strlen(ruser)) { if (RpdCheckHostsEquiv(gOpenHost.c_str(),ruser,user,errheq)) { auth = 3; strlcpy(gUser, user, sizeof(gUser)); return auth; } } // Check if of type anonymous ... if (!strcmp(pw->pw_shell, "/bin/false")) { err = kErrNoAnon; gAnon = 1; gReUseRequired = 0; } // Check if authorized // If not anonymous, try to get passwd // (if our system uses shadow passwds and we are not superuser // we cannot authenticate users ...) // char *passw = 0; gPasswd[0] = 0; char *passw = gPasswd; int errrdp = 0; if (gAnon == 0) { // Check ROOT specific passwd first int rcsp = RpdRetrieveSpecialPass(user,gRootdPass.c_str(), gPasswd,sizeof(gPasswd)); if (rcsp < 0) errrdp = (rcsp == -2) ? 3 : 0; if (strlen(passw) == 0 || !strcmp(passw, "x")) { #ifdef R__AFS gSaltRequired = 0; #else #ifdef R__SHADOWPW struct spwd *spw = 0; // System V Rel 4 style shadow passwords if ((spw = getspnam(user)) == 0) { if (gDebug > 0) { ErrorInfo("RpdUser: Shadow passwd not accessible for user %s",user); ErrorInfo("RpdUser: trying normal system passwd"); } } else passw = spw->sp_pwdp; #else passw = pw->pw_passwd; #endif // Check if successful if (strlen(passw) == 0 || !strcmp(passw, "x")) { if (gClientProtocol > 11) NetSend(gUsrPwdErr[errrdp][errheq], kROOTD_ERR); else NetSend(kErrNotAllowed, kROOTD_ERR); ErrorInfo("RpdUser: passwd hash not available for user %s", user); ErrorInfo ("RpdUser: user %s cannot be authenticated with this method", user); return auth; } #endif } } // Ok: Save username and go to next steps strlcpy(gUser, user, sizeof(gUser)); // Salt vars char salt[30] = { 0 }; char ctag[11] = { 0 }; int rtag = 0; int lenS = 0; if (gClientProtocol > 8) { // Prepare status flag to send back if (gAnon == 1) { // Anonymous user: we will receive a text pass in the form // user@remote.host.dom NetSend(-1, kROOTD_AUTH); } else { if (gCryptRequired) { // Named user: first we receive a session public key // Ask for the RSA key NetSend(gRSAKey, kROOTD_RSAKEY); // Receive the key securely if (RpdRecvClientRSAKey()) { ErrorInfo("RpdUser: could not import a valid key -" " switch off reuse for this session"); gReUseRequired = 0; } // We get a random tag if (gClientProtocol > 11) { RpdInitRand(); rtag = rpd_rand(); SPrintf(ctag, 11, "#%08x#",rtag); } if (gSaltRequired) { // The crypt man page says that alternative salts can be in the form '$1$...$'; // but on Ubuntu 10.04 the salt are in the form '$j$...$' where j is 6 or other. if (passw[0] == '$' && passw[2] == '$') { // Shadow passwd char *pd = strstr(passw + 4, "$"); lenS = (int) (pd - passw); strncpy(salt, passw, lenS); salt[lenS] = 0; } else { lenS = 2; strncpy(salt, passw, lenS); salt[lenS] = 0; } if (gDebug > 2) ErrorInfo("RpdUser: salt: '%s' ",salt); // We add the random tag here if (gClientProtocol > 11) { strncpy(&salt[lenS],ctag,10); salt[lenS+10] = 0; } // Send it over encrypted if (RpdSecureSend(salt) == -1) { ErrorInfo("RpdUser: problems secure-sending salt -" " may result in corrupted salt"); } } else { if (gClientProtocol > 11) { // We send the random tag here if (RpdSecureSend(ctag) == -1) { ErrorInfo("RpdUser: problems secure-sending rndmtag -" " may result in corrupted rndmtag"); } } else NetSend(0, kMESS_ANY); } } else { // We continue the authentication process in clear NetSend(0, kROOTD_AUTH); } } } else { // If we are talking to a old client protocol NetSend(0, kROOTD_AUTH); } // Get the password hash or anonymous string if (NetRecv(recvbuf, kMaxBuf, kind) < 0) { NetSend(kErrFatal, kROOTD_ERR); ErrorInfo("RpdUser: error receiving message"); return auth; } if (kind != kROOTD_PASS) { NetSend(kErrFatal, kROOTD_ERR); ErrorInfo("RpdUser: received wrong message type: %d (expecting: %d)", kind, (int) kROOTD_PASS); return auth; } if (!strncmp(recvbuf,"-1",2)) { if (gDebug > 0) ErrorInfo("RpdUser: client did not send a password - return"); return auth; } // Get passwd char *passwd = 0; int lpwd = 0; if (gAnon == 0 && gClientProtocol > 8 && gCryptRequired) { // Receive encrypted pass or its hash if (RpdSecureRecv(&passwd) == -1) { ErrorInfo ("RpdUser: problems secure-receiving pass hash - %s", "may result in authentication failure"); } // Length of the password buffer lpwd = strlen(passwd); // Check the random tag, if any if (strlen(ctag)) { // Check first that there is enough space for the tag int plen = lpwd; if (plen > 9 && passwd[plen-1] == '#' && passwd[plen-10] == '#') { if (strncmp(ctag,&passwd[plen-10],10)) { // The tag does not match; failure if (gClientProtocol > 11) NetSend(gUsrPwdErr[2][errheq], kROOTD_ERR); else NetSend(kErrBadPasswd, kROOTD_ERR); ErrorInfo("RpdUser: rndm tag mis-match" " (%s vs %s) - Failure",&passwd[plen-10],ctag); delete[] passwd; return auth; } // Tag ok: drop it plen -= 10; passwd[plen] = 0; } else { // The tag is not there or incomplete; failure if (gClientProtocol > 11) NetSend(gUsrPwdErr[2][errheq], kROOTD_ERR); else NetSend(kErrBadPasswd, kROOTD_ERR); ErrorInfo("RpdUser: rndm tag missing or incomplete" " (pw length: %d) - Failure", plen); delete[] passwd; return auth; } } // If we required an hash check that we got it // (the client sends the passwd if the crypt version is different) if (gSaltRequired && lenS) { if (strncmp(passwd,salt,lenS)) gSaltRequired = 0; } } else { // Receive clear or anonymous pass passwd = new char[strlen(recvbuf) + 1]; // Re-invert pass int i, n = strlen(recvbuf); for (i = 0; i < n; i++) passwd[i] = ~recvbuf[i]; passwd[i] = '\0'; if (gDebug > 2 && gAnon) ErrorInfo("RpdUser: received anonymous pass: '%s'", passwd); } // Check the passwd and login if ok ... auth = RpdPass(passwd,errheq); // Erase memory used for password passwd = (char *)rpdmemset((volatile void *)passwd,0,lpwd); delete[] passwd; return auth; } //______________________________________________________________________________ int RpdGuessClientProt(const char *buf, EMessageTypes kind) { // Try a guess of the client protocol from what she/he sent over // the net ... if (gDebug > 2) ErrorInfo("RpdGuessClientProt: Enter: buf: '%s', kind: %d", buf, (int) kind); // Assume same version as us. int proto = 9; // Clear authentication if (kind == kROOTD_USER) { char usr[64], rest[256]; int ns = sscanf(buf, "%63s %255s", usr, rest); if (ns == 1) proto = 8; } // SRP authentication if (kind == kROOTD_SRPUSER) { char usr[64], rest[256]; int ns = sscanf(buf, "%63s %255s", usr, rest); if (ns == 1) proto = 8; } // Kerberos authentication if (kind == kROOTD_KRB5) { if (strlen(buf) == 0) proto = 8; } if (gDebug > 2) ErrorInfo("RpdGuessClientProt: guess for gClientProtocol is %d", proto); // Return the guess return proto; } //______________________________________________________________________________ char *RpdGetRandString(int Opt, int Len) { // Allocates and Fills a NULL terminated buffer of length Len+1 with // Len random characters. // Return pointer to the buffer (to be deleted by the caller) // Opt = 0 any non dangerous char // 1 letters and numbers (upper and lower case) // 2 hex characters (upper and lower case) // 3 crypt like [a-zA-Z0-9./] unsigned int iimx[4][4] = { { 0x0, 0xffffff08, 0xafffffff, 0x2ffffffe }, // Opt = 0 { 0x0, 0x3ff0000, 0x7fffffe, 0x7fffffe }, // Opt = 1 { 0x0, 0x3ff0000, 0x7e, 0x7e }, // Opt = 2 { 0x0, 0x3ffc000, 0x7fffffe, 0x7fffffe } // Opt = 3 }; const char *cOpt[4] = { "Any", "LetNum", "Hex", "Crypt" }; // Default option 0 if (Opt < 0 || Opt > 3) { Opt = 0; if (gDebug > 2) ErrorInfo("RpdGetRandString: Unknown option: %d : assume 0", Opt); } if (gDebug > 2) ErrorInfo("RpdGetRandString: Enter ... Len: %d %s", Len, cOpt[Opt]); // Allocate buffer char *buf = new char[Len + 1]; // Init Random machinery ... if (!gRandInit) RpdInitRand(); // randomize int k = 0; int i, j, l, m, frnd; while (k < Len) { frnd = rpd_rand(); for (m = 7; m < 32; m += 7) { i = 0x7F & (frnd >> m); j = i / 32; l = i - j * 32; if ((iimx[Opt][j] & (1 << l))) { buf[k] = i; k++; } if (k == Len) break; } } // NULL terminated buf[Len] = 0; if (gDebug > 2) ErrorInfo("RpdGetRandString: got '%s' ", buf); return buf; } //______________________________________________________________________________ int RpdGetRSAKeys(const char *pubkey, int Opt) { // Get public key from file pubkey (Opt == 1) or string pubkey (Opt == 0). char str[kMAXPATHLEN] = { 0 }; int keytype = 0; if (gDebug > 2) ErrorInfo("RpdGetRSAKeys: enter: string len: %d, opt %d ", gPubKeyLen, Opt); if (!pubkey) return keytype; char *theKey = 0; FILE *fKey = 0; // Parse input type if (Opt == 1) { // Ok, now open it fKey = fopen(pubkey, "r"); if (!fKey) { if (GetErrno() == EACCES) { struct passwd *pw = getpwuid(getuid()); char *usr = 0; if (pw) usr = pw->pw_name; ErrorInfo("RpdGetRSAKeys: access to key file %s denied" " to user: %s", pubkey, (usr ? usr : (char *)"????")); } else ErrorInfo("RpdGetRSAKeys: cannot open key file" " %s (errno: %d)", pubkey, GetErrno()); return 0; } // Check first the permissions: should be 0600 struct stat st; if (fstat(fileno(fKey), &st) == -1) { ErrorInfo("RpdGetRSAKeys: cannot stat descriptor %d" " %s (errno: %d)", fileno(fKey), GetErrno()); fclose(fKey); return 0; } if (!S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || (st.st_mode & 0777) != (S_IRUSR | S_IWUSR)) { ErrorInfo("RpdGetRSAKeys: key file %s: wrong permissions" " 0%o (should be 0600)", pubkey, (st.st_mode & 0777)); fclose(fKey); return 0; } gPubKeyLen = fread((void *)str,1,sizeof(str),fKey); if (gDebug > 2) ErrorInfo("RpdGetRSAKeys: length of the read key: %d",gPubKeyLen); // This the key theKey = str; } else { // the key is the argument theKey = (char *)pubkey; } if (gPubKeyLen > 0) { // Skip spaces at beginning, if any int k = 0; while (theKey[k] == 32) k++; keytype = gRSAKey; // The format of keytype 1 is ### char *pd1 = 0, *pd2 = 0, *pd3 = 0; pd1 = strstr(theKey, "#"); if (pd1) pd2 = strstr(pd1 + 1, "#"); if (pd2) pd3 = strstr(pd2 + 1, "#"); if (keytype == 1) { if (!pd1 || !pd2 || !pd3) { if (gDebug > 0) ErrorInfo("RpdGetRSAKeys: bad format for keytype %d" " - exit", keytype); keytype = 0; } } if (keytype == 1) { if (gDebug > 2) ErrorInfo("RpdGetRSAKeys: keytype %d ", keytype); // Get ... int l1 = (int) (pd2 - pd1 - 1); char *n_exp_RSA = new char[l1 + 1]; strncpy(n_exp_RSA, pd1 + 1, l1); n_exp_RSA[l1] = 0; if (gDebug > 2) ErrorInfo("RpdGetRSAKeys: got %d bytes for n_exp_RSA", strlen(n_exp_RSA)); // Now int l2 = (int) (pd3 - pd2 - 1); char *d_exp_RSA = new char[l2 + 1]; strncpy(d_exp_RSA, pd2 + 1, l2); d_exp_RSA[l2] = 0; if (gDebug > 2) ErrorInfo("RpdGetRSAKeys: got %d bytes for d_exp_RSA", strlen(d_exp_RSA)); rsa_num_sget(&gRSA_n, n_exp_RSA); rsa_num_sget(&gRSA_d, d_exp_RSA); delete[] n_exp_RSA; delete[] d_exp_RSA; } else if (keytype == 2){ #ifdef R__SSL // try SSL if (gDebug > 2) ErrorInfo("RpdGetRSAKeys: keytype %d ", keytype); // Now set the key locally in BF form BF_set_key(&gBFKey, gPubKeyLen, (const unsigned char *)theKey); #else if (gDebug > 0) { ErrorInfo("RpdGetRSAKeys: not compiled with SSL support:" " you should not have got here!"); } #endif } } if (fKey) fclose(fKey); return keytype; } //______________________________________________________________________________ int RpdSavePubKey(const char *PubKey, int OffSet, char *user) { // Save RSA public key into file for later use by other rootd/proofd. // Return: 0 if ok // 1 if not ok // 2 if not ok because file already exists and cannot be // overwritten int retval = 0; if (gRSAKey == 0 || OffSet < 0) return 1; std::string pukfile = gRpdKeyRoot; pukfile.append(ItoA(OffSet)); // Unlink the file first if (unlink(pukfile.c_str()) == -1) { if (GetErrno() != ENOENT) // File exists and cannot overwritten by this process return 2; } // Create file int ipuk = -1; ipuk = open(pukfile.c_str(), O_WRONLY | O_CREAT, 0600); if (ipuk == -1) { ErrorInfo("RpdSavePubKey: cannot open file %s (errno: %d)", pukfile.c_str(),GetErrno()); if (GetErrno() == ENOENT) return 2; else return 1; } // If root process set ownership of the pub key to the user if (getuid() == 0) { struct passwd *pw = getpwnam(user); if (pw) { if (fchown(ipuk,pw->pw_uid,pw->pw_gid) == -1) { ErrorInfo("RpdSavePubKey: cannot change ownership" " of %s (errno: %d)",pukfile.c_str(),GetErrno()); retval = 1; } } else { ErrorInfo("RpdSavePubKey: getpwnam failure (errno: %d)",GetErrno()); retval = 1; } } // Write the key if no error occured if (retval == 0) { while (write(ipuk, PubKey, gPubKeyLen) < 0 && GetErrno() == EINTR) ResetErrno(); } // close the file close(ipuk); // Over return retval; } //______________________________________________________________________________ int RpdSecureSend(char *str) { // Encode null terminated str using the session private key indcated by Key // and sends it over the network. // Returns number of bytes sent.or -1 in case of error. char buftmp[kMAXSECBUF]; char buflen[20]; int slen = strlen(str) + 1; int ttmp = 0; int nsen = -1; if (gRSAKey == 1) { strncpy(buftmp, str, slen); buftmp[slen] = 0; ttmp = rsa_encode(buftmp, slen, gRSA_n, gRSA_d); } else if (gRSAKey == 2) { #ifdef R__SSL ttmp = strlen(str); if ((ttmp % 8) > 0) // It should be a multiple of 8! ttmp = ((ttmp + 8)/8) * 8; unsigned char iv[8]; memset((void *)&iv[0],0,8); BF_cbc_encrypt((const unsigned char *)str, (unsigned char *)buftmp, strlen(str), &gBFKey, iv, BF_ENCRYPT); #else ErrorInfo("RpdSecureSend: Not compiled with SSL support:" " you should not have got here! - return"); #endif } else { ErrorInfo("RpdSecureSend: Unknown key option (%d) - return", gRSAKey); } // Send the buffer now SPrintf(buflen, 20, "%d", ttmp); NetSend(buflen, kROOTD_ENCRYPT); nsen = NetSendRaw(buftmp, ttmp); if (gDebug > 4) ErrorInfo("RpdSecureSend: sent %d bytes (expected: %d) - keytype: %d", nsen, ttmp, gRSAKey); return nsen; } //______________________________________________________________________________ int RpdSecureRecv(char **str) { // Receive buffer and decode it in str using key indicated by Key type. // Return number of received bytes or -1 in case of error. char buftmp[kMAXSECBUF]; char buflen[20]; int nrec = -1; // We must get a pointer ... if (!str) return nrec; if (gDebug > 2) ErrorInfo("RpdSecureRecv: enter ... (key is %d)", gRSAKey); EMessageTypes kind; NetRecv(buflen, 20, kind); int len = atoi(buflen); if (gDebug > 4) ErrorInfo("RpdSecureRecv: got len '%s' %d ", buflen, len); if (!strncmp(buflen, "-1", 2)) return nrec; // receive the buffer nrec = NetRecvRaw(buftmp,len); // decode it if (gRSAKey == 1) { rsa_decode(buftmp, len, gRSA_n, gRSA_d); if (gDebug > 2) ErrorInfo("RpdSecureRecv: Local: decoded string is %d bytes long", strlen(buftmp)); // Prepare output const size_t strSize = strlen(buftmp) + 1; *str = new char[strSize]; strlcpy(*str, buftmp, strSize); } else if (gRSAKey == 2) { #ifdef R__SSL unsigned char iv[8]; memset((void *)&iv[0],0,8); *str = new char[nrec + 1]; BF_cbc_encrypt((const unsigned char *)buftmp, (unsigned char *)(*str), nrec, &gBFKey, iv, BF_DECRYPT); (*str)[nrec] = '\0'; #else ErrorInfo("RpdSecureRecv: Not compiled with SSL support:" " you should not have got here! - return"); #endif } else { ErrorInfo("RpdSecureRecv: Unknown key option (%d) - return", gRSAKey); } return nrec; } //______________________________________________________________________________ int RpdGenRSAKeys(int setrndinit) { // Generate a valid pair of private/public RSA keys to protect for // authentication password and token exchange // Returns 1 if a good key pair is not found after kMAXRSATRIES attempts // Returns 0 if a good key pair is found // If setrndinit = 1, no futher init of the random engine if (gDebug > 2) ErrorInfo("RpdGenRSAKeys: enter"); // Init Random machinery ... if (!gRandInit) RpdInitRand(); gRandInit = setrndinit; #ifdef R__NOCRYPT // Generate a random salt char *rsalt = RpdGetRandString(3,8); if (rsalt) { gRndmSalt = std::string(rsalt); delete[] rsalt; } else { if (gDebug > 0) ErrorInfo("RpdGenRSAKeys: could not generate random salt"); } #endif #ifdef R__SSL // Generate also the SSL key if (gDebug > 2) ErrorInfo("RpdGenRSAKeys: Generate RSA SSL keys"); // Init SSL ... SSL_library_init(); // ... and its error strings SSL_load_error_strings(); // Load Ciphers OpenSSL_add_all_ciphers(); // Number of bits for key Int_t nbits = 1024; // Public exponent Int_t pubex = 17; // Init random engine char *rbuf = RpdGetRandString(0,40); RAND_seed(rbuf,strlen(rbuf)); // Generate Key gRSASSLKey = RSA_generate_key(nbits,pubex,0,0); // Bio for exporting the pub key BIO *bkey = BIO_new(BIO_s_mem()); // Write public key to BIO PEM_write_bio_RSAPublicKey(bkey,gRSASSLKey); // Read key from BIO to buf Int_t sbuf = 2*RSA_size(gRSASSLKey); char *kbuf = new char[sbuf]; BIO_read(bkey,(void *)kbuf,sbuf); BIO_free(bkey); // Prepare export gRSAPubExport[1].len = sbuf; gRSAPubExport[1].keys = new char[gRSAPubExport[1].len + 2]; strncpy(gRSAPubExport[1].keys,kbuf,gRSAPubExport[1].len); gRSAPubExport[1].keys[gRSAPubExport[1].len-1] = '\0'; delete[] kbuf; if (gDebug > 2) ErrorInfo("RpdGenRSAKeys: SSL: export pub:\n%.*s", gRSAPubExport[1].len,gRSAPubExport[1].keys); // We have at least one key gRSAInit = 1; #endif // Sometimes some bunch is not decrypted correctly // That's why we make retries to make sure that encryption/decryption // works as expected bool notOK = 1; rsa_NUMBER p1, p2, rsa_n, rsa_e, rsa_d; int l_n = 0, l_d = 0; #if R__RSADEB Int_t l_e = 0; char buf[rsa_STRLEN]; #endif char buf_n[rsa_STRLEN], buf_e[rsa_STRLEN], buf_d[rsa_STRLEN]; int nAttempts = 0; int thePrimeLen = kPRIMELENGTH; int thePrimeExp = kPRIMEEXP + 5; // Prime probability = 1-0.5^thePrimeExp while (notOK && nAttempts < kMAXRSATRIES) { nAttempts++; if (gDebug > 2 && nAttempts > 1) { ErrorInfo("RpdGenRSAKeys: retry no. %d",nAttempts); srand(rpd_rand()); } // Valid pair of primes p1 = rsa_genprim(thePrimeLen, thePrimeExp); p2 = rsa_genprim(thePrimeLen+1, thePrimeExp); // Retry if equal int nPrimes = 0; while (rsa_cmp(&p1, &p2) == 0 && nPrimes < kMAXRSATRIES) { nPrimes++; if (gDebug > 2) ErrorInfo("RpdGenRSAKeys: equal primes: regenerate (%d times)",nPrimes); srand(rpd_rand()); p1 = rsa_genprim(thePrimeLen, thePrimeExp); p2 = rsa_genprim(thePrimeLen+1, thePrimeExp); } #if R__RSADEB if (gDebug > 2) { rsa_num_sput(&p1, buf, rsa_STRLEN); ErrorInfo("RpdGenRSAKeys: local: p1: '%s' ", buf); rsa_num_sput(&p2, buf, rsa_STRLEN); ErrorInfo("RpdGenRSAKeys: local: p2: '%s' ", buf); } #endif // Generate keys if (rsa_genrsa(p1, p2, &rsa_n, &rsa_e, &rsa_d)) { if (gDebug > 0) ErrorInfo("RpdGenRSAKeys: genrsa: attempt %d to generate" " keys failed",nAttempts); continue; } // Determine their lengths rsa_num_sput(&rsa_n, buf_n, rsa_STRLEN); l_n = strlen(buf_n); rsa_num_sput(&rsa_e, buf_e, rsa_STRLEN); #if R__RSADEB l_e = strlen(buf_e); #endif rsa_num_sput(&rsa_d, buf_d, rsa_STRLEN); l_d = strlen(buf_d); #if R__RSADEB if (gDebug > 2) { ErrorInfo("RpdGenRSAKeys: local: n: '%s' length: %d", buf_n, l_n); ErrorInfo("RpdGenRSAKeys: local: e: '%s' length: %d", buf_e, l_e); ErrorInfo("RpdGenRSAKeys: local: d: '%s' length: %d", buf_d, l_d); } #endif if (rsa_cmp(&rsa_n, &rsa_e) <= 0) continue; if (rsa_cmp(&rsa_n, &rsa_d) <= 0) continue; // Now we try the keys char test[2 * rsa_STRLEN] = "ThisIsTheStringTest01203456-+/"; Int_t lTes = 31; char *dumT = RpdGetRandString(0, lTes - 1); strncpy(test, dumT, lTes); delete[]dumT; char buf[2 * rsa_STRLEN]; if (gDebug > 3) ErrorInfo("RpdGenRSAKeys: local: test string: '%s' ", test); // Private/Public strncpy(buf, test, lTes); buf[lTes] = 0; // Try encryption with private key int lout = rsa_encode(buf, lTes, rsa_n, rsa_e); if (gDebug > 3) ErrorInfo("GenRSAKeys: local: length of crypted string: %d bytes", lout); // Try decryption with public key rsa_decode(buf, lout, rsa_n, rsa_d); buf[lTes] = 0; if (gDebug > 3) ErrorInfo("RpdGenRSAKeys: local: after private/public : '%s' ", buf); if (strncmp(test, buf, lTes)) continue; // Public/Private strncpy(buf, test, lTes); buf[lTes] = 0; // Try encryption with public key lout = rsa_encode(buf, lTes, rsa_n, rsa_d); if (gDebug > 3) ErrorInfo("RpdGenRSAKeys: local: length of crypted string: %d bytes ", lout); // Try decryption with private key rsa_decode(buf, lout, rsa_n, rsa_e); buf[lTes] = 0; if (gDebug > 3) ErrorInfo("RpdGenRSAKeys: local: after public/private : '%s' ", buf); if (strncmp(test, buf, lTes)) continue; notOK = 0; } if (notOK) { ErrorInfo("RpdGenRSAKeys: unable to generate good RSA key pair" " (%d attempts)- return",kMAXRSATRIES); return 1; } // Save Private key rsa_assign(&gRSAPriKey.n, &rsa_n); rsa_assign(&gRSAPriKey.e, &rsa_e); // Save Public key rsa_assign(&gRSAPubKey.n, &rsa_n); rsa_assign(&gRSAPubKey.e, &rsa_d); #if R__RSADEB if (gDebug > 2) { // Determine their lengths ErrorInfo("RpdGenRSAKeys: local: generated keys are:"); ErrorInfo("RpdGenRSAKeys: local: n: '%s' length: %d", buf_n, l_n); ErrorInfo("RpdGenRSAKeys: local: e: '%s' length: %d", buf_e, l_e); ErrorInfo("RpdGenRSAKeys: local: d: '%s' length: %d", buf_d, l_d); } #endif // Export form gRSAPubExport[0].len = l_n + l_d + 4; if (gRSAPubExport[0].keys) delete[] gRSAPubExport[0].keys; gRSAPubExport[0].keys = new char[gRSAPubExport[0].len]; gRSAPubExport[0].keys[0] = '#'; memcpy(gRSAPubExport[0].keys + 1, buf_n, l_n); gRSAPubExport[0].keys[l_n + 1] = '#'; memcpy(gRSAPubExport[0].keys + l_n + 2, buf_d, l_d); gRSAPubExport[0].keys[l_n + l_d + 2] = '#'; gRSAPubExport[0].keys[l_n + l_d + 3] = 0; #if R__RSADEB if (gDebug > 2) ErrorInfo("RpdGenRSAKeys: local: export pub: '%s'", gRSAPubExport[0].keys); #else if (gDebug > 2) ErrorInfo("RpdGenRSAKeys: local: export pub length: %d bytes", gRSAPubExport[0].len); #endif gRSAInit = 1; return 0; } //______________________________________________________________________________ int RpdRecvClientRSAKey() { // Generates local public/private RSA key pair // Send request for Client Public Key and Local public key // Receive encoded Client Key // Decode Client public key // NB: key is not saved to file here if (gRSAInit == 0) { // Generate Local RSA keys for the session if (RpdGenRSAKeys(1)) { ErrorInfo("RpdRecvClientRSAKey: unable to generate local keys"); return 1; } } // Send server public key int key = gRSAKey - 1; NetSend(gRSAPubExport[key].keys, gRSAPubExport[key].len, kROOTD_RSAKEY); // Receive length of message with encode client public key EMessageTypes kind; char buflen[40]; NetRecv(buflen, 20, kind); gPubKeyLen = atoi(buflen); if (gDebug > 3) ErrorInfo("RpdRecvClientRSAKey: got len '%s' %d ", buflen, gPubKeyLen); int nrec = 0; if (gRSAKey == 1) { // Receive and decode encoded public key nrec = NetRecvRaw(gPubKey, gPubKeyLen); rsa_decode(gPubKey, gPubKeyLen, gRSAPriKey.n, gRSAPriKey.e); if (gDebug > 2) ErrorInfo("RpdRecvClientRSAKey: Local: decoded string is %d bytes long ", strlen(gPubKey)); gPubKeyLen = strlen(gPubKey); } else if (gRSAKey == 2) { #ifdef R__SSL int ndec = 0; int lcmax = RSA_size(gRSASSLKey); char btmp[kMAXSECBUF]; int nr = gPubKeyLen; int kd = 0; while (nr > 0) { // Receive and decode encoded public key nrec += NetRecvRaw(btmp, lcmax); if ((ndec = RSA_private_decrypt(lcmax,(unsigned char *)btmp, (unsigned char *)&gPubKey[kd], gRSASSLKey, RSA_PKCS1_PADDING)) < 0) { char cerr[120]; ERR_error_string(ERR_get_error(), cerr); ErrorInfo("RpdRecvClientRSAKey: SSL: error: '%s' ",cerr); } nr -= lcmax; kd += ndec; } gPubKeyLen = kd; #else if (gDebug > 0) ErrorInfo("RpdRecvClientRSAKey: not compiled with SSL support" ": you should not have got here!"); return 1; #endif } else { if (gDebug > 0) ErrorInfo("RpdRecvClientRSAKey: unknown key type (%d)", gRSAKey); } // Import Key and Determine key type if (RpdGetRSAKeys(gPubKey, 0) != gRSAKey) { ErrorInfo("RpdRecvClientRSAKey:" " could not import a valid key (type %d)",gRSAKey); char *elogfile = new char[gRpdKeyRoot.length() + 11]; SPrintf(elogfile, gRpdKeyRoot.length() + 11, "%.*serr.XXXXXX", (int)gRpdKeyRoot.length(), gRpdKeyRoot.c_str()); mode_t oldumask = umask(0700); int ielog = mkstemp(elogfile); umask(oldumask); if (ielog != -1) { char line[kMAXPATHLEN] = {0}; // SPrintf(line,kMAXPATHLEN, " + RpdRecvClientRSAKey: error importing key\n + type: %d\n" " + length: %d\n + key: %s\n + (%d bytes were received)", gRSAKey, gPubKeyLen, gPubKey, nrec); while (write(ielog, line, strlen(line)) < 0 && GetErrno() == EINTR) ResetErrno(); close (ielog); } delete [] elogfile; return 2; } return 0; } //______________________________________________________________________________ void RpdInitRand() { // Init random machine. const char *randdev = "/dev/urandom"; int fd; unsigned int seed; if ((fd = open(randdev, O_RDONLY)) != -1) { if (gDebug > 2) ErrorInfo("RpdInitRand: taking seed from %s", randdev); if (read(fd, &seed, sizeof(seed))) {;} close(fd); } else { if (gDebug > 2) ErrorInfo("RpdInitRand: %s not available: using time()", randdev); seed = time(0); //better use times() + win32 equivalent } srand(seed); } //______________________________________________________________________________ int RpdAuthenticate() { // Handle user authentication. char buf[kMAXRECVBUF]; EMessageTypes kind; //#define R__DEBUG #ifdef R__DEBUG int debug = 1; while (debug) ; #endif // Reset gAuth (if we have been called this means that we need // to check at least that a valid authentication exists ...) int auth = 0; while (!auth) { // Receive next if (!gClientOld) { if (NetRecv(buf, kMAXRECVBUF, kind) < 0) { Error(gErr, -1, "RpdAuthenticate: error receiving message"); return auth; } } else { strlcpy(buf,gBufOld, sizeof(buf)); kind = gKindOld; gBufOld[0] = '\0'; gClientOld = 0; } // If this is a rootd contacted via a TXNetFile we need to // receive again the buffer if (gService == kROOTD && kind == kROOTD_PROTOCOL) { if (NetRecv(buf, kMAXRECVBUF, kind) < 0) { Error(gErr, -1, "RpdAuthenticate: error receiving message"); return auth; } } // Decode the method ... gAuthProtocol = RpdGetAuthMethod(kind); if (gDebug > 2) { if (kind != kROOTD_PASS) { ErrorInfo("RpdAuthenticate got: %d -- %s", kind, buf); } else { ErrorInfo("RpdAuthenticate got: %d ", kind); } } // Guess the client procotol if not received via Rootd/ProofdProtocol if (gClientProtocol == 0) gClientProtocol = RpdGuessClientProt(buf, kind); // If the client supports it check if we accept the method proposed; // if not send back the list of accepted methods, if any ... if (gAuthProtocol != -1 && gClientProtocol > 8) { // Check if accepted ... if (RpdCheckAuthAllow(gAuthProtocol, gOpenHost.c_str())) { if (gNumAllow>0) { if (gAuthListSent == 0) { if (gDebug > 0) ErrorInfo("Authenticate: %s method not" " accepted from host: %s", gAuthMeth[gAuthProtocol].c_str(), gOpenHost.c_str()); NetSend(kErrNotAllowed, kROOTD_ERR); RpdSendAuthList(); gAuthListSent = 1; goto next; } else { Error(gErr,kErrNotAllowed,"Authenticate: method not" " in the list sent to the client"); return auth; } } else { Error(gErr,kErrConnectionRefused,"Authenticate:" " connection refused from host %s", gOpenHost.c_str()); return auth; } } // Then check if a previous authentication exists and is valid // ReUse does not apply for RFIO if (kind != kROOTD_RFIO && (auth = RpdReUseAuth(buf, kind))) goto next; } // Reset global variable auth = 0; switch (kind) { case kROOTD_USER: auth = RpdUser(buf); break; case kROOTD_SRPUSER: auth = RpdSRPUser(buf); break; case kROOTD_PASS: auth = RpdPass(buf); break; case kROOTD_KRB5: auth = RpdKrb5Auth(buf); break; case kROOTD_GLOBUS: auth = RpdGlobusAuth(buf); break; case kROOTD_SSH: auth = RpdSshAuth(buf); break; case kROOTD_RFIO: auth = RpdRfioAuth(buf); break; case kROOTD_CLEANUP: RpdAuthCleanup(buf,1); ErrorInfo("RpdAuthenticate: authentication stuff cleaned - exit"); // Fallthrough next case now to free the keys case kROOTD_BYE: RpdFreeKeys(); return auth; break; default: Error(gErr,-1,"RpdAuthenticate: received bad opcode %d", kind); return auth; } if (gClientProtocol > 8) { // If failure prepare or continue negotiation // Don't do this if this was a SSH notification failure // because in such a case it was already done in the // appropriate daemon child int doneg = (gAuthProtocol != -1 || kind == kROOTD_PASS) && (gRemPid > 0 || kind != kROOTD_SSH); if (gDebug > 2 && doneg) ErrorInfo("RpdAuthenticate: kind:%d meth:%d auth:%d gNumLeft:%d", kind, gAuthProtocol, auth, gNumLeft); // If authentication failure, check if other methods could be tried ... if (auth == 0 && doneg) { if (gNumLeft > 0) { if (gAuthListSent == 0) { RpdSendAuthList(); gAuthListSent = 1; } else NetSend(-1, kROOTD_NEGOTIA); } else { NetSend(0, kROOTD_NEGOTIA); Error(gErr, -1, "RpdAuthenticate: authentication failed"); return auth; } } } next: continue; } return auth; } //______________________________________________________________________________ void RpdFreeKeys() { // Free space allocated for encryption keys if (gRSAPubExport[0].keys) delete[] gRSAPubExport[0].keys; if (gRSAPubExport[1].keys) delete[] gRSAPubExport[1].keys; #ifdef R__SSL RSA_free(gRSASSLKey); #endif } //______________________________________________________________________________ int RpdProtocol(int ServType) { // Receives client protocol and returns daemon protocol. // Returns: 0 if ok // -1 if any error occured // -2 if special action (e.g. cleanup): no need to continue int rc = 0; //#define R__DEBUG #ifdef R__DEBUG int debug = 1; while (debug) ; #endif if (gDebug > 2) ErrorInfo("RpdProtocol: Enter: server type = %d", ServType); int readbuf = 1; EMessageTypes kind; char proto[kMAXRECVBUF]; // For backward compatibility, for rootd we need to understand // whether we are talking to a OLD client: protocol information is // available only later on ... int lbuf[2]; if (NetRecvRaw(lbuf, sizeof(lbuf)) < 0) { NetSend(kErrFatal, kROOTD_ERR); ErrorInfo("RpdProtocol: error receiving message"); return -1; } // if kind is {kROOTD_PROTOCOL, kROOTD_CLEANUP, kROOTD_SSH} // receive the rest kind = (EMessageTypes) ntohl(lbuf[1]); int len = ntohl(lbuf[0]); if (gDebug > 1) ErrorInfo("RpdProtocol: kind: %d %d",kind,len); if (kind == kROOTD_PROTOCOL || kind == kROOTD_CLEANUP || kind == kROOTD_SSH) { // Receive the rest char *buf = 0; len -= sizeof(int); if (gDebug > 1) ErrorInfo("RpdProtocol: len: %d",len); if (len) { buf = new char[len]; if (NetRecvRaw(buf, len) < 0) { NetSend(kErrFatal, kROOTD_ERR); ErrorInfo("RpdProtocol: error receiving message"); delete[] buf; return -1; } strlcpy(proto, buf, sizeof(proto)); } else { // Empty buffer proto[0] = '\0'; } if (gDebug > 1) ErrorInfo("RpdProtocol: proto buff: %s", buf ? buf : "---"); // Copy buffer for later use readbuf = 0; if (buf) delete[] buf; } else if (ServType == kROOTD && kind == 0 && len == 0) { // TNetFile via TXNetFile: receive client protocol // read first next 12 bytes and discard them int llen = 12; char *buf = new char[llen]; if (NetRecvRaw(buf, llen) < 0) { NetSend(kErrFatal, kROOTD_ERR); ErrorInfo("RpdProtocol: error receiving message"); if (buf) delete[] buf; return -1; } if (buf) delete[] buf; // Send back the 'type' int type = htonl(8); if (NetSendRaw(&type,sizeof(type)) < 0) { NetSend(kErrFatal, kROOTD_ERR); ErrorInfo("RpdProtocol: error sending type to TXNetFile"); return -1; } // Now read the client protocol llen = 4; buf = new char[llen]; if (NetRecvRaw(buf,llen) < 0) { NetSend(kErrFatal, kROOTD_ERR); ErrorInfo("RpdProtocol: error receiving message"); delete[] buf; return -1; } strlcpy(proto,buf, sizeof(proto)); kind = kROOTD_PROTOCOL; readbuf = 0; delete[] buf; } else { // Need to open parallel sockets first int size = ntohl(lbuf[1]); // Read port int port; if (NetRecvRaw(&port, sizeof(int)) < 0) { NetSend(kErrFatal, kROOTD_ERR); ErrorInfo("RpdProtocol: error receiving message"); return -1; } port = ntohl(port); if (gDebug > 0) ErrorInfo("RpdProtocol: port = %d, size = %d", port, size); if (size > 1) NetParOpen(port, size); } int done = 0; gClientOld = 0; while (!done) { // Receive next if (readbuf) { if (NetRecv(proto, kMAXRECVBUF, kind) < 0) { ErrorInfo("RpdProtocol: error receiving message"); return -1; } } readbuf = 1; switch(kind) { case kROOTD_CLEANUP: RpdAuthCleanup(proto,1); ErrorInfo("RpdProtocol: authentication stuff cleaned"); done = 1; rc = -2; break; case kROOTD_BYE: RpdFreeKeys(); NetClose(); done = 1; rc = -2; break; case kROOTD_PROTOCOL: if (strlen(proto) > 0) { gClientProtocol = atoi(proto); } else { if (ServType == kROOTD) { // This is an old (TNetFile,TFTP) client: // send our protocol first ... if (NetSend(gServerProtocol, kROOTD_PROTOCOL) < 0) { ErrorInfo("RpdProtocol: error sending kROOTD_PROTOCOL"); rc = -1; } // ... and receive protocol via kROOTD_PROTOCOL2 if (NetRecv(proto, kMAXRECVBUF, kind) < 0) { ErrorInfo("RpdProtocol: error receiving message"); rc = -1; } if (kind != kROOTD_PROTOCOL2) { strlcpy(gBufOld, proto, sizeof(gBufOld)); gKindOld = kind; gClientOld = 1; gClientProtocol = 0; } else gClientProtocol = atoi(proto); } else gClientProtocol = 0; } if (!gClientOld) { // send our protocol // if we do not require authentication say it here Int_t protoanswer = gServerProtocol; if (!gRequireAuth && gClientProtocol > 10) protoanswer += 1000; // Notify if (gDebug > 0) { ErrorInfo("RpdProtocol: gClientProtocol = %d", gClientProtocol); ErrorInfo("RpdProtocol: Sending gServerProtocol = %d", protoanswer); } if (NetSend(protoanswer, kROOTD_PROTOCOL) < 0) { ErrorInfo("RpdProtocol: error sending kROOTD_PROTOCOL"); rc = -1; } } done = 1; break; case kROOTD_SSH: // Failure notification ... RpdSshAuth(proto); NetSend(kErrAuthNotOK, kROOTD_ERR); ErrorInfo("RpdProtocol: SSH failure notified"); rc = -2; done = 1; break; default: ErrorInfo("RpdProtocol: received bad option (%d)",kind); rc = -1; done = 1; break; } // Switch } // done return rc; } //______________________________________________________________________________ int RpdLogin(int ServType, int auth) { // Authentication was successful, set user environment. // if (gDebug > 2) ErrorInfo("RpdLogin: enter: Server: %d, gUser: %s, auth: %d", ServType, gUser, auth); // Login only if requested if (gDoLogin == 0) return -2; struct passwd *pw = getpwnam(gUser); if (!pw) { ErrorInfo("RpdLogin: user %s does not exist locally\n", gUser); return -1; } if (getuid() == 0) { #ifdef R__GLBS if (ServType == 2) { // We need to change the ownership of the shared memory segments used // for credential export to allow proofserv to destroy them struct shmid_ds shm_ds; if (gShmIdCred > 0) { if (shmctl(gShmIdCred, IPC_STAT, &shm_ds) == -1) { ErrorInfo("RpdLogin: can't get info about shared memory" " segment %d (errno: %d)",gShmIdCred,GetErrno()); return -1; } shm_ds.shm_perm.uid = pw->pw_uid; shm_ds.shm_perm.gid = pw->pw_gid; if (shmctl(gShmIdCred, IPC_SET, &shm_ds) == -1) { ErrorInfo("RpdLogin: can't change ownership of shared" " memory segment %d (errno: %d)", gShmIdCred,GetErrno()); return -1; } } } #endif // // Anonymous users are confined to their corner if (gAnon) { // We need to do it before chroot, otherwise it does not work if (chdir(pw->pw_dir) == -1) { ErrorInfo("RpdLogin: can't change directory to %s (errno: %d)", pw->pw_dir, errno); return -1; } if (chroot(pw->pw_dir) == -1) { ErrorInfo("RpdLogin: can't chroot to %s", pw->pw_dir); return -1; } } // set access control list from /etc/initgroup initgroups(gUser, pw->pw_gid); // set uid and gid if (setresgid(pw->pw_gid, pw->pw_gid, 0) == -1) { ErrorInfo("RpdLogin: can't setgid for user %s", gUser); return -1; } if (setresuid(pw->pw_uid, pw->pw_uid, 0) == -1) { ErrorInfo("RpdLogin: can't setuid for user %s", gUser); return -1; } } if (ServType == 2) { // set HOME env char *home = new char[8+strlen(pw->pw_dir)]; SPrintf(home, 8+strlen(pw->pw_dir), "HOME=%s", pw->pw_dir); putenv(home); } // Change user's HOME, if required (for anon it is already done) if (gDoLogin == 2 && !gAnon) { if (chdir(pw->pw_dir) == -1) { ErrorInfo("RpdLogin: can't change directory to %s (errno: %d)", pw->pw_dir, errno); return -1; } } umask(022); // Notify authentication to client ... NetSend(auth, kROOTD_AUTH); // Send also new offset if it changed ... if (auth == 2) NetSend(gOffSet, kROOTD_AUTH); if (gDebug > 0) ErrorInfo("RpdLogin: user %s logged in", gUser); return 0; } //______________________________________________________________________________ int RpdInitSession(int servtype, std::string &user, int &cproto, int &meth, int &type, std::string &ctoken) { // Perform the action needed to commence the new session: // Version called by TServerSocket. // - set debug flag // - check authentication table // - Inquire protocol // - authenticate the client // Returns logged-in user, the remote client procotol cproto, // the authentication protocol (ROOT internal) number is returned // in meth, type indicates the kind of authentication: // 0 = new authentication // 1 = existing authentication // 2 = existing authentication with updated offset // and the crypted-token in ctoken (used later for cleaning). // Called just after opening the connection std::string pwd; int auth = RpdInitSession(servtype,user,cproto,meth,pwd); if (auth == 1) if (gExistingAuth) type = 1; else type = 0; else if (auth == 2) type = 2; ctoken = gCryptToken; return auth; } //______________________________________________________________________________ int RpdInitSession(int servtype, std::string &user, int &cproto, int &anon, std::string &passwd) { // Perform the action needed to commence the new session: // - set debug flag // - check authentication table // - Inquire protocol // - authenticate the client // - login the client // Returns 1 for a PROOF master server, 0 otherwise // Returns logged-in user, the remote client procotol cproto, the // client kind of user anon and, if anonymous user, the client passwd. // If TServerSocket (servtype==kSOCKD), the protocol number is returned // in anon. // Called just after opening the connection if (gDebug > 2) ErrorInfo("RpdInitSession: %s", gServName[servtype].c_str()); int retval = 0; // CleanUp authentication table, if needed or required ... RpdInitAuth(); // Get Host name NetGetRemoteHost(gOpenHost); if (servtype == kPROOFD) { // find out if we are supposed to be a master or a slave server char msg[80]; if (NetRecv(msg, sizeof(msg)) < 0) { ErrorInfo("RpdInitSession: Cannot receive master/slave status"); return -1; } retval = !strcmp(msg, "master") ? 1 : 0; if (gDebug > 0) ErrorInfo("RpdInitSession: PROOF master/slave = %s", msg); } // Get protocol first // Failure typically indicate special actions like cleanup // which do not need additional work // The calling program will then decide what to do int rcp = RpdProtocol(servtype); if (rcp != 0) { if (rcp == -1) ErrorInfo("RpdInitSession: error getting remote protocol"); else if (rcp != -2) ErrorInfo("RpdInitSession: unknown error from RpdProtocol"); return rcp; } // Check if authentication is required // Old clients do not support no authentication mode bool runAuth = (gClientProtocol < 11 || gRequireAuth) ? 1 : 0; // user authentication (does not return in case of failure) int auth = 0; if (runAuth) { auth = RpdAuthenticate(); if (auth == 0) { ErrorInfo("RpdInitSession: unsuccessful authentication attempt"); return -1; } } else { auth = RpdNoAuth(servtype); } // Login the user (if in rootd/proofd environment) if (gDoLogin > 0) { if (RpdLogin(servtype,auth) != 0) { ErrorInfo("RpdInitSession: unsuccessful login attempt"); // Notify failure to client ... NetSend(0, kROOTD_AUTH); return -1; } } else { // Notify authentication to client ... NetSend(auth, kROOTD_AUTH); // Send also new offset if it changed ... if (auth == 2) NetSend(gOffSet, kROOTD_AUTH); if (gDebug > 0) ErrorInfo("RpdInitSession: User '%s' authenticated", gUser); retval = auth; } // Output vars user = std::string(gUser); cproto = gClientProtocol; if (servtype == kSOCKD) anon = gSec; else anon = gAnon; if (gAnon) passwd = std::string(gPasswd); return retval; } //______________________________________________________________________________ int RpdInitSession(int servtype, std::string &user, int &rid) { // Perform the action needed to commence the new session: // - set debug flag // - check authentication table // - Inquire protocol // - authenticate the client // - login the client // Returns 1 for a PROOF master server, 0 otherwise // Returns logged-in user and remote process id in rid // Called just after opening the connection int dum1 = 0, dum2 = 0; std::string dum3; rid = gRemPid; return RpdInitSession(servtype,user,dum1,dum2,dum3); } //______________________________________________________________________________ int RpdNoAuth(int servtype) { // Perform entrance formalities in case of no authentication // mode, i.e. get target user and check if authorized // Don't return if something goes wrong if (gDebug > 1) ErrorInfo("RpdNoAuth: no authentication required"); // Special value for this case int auth = 0; // Receive target username if (servtype == kROOTD || servtype == kPROOFD) { char buf[kMAXPATHLEN]; EMessageTypes kind; if (NetRecv(buf, kMAXPATHLEN, kind) < 0) { NetSend(kErrBadMess, kROOTD_ERR); ErrorInfo("RpdNoAuth: error receiving target user"); goto quit; } if (kind == kROOTD_BYE) goto quit; if (kind != kROOTD_USER) { NetSend(kErrBadOp, kROOTD_ERR); ErrorInfo("RpdNoAuth: protocol error:" " received msg type: %d, expecting: %d", kind, kROOTD_USER); goto quit; } // Decode buffer char ruser[kMAXUSERLEN], user[kMAXUSERLEN]; int nw = sscanf(buf,"%64s %64s",ruser,user); if (nw <= 0 || !strcmp(ruser,"-1")) { NetSend(kErrBadMess, kROOTD_ERR); ErrorInfo("RpdNoAuth: received uncorrect information: %s", buf); goto quit; } // If target user not send, assume user == ruser if (nw == 1) snprintf(user,kMAXUSERLEN,"%s",ruser); struct passwd *pw = 0; if ((pw = getpwnam(user)) == 0) { NetSend(kErrNoUser, kROOTD_ERR); ErrorInfo("RpdNoAuth: user %s unknown", user); goto quit; } // If server is not started as root, require user to be the same // as the one who started rootd uid_t uid = getuid(); if (uid && uid != pw->pw_uid) { NetSend(kErrBadUser, kROOTD_ERR); ErrorInfo("RpdNoAuth: user not same as effective user of rootd"); goto quit; } if (gDebug > 2) ErrorInfo("RpdNoAuth: remote user: %s, target user: %s",ruser,user); SPrintf(gUser, 63, "%s", user); } auth = 4; quit: return auth; } //______________________________________________________________________________ int RpdSetUid(int uid) { // Change current user id to uid (and gid). if (gDebug > 2) ErrorInfo("RpdSetUid: enter ...uid: %d", uid); struct passwd *pw = getpwuid(uid); if (!pw) { ErrorInfo("RpdSetUid: uid %d does not exist locally", uid); return -1; } else if (chdir(pw->pw_dir) == -1) { ErrorInfo("RpdSetUid: can't change directory to %s", pw->pw_dir); return -1; } if (getuid() == 0) { // set access control list from /etc/initgroup initgroups(pw->pw_name, pw->pw_gid); // set uid and gid if (setresgid(pw->pw_gid, pw->pw_gid, 0) == -1) { ErrorInfo("RpdSetUid: can't setgid for uid %d", uid); return -1; } if (setresuid(pw->pw_uid, pw->pw_uid, 0) == -1) { ErrorInfo("RpdSetUid: can't setuid for uid %d", uid); return -1; } } if (gDebug > 0) ErrorInfo("RpdSetUid: uid set (%d,%s)", uid, pw->pw_name); return 0; } //_____________________________________________________________________________ void RpdInit(EService serv, int pid, int sproto, unsigned int options, int rumsk, int sshp, const char *tmpd, const char *asrpp, int login) { // Change defaults job control options. gService = serv; gParentId = pid; gServerProtocol = sproto; gReUseAllow = rumsk; gSshdPort = sshp; gDoLogin = login; // Parse options gCheckHostsEquiv= (bool)((options & kDMN_HOSTEQ) != 0); gRequireAuth = (bool)((options & kDMN_RQAUTH) != 0); gSysLog = (bool)((options & kDMN_SYSLOG) != 0); if (tmpd && strlen(tmpd)) { gTmpDir = tmpd; gRpdAuthTab = gTmpDir + gAuthTab; gRpdKeyRoot = gTmpDir + gKeyRoot; } // Auth Tab and public key files are exclusive to this family gRpdAuthTab.append("."); gRpdAuthTab.append(ItoA(getuid())); gRpdKeyRoot.append(ItoA(getuid())); gRpdKeyRoot.append("_"); if (asrpp && strlen(asrpp)) gAltSRPPass = asrpp; #ifdef R__GLBS // Init globus if (RpdGlobusInit() != 0) ErrorInfo("RpdInit: failure initializing globus authentication"); #endif if (gDebug > 0) { ErrorInfo("RpdInit: gService= %s, gSysLog= %d, gSshdPort= %d", gServName[gService].c_str(), gSysLog, gSshdPort); ErrorInfo("RpdInit: gParentId= %d", gParentId); ErrorInfo("RpdInit: gRequireAuth= %d, gCheckHostEquiv= %d", gRequireAuth, gCheckHostsEquiv); ErrorInfo("RpdInit: gReUseAllow= 0x%x", gReUseAllow); ErrorInfo("RpdInit: gServerProtocol= %d", gServerProtocol); ErrorInfo("RpdInit: gDoLogin= %d", gDoLogin); if (tmpd) ErrorInfo("RpdInit: gTmpDir= %s", gTmpDir.c_str()); if (asrpp) ErrorInfo("RpdInit: gAltSRPPass= %s", gAltSRPPass.c_str()); #ifdef R__GLBS ErrorInfo("RpdInit: gHaveGlobus: %d", (int) gHaveGlobus); #endif } } //______________________________________________________________________________ int SPrintf(char *buf, size_t size, const char *va_(fmt), ...) { // Acts like snprintf with some printout in case of error if required // Returns number of characters printed (excluding the trailing `\0'). // Returns 0 is buf or size are not defined or inconsistent. // Returns -1 if the buffer is truncated. // Check buf if (!buf) { if (gDebug > 0) ErrorInfo("SPrintf: buffer not allocated: do nothing"); return 0; } // Check size if (size < 1) { if (gDebug > 0) ErrorInfo("SPrintf: cannot determine buffer size (%d): do nothing",size); return 0; } // Now fill buf va_list ap; va_start(ap,va_(fmt)); int np = vsnprintf(buf, size, fmt, ap); va_end(ap); if (np == -1 && gDebug > 0) ErrorInfo("SPrintf: buffer truncated (%s)",buf); return np; } //______________________________________________________________________________ char *ItoA(int i) { // Return pointer to a static string containing the string // version of integer 'i', up to a max of kMAXCHR (=30) // characters; returns "-1" if more chars are needed. const int kMAXCHR = 30; static char str[kMAXCHR]; // This is the number of characters we need int nchr = (int)log10(double(i)) + 1; if (nchr > kMAXCHR) strlcpy(str,"-1", sizeof(str)); else snprintf(str,30,"%d",i); return str; } //______________________________________________________________________________ void RpdSetErrorHandler(ErrorHandler_t err, ErrorHandler_t sys, ErrorHandler_t fatal) { // Set global pointers to error handler functions gErr = err; gErrSys = sys; gErrFatal = fatal; } #ifdef R__GLBS //______________________________________________________________________________ int RpdGetShmIdCred() { // Returns shared memory id return gShmIdCred; } #endif //______________________________________________________________________________ int RpdRetrieveSpecialPass(const char *usr, const char *fpw, char *pass, int lpwmax) { // Retrieve specific ROOT password from $HOME/fpw, if any. // To avoid problems with NFS-root-squashing, if 'root' changes temporarly the // uid/gid to those of the target user (usr). // If OK, returns pass length and fill 'pass' with the password, null-terminated. // ('pass' is allocated externally to contain max lpwmax bytes). // If the file does not exists, return 0 and an empty pass. // If any problems with the file occurs, return a negative // code, -2 indicating wrong file permissions. // If any problem with changing ugid's occurs, prints a warning trying anyhow // to read the password hash. int rc = -1; int len = 0, n = 0, fid = -1; // Check inputs if (!usr || !pass) { if (gDebug > 0) ErrorInfo("RpdRetrieveSpecialPass: invalid arguments:" " us:%p, sp:%p", usr, pass); return rc; } struct passwd *pw = getpwnam(usr); if (!pw) { if (gDebug > 0) ErrorInfo("RpdRetrieveSpecialPass: user '%s' does not exist", usr); return rc; } // target and actual uid int uid = pw->pw_uid; int ouid = getuid(); // Temporary change to target user ID to avoid NFS squashing problems if (ouid == 0) { // set access control list from /etc/initgroup if (initgroups(pw->pw_name, pw->pw_gid) == -1) ErrorInfo("RpdRetrieveSpecialPass: can't initgroups for uid %d" " (errno: %d)", uid, GetErrno()); // set uid and gid if (setresgid(pw->pw_gid, pw->pw_gid, 0) == -1) ErrorInfo("RpdRetrieveSpecialPass: can't setgid for gid %d" " (errno: %d)", pw->pw_gid, GetErrno()); if (setresuid(pw->pw_uid, pw->pw_uid, 0) == -1) ErrorInfo("RpdRetrieveSpecialPass: can't setuid for uid %d" " (errno: %d)", uid, GetErrno()); } // The file now char rootdpass[kMAXPATHLEN]; SPrintf(rootdpass, kMAXPATHLEN, "%s/%s", pw->pw_dir, fpw); if (gDebug > 0) ErrorInfo ("RpdRetrieveSpecialPass: checking file %s for user %s",rootdpass, pw->pw_name); if ((fid = open(rootdpass, O_RDONLY)) == -1) { ErrorInfo("RpdRetrieveSpecialPass: cannot open password file" " %s (errno: %d)", rootdpass, GetErrno()); rc = -1; goto back; } // Check first the permissions: should be 0600 struct stat st; if (fstat(fid, &st) == -1) { ErrorInfo("RpdRetrieveSpecialPass: cannot stat descriptor %d" " %s (errno: %d)", fid, GetErrno()); close(fid); rc = -1; goto back; } if (!S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || (st.st_mode & (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH)) != 0) { ErrorInfo("RpdRetrieveSpecialPass: pass file %s: wrong permissions" " 0%o (should be 0600)", rootdpass, (st.st_mode & 0777)); ErrorInfo("RpdRetrieveSpecialPass: %d %d", S_ISREG(st.st_mode),S_ISDIR(st.st_mode)); close(fid); rc = -2; goto back; } if ((n = read(fid, pass, lpwmax - 1)) <= 0) { close(fid); ErrorInfo("RpdRetrieveSpecialPass: cannot read password file" " %s (errno: %d)", rootdpass, GetErrno()); rc = -1; goto back; } close(fid); // Get rid of special trailing chars len = n; while (len-- && (pass[len] == '\n' || pass[len] == 32)) pass[len] = 0; // Null-terminate pass[++len] = 0; rc = len; back: // Change back uid's if (ouid == 0) { // set uid and gid if (setresgid(0, 0, 0) == -1) ErrorInfo("RpdRetrieveSpecialPass: can't re-setgid for gid 0" " (errno: %d)", GetErrno()); if (setresuid(0, 0, 0) == -1) ErrorInfo("RpdRetrieveSpecialPass: can't re-setuid for uid 0" " (errno: %d)", GetErrno()); } // We are done return rc; } } // namespace ROOT