📄 pwsfile.cpp
字号:
#include "PWSfile.h"#include <io.h>#include <fcntl.h>#include <sys/stat.h>#include <errno.h>bool PWSfile::FileExists(const CMyString &filename){ struct _stat statbuf; int status; status = ::_tstat(filename, &statbuf); return (status == 0);}int PWSfile::RenameFile(const CMyString &oldname, const CMyString &newname){ int status = _trename(oldname, newname); return (status == 0) ? SUCCESS : FAILURE;}PWSfile::PWSfile(const CMyString &filename, const CMyString &passkey) : m_filename(filename), m_passkey(passkey), m_defusername(_T("")), m_curversion(UNKNOWN_VERSION), m_fd(NULL), m_prefString(_T("")){}PWSfile::~PWSfile(){ CloseFile(); // idempotent}// Used to warn pre-2.0 users, and to identify the database as 2.x:static const CMyString V2ItemName(" !!!Version 2 File Format!!! " "Please upgrade to PasswordSafe 2.0" " or later");// Used to specify the exact versionstatic const CMyString VersionString("2.0");int PWSfile::WriteV2Header(){ CItemData header; // Fill out with V2-specific info header.SetName(V2ItemName, _T("")); header.SetPassword(VersionString); header.SetNotes(m_prefString); // need to fallback to V17, since the record // won't be readable otherwise! VERSION sv = m_curversion; m_curversion = V17; int status = WriteRecord(header); // restore after writing V17-format header m_curversion = sv; return status;}int PWSfile::ReadV2Header(){ CItemData header; // need to fallback to V17, since the header // is always written in this format VERSION sv = m_curversion; m_curversion = V17; int status = ReadRecord(header); // restore after reading V17-format header m_curversion = sv; if (status == SUCCESS) { const CMyString name = header.GetName(); // XXX Need to compare header.GetPassword() against VersionString // XXX as well, for inter-2.x version checks status = (name == V2ItemName) ? SUCCESS : WRONG_VERSION; } if (status == SUCCESS) m_prefString = header.GetNotes(); return status;}int PWSfile::OpenWriteFile(VERSION v){ if (v != V17 && v != V20) return UNSUPPORTED_VERSION;#ifdef UNICODE m_fd = _wfopen((LPCTSTR)m_filename, _T("wb") );#else m_fd = fopen((LPCTSTR)m_filename, _T("wb") );#endif if (m_fd == NULL) return CANT_OPEN_FILE; m_curversion = v; // Following used to verify passkey against file's passkey unsigned char randstuff[StuffSize]; unsigned char randhash[20]; // HashSize int x; for (x=0; x<8; x++) randstuff[x] = newrand(); randstuff[8] = randstuff[9] = '\0'; GenRandhash(m_passkey, randstuff, randhash); fwrite(randstuff, 1, 8, m_fd); fwrite(randhash, 1, 20, m_fd); for (x=0; x<SaltLength; x++) m_salt[x] = newrand(); fwrite(m_salt, 1, SaltLength, m_fd); for (x=0; x<8; x++) m_ipthing[x] = newrand(); fwrite(m_ipthing, 1, 8, m_fd); if (v == V20) { int status = WriteV2Header(); if (status != SUCCESS) return status; } return SUCCESS;}int PWSfile::OpenReadFile(VERSION v){ if (v != V17 && v != V20) return UNSUPPORTED_VERSION;#ifdef UNICODE m_fd = _wfopen((LPCTSTR) m_filename, _T("rb"));#else m_fd = fopen((LPCTSTR) m_filename, _T("rb"));#endif if (m_fd == NULL) return CANT_OPEN_FILE; m_curversion = v; int status = CheckPassword(); if (status != SUCCESS) { CloseFile(); return status; } fread(m_salt, 1, SaltLength, m_fd); fread(m_ipthing, 1, 8, m_fd); if (v == V20) status = ReadV2Header(); return status;}void PWSfile::CloseFile(){ if (m_fd != NULL) { fclose(m_fd); m_fd = NULL; }}PWSfile::VERSION PWSfile::GetFileVersion(){ VERSION v; int status = OpenReadFile(V20); CloseFile(); switch (status) { case SUCCESS: v = V20; break; case WRONG_VERSION: v = V17; break; default: v = UNKNOWN_VERSION; break; } return v;}int PWSfile::CheckPassword(){ // if file was opened, leave it open, // else open it for check, close when done const bool was_open = (m_fd != NULL); if (!was_open) {#ifdef UNICODE m_fd = _wfopen((LPCTSTR) m_filename, _T("rb"));#else m_fd = fopen((LPCTSTR) m_filename, _T("rb"));#endif if (m_fd == NULL) return CANT_OPEN_FILE; } unsigned char randstuff[StuffSize]; unsigned char randhash[20]; // HashSize fread(randstuff, 1, 8, m_fd); randstuff[8] = randstuff[9] = '\0'; // Gross fugbix fread(randhash, 1, 20, m_fd); if (!was_open) CloseFile(); unsigned char temphash[20]; // HashSize GenRandhash(m_passkey, randstuff, temphash); if (0 != ::memcmp((char*)randhash, (char*)temphash, 20)) {// HashSize return WRONG_PASSWORD; } else { return SUCCESS; }}int PWSfile::WriteCBC(unsigned char type, const CString &data){ // We do a double cast because the LPCSTR cast operator is overridden by the CString class // to access the pointer we need, // but we in fact need it as an unsigned char. Grrrr. LPCSTR passstr = LPCSTR(m_passkey); LPCSTR datastr = LPCSTR(data); return _writecbc(m_fd, (const unsigned char *)datastr, data.GetLength(), type, (const unsigned char *)passstr, m_passkey.GetLength(), m_salt, SaltLength, m_ipthing);}int PWSfile::WriteCBC(unsigned char type, const unsigned char *data, unsigned int length){ // We do a double cast because the LPCSTR cast operator is overridden by the CString class // to access the pointer we need, // but we in fact need it as an unsigned char. Grrrr. LPCSTR passstr = LPCSTR(m_passkey); return _writecbc(m_fd, data, length, type, (const unsigned char *)passstr, m_passkey.GetLength(), m_salt, SaltLength, m_ipthing);}int PWSfile::WriteRecord(const CItemData &item){ ASSERT(m_fd != NULL); ASSERT(m_curversion != UNKNOWN_VERSION); switch (m_curversion) { case V17: { // 1.x programs totally ignore the type byte, hence safe to write it // (no need for two WriteCBC functions) CMyString name = item.GetName(); // If name field already ecists - use it. This is for the 2.0 header, as well as for files // that were imported and re-exported. if (name.IsEmpty()) { // The name in 1.7 consists of title + SPLTCHR + username // DEFUSERNAME was used in previous versions, but 2.0 converts this upon import // so it is not an issue here. // Prepend 2.0 group field to name, if not empty // i.e. group "finances" name "broker" -> "finances.broker" CMyString group = item.GetGroup(); CMyString title = item.GetTitle(); if (!group.IsEmpty()) { group += _T("."); group += title; title = group; } name = title; name += SPLTCHR; name += item.GetUser(); } WriteCBC(CItemData::NAME, name); WriteCBC(CItemData::PASSWORD, item.GetPassword()); WriteCBC(CItemData::NOTES, item.GetNotes()); return SUCCESS; } break; case V20: { { uuid_array_t uuid_array; item.GetUUID(uuid_array); WriteCBC(CItemData::UUID, uuid_array, sizeof(uuid_array)); } WriteCBC(CItemData::GROUP, item.GetGroup()); WriteCBC(CItemData::TITLE, item.GetTitle()); WriteCBC(CItemData::USER, item.GetUser()); WriteCBC(CItemData::PASSWORD, item.GetPassword()); WriteCBC(CItemData::NOTES, item.GetNotes()); WriteCBC(CItemData::END, _T("")); return SUCCESS; } default: ASSERT(0); return UNSUPPORTED_VERSION; } return SUCCESS;}intPWSfile::ReadCBC(unsigned char &type, CMyString &data){ // We do a double cast because the LPCSTR cast operator is overridden by the CString class // to access the pointer we need, // but we in fact need it as an unsigned char. Grrrr. LPCSTR passstr = LPCSTR(m_passkey); unsigned char *buffer = NULL; unsigned int buffer_len = 0; int retval; retval = _readcbc(m_fd, buffer, buffer_len, type, (const unsigned char *)passstr, m_passkey.GetLength(), m_salt, SaltLength, m_ipthing); if (buffer_len > 0) { CMyString str(LPCSTR(buffer), buffer_len); data = str; trashMemory(buffer, buffer_len); delete[] buffer; } else { data = _T(""); // no need to delete[] buffer, since _readcbc will not allocate if // buffer_len is zero } return retval;}int PWSfile::ReadRecord(CItemData &item){ ASSERT(m_fd != NULL); ASSERT(m_curversion != UNKNOWN_VERSION); CMyString tempdata; int numread = 0; unsigned char type; switch (m_curversion) { case V17: { // type is meaningless, but why write two versions of ReadCBC? numread += ReadCBC(type, tempdata); item.SetName(tempdata, m_defusername); numread += ReadCBC(type, tempdata); item.SetPassword(tempdata); numread += ReadCBC(type, tempdata); item.SetNotes(tempdata); // No UUID, so we create one here item.CreateUUID(); // No Group - currently leave empty return (numread > 0) ? SUCCESS : END_OF_FILE; } case V20: { int emergencyExit = 255; // to avoid endless loop. int fieldLen; // zero means end of file reached bool endFound = false; // set to true when record end detected - happy end do { fieldLen = ReadCBC(type, tempdata); if (fieldLen > 0) { numread += fieldLen; switch (type) { case CItemData::TITLE: item.SetTitle(tempdata); break; case CItemData::USER: item.SetUser(tempdata); break; case CItemData::PASSWORD: item.SetPassword(tempdata); break; case CItemData::NOTES: item.SetNotes(tempdata); break; case CItemData::END: endFound = true; break; case CItemData::UUID: { LPCSTR ptr = LPCSTR(tempdata); uuid_array_t uuid_array; for (int i = 0; i < sizeof(uuid_array); i++) uuid_array[i] = ptr[i]; item.SetUUID(uuid_array); break; } case CItemData::GROUP: item.SetGroup(tempdata); break; // just silently ignore fields we don't support. // this is forward compatability... case CItemData::CTIME: case CItemData::MTIME: case CItemData::ATIME: case CItemData::LTIME: case CItemData::POLICY: default: // XXX Set a flag here so user can be warned that // XXX we read a file format we don't fully support break; } // switch } // if (fieldLen > 0) } while (!endFound && fieldLen > 0 && --emergencyExit > 0); return (numread > 0) ? SUCCESS : END_OF_FILE; } default: ASSERT(0); return UNSUPPORTED_VERSION; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -