1 /*
2 KeePass Password Safe - The Open-Source Password Manager
3 Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "ShInstUtil.h"
21
22 #pragma warning(push)
23 #pragma warning(disable: 4996) // SCL warning
24 #include <boost/smart_ptr.hpp>
25 #include <boost/algorithm/string/trim.hpp>
26 #pragma warning(pop)
27
28 static const std_string g_strNGenInstall = _T("ngen_install");
29 static const std_string g_strNGenUninstall = _T("ngen_uninstall");
30 static const std_string g_strNetCheck = _T("net_check");
31 static const std_string g_strPreLoadRegister = _T("preload_register");
32 static const std_string g_strPreLoadUnregister = _T("preload_unregister");
33
34 static LPCTSTR g_lpPathTrimChars = _T("\"' \t\r\n");
35
_tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)36 int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
37 LPTSTR lpCmdLine, int nCmdShow)
38 {
39 UNREFERENCED_PARAMETER(hInstance);
40 UNREFERENCED_PARAMETER(hPrevInstance);
41 UNREFERENCED_PARAMETER(lpCmdLine);
42 UNREFERENCED_PARAMETER(nCmdShow);
43
44 INITCOMMONCONTROLSEX icc;
45 ZeroMemory(&icc, sizeof(INITCOMMONCONTROLSEX));
46 icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
47 icc.dwICC = ICC_STANDARD_CLASSES;
48 InitCommonControlsEx(&icc);
49
50 std_string strCmdLine = GetCommandLine();
51 boost::trim_if(strCmdLine, boost::is_any_of(g_lpPathTrimChars));
52 std::transform(strCmdLine.begin(), strCmdLine.end(), strCmdLine.begin(), _totlower);
53
54 if(StrEndsWith(strCmdLine, g_strNGenInstall))
55 {
56 UpdateNativeImage(false);
57 Sleep(200);
58 UpdateNativeImage(true);
59 }
60
61 if(StrEndsWith(strCmdLine, g_strNGenUninstall))
62 UpdateNativeImage(false);
63
64 if(StrEndsWith(strCmdLine, g_strPreLoadRegister))
65 {
66 RegisterPreLoad(false); // Remove old value in 32-bit reg. view on 64-bit systems
67 RegisterPreLoad(true);
68 }
69
70 if(StrEndsWith(strCmdLine, g_strPreLoadUnregister))
71 RegisterPreLoad(false);
72
73 if(StrEndsWith(strCmdLine, g_strNetCheck))
74 CheckDotNetInstalled();
75
76 return 0;
77 }
78
StrEndsWith(const std_string & strText,const std_string & strEnd)79 bool StrEndsWith(const std_string& strText, const std_string& strEnd)
80 {
81 if(strEnd.size() == 0) return true;
82 if(strEnd.size() > strText.size()) return false;
83 return (strText.substr(strText.size() - strEnd.size()) == strEnd);
84 }
85
EnsureTerminatingSeparator(std_string & strPath)86 void EnsureTerminatingSeparator(std_string& strPath)
87 {
88 if(strPath.size() == 0) return;
89 if(strPath[strPath.size() - 1] == _T('\\')) return;
90
91 strPath += _T("\\");
92 }
93
UpdateNativeImage(bool bInstall)94 void UpdateNativeImage(bool bInstall)
95 {
96 const std_string strNGen = FindNGen();
97 if(strNGen.size() == 0) return;
98
99 const std_string strKeePassExe = GetKeePassExePath();
100 if(strKeePassExe.size() == 0) return;
101
102 std_string strParam = (bInstall ? _T("") : _T("un"));
103 strParam += (_T("install \"") + strKeePassExe) + _T("\"");
104
105 SHELLEXECUTEINFO sei;
106 ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));
107 sei.cbSize = sizeof(SHELLEXECUTEINFO);
108 sei.fMask = SEE_MASK_NOCLOSEPROCESS;
109 sei.lpVerb = _T("open");
110 sei.lpFile = strNGen.c_str();
111 sei.lpParameters = strParam.c_str();
112 sei.nShow = SW_HIDE;
113 ShellExecuteEx(&sei);
114
115 if(sei.hProcess != NULL)
116 {
117 WaitForSingleObject(sei.hProcess, 16000);
118 CloseHandle(sei.hProcess);
119 }
120 }
121
RegisterPreLoad(bool bRegister)122 void RegisterPreLoad(bool bRegister)
123 {
124 LPCTSTR lpKey = _T("Software\\Microsoft\\Windows\\CurrentVersion\\Run");
125 LPCTSTR lpName = _T("KeePass 2 PreLoad");
126
127 const std_string strExe = GetKeePassExePath();
128 if(strExe.size() == 0) return;
129 const std_string strValue = (_T("\"") + strExe) + _T("\" --preload");
130
131 HKEY hRoot = HKEY_LOCAL_MACHINE;
132 HKEY h = NULL;
133 LSTATUS l;
134
135 if(bRegister)
136 {
137 l = RegOpenKeyEx(hRoot, lpKey, 0, KEY_WRITE | KEY_WOW64_64KEY, &h);
138 if((l != ERROR_SUCCESS) || (h == NULL))
139 l = RegCreateKeyEx(hRoot, lpKey, 0, NULL, 0, KEY_WRITE |
140 KEY_WOW64_64KEY, NULL, &h, NULL);
141 if((l == ERROR_SUCCESS) && (h != NULL))
142 {
143 RegSetValueEx(h, lpName, 0, REG_SZ, (const BYTE*)strValue.c_str(),
144 static_cast<DWORD>((strValue.size() + 1) * sizeof(TCHAR)));
145 RegCloseKey(h);
146 }
147 }
148 else // Unregister
149 {
150 for(size_t i = 0; i < 2; ++i)
151 {
152 l = RegOpenKeyEx(hRoot, lpKey, 0, KEY_WRITE |
153 ((i == 0) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY), &h);
154 if((l == ERROR_SUCCESS) && (h != NULL))
155 {
156 RegDeleteValue(h, lpName);
157 RegCloseKey(h);
158 h = NULL;
159 }
160 }
161 }
162 }
163
GetNetInstallRoot()164 std_string GetNetInstallRoot()
165 {
166 std_string str;
167
168 HKEY hNet = NULL;
169 LONG lRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\.NETFramework"),
170 0, KEY_READ | KEY_WOW64_64KEY, &hNet);
171 if((lRes != ERROR_SUCCESS) || (hNet == NULL)) return str;
172
173 const DWORD cbData = 2050;
174 BYTE pbData[cbData];
175 ZeroMemory(pbData, cbData * sizeof(BYTE));
176 DWORD dwData = cbData - 2;
177 lRes = RegQueryValueEx(hNet, _T("InstallRoot"), NULL, NULL, pbData, &dwData);
178 if(lRes == ERROR_SUCCESS) str = (LPCTSTR)(LPTSTR)pbData;
179
180 RegCloseKey(hNet);
181 return str;
182 }
183
GetKeePassExePath()184 std_string GetKeePassExePath()
185 {
186 const DWORD cbData = 2050;
187 TCHAR tszName[cbData];
188 ZeroMemory(tszName, cbData * sizeof(TCHAR));
189
190 GetModuleFileName(NULL, tszName, cbData - 2);
191
192 for(int i = static_cast<int>(_tcslen(tszName)) - 1; i >= 0; --i)
193 {
194 if(tszName[i] == _T('\\')) break;
195 else tszName[i] = 0;
196 }
197
198 std_string strPath = tszName;
199 boost::trim_if(strPath, boost::is_any_of(g_lpPathTrimChars));
200 if(strPath.size() == 0) return strPath;
201
202 return (strPath + _T("KeePass.exe"));
203 }
204
FindNGen()205 std_string FindNGen()
206 {
207 std_string strNGen;
208
209 std_string strRoot = GetNetInstallRoot();
210 if(strRoot.size() == 0) return strNGen;
211 EnsureTerminatingSeparator(strRoot);
212
213 ULONGLONG ullVersion = 0;
214 FindNGenRec(strRoot, strNGen, ullVersion);
215
216 return strNGen;
217 }
218
FindNGenRec(const std_string & strPath,std_string & strNGenPath,ULONGLONG & ullVersion)219 void FindNGenRec(const std_string& strPath, std_string& strNGenPath,
220 ULONGLONG& ullVersion)
221 {
222 const std_string strSearch = strPath + _T("*.*");
223 const std_string strNGen = _T("ngen.exe");
224
225 WIN32_FIND_DATA wfd;
226 ZeroMemory(&wfd, sizeof(WIN32_FIND_DATA));
227 HANDLE hFind = FindFirstFile(strSearch.c_str(), &wfd);
228 if(hFind == INVALID_HANDLE_VALUE) return;
229
230 do
231 {
232 if((wfd.cFileName[0] == _T('\0')) || (_tcsicmp(wfd.cFileName, _T(".")) == 0) ||
233 (_tcsicmp(wfd.cFileName, _T("..")) == 0)) { }
234 else if((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
235 FindNGenRec((strPath + wfd.cFileName) + _T("\\"), strNGenPath, ullVersion);
236 else if(_tcsicmp(wfd.cFileName, strNGen.c_str()) == 0)
237 {
238 const std_string strFullPath = strPath + strNGen;
239 const ULONGLONG ullThisVer = SiuGetFileVersion(strFullPath);
240 if(ullThisVer >= ullVersion)
241 {
242 strNGenPath = strFullPath;
243 ullVersion = ullThisVer;
244 }
245 }
246 }
247 while(FindNextFile(hFind, &wfd) != FALSE);
248
249 FindClose(hFind);
250 }
251
SiuGetFileVersion(const std_string & strFilePath)252 ULONGLONG SiuGetFileVersion(const std_string& strFilePath)
253 {
254 DWORD dwDummy = 0;
255 const DWORD dwVerSize = GetFileVersionInfoSize(
256 strFilePath.c_str(), &dwDummy);
257 if(dwVerSize == 0) return 0;
258
259 boost::scoped_array<BYTE> vVerInfo(new BYTE[dwVerSize]);
260 if(vVerInfo.get() == NULL) return 0; // Out of memory
261
262 if(GetFileVersionInfo(strFilePath.c_str(), 0, dwVerSize,
263 vVerInfo.get()) == FALSE) return 0;
264
265 VS_FIXEDFILEINFO* pFileInfo = NULL;
266 UINT uFixedInfoLen = 0;
267 if(VerQueryValue(vVerInfo.get(), _T("\\"), (LPVOID*)&pFileInfo,
268 &uFixedInfoLen) == FALSE) return 0;
269 if(pFileInfo == NULL) return 0;
270
271 return ((static_cast<ULONGLONG>(pFileInfo->dwFileVersionMS) <<
272 32) | static_cast<ULONGLONG>(pFileInfo->dwFileVersionLS));
273 }
274
CheckDotNetInstalled()275 void CheckDotNetInstalled()
276 {
277 OSVERSIONINFO osv;
278 ZeroMemory(&osv, sizeof(OSVERSIONINFO));
279 osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
280 GetVersionEx(&osv);
281 if(osv.dwMajorVersion >= 6) return; // .NET ships with Vista and higher
282
283 const std_string strNGen = FindNGen();
284 if(strNGen.size() == 0)
285 {
286 std_string strMsg = _T("KeePass 2.x requires the Microsoft .NET Framework 2.0 or higher. ");
287 strMsg += _T("This framework currently does not seem to be installed ");
288 strMsg += _T("on your computer. Without this framework, KeePass will not run.\r\n\r\n");
289 strMsg += _T("The Microsoft .NET Framework is available as free download from the ");
290 strMsg += _T("Microsoft website.\r\n\r\n");
291 strMsg += _T("Do you want to visit the Microsoft website now?");
292
293 const int nRes = MessageBox(NULL, strMsg.c_str(), _T("KeePass Setup"),
294 MB_ICONQUESTION | MB_YESNO);
295 if(nRes == IDYES)
296 {
297 SHELLEXECUTEINFO sei;
298 ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));
299 sei.cbSize = sizeof(SHELLEXECUTEINFO);
300 sei.lpVerb = _T("open");
301 sei.lpFile = _T("https://msdn.microsoft.com/en-us/netframework/aa569263.aspx");
302 sei.nShow = SW_SHOW;
303 ShellExecuteEx(&sei);
304 }
305 }
306 }
307