OW_LocalAuthentication.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2001-2004 Vintela, Inc. All rights reserved.
00003 *
00004 * Redistribution and use in source and binary forms, with or without
00005 * modification, are permitted provided that the following conditions are met:
00006 *
00007 *  - Redistributions of source code must retain the above copyright notice,
00008 *    this list of conditions and the following disclaimer.
00009 *
00010 *  - Redistributions in binary form must reproduce the above copyright notice,
00011 *    this list of conditions and the following disclaimer in the documentation
00012 *    and/or other materials provided with the distribution.
00013 *
00014 *  - Neither the name of Vintela, Inc. nor the names of its
00015 *    contributors may be used to endorse or promote products derived from this
00016 *    software without specific prior written permission.
00017 *
00018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
00019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00021 * ARE DISCLAIMED. IN NO EVENT SHALL Vintela, Inc. OR THE CONTRIBUTORS
00022 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00028 * POSSIBILITY OF SUCH DAMAGE.
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       // only root can change file ownership, so unless we can, we'll have to rely on owlocalhelper to do it.
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); //, inputCmd + "\n" + extraInput);
00088          String input = inputCmd + "\n" + extraInput;
00089          
00090          // purposely ignore this return code
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       // remove the trailing \n
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          // eat all exceptions
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    // Generate random number to put in file for client to read
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 } // end unnamed namespace
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       // look for an initial connection where the client specifies their uid
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          // Lookup the username given the uid
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          // give them back the challenge
00312          htcon->addHeader("WWW-Authenticate", createNewChallenge(uidStr, userName));
00313          htcon->setErrorDetails("You must authenticate to access this resource");
00314          return false;
00315       }
00316    
00317       // it's not an initial connection, so it's phase 2, look for the nonce and cookie
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          // Match! Authenticated. Clean up.
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    // beginning of m_authEntries are the oldest, so we'll just keep erasing the
00419    // entries from the front as long as they're older than 1 minute.
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 } // end namespace OW_NAMESPACE
00428 

Generated on Thu Feb 9 08:48:03 2006 for openwbem by  doxygen 1.4.6