00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00036 #include "OW_config.h"
00037 #include "OW_LocalAuthentication.hpp"
00038 #include "OW_HTTPSvrConnection.hpp"
00039 #include "OW_CryptographicRandomNumber.hpp"
00040 #include "OW_UUID.hpp"
00041 #include "OW_FileSystem.hpp"
00042 #include "OW_Format.hpp"
00043 #include "OW_AutoPtr.hpp"
00044 #include "OW_UserUtils.hpp"
00045 #include "OW_AuthenticationException.hpp"
00046 #include "OW_Exec.hpp"
00047 #include "OW_LocalAuthenticationCommon.hpp"
00048 
00049 #ifdef OW_HAVE_PWD_H
00050 #include <pwd.h>
00051 #endif
00052 #ifdef OW_HAVE_UNISTD_H
00053 #include <unistd.h>
00054 #endif
00055 #ifdef OW_HAVE_SYS_TYPES_H
00056 #include <sys/types.h>
00057 #endif
00058 #ifdef OW_HAVE_SYS_STAT_H
00059 #include <sys/stat.h>
00060 #endif
00061 #ifdef OW_HAVE_SYS_WAIT_H
00062 #include <sys/wait.h>
00063 #endif
00064 #include <cerrno>
00065 
00066 namespace OW_NAMESPACE
00067 {
00068 
00069 using namespace LocalAuthenticationCommon;
00070 
00071 namespace
00072 {
00073    bool useHelper()
00074    {
00075       
00076       return ::geteuid() != 0;
00077    }
00078 
00079    String runHelper(const String& inputCmd, const String& extraInput = String())
00080    {
00081       StringArray cmd;
00082       cmd.push_back(OWLOCALHELPER_BINARY);
00083       String output;
00084       int processStatus = -1;
00085       try
00086       {
00087          PopenStreams helper = Exec::safePopen(cmd); 
00088          String input = inputCmd + "\n" + extraInput;
00089          
00090          
00091          helper.in()->write(input.c_str(), input.length());
00092          helper.in()->close();
00093 
00094          const int TIMEOUT = 10;
00095          const int OUTPUT_LIMIT = 1024;
00096          Exec::gatherOutput(output, helper, processStatus, TIMEOUT, OUTPUT_LIMIT);
00097          if (processStatus == -1)
00098          {
00099             processStatus = helper.getExitStatus();
00100          }
00101       }
00102       catch (Exception& e)
00103       {
00104          OW_THROW(LocalAuthenticationException, Format("Failed running %1: %2. command = %3, output = \"%4\"", OWLOCALHELPER_BINARY, e, inputCmd, output).c_str());
00105       }
00106       if (!WIFEXITED(processStatus) || WEXITSTATUS(processStatus) != 0)
00107       {
00108          OW_THROW(LocalAuthenticationException, Format("%1 failed with exit status %2. command = %3, output = \"%4\"", 
00109             OWLOCALHELPER_BINARY, processStatus, inputCmd, output).c_str());
00110       }
00111       return output;
00112    }
00113 
00114    void initializeHelper()
00115    {
00116       runHelper(INITIALIZE_CMD);
00117    }
00118 
00119    void cleanupEntryHelper(const String& pathToFile, const String& cookie)
00120    {
00121       size_t begin = pathToFile.lastIndexOf(OW_FILENAME_SEPARATOR);
00122       if (begin == String::npos)
00123       {
00124          begin = 0;
00125       }
00126       String fileName = pathToFile.substring(begin + 1);
00127       runHelper(REMOVE_CMD, fileName + "\n" + cookie + "\n");
00128    }
00129 
00130    String createFileHelper(const String& uid, const String& cookie)
00131    {
00132       String filename = runHelper(CREATE_CMD, uid + "\n" + cookie + "\n");
00133       
00134       if (filename.length() > 0 && filename[filename.length()-1] == '\n')
00135       {
00136          filename.erase(filename.length()-1);
00137       }
00138       return filename;
00139 
00140    }
00141 
00142 }
00143 
00144 
00146 LocalAuthentication::LocalAuthentication(const LoggerRef& logger)
00147    : m_logger(logger)
00148 {
00149    if (useHelper())
00150    {
00151       initializeHelper();
00152    }
00153    else
00154    {
00155       LocalAuthenticationCommon::initializeDir();
00156    }
00157 }
00158 
00160 LocalAuthentication::~LocalAuthentication()
00161 {
00162    for (size_t i = 0; i < m_authEntries.size(); ++i)
00163    {
00164       try
00165       {
00166          cleanupEntry(m_authEntries[i]);
00167       }
00168       catch (Exception& e)
00169       {
00170          try
00171          {
00172             OW_LOG_ERROR(m_logger, Format("LocalAuthentication::~LocalAuthentication() caught exception from cleanupEntry(): %1", e));
00173          }
00174          catch (...)
00175          {
00176          }
00177       }
00178       catch (...)
00179       {
00180          
00181       }
00182    }
00183 }
00184 
00186 namespace {
00187 
00189 String
00190 generateNewNonce()
00191 {
00192    UUID u;
00193    return u.toString();
00194 }
00195 
00196 void
00197 parseInfo(const String& pinfo, SortedVectorMap<String, String>& infoMap)
00198 {
00199    size_t idx = pinfo.indexOf("OWLocal");
00200    String info;
00201    if (idx != String::npos)
00202    {
00203       info = pinfo.substring(8);
00204    }
00205    else
00206    {
00207       OW_THROW(AuthenticationException, "Error parsing OWLocal Response");
00208    }
00209    Array<String> infoAr = info.tokenize(",");
00210    for (size_t i = 0; i < infoAr.size(); ++i)
00211    {
00212       String lhs, rhs;
00213       idx = infoAr[i].indexOf('=');
00214       if (idx != String::npos)
00215       {
00216          lhs = infoAr[i].substring(0, idx);
00217          lhs.trim();
00218          if (idx + 1 < infoAr[i].length())
00219          {
00220             rhs = infoAr[i].substring(idx + 1);
00221             rhs.trim();
00222             if (rhs[0] == '\"')
00223             {
00224                rhs = rhs.substring(1);
00225                rhs = rhs.substring(0, rhs.length() - 1);
00226             }
00227             infoMap.insert(std::make_pair(lhs, rhs));
00228             continue;
00229          }
00230       }
00231       OW_THROW(AuthenticationException, "Error parsing OWLocal Response");
00232    }
00233 }
00234 
00236 void
00237 generateNewCookieFile(const String& uid, String& cookieFileName, String& cookie)
00238 {
00239    
00240    CryptographicRandomNumber rng;
00241    UInt32 rn1 = rng.getNextNumber();
00242    UInt32 rn2 = rng.getNextNumber();
00243    UInt32 rn3 = rng.getNextNumber();
00244    UInt32 rn4 = rng.getNextNumber();
00245    UInt32 rn5 = rng.getNextNumber();
00246    cookie = Format("%1%2%3%4%5", rn1, rn2, rn3, rn4, rn5);
00247 
00248    if (useHelper())
00249    {
00250       cookieFileName = createFileHelper(uid, cookie);
00251    }
00252    else
00253    {
00254       cookieFileName = LocalAuthenticationCommon::createFile(uid, cookie);
00255    }
00256 }
00257 
00258 } 
00259 
00261 bool
00262 LocalAuthentication::authenticate(String& userName,
00263       const String& info, HTTPSvrConnection* htcon)
00264 {
00265    try
00266    {
00267 
00268       cleanupStaleEntries();
00269    
00270       if (info.empty())
00271       {
00272          htcon->setErrorDetails("You must authenticate to access this resource");
00273          return false;
00274       }
00275       
00276       typedef SortedVectorMap<String, String> map_t;
00277       map_t infoMap;
00278    
00279       parseInfo(info, infoMap);
00280    
00281       
00282       map_t::const_iterator iter = infoMap.find("uid");
00283       if (iter != infoMap.end() && !iter->second.empty())
00284       {
00285          String uidStr = iter->second;
00286       
00287          
00288          uid_t uid;
00289          try
00290          {
00291             uid = uidStr.toUInt32();
00292          }
00293          catch (StringConversionException& e)
00294          {
00295             htcon->setErrorDetails("Invalid uid");
00296             return false;
00297          }
00298          
00299          bool ok;
00300          String uname(UserUtils::getUserName(uid, ok));
00301          if (ok)
00302          {
00303             userName = uname;
00304          }
00305          else
00306          {
00307             htcon->setErrorDetails("Invalid uid");
00308             return false;
00309          }
00310    
00311          
00312          htcon->addHeader("WWW-Authenticate", createNewChallenge(uidStr, userName));
00313          htcon->setErrorDetails("You must authenticate to access this resource");
00314          return false;
00315       }
00316    
00317       
00318       iter = infoMap.find("nonce");
00319       if (iter == infoMap.end() || iter->second.empty())
00320       {
00321          htcon->setErrorDetails("No nonce was provided");
00322          return false;
00323       }
00324    
00325       String sNonce = iter->second;
00326    
00327       bool nonceFound = false;
00328       size_t i;
00329       if (!sNonce.empty())
00330       {
00331          for (i = 0; i < m_authEntries.size(); ++i)
00332          {
00333             if (sNonce == m_authEntries[i].nonce)
00334             {
00335                nonceFound = true;
00336                break;
00337             }
00338          }
00339       }
00340       if (!nonceFound)
00341       {
00342          htcon->setErrorDetails("invalid nonce");
00343          return false;
00344       }
00345    
00346       userName = m_authEntries[i].userName;
00347    
00348       iter = infoMap.find("cookie");
00349       if (iter == infoMap.end() || iter->second.empty())
00350       {
00351          htcon->setErrorDetails("No cookie was provided");
00352          return false;
00353       }
00354       String cookie = iter->second;
00355       if ( cookie == m_authEntries[i].cookie )
00356       {
00357          
00358          cleanupEntry(m_authEntries[i]);
00359          m_authEntries.erase(m_authEntries.begin() + i);
00360          return true;
00361       }
00362    
00363       htcon->setErrorDetails("invalid cookie");
00364       return false;
00365    }
00366    catch(LocalAuthenticationException& e)
00367    {
00368       OW_LOG_ERROR(m_logger, Format("LocalAuthentication::authenticate(): %1", e));
00369       htcon->setErrorDetails(Format("%1", e));
00370       return false;
00371    }
00372 }
00374 String
00375 LocalAuthentication::createNewChallenge(const String& uid, const String& userName)
00376 {
00377    String nonce = generateNewNonce();
00378    String cookieFileName;
00379    String cookie;
00380    generateNewCookieFile(uid, cookieFileName, cookie);
00381 
00382    AuthEntry newEntry;
00383    newEntry.fileName = cookieFileName;
00384    newEntry.cookie = cookie;
00385    newEntry.nonce = nonce;
00386    newEntry.creationTime.setToCurrent();
00387    newEntry.userName = userName;
00388    m_authEntries.push_back(newEntry);
00389 
00390    return String("OWLocal nonce=\"" + nonce + "\", cookiefile=\"" + cookieFileName + "\"");
00391 }
00392 
00394 void
00395 LocalAuthentication::cleanupEntry(const AuthEntry& entry)
00396 {
00397    if (useHelper())
00398    {
00399       cleanupEntryHelper(entry.fileName, entry.cookie);
00400    }
00401    else
00402    {
00403       if (!FileSystem::removeFile(entry.fileName))
00404       {
00405          OW_LOG_ERROR(m_logger, Format("LocalAuthentication::cleanupEntry(): Failed to remove %1: %2", entry.fileName, errno));
00406       }
00407    }
00408 }
00409 
00411 void
00412 LocalAuthentication::cleanupStaleEntries()
00413 {
00414    DateTime oneMinuteAgo; 
00415    oneMinuteAgo.setToCurrent();
00416    oneMinuteAgo.addMinutes(-1);
00417 
00418    
00419    
00420    while (m_authEntries.size() > 0 && m_authEntries[0].creationTime < oneMinuteAgo)
00421    {
00422       cleanupEntry(m_authEntries[0]);
00423       m_authEntries.erase(m_authEntries.begin());
00424    }
00425 }
00426 
00427 } 
00428