OSDN Git Service

Add options for disabling weak encryption methods.
[ffftp/ffftp.git] / updater.c
1 // updater.c\r
2 // Copyright (C) 2014 Suguru Kawamoto\r
3 // ソフトウェア自動更新\r
4 // コードの再利用のため表記はwchar_t型だが実体はchar型でUTF-8\r
5 \r
6 #include <tchar.h>\r
7 #include <ws2tcpip.h>\r
8 #include <windows.h>\r
9 #include <mmsystem.h>\r
10 typedef SOCKADDR_STORAGE_XP SOCKADDR_STORAGE;\r
11 typedef SOCKADDR_STORAGE *PSOCKADDR_STORAGE, FAR *LPSOCKADDR_STORAGE;\r
12 #include <winhttp.h>\r
13 \r
14 #include "updater.h"\r
15 #include "socketwrapper.h"\r
16 #include "protectprocess.h"\r
17 #include "mbswrapper.h"\r
18 \r
19 typedef struct\r
20 {\r
21         BYTE Signature[64];\r
22         BYTE ListHash[64];\r
23 } UPDATE_HASH;\r
24 \r
25 #define UPDATE_LIST_FILE_FLAG_DIRECTORY 0x00000001\r
26 \r
27 typedef struct\r
28 {\r
29         DWORD Flags;\r
30         CHAR SrcPath[128];\r
31         BYTE SrcHash[64];\r
32         CHAR DstPath[128];\r
33         FILETIME Timestamp;\r
34 } UPDATE_LIST_FILE;\r
35 \r
36 typedef struct\r
37 {\r
38         DWORD Version;\r
39         CHAR VersionString[32];\r
40         DWORD FileCount;\r
41         UPDATE_LIST_FILE File[1];\r
42 } UPDATE_LIST;\r
43 \r
44 BOOL ReadFileViaHTTPW(void* pOut, DWORD Length, DWORD* pLength, LPCWSTR UserAgent, LPCWSTR ServerName, LPCWSTR ObjectName)\r
45 {\r
46         BOOL bResult;\r
47         HINTERNET hSession;\r
48         HINTERNET hConnect;\r
49         HINTERNET hRequest;\r
50         bResult = FALSE;\r
51         if(hSession = WinHttpOpen(UserAgent, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0))\r
52         {\r
53                 if(hConnect = WinHttpConnect(hSession, ServerName, INTERNET_DEFAULT_HTTP_PORT, 0))\r
54                 {\r
55                         if(hRequest = WinHttpOpenRequest(hConnect, L"GET", ObjectName, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0))\r
56                         {\r
57                                 if(WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))\r
58                                 {\r
59                                         if(WinHttpReceiveResponse(hRequest, NULL))\r
60                                         {\r
61                                                 if(WinHttpQueryDataAvailable(hRequest, pLength))\r
62                                                 {\r
63                                                         if(*pLength <= Length)\r
64                                                         {\r
65                                                                 if(WinHttpReadData(hRequest, pOut, Length, pLength))\r
66                                                                         bResult = TRUE;\r
67                                                         }\r
68                                                 }\r
69                                         }\r
70                                 }\r
71                                 WinHttpCloseHandle(hRequest);\r
72                         }\r
73                         WinHttpCloseHandle(hConnect);\r
74                 }\r
75                 WinHttpCloseHandle(hSession);\r
76         }\r
77         return bResult;\r
78 }\r
79 \r
80 BOOL ReadFileViaHTTP(void* pOut, DWORD Length, DWORD* pLength, LPCSTR UserAgent, LPCSTR ServerName, LPCSTR ObjectName)\r
81 {\r
82         BOOL r;\r
83         wchar_t* pw0;\r
84         wchar_t* pw1;\r
85         wchar_t* pw2;\r
86         pw0 = DuplicateMtoW(UserAgent, -1);\r
87         pw1 = DuplicateMtoW(ServerName, -1);\r
88         pw2 = DuplicateMtoW(ObjectName, -1);\r
89         r = ReadFileViaHTTPW(pOut, Length, pLength, pw0, pw1, pw2);\r
90         FreeDuplicatedString(pw0);\r
91         FreeDuplicatedString(pw1);\r
92         FreeDuplicatedString(pw2);\r
93         return r;\r
94 }\r
95 \r
96 BOOL SaveMemoryToFileWithTimestamp(LPCTSTR FileName, void* pData, DWORD Size, FILETIME* pTimestamp)\r
97 {\r
98         BOOL bResult;\r
99         HANDLE hFile;\r
100         bResult = FALSE;\r
101         if((hFile = CreateFile(FileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)\r
102         {\r
103                 if(WriteFile(hFile, pData, Size, &Size, NULL))\r
104                 {\r
105                         if(SetFileTime(hFile, NULL, NULL, pTimestamp))\r
106                                 bResult = TRUE;\r
107                 }\r
108                 CloseHandle(hFile);\r
109         }\r
110         return bResult;\r
111 }\r
112 \r
113 BOOL CopyAllFilesInDirectory(LPCTSTR From, LPCTSTR To)\r
114 {\r
115         BOOL bResult;\r
116         TCHAR* pFrom;\r
117         TCHAR* pTo;\r
118         SHFILEOPSTRUCT fop;\r
119         bResult = FALSE;\r
120         if(pFrom = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(From) + _tcslen(_T("\\*")) + 2)))\r
121         {\r
122                 _tcscpy(pFrom, From);\r
123                 _tcsncpy(pFrom + _tcslen(pFrom), _T("\\*"), _tcslen(_T("\\*")) + 2);\r
124                 if(pTo = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(To) + 2)))\r
125                 {\r
126                         _tcsncpy(pTo, To, _tcslen(To) + 2);\r
127                         memset(&fop, 0, sizeof(SHFILEOPSTRUCT));\r
128                         fop.wFunc = FO_COPY;\r
129                         fop.pFrom = pFrom;\r
130                         fop.pTo = pTo;\r
131                         fop.fFlags = FOF_NO_UI;\r
132                         if(SHFileOperation(&fop) == 0)\r
133                                 bResult = TRUE;\r
134                         free(pTo);\r
135                 }\r
136                 free(pFrom);\r
137         }\r
138         return bResult;\r
139 }\r
140 \r
141 BOOL DeleteDirectoryAndContents(LPCTSTR Path)\r
142 {\r
143         BOOL bResult;\r
144         TCHAR* pFrom;\r
145         SHFILEOPSTRUCT fop;\r
146         bResult = FALSE;\r
147         if(pFrom = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(Path) + 2)))\r
148         {\r
149                 _tcsncpy(pFrom, Path, _tcslen(Path) + 2);\r
150                 memset(&fop, 0, sizeof(SHFILEOPSTRUCT));\r
151                 fop.wFunc = FO_DELETE;\r
152                 fop.pFrom = pFrom;\r
153                 fop.fFlags = FOF_NO_UI;\r
154                 if(SHFileOperation(&fop) == 0)\r
155                         bResult = TRUE;\r
156                 free(pFrom);\r
157         }\r
158         return bResult;\r
159 }\r
160 \r
161 // FFFTPの更新情報を確認\r
162 BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir, DWORD* pVersion, LPTSTR pVersionString)\r
163 {\r
164         BOOL bResult;\r
165         DWORD Length;\r
166         BYTE Buf1[65536];\r
167         BYTE Buf2[1024];\r
168         UPDATE_HASH UpdateHash;\r
169         BYTE Hash[64];\r
170         UPDATE_LIST* pUpdateList;\r
171         bResult = FALSE;\r
172         if(ReadFileViaHTTP(&Buf1, sizeof(Buf1), &Length, HTTP_USER_AGENT, UPDATE_SERVER, UPDATE_HASH_PATH))\r
173         {\r
174                 if(DecryptSignature(UPDATE_RSA_PUBLIC_KEY, &Buf1, Length, &Buf2, sizeof(Buf2), &Length))\r
175                 {\r
176                         if(Length == sizeof(UPDATE_HASH))\r
177                         {\r
178                                 memcpy(&UpdateHash, &Buf2, sizeof(UPDATE_HASH));\r
179                                 if(memcmp(&UpdateHash.Signature, UPDATE_SIGNATURE, 64) == 0)\r
180                                 {\r
181                                         if(ReadFileViaHTTP(&Buf1, sizeof(Buf1), &Length, HTTP_USER_AGENT, UPDATE_SERVER, UPDATE_LIST_PATH))\r
182                                         {\r
183                                                 if(GetHashSHA512(&Buf1, Length, &Hash))\r
184                                                 {\r
185                                                         if(memcmp(&Hash, &UpdateHash.ListHash, 64) == 0)\r
186                                                         {\r
187                                                                 if(Length >= sizeof(UPDATE_LIST))\r
188                                                                 {\r
189                                                                         bResult = TRUE;\r
190                                                                         pUpdateList = (UPDATE_LIST*)&Buf1;\r
191                                                                         if(pUpdateList->Version > *pVersion)\r
192                                                                         {\r
193                                                                                 *pVersion = pUpdateList->Version;\r
194                                                                                 _tcscpy(pVersionString, pUpdateList->VersionString);\r
195                                                                         }\r
196                                                                         if(bDownload)\r
197                                                                                 bResult = PrepareUpdates(&Buf1, Length, DownloadDir);\r
198                                                                 }\r
199                                                         }\r
200                                                 }\r
201                                         }\r
202                                 }\r
203                         }\r
204                 }\r
205         }\r
206         return bResult;\r
207 }\r
208 \r
209 // 更新するファイルをダウンロード\r
210 BOOL PrepareUpdates(void* pList, DWORD ListLength, LPCTSTR DownloadDir)\r
211 {\r
212         BOOL bResult;\r
213         UPDATE_LIST* pUpdateList;\r
214         void* pBuf;\r
215         DWORD i;\r
216         BOOL b;\r
217         DWORD Length;\r
218         BYTE Hash[64];\r
219         TCHAR Path[MAX_PATH];\r
220         bResult = FALSE;\r
221         if(ListLength >= sizeof(UPDATE_LIST))\r
222         {\r
223                 pUpdateList = (UPDATE_LIST*)pList;\r
224                 if((pUpdateList->FileCount - 1) * sizeof(UPDATE_LIST_FILE) + sizeof(UPDATE_LIST) >= ListLength)\r
225                 {\r
226                         bResult = TRUE;\r
227                         DeleteDirectoryAndContents(DownloadDir);\r
228                         CreateDirectory(DownloadDir, NULL);\r
229                         pBuf = malloc(16777216);\r
230                         for(i = 0; i < pUpdateList->FileCount; i++)\r
231                         {\r
232                                 b = FALSE;\r
233                                 if(pUpdateList->File[i].Flags & UPDATE_LIST_FILE_FLAG_DIRECTORY)\r
234                                 {\r
235                                         _tcscpy(Path, DownloadDir);\r
236                                         _tcscat(Path, _T("\\"));\r
237                                         _tcscat(Path, pUpdateList->File[i].DstPath);\r
238                                         if(CreateDirectory(Path, NULL))\r
239                                                 b = TRUE;\r
240                                 }\r
241                                 if(strlen(pUpdateList->File[i].SrcPath) > 0)\r
242                                 {\r
243                                         if(ReadFileViaHTTP(pBuf, 16777216, &Length, HTTP_USER_AGENT, UPDATE_SERVER, pUpdateList->File[i].SrcPath))\r
244                                         {\r
245                                                 if(GetHashSHA512(pBuf, Length, &Hash))\r
246                                                 {\r
247                                                         if(memcmp(&Hash, &pUpdateList->File[i].SrcHash, 64) == 0)\r
248                                                         {\r
249                                                                 _tcscpy(Path, DownloadDir);\r
250                                                                 _tcscat(Path, _T("\\"));\r
251                                                                 _tcscat(Path, pUpdateList->File[i].DstPath);\r
252                                                                 if(SaveMemoryToFileWithTimestamp(Path, pBuf, Length, &pUpdateList->File[i].Timestamp))\r
253                                                                         b = TRUE;\r
254                                                         }\r
255                                                 }\r
256                                         }\r
257                                 }\r
258                                 if(!b)\r
259                                 {\r
260                                         bResult = FALSE;\r
261                                         break;\r
262                                 }\r
263                         }\r
264                         free(pBuf);\r
265                 }\r
266         }\r
267         return bResult;\r
268 }\r
269 \r
270 // FFFTPを更新\r
271 BOOL ApplyUpdates(LPCTSTR DestinationDir, LPCTSTR BackupDirName)\r
272 {\r
273         BOOL bResult;\r
274         TCHAR Source[MAX_PATH];\r
275         TCHAR Backup[MAX_PATH];\r
276         TCHAR DestinationBackup[MAX_PATH];\r
277         TCHAR* p;\r
278         bResult = FALSE;\r
279         if(GetModuleFileName(NULL, Source, MAX_PATH) > 0)\r
280         {\r
281                 if(p = _tcsrchr(Source, _T('\\')))\r
282                         *p = _T('\0');\r
283                 _tcscpy(Backup, Source);\r
284                 _tcscat(Backup, _T("\\"));\r
285                 _tcscat(Backup, BackupDirName);\r
286                 DeleteDirectoryAndContents(Backup);\r
287                 if(CopyAllFilesInDirectory(DestinationDir, Backup))\r
288                 {\r
289                         _tcscpy(DestinationBackup, DestinationDir);\r
290                         _tcscat(DestinationBackup, _T("\\"));\r
291                         _tcscat(DestinationBackup, BackupDirName);\r
292                         if(CopyAllFilesInDirectory(Source, DestinationDir))\r
293                         {\r
294                                 DeleteDirectoryAndContents(DestinationBackup);\r
295                                 bResult = TRUE;\r
296                         }\r
297                         else\r
298                         {\r
299                                 DeleteDirectoryAndContents(DestinationBackup);\r
300                                 CopyAllFilesInDirectory(Backup, DestinationDir);\r
301                         }\r
302                 }\r
303         }\r
304         return bResult;\r
305 }\r
306 \r
307 // 更新するファイルをダウンロード\r
308 BOOL CleanupUpdates(LPCTSTR DownloadDir)\r
309 {\r
310         BOOL bResult;\r
311         bResult = FALSE;\r
312         if(DeleteDirectoryAndContents(DownloadDir))\r
313                 bResult = TRUE;\r
314         return bResult;\r
315 }\r
316 \r
317 // 更新用のプロセスを起動\r
318 BOOL StartUpdateProcess(LPCTSTR DownloadDir, LPCTSTR CommandLine)\r
319 {\r
320         BOOL bResult;\r
321         TCHAR Name[MAX_PATH];\r
322         TCHAR* p;\r
323         TCHAR Path[MAX_PATH];\r
324         bResult = FALSE;\r
325         if(GetModuleFileName(NULL, Name, MAX_PATH) > 0)\r
326         {\r
327                 if(p = _tcsrchr(Name, _T('\\')))\r
328                         p++;\r
329                 else\r
330                         p = Name;\r
331                 _tcscpy(Path, DownloadDir);\r
332                 _tcscat(Path, _T("\\"));\r
333                 _tcscat(Path, p);\r
334                 if(ShellExecute(NULL, _T("open"), Path, CommandLine, NULL, SW_SHOW) > (HINSTANCE)32)\r
335                         bResult = TRUE;\r
336         }\r
337         return bResult;\r
338 }\r
339 \r
340 // 更新用のプロセスを管理者権限で起動\r
341 // Windows XP以前など起動できない場合は現在のプロセスで処理を続行\r
342 BOOL RestartUpdateProcessAsAdministrator(LPCTSTR CommandLine, LPCTSTR Keyword)\r
343 {\r
344         BOOL bResult;\r
345         TCHAR* NewCommandLine;\r
346         TCHAR Path[MAX_PATH];\r
347         SHELLEXECUTEINFO Info;\r
348         bResult = FALSE;\r
349         if(_tcslen(CommandLine) < _tcslen(Keyword) || _tcscmp(CommandLine + _tcslen(CommandLine) - _tcslen(Keyword), Keyword) != 0)\r
350         {\r
351                 if(NewCommandLine = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(CommandLine) + _tcslen(Keyword) + 1)))\r
352                 {\r
353                         _tcscpy(NewCommandLine, CommandLine);\r
354                         _tcscat(NewCommandLine, Keyword);\r
355                         if(GetModuleFileName(NULL, Path, MAX_PATH) > 0)\r
356                         {\r
357                                 memset(&Info, 0, sizeof(SHELLEXECUTEINFO));\r
358                                 Info.cbSize = sizeof(SHELLEXECUTEINFO);\r
359                                 Info.fMask = SEE_MASK_NOCLOSEPROCESS;\r
360                                 Info.lpVerb = _T("runas");\r
361                                 Info.lpFile = Path;\r
362                                 Info.lpParameters = NewCommandLine;\r
363                                 Info.nShow = SW_SHOW;\r
364                                 if(ShellExecuteEx(&Info))\r
365                                 {\r
366                                         WaitForSingleObject(Info.hProcess, INFINITE);\r
367                                         CloseHandle(Info.hProcess);\r
368                                         bResult = TRUE;\r
369                                 }\r
370                         }\r
371                         free(NewCommandLine);\r
372                 }\r
373         }\r
374         return bResult;\r
375 }\r
376 \r