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