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 #include "DcLoginManager.h"
00027
00028
00029
00030 CdcLoginManager::t_users_vector CdcLoginManager::vUsers;
00031 CdcLoginManager::t_challenges_map CdcLoginManager::mChallenges;
00032 CdcMutex CdcLoginManager::oMapMutex;
00033 CdcString CdcLoginManager::sLoginFileName;
00034 CdcString CdcLoginManager::sDenyFileName;
00035 bool CdcLoginManager::bBuiltInLogin;
00036 bool CdcLoginManager::bAutoCookie;
00037 int CdcLoginManager::nStaleNonce;
00038 int CdcLoginManager::nExpireNonce;
00039 int CdcLoginManager::nOldCodeValid;
00040 CdcThread CdcLoginManager::oCleanThread;
00041 CdcSemaphore CdcLoginManager::oStartThread;
00042 bool CdcLoginManager::isCleanThreadGo = true;
00043
00044 int volatile CdcLoginManager::nSomeChalTaken = 0;
00045
00046 CdcLoginManager::CdcUser::CdcUser (ifstream& inf)
00047 : nCode (EMPTY_STR), isLogedin (false)
00048 {
00049 oMutex.Lock ();
00050
00051 char temp [BUFF_LEN];
00052 inf.getline (temp, BUFF_LEN, '~');
00053 sUsername = CdcString (temp);
00054 inf.getline (temp, BUFF_LEN, '~');
00055
00056 for (int i = 0; i < strlen (temp); ++i)
00057 temp[i] -= 127;
00058 sPassword = CdcString (temp);
00059 oMutex.Unlock ();
00060 }
00061
00062 bool CdcLoginManager::CdcUser::WriteToFile (ofstream& outf)
00063 {
00064 if (sUsername.IsEmpty ()) return false;
00065
00066 oMutex.Lock ();
00067 outf.write (sUsername.GetBuffer (), sUsername.GetLength ());
00068 outf.put ('~');
00069
00070 const char* ptemp = sPassword.GetBuffer ();
00071 for (int i = 0; i < strlen (ptemp); ++i)
00072 outf.put ((char)(ptemp[i] + 127));
00073 outf.put ('~');
00074 oMutex.Unlock ();
00075 return true;
00076 }
00077
00078
00079 void CdcLoginManager::Load (const char* file_name)
00080 {
00081 sLoginFileName = CdcParameters::GetParam (
00082 "Login-Manager->Login-Page");
00083 sDenyFileName = CdcParameters::GetParam (
00084 "Login-Manager->Denied-Page");
00085 bAutoCookie = CdcParameters::GetParam (
00086 "Login-Manager->Auto-Cookie") == "yes";
00087 bBuiltInLogin = sLoginFileName.IsEmpty () == false;
00088 nStaleNonce = CdcParameters::ToInt (CdcParameters::GetParam (
00089 "Login-Manager->Stale"));
00090 nExpireNonce = CdcParameters::ToInt (CdcParameters::GetParam (
00091 "Login-Manager->Expire"));
00092 nOldCodeValid = CdcParameters::ToInt (CdcParameters::GetParam (
00093 "Login-Manager->Old-Code-Life"));
00094
00095 ifstream inf (file_name);
00096 if (inf.is_open () == 0) return;
00097
00098 while (inf.eof () == 0)
00099 {
00100 CdcUser* user = new CdcUser (inf);
00101 if (user == NULL)
00102 throw CdcException ("Out of memory.");
00103 vUsers.push_back (user);
00104 }
00105 inf.close ();
00106
00107
00108 oCleanThread.MakeThread (DcClean, &isCleanThreadGo);
00109 oCleanThread.SetPriority (DC_TP_LOW);
00110 }
00111
00112
00113 void CdcLoginManager::Save (const char* file_name)
00114 {
00115 ofstream outf (file_name);
00116 if (outf.is_open () == false)
00117 throw CdcExceptionFile ("Cannot open users database file.");
00118
00119 t_users_vector::iterator itr;
00120 for (itr = vUsers.begin(); itr != vUsers.end(); itr++)
00121 (*itr)->WriteToFile (outf);
00122 outf.close ();
00123 }
00124
00125
00126 void CdcLoginManager::LoginUser (int id, CdcRequest& req, CdcResponse& res)
00127 {
00128 vUsers[id]->GetStatusObject ()->Login ();
00129 vUsers[id]->Logedin (true);
00130 vUsers[id]->SetIP (req.GetVariable ("BN_ClentIP"));
00131 }
00132
00133 void CdcLoginManager::SetRandCode (int id, CdcRequest& req, CdcResponse& res)
00134 {
00135 vUsers[id]->SetCode (CdcCrypto::GetRandomString ());
00136 if (bAutoCookie)
00137 {
00138 res.SetCookie (DC_USER_ID, CdcString::IntToString (id));
00139 res.SetCookie (DC_USER_CODE, vUsers[id]->GetCode ());
00140 }
00141 else if (bBuiltInLogin)
00142 {
00143 req.SetVariable (DC_USER_ID, CdcString::IntToString (id));
00144 req.SetVariable (DC_USER_CODE, vUsers[id]->GetCode ());
00145 }
00146 }
00147
00148 void CdcLoginManager::Clean ()
00149 {
00150
00151 if (nSomeChalTaken > 0) return;
00152 CdcHttpTime now = CdcHttpTime::GetCurrentTime ();
00153 oMapMutex.Lock ();
00154 t_challenges_map::iterator itr = mChallenges.begin();
00155 while (itr != mChallenges.end())
00156 {
00157 if (nSomeChalTaken > 0) break;
00158 t_challenges_map::iterator prev = itr++;
00159 if (prev->second->isCanceld == true ||
00160 prev->second->isForDelete == true ||
00161 now - prev->second->oBuildTime > nExpireNonce)
00162 {
00163 delete prev->second;
00164 mChallenges.erase (prev);
00165 }
00166 }
00167 oMapMutex.Unlock ();
00168 }
00169
00170 bool CdcLoginManager::CheckLogout (CdcRequest& req, CdcResponse& res)
00171 {
00172 if (req.GetVariable (DC_LOGOUT) == "yes")
00173 {
00174 LogoutUser (req, res);
00175 return true;
00176 }
00177 return false;
00178 }
00179
00180 void CdcLoginManager::LogoutUser (CdcRequest& req, CdcResponse& res)
00181 {
00182
00183 CdcDaaResponse temp;
00184 bool isRes = false, isVar = false;
00185 int id = GetUserID (req, temp, isRes, isVar);
00186 if (id < 0) return;
00187
00188 vUsers[id]->GetStatusObject ()->Logout ();
00189 vUsers[id]->Logedin (false);
00190 vUsers[id]->SetCode (EMPTY_STR);
00191 if (bBuiltInLogin)
00192 {
00193 if (isRes) req.SetVariable (DC_NONCE, EMPTY_STR);
00194 req.SetVariable (DC_USER_ID, EMPTY_STR);
00195 req.SetVariable (DC_USER_CODE, EMPTY_STR);
00196 }
00197 else if (isRes)
00198 req.EraseHeaderField ("Authorization");
00199 if (isRes)
00200 {
00201 oMapMutex.Lock ();
00202 t_challenges_map::iterator itr = mChallenges.find (temp.sNonce);
00203 if (itr != mChallenges.end ())
00204 {
00205 itr->second->isCanceld = true;
00206 itr->second->isForDelete = true;
00207 oStartThread.Call ();
00208 }
00209 oMapMutex.Unlock ();
00210 }
00211 if (bAutoCookie)
00212 {
00213 res.SetCookie (DC_USER_ID, "-1");
00214 res.SetCookie (DC_USER_CODE, EMPTY_STR, -1);
00215 }
00216 }
00217
00218
00219 bool CdcLoginManager::CheckCode (int id, CdcRequest& req, CdcResponse& res)
00220 {
00221 CdcString code = req.GetVariable (DC_USER_CODE);
00222
00223 if (code.IsEmpty () == false &&
00224 req.GetVariable ("BN_ClentIP") == vUsers[id]->GetIP ())
00225 {
00226 if (vUsers[id]->GetCode () == code)
00227 {
00228 SetRandCode (id, req, res);
00229 return true;
00230 }
00231 if (nOldCodeValid > 0 &&
00232 vUsers[id]->GetLastCode () == code &&
00233 CdcHttpTime::GetCurrentTime () - vUsers[id]->GetLastLoginTime ()
00234 < nOldCodeValid)
00235 return true;
00236
00237 }
00238 return false;
00239 }
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253 bool CdcLoginManager::CheckResponse (int id, const CdcDaaResponse& DaaRes,
00254 bool& stale, bool& wrong)
00255 {
00256 ++nSomeChalTaken;
00257 oMapMutex.Lock ();
00258 oMapMutex.Unlock ();
00259 t_challenges_map::iterator itr = mChallenges.find (DaaRes.sNonce);
00260 if (itr == mChallenges.end () || itr->second->isCanceld == true)
00261 {
00262 --nSomeChalTaken;
00263 return false;
00264 }
00265 CdcDaaChallenge* pchal = itr->second;
00266 int nc = strtoul(DaaRes.sNC.GetBuffer (), NULL, 16);
00267
00268 bool fail = pchal->sRealm != DaaRes.sRealm ||
00269 pchal->sOpaque != DaaRes.sOpaque;
00270
00271 if (fail == false)
00272 {
00273 CdcString HA1 = CdcCrypto::MakeHash (vUsers[id]->GetUsername () + ':'
00274 + DaaRes.sRealm + ':' + vUsers[id]->GetPassword ());
00275 CdcString HA2 = CdcCrypto::MakeHash (DaaRes.sMethod + ':' + DaaRes.sURL);
00276 CdcString ResHash = CdcCrypto::MakeHash (HA1 + ':' + DaaRes.sNonce +
00277 ':' + DaaRes.sNC + ':' + DaaRes.sCnonce + ':' + DaaRes.sQOP + ':'
00278 + HA2);
00279 fail = wrong = !(ResHash == DaaRes.sResponse);
00280 }
00281 if (fail == false)
00282 {
00283 int time_diff = CdcHttpTime::GetCurrentTime () - pchal->oBuildTime;
00284 fail = time_diff > nStaleNonce;
00285 stale = (time_diff > nStaleNonce && time_diff < nExpireNonce) ||
00286 (pchal->nNC > nc);
00287 }
00288 if (fail == true)
00289 {
00290
00291 pchal->isForDelete = true;
00292 --nSomeChalTaken;
00293 oStartThread.Call ();
00294 return false;
00295 }
00296
00297 pchal->nNC = nc + 1;
00298 --nSomeChalTaken;
00299 return true;
00300 }
00301
00302
00303 bool CdcLoginManager::CheckGroupAccess (int id, const CdcString& group)
00304 {
00305 CdcString group_name = CdcParameters::GetFullName (group,
00306 "Login-Manager->Groups");
00307 return CdcParameters::IsUnder (group_name,
00308 vUsers[id]->GetUsername (), true);
00309 }
00310
00311
00312 void CdcLoginManager::BuildLoginPage (CdcRequest& req, CdcResponse& res,
00313 bool stale)
00314 {
00315 CdcDaaChallenge* pchal = new CdcDaaChallenge ();
00316 if (pchal == NULL) throw CdcException ("Out of memory.");
00317
00318 pchal->sNonce = CdcCrypto::GetRandomString (35);
00319 pchal->sOpaque = CdcCrypto::GetRandomString ();
00320 pchal->sRealm = req.GetAliasHost ();
00321 pchal->isForDelete = false;
00322 pchal->nNC = 1;
00323
00324 mChallenges[pchal->sNonce] = pchal;
00325
00326 if (bBuiltInLogin)
00327 {
00328 req.SetVariable (DC_NONCE, pchal->sNonce);
00329 req.SetVariable (DC_OPAQUE, pchal->sOpaque);
00330 req.SetVariable (DC_REALM, pchal->sRealm);
00331 }
00332 else
00333 {
00334 CdcString DAA = "Digest qop=\"auth\",";
00335 DAA = DAA + "realm=\"" + pchal->sRealm + "\",";
00336 DAA = DAA + "nonce=\"" + pchal->sNonce + "\",";
00337 if (stale)
00338 DAA = DAA + "stale=\"true\",";
00339 DAA = DAA + "opaque=\"" + pchal->sOpaque + "\"";
00340 res.SetHeaderField ("WWW-Authenticate", DAA);
00341 res.SetStatus (401);
00342 }
00343 }
00344
00345
00346 CdcUserStatus* CdcLoginManager::CheckAccess (const CdcString& group,
00347 CdcRequest& req,
00348 CdcResponse& res,
00349 int& ret_code,
00350 CdcString& new_page)
00351 {
00352 CdcDaaResponse DaaRes;
00353 bool isRes = false, isVar = false, stale = false, wrong = false, access = false;
00354 int id = GetUserID (req, DaaRes, isRes, isVar);
00355
00356 if (id >= 0)
00357 {
00358 access = CheckCode (id, req, res);
00359 if (access == false && isRes && DaaRes.sURL == req.GetFullURL ())
00360 {
00361 if ((access = CheckResponse (id, DaaRes, stale, wrong)) == true)
00362 {
00363 SetRandCode (id, req, res);
00364 if (vUsers[id]->IsLogedin () == false)
00365 LoginUser (id, req, res);
00366 }
00367 if (access == false && stale == false)
00368 PrintString ("Login Faild - " + req.GetVariable ("BN_ClentIP"), PS_SEC);
00369 }
00370 }
00371
00372 if (access == false)
00373 {
00374 BuildLoginPage (req, res, stale);
00375 ret_code = DC_LM_SEND;
00376 new_page = sLoginFileName;
00377 if (bBuiltInLogin)
00378 {
00379 if (id < 0) req.SetVariable (DC_LOGIN_MSG, "Please log in");
00380 else if (wrong) req.SetVariable (DC_LOGIN_MSG, "Username or Password is wrong!");
00381 else req.SetVariable (DC_LOGIN_MSG, "You have to log in again");
00382 }
00383 return NULL;
00384 }
00385 req.SetVariable (DC_USER_NAME, vUsers[id]->GetUsername ());
00386
00387 if (group.IsEmpty ())
00388 {
00389 ret_code = DC_LM_NONE;
00390 return vUsers[id]->GetStatusObject ();
00391 }
00392
00393 if (CheckGroupAccess (id, group) == false)
00394 {
00395 ret_code = DC_LM_SEND;
00396 new_page = sDenyFileName;
00397 return NULL;
00398 }
00399
00400 ret_code = DC_LM_OK;
00401 return vUsers[id]->GetStatusObject ();
00402 }
00403
00404 int CdcLoginManager::GetUserID (const CdcRequest& req, CdcDaaResponse& DaaRes,
00405 bool& isRes, bool& isVar)
00406 {
00407
00408 if (bBuiltInLogin)
00409 isRes = BuildResFromVar (req, DaaRes);
00410 else
00411 isRes = BuildResFromHeader (req, DaaRes);
00412 if (isRes == true)
00413 return FindUser (DaaRes.sUsername);
00414
00415 int id = -1;
00416 CdcString user_id = req.GetVariable (DC_USER_ID);
00417 isVar = !user_id.IsEmpty ();
00418 if (isVar)
00419 id = CdcParameters::ToInt (user_id);
00420 return id > vUsers.size () ? -1 : id;
00421 }
00422
00423 bool CdcLoginManager::BuildResFromVar (const CdcRequest& req, CdcDaaResponse& DaaRes)
00424 {
00425 DaaRes.sNonce = req.GetVariable (DC_NONCE);
00426 if (DaaRes.sNonce.IsEmpty ()) return false;
00427 DaaRes.sUsername = req.GetVariable (DC_USER_NAME);
00428 DaaRes.sRealm = req.GetVariable (DC_REALM);
00429 DaaRes.sOpaque = req.GetVariable (DC_OPAQUE);
00430 DaaRes.sURL = req.GetVariable (DC_URL);
00431 DaaRes.sMethod = req.GetMethod ();
00432 DaaRes.sQOP = "auth";
00433 DaaRes.sCnonce = req.GetVariable (DC_CNONCE);
00434 DaaRes.sResponse = req.GetVariable (DC_RESPONSE);
00435 DaaRes.sNC = req.GetVariable (DC_NC);
00436 return true;
00437 }
00438
00439 bool CdcLoginManager::BuildResFromHeader (const CdcRequest& req, CdcDaaResponse& DaaRes)
00440 {
00441 CdcString Auth = req.GetHeaderField ("Authorization");
00442 if (Auth.IsEmpty ()) return false;
00443 DaaRes.sUsername = GetProp ("username", Auth);
00444 DaaRes.sRealm = GetProp ("realm", Auth);
00445 DaaRes.sNonce = GetProp ("nonce", Auth);
00446 DaaRes.sOpaque = GetProp ("opaque", Auth);
00447 DaaRes.sURL = GetProp ("uri", Auth);
00448 DaaRes.sMethod = req.GetMethod ();
00449 DaaRes.sQOP = GetProp ("qop", Auth);
00450 DaaRes.sCnonce = GetProp ("cnonce", Auth);
00451 DaaRes.sResponse = GetProp ("response", Auth);
00452 DaaRes.sNC = GetProp ("nc", Auth);
00453 return true;
00454 }
00455
00456 CdcString CdcLoginManager::GetProp (const CdcString& name, const CdcString& tag)
00457 {
00458 int place = tag.Find (name + '=');
00459
00460 if (place < 0) return EMPTY_STR;
00461 CdcString ret = tag.Mid (place + name.GetLength () + 1);
00462 ret.TrimLeft ();
00463 ret = ret.Left (ret.FindOneOf (",\n\r"));
00464 ret.TrimRight ();
00465 if (ret[0] == '\"' && ret[ret.GetLength () - 1] == '\"')
00466 ret = ret.Mid (1, ret.GetLength () - 2);
00467 return ret;
00468 }
00469
00470 int CdcLoginManager::FindUser (const CdcString& username)
00471 {
00472 int id;
00473 if (username.IsEmpty ()) return -1;
00474 t_users_vector::iterator itr;
00475 for (itr = vUsers.begin(), id = 0; itr != vUsers.end(); itr++, id++)
00476 {
00477 if ((*itr)->GetUsername () == username)
00478 break;
00479 }
00480 if (itr == vUsers.end()) return -1;
00481 return id;
00482 }
00483
00484
00485 bool CdcLoginManager::AddUser (const CdcString& username,
00486 const CdcString& password)
00487 {
00488 int id = FindUser (username);
00489 if (id >= 0) return false;
00490
00491 CdcUser* user = new CdcUser (username, password);
00492 if (user == NULL)
00493 throw CdcException ("Out of memory.");
00494
00495 vUsers.push_back (user);
00496
00497 return true;
00498 }
00499
00500
00501 bool CdcLoginManager::ChangePassword (const CdcString& username,
00502 const CdcString& old_password,
00503 const CdcString new_password)
00504 {
00505 int id = FindUser (username);
00506 if (id < 0) return false;
00507
00508 if (vUsers[id]->GetPassword () != old_password)
00509 return false;
00510
00511 vUsers[id]->SetPassword (new_password);
00512 return true;
00513 }
00514
00515
00516 bool CdcLoginManager::RemoveUser (const CdcString& username,
00517 const CdcString& password)
00518 {
00519 int id = FindUser (username);
00520 if (id < 0) return false;
00521
00522 if (vUsers[id]->GetPassword () != password)
00523 return false;
00524
00525 CdcUser* ptemp = vUsers[id];
00526 vUsers.erase(vUsers.begin() + id);
00527 delete ptemp;
00528
00529 return true;
00530 }
00531
00532
00533 int DcClean (void* pParam)
00534 {
00535 bool* pRun = (bool*)pParam;
00536
00537 while (*pRun == true)
00538 {
00539 CdcLoginManager::oStartThread.Wait (600000);
00540 if (*pRun == false) return 0;
00541 Sleep (DC_CHL_STL_ADD * 1000);
00542
00543 if (*pRun == false) return 0;
00544 CdcLoginManager::Clean ();
00545 }
00546 return 0;
00547 }