TEST_F(ItemDataTest, PasswordHistory) { size_t pwh_max, num_err; PWHistList pwhl; const StringX pw1(L"banana-0rchid"); const StringX pw2(L"banana-1rchid"); const StringX pw3(L"banana-2rchid"); const StringX pw4(L"banana-5rchid"); PWSprefs *prefs = PWSprefs::GetInstance(); prefs->SetPref(PWSprefs::SavePasswordHistory, true); prefs->SetPref(PWSprefs::NumPWHistoryDefault, 3); CItemData di; di.SetCTime(); di.SetPassword(pw1); // first time must be Set, not Update! di.UpdatePassword(pw2); EXPECT_FALSE(di.GetPWHistory().empty()); EXPECT_TRUE(CreatePWHistoryList(di.GetPWHistory(), pwh_max, num_err, pwhl, PWSUtil::TMC_ASC_UNKNOWN)); EXPECT_EQ(0, num_err); EXPECT_EQ(3, pwh_max); EXPECT_EQ(1, pwhl.size()); EXPECT_EQ(pw1, pwhl[0].password); di.UpdatePassword(pw3); EXPECT_TRUE(CreatePWHistoryList(di.GetPWHistory(), pwh_max, num_err, pwhl, PWSUtil::TMC_ASC_UNKNOWN)); EXPECT_EQ(0, num_err); EXPECT_EQ(3, pwh_max); EXPECT_EQ(2, pwhl.size()); EXPECT_EQ(pw1, pwhl[0].password); EXPECT_EQ(pw2, pwhl[1].password); di.UpdatePassword(pw4); EXPECT_TRUE(CreatePWHistoryList(di.GetPWHistory(), pwh_max, num_err, pwhl, PWSUtil::TMC_ASC_UNKNOWN)); EXPECT_EQ(0, num_err); EXPECT_EQ(3, pwh_max); EXPECT_EQ(3, pwhl.size()); EXPECT_EQ(pw1, pwhl[0].password); EXPECT_EQ(pw2, pwhl[1].password); EXPECT_EQ(pw3, pwhl[2].password); di.UpdatePassword(L"Last1"); EXPECT_TRUE(CreatePWHistoryList(di.GetPWHistory(), pwh_max, num_err, pwhl, PWSUtil::TMC_ASC_UNKNOWN)); EXPECT_EQ(0, num_err); EXPECT_EQ(3, pwh_max); EXPECT_EQ(3, pwhl.size()); EXPECT_EQ(pw2, pwhl[0].password); EXPECT_EQ(pw3, pwhl[1].password); EXPECT_EQ(pw4, pwhl[2].password); }
inline wostream& print_field_value(wostream &os, wchar_t tag, const CItemData &item, CItemData::FieldType ft) { StringX fieldValue; switch (ft) { case CItemData::DCA: case CItemData::SHIFTDCA: { int16 dca = -1; if (item.GetDCA(dca) != -1) { LoadAString(fieldValue, dca2str(dca)); } break; } case CItemData::PWHIST: { const StringX pwh_str = item.GetPWHistory(); if (!pwh_str.empty()) { StringXStream value_stream; size_t ignored; PWHistList pwhl; const bool save_pwhistory = CreatePWHistoryList(pwh_str, ignored, ignored, pwhl, PWSUtil::TMC_LOCALE); value_stream << L"Save: " << (save_pwhistory? L"Yes" : L"No"); if ( !pwhl.empty() ) value_stream << endl; for( const auto &pwh: pwhl) value_stream << pwh.changedate << L": " << pwh.password << endl; fieldValue = value_stream.str(); } break; } case CItemData::POLICY: { PWPolicy policy; item.GetPWPolicy(policy); fieldValue = policy.GetDisplayString(); break; } default: fieldValue = item.GetFieldValue(ft); break; } const StringX sep1{L' '}, sep2{L": "}; StringXStream tmpStream; tmpStream << tag << L' ' << item.FieldName(ft) << L": " << fieldValue; const auto offset = 1 /*tag*/ + sep1.size() + sep2.size() + item.FieldName(ft).size(); lines_vec lines{ stream2vec(tmpStream)}; if ( lines.size() > 1) { std::for_each( lines.begin()+1, lines.end(), [offset](StringX &line) { line.insert(0, offset, L' '); }); } for( const auto &line: lines ) os << line << endl; return os; }
bool CreatePWHistoryList(const StringX &pwh_str, size_t &pwh_max, size_t &num_err, PWHistList &pwhl, PWSUtil::TMC time_format) { // Return boolean value stating if PWHistory status is active pwh_max = num_err = 0; pwhl.clear(); StringX pwh_s = pwh_str; const size_t len = pwh_s.length(); if (len < 5) { num_err = len != 0 ? 1 : 0; return false; } bool bStatus = pwh_s[0] != charT('0'); int n; iStringXStream ism(StringX(pwh_s, 1, 2)); // max history 1 byte hex ism >> hex >> pwh_max; if (!ism) return false; iStringXStream isn(StringX(pwh_s, 3, 2)); // cur # entries 1 byte hex isn >> hex >> n; if (!isn) return false; // Sanity check: Each entry has at least 12 bytes representing // time + pw length // so if pwh_s isn't long enough check if it contains integral number of history records if (len - 5 < unsigned(12 * n)) { size_t offset = 5; bool err=false; while (offset < len) { offset += 8; //date if (offset + 4 >= len) { //passwd len err = true; break; } iStringXStream ispwlen(StringX(pwh_s, offset, 4)); // pw length 2 byte hex if (!ispwlen){ err = true; break; } int ipwlen = 0; ispwlen >> hex >> ipwlen; if ( (ipwlen <= 0) || (offset + 4 + ipwlen > len) ) { err = true; break; } offset += 4 + ipwlen; } if ( err || (offset != len) ) { num_err = n; return false; } //number of errors will be counted later }
void PasswordSafeSearch::FindMatches(const StringX& searchText, bool fCaseSensitive, SearchPointer& searchPtr, const CItemData::FieldBits& bsFields, bool fUseSubgroups, const wxString& subgroupText, CItemData::FieldType subgroupObject, PWSMatch::MatchRule subgroupFunction, bool subgroupFunctionCaseSensitive, Iter begin, Iter end, Accessor afn) { if (searchText.empty()) return; searchPtr.Clear(); typedef StringX (CItemData::*ItemDataFuncT)() const; struct { CItemData::FieldType type; ItemDataFuncT func; } ItemDataFields[] = { {CItemData::GROUP, &CItemData::GetGroup}, {CItemData::TITLE, &CItemData::GetTitle}, {CItemData::USER, &CItemData::GetUser}, {CItemData::PASSWORD, &CItemData::GetPassword}, // {CItemData::NOTES, &CItemData::GetNotes}, {CItemData::URL, &CItemData::GetURL}, {CItemData::EMAIL, &CItemData::GetEmail}, {CItemData::RUNCMD, &CItemData::GetRunCommand}, {CItemData::AUTOTYPE, &CItemData::GetAutoType}, {CItemData::XTIME_INT, &CItemData::GetXTimeInt}, }; for ( Iter itr = begin; itr != end; ++itr) { const int fn = (subgroupFunctionCaseSensitive? -subgroupFunction: subgroupFunction); if (fUseSubgroups && !afn(itr).Matches(stringT(subgroupText.c_str()), subgroupObject, fn)) continue; bool found = false; for (size_t idx = 0; idx < NumberOf(ItemDataFields) && !found; ++idx) { if (bsFields.test(ItemDataFields[idx].type)) { const StringX str = (afn(itr).*ItemDataFields[idx].func)(); found = fCaseSensitive? str.find(searchText) != StringX::npos: FindNoCase(searchText, str); } } if (!found && bsFields.test(CItemData::NOTES)) { StringX str = afn(itr).GetNotes(); found = fCaseSensitive? str.find(searchText) != StringX::npos: FindNoCase(searchText, str); } if (!found && bsFields.test(CItemData::PWHIST)) { size_t pwh_max, err_num; PWHistList pwhistlist; CreatePWHistoryList(afn(itr).GetPWHistory(), pwh_max, err_num, pwhistlist, PWSUtil::TMC_XML); for (PWHistList::iterator iter = pwhistlist.begin(); iter != pwhistlist.end(); iter++) { PWHistEntry pwshe = *iter; found = fCaseSensitive? pwshe.password.find(searchText) != StringX::npos: FindNoCase(searchText, pwshe.password ); if (found) break; // break out of for loop } pwhistlist.clear(); } if (found) { uuid_array_t uuid; afn(itr).GetUUID(uuid); searchPtr.Add(pws_os::CUUID(uuid)); } } }
bool PWSFilterManager::PassesPWHFiltering(const CItemData *pci) const { bool thistest_rc, bPresent; bool bValue(false); int iValue(0); size_t pwh_max, err_num; PWHistList pwhistlist; bool status = CreatePWHistoryList(pci->GetPWHistory(), pwh_max, err_num, pwhistlist, PWSUtil::TMC_EXPORT_IMPORT); bPresent = pwh_max > 0 || !pwhistlist.empty(); for (auto group_iter = m_vHflgroups.begin(); group_iter != m_vHflgroups.end(); group_iter++) { const vfiltergroup &group = *group_iter; int tests(0); bool thisgroup_rc = false; for (auto num_iter = group.begin(); num_iter != group.end(); num_iter++) { const int &num = *num_iter; if (num == -1) // Padding for FT_PWHIST & FT_POLICY - shouldn't happen here continue; const st_FilterRow &st_fldata = m_currentfilter.vHfldata.at(num); thistest_rc = false; PWSMatch::MatchType mt(PWSMatch::MT_INVALID); const FieldType ft = st_fldata.ftype; switch (ft) { case HT_PRESENT: bValue = bPresent; mt = PWSMatch::MT_BOOL; break; case HT_ACTIVE: bValue = status == TRUE; mt = PWSMatch::MT_BOOL; break; case HT_NUM: iValue = (int)pwhistlist.size(); mt = PWSMatch::MT_INTEGER; break; case HT_MAX: iValue = (int)pwh_max; mt = PWSMatch::MT_INTEGER; break; case HT_CHANGEDATE: mt = PWSMatch::MT_DATE; break; case HT_PASSWORDS: mt = PWSMatch::MT_STRING; break; default: ASSERT(0); } const int ifunction = (int)st_fldata.rule; switch (mt) { case PWSMatch::MT_STRING: for (auto pwshe_iter = pwhistlist.begin(); pwshe_iter != pwhistlist.end(); pwshe_iter++) { PWHistEntry pwshe = *pwshe_iter; thistest_rc = PWSMatch::Match(st_fldata.fstring, pwshe.password, st_fldata.fcase ? -ifunction : ifunction); tests++; if (thistest_rc) break; } break; case PWSMatch::MT_INTEGER: thistest_rc = PWSMatch::Match(st_fldata.fnum1, st_fldata.fnum2, iValue, ifunction); tests++; break; case PWSMatch::MT_DATE: for (auto pwshe_iter = pwhistlist.begin(); pwshe_iter != pwhistlist.end(); pwshe_iter++) { const PWHistEntry pwshe = *pwshe_iter; // Following throws away hours/min/sec from changetime, for proper date comparison time_t changetime = pwshe.changetttdate - (pwshe.changetttdate % (24*60*60)); thistest_rc = PWSMatch::Match(st_fldata.fdate1, st_fldata.fdate2, changetime, ifunction); tests++; if (thistest_rc) break; } break; case PWSMatch::MT_BOOL: thistest_rc = PWSMatch::Match(bValue, ifunction); tests++; break; default: ASSERT(0); } if (tests <= 1) thisgroup_rc = thistest_rc; else { //Within groups, tests are always "AND" connected thisgroup_rc = thistest_rc && thisgroup_rc; } } // This group of tests completed - // if 'thisgroup_rc == true', leave now; else go on to next group if (thisgroup_rc) return true; } // We finished all the groups and haven't found one that is true - exclude entry. return false; }