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