LRN: Fix automake deps to allow -j* builds again
[oweals/gnunet.git] / src / util / win.cc
1 /*\r
2      This file is part of GNUnet.\r
3      (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors)\r
4 \r
5      GNUnet is free software; you can redistribute it and/or modify\r
6      it under the terms of the GNU General Public License as published\r
7      by the Free Software Foundation; either version 2, or (at your\r
8      option) any later version.\r
9 \r
10      GNUnet is distributed in the hope that it will be useful, but\r
11      WITHOUT ANY WARRANTY; without even the implied warranty of\r
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
13      General Public License for more details.\r
14 \r
15      You should have received a copy of the GNU General Public License\r
16      along with GNUnet; see the file COPYING.  If not, write to the\r
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,\r
18      Boston, MA 02111-1307, USA.\r
19 */\r
20 \r
21 /**\r
22  * @file util/win.cc\r
23  * @brief Helper functions for MS Windows in C++\r
24  * @author Nils Durner\r
25  */\r
26 \r
27 #ifndef _WIN_CC\r
28 #define _WIN_CC\r
29 \r
30 #include "winproc.h"\r
31 #include "platform.h"\r
32 #include "gnunet_common.h"\r
33 #include "gnunet_connection_lib.h"\r
34 \r
35 #include <list>\r
36 using namespace std;\r
37 #include <ntdef.h>\r
38 \r
39 #ifndef INHERITED_ACE\r
40 #define INHERITED_ACE 0x10\r
41 #endif\r
42 \r
43 extern "C" {\r
44 \r
45 int plibc_conv_to_win_path(const char *pszUnix, char *pszWindows);\r
46 \r
47 /**\r
48  * Enumerate all network adapters\r
49  */\r
50 void EnumNICs(PMIB_IFTABLE *pIfTable, PMIB_IPADDRTABLE *pAddrTable)\r
51 {\r
52   DWORD dwSize, dwRet;\r
53 \r
54   *pIfTable = NULL;\r
55 \r
56   if (pAddrTable)\r
57     *pAddrTable = NULL;\r
58 \r
59   if (GNGetIfTable)\r
60   {\r
61     dwSize = dwRet = 0;\r
62 \r
63     *pIfTable = (MIB_IFTABLE *) GlobalAlloc(GPTR, sizeof(MIB_IFTABLE));\r
64 \r
65     /* Get size of table */\r
66     if (GNGetIfTable(*pIfTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER)\r
67     {\r
68       GlobalFree(*pIfTable);\r
69       *pIfTable = (MIB_IFTABLE *) GlobalAlloc(GPTR, dwSize);\r
70     }\r
71 \r
72     if ((dwRet = GNGetIfTable(*pIfTable, &dwSize, 0)) == NO_ERROR &&\r
73       pAddrTable)\r
74     {\r
75       DWORD dwIfIdx, dwSize = sizeof(MIB_IPADDRTABLE);\r
76       *pAddrTable = (MIB_IPADDRTABLE *) GlobalAlloc(GPTR, dwSize);\r
77 \r
78       /* Make an initial call to GetIpAddrTable to get the\r
79          necessary size */\r
80       if (GNGetIpAddrTable(*pAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER)\r
81       {\r
82         GlobalFree(*pAddrTable);\r
83         *pAddrTable = (MIB_IPADDRTABLE *) GlobalAlloc(GPTR, dwSize);\r
84       }\r
85       GNGetIpAddrTable(*pAddrTable, &dwSize, 0);\r
86     }\r
87   }\r
88 }\r
89 \r
90 /**\r
91  * Lists all network interfaces in a combo box\r
92  * Used by the basic GTK configurator\r
93  *\r
94  * @param callback function to call for each NIC\r
95  * @param callback_cls closure for callback\r
96  */\r
97   int ListNICs(void (*callback) (void *, const char *, int), void * callback_cls)\r
98 {\r
99   PMIB_IFTABLE pTable;\r
100   PMIB_IPADDRTABLE pAddrTable;\r
101   DWORD dwIfIdx, dwExternalNIC;\r
102   IPAddr theIP;\r
103 \r
104   /* Determine our external NIC  */\r
105   theIP = inet_addr("192.0.34.166"); /* www.example.com */\r
106   if ((! GNGetBestInterface) ||\r
107       (GNGetBestInterface(theIP, &dwExternalNIC) != NO_ERROR))\r
108   {\r
109     dwExternalNIC = 0;\r
110   }\r
111 \r
112   /* Enumerate NICs */\r
113   EnumNICs(&pTable, &pAddrTable);\r
114 \r
115   if (pTable)\r
116   {\r
117     for(dwIfIdx=0; dwIfIdx <= pTable->dwNumEntries; dwIfIdx++)\r
118     {\r
119       char szEntry[1001];\r
120       DWORD dwIP = 0;\r
121       int iItm;\r
122                 PIP_ADAPTER_INFO pAdapterInfo;\r
123                 PIP_ADAPTER_INFO pAdapter = NULL;\r
124                 DWORD dwRetVal = 0;\r
125 \r
126       /* Get IP-Address */\r
127       int i;\r
128       for(i = 0; i < pAddrTable->dwNumEntries; i++)\r
129       {\r
130         if (pAddrTable->table[i].dwIndex == pTable->table[dwIfIdx].dwIndex)\r
131         {\r
132           dwIP = pAddrTable->table[i].dwAddr;\r
133           break;\r
134         }\r
135       }\r
136 \r
137       if (dwIP)\r
138       {\r
139         BYTE bPhysAddr[MAXLEN_PHYSADDR];\r
140                   char *pszIfName = NULL;\r
141         char dst[INET_ADDRSTRLEN];\r
142 \r
143         /* Get friendly interface name */\r
144                         pAdapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof(IP_ADAPTER_INFO));\r
145                         ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);\r
146 \r
147                         /* Make an initial call to GetAdaptersInfo to get\r
148                            the necessary size into the ulOutBufLen variable */\r
149                         if (GGetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {\r
150                           free(pAdapterInfo);\r
151                           pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);\r
152                         }\r
153 \r
154                         if ((dwRetVal = GGetAdaptersInfo( pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {\r
155                           pAdapter = pAdapterInfo;\r
156                           while (pAdapter) {\r
157                                 if (pTable->table[dwIfIdx].dwIndex == pAdapter->Index)\r
158                                 {\r
159                                         char szKey[251];\r
160                                         long lLen = 250;\r
161 \r
162                                         sprintf(szKey, "SYSTEM\\CurrentControlSet\\Control\\Network\\"\r
163                                                 "{4D36E972-E325-11CE-BFC1-08002BE10318}\\%s\\Connection",\r
164                                                 pAdapter->AdapterName);\r
165                                         pszIfName = (char *) malloc(251);\r
166                                         if (QueryRegistry(HKEY_LOCAL_MACHINE, szKey, "Name", pszIfName,\r
167                                                 &lLen) != ERROR_SUCCESS)\r
168                                         {\r
169                                                 free(pszIfName);\r
170                                                 pszIfName = NULL;\r
171                                         }\r
172                                 }\r
173                             pAdapter = pAdapter->Next;\r
174                           }\r
175                         }\r
176                         free(pAdapterInfo);\r
177 \r
178                         /* Set entry */\r
179         memset(bPhysAddr, 0, MAXLEN_PHYSADDR);\r
180         memcpy(bPhysAddr,\r
181           pTable->table[dwIfIdx].bPhysAddr,\r
182           pTable->table[dwIfIdx].dwPhysAddrLen);\r
183 \r
184         snprintf(szEntry, 1000, "%s (%s - %I64u)",\r
185           pszIfName ? pszIfName : (char *) pTable->table[dwIfIdx].bDescr,\r
186           inet_ntop (AF_INET, &dwIP, dst, INET_ADDRSTRLEN),\r
187           *((unsigned long long *) bPhysAddr));\r
188         szEntry[1000] = 0;\r
189 \r
190         if (pszIfName)\r
191                 free(pszIfName);\r
192 \r
193         callback(callback_cls,\r
194                  szEntry, \r
195                  pAddrTable->table[dwIfIdx].dwIndex == dwExternalNIC);\r
196       }\r
197     }\r
198     GlobalFree(pAddrTable);\r
199     GlobalFree(pTable);\r
200   }\r
201 \r
202   return GNUNET_YES;\r
203 }\r
204 \r
205 /**\r
206  * @brief Installs the Windows service\r
207  * @param servicename name of the service as diplayed by the SCM\r
208  * @param application path to the application binary\r
209  * @param username the name of the service's user account\r
210  * @returns 0 on success\r
211  *          1 if the Windows version doesn't support services\r
212  *          2 if the SCM could not be opened\r
213  *          3 if the service could not be created\r
214  */\r
215 int InstallAsService(char *servicename, char *application, char *username)\r
216 {\r
217   SC_HANDLE hManager, hService;\r
218   char szEXE[_MAX_PATH + 17] = "\"";\r
219   char *user = NULL;\r
220 \r
221   if (! GNOpenSCManager)\r
222     return 1;\r
223 \r
224   plibc_conv_to_win_path(application, szEXE + 1);\r
225   strcat(szEXE, "\" --win-service");\r
226   hManager = GNOpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);\r
227   if (! hManager)\r
228     return 2;\r
229 \r
230   if (username)\r
231   {\r
232         user = (char *) malloc(strlen(username) + 3);\r
233         sprintf(user, ".\\%s", username);\r
234   }\r
235 \r
236   hService = GNCreateService(hManager, (LPCTSTR) servicename, (LPCTSTR) servicename, 0,\r
237     SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, (LPCTSTR) szEXE,\r
238     NULL, NULL, NULL, (LPCTSTR) user, (LPCTSTR) username);\r
239 \r
240   if (user)\r
241     free(user);\r
242 \r
243   if (! hService)\r
244     return 3;\r
245 \r
246   GNCloseServiceHandle(hService);\r
247 \r
248   return 0;\r
249 }\r
250 \r
251 /**\r
252  * @brief Uninstall Windows service\r
253  * @param servicename name of the service to delete\r
254  * @returns 0 on success\r
255  *          1 if the Windows version doesn't support services\r
256  *          2 if the SCM could not be openend\r
257  *          3 if the service cannot be accessed\r
258  *          4 if the service cannot be deleted\r
259  */\r
260 int UninstallService(char *servicename)\r
261 {\r
262   SC_HANDLE hManager, hService;\r
263 \r
264   if (! GNOpenSCManager)\r
265     return 1;\r
266 \r
267   hManager = GNOpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);\r
268   if (! hManager)\r
269     return 2;\r
270 \r
271   if (! (hService = GNOpenService(hManager, (LPCTSTR) servicename, DELETE)))\r
272     if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)\r
273       return 3;\r
274      else\r
275         goto closeSCM;\r
276 \r
277   if (! GNDeleteService(hService))\r
278     if (GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)\r
279       return 4;\r
280 \r
281 closeSCM:\r
282   GNCloseServiceHandle(hService);\r
283 \r
284   return 0;\r
285 }\r
286 \r
287 /**\r
288  * @author Scott Field, Microsoft\r
289  * @see http://support.microsoft.com/?scid=kb;en-us;132958\r
290  * @date 12-Jul-95\r
291  */\r
292 void _InitLsaString(PLSA_UNICODE_STRING LsaString, LPWSTR String)\r
293 {\r
294   DWORD StringLength;\r
295 \r
296   if(String == NULL)\r
297   {\r
298     LsaString->Buffer = NULL;\r
299     LsaString->Length = 0;\r
300     LsaString->MaximumLength = 0;\r
301     return;\r
302   }\r
303 \r
304   StringLength = wcslen(String);\r
305   LsaString->Buffer = String;\r
306   LsaString->Length = (USHORT) StringLength *sizeof(WCHAR);\r
307   LsaString->MaximumLength = (USHORT) (StringLength + 1) * sizeof(WCHAR);\r
308 }\r
309 \r
310 \r
311 /**\r
312  * @author Scott Field, Microsoft\r
313  * @see http://support.microsoft.com/?scid=kb;en-us;132958\r
314  * @date 12-Jul-95\r
315  */\r
316 NTSTATUS _OpenPolicy(LPWSTR ServerName, DWORD DesiredAccess, PLSA_HANDLE PolicyHandle)\r
317 {\r
318   LSA_OBJECT_ATTRIBUTES ObjectAttributes;\r
319   LSA_UNICODE_STRING ServerString;\r
320   PLSA_UNICODE_STRING Server = NULL;\r
321 \r
322   /* Always initialize the object attributes to all zeroes. */\r
323   ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));\r
324 \r
325   if(ServerName != NULL)\r
326   {\r
327     /* Make a LSA_UNICODE_STRING out of the LPWSTR passed in */\r
328     _InitLsaString(&ServerString, ServerName);\r
329     Server = &ServerString;\r
330   }\r
331 \r
332   /* Attempt to open the policy. */\r
333   return GNLsaOpenPolicy(Server,\r
334                        &ObjectAttributes, DesiredAccess, PolicyHandle);\r
335 }\r
336 \r
337 /**\r
338  * @brief Obtain a SID representing the supplied account on the supplied system\r
339  * @return TRUE on success, FALSE on failure\r
340  * @author Scott Field, Microsoft\r
341  * @date 12-Jul-95\r
342  * @remarks A buffer is allocated which contains the SID representing the\r
343  *          supplied account. This buffer should be freed when it is no longer\r
344  *          needed by calling\n\r
345  *            HeapFree(GetProcessHeap(), 0, buffer)\r
346  * @remarks Call GetLastError() to obtain extended error information.\r
347  * @see http://support.microsoft.com/?scid=kb;en-us;132958\r
348  */\r
349 BOOL _GetAccountSid(LPCTSTR SystemName, LPCTSTR AccountName, PSID * Sid)\r
350 {\r
351   LPTSTR ReferencedDomain = NULL;\r
352   DWORD cbSid = 128;                                                    /* initial allocation attempt */\r
353   DWORD cchReferencedDomain = 16;       /* initial allocation size */\r
354   SID_NAME_USE peUse;\r
355   BOOL bSuccess = FALSE;                                        /* assume this function will fail */\r
356 \r
357   /* initial memory allocations */\r
358   if ((*Sid = HeapAlloc (GetProcessHeap (), 0, cbSid)) == NULL)\r
359         return FALSE;\r
360 \r
361   if ((ReferencedDomain = (LPTSTR) HeapAlloc (GetProcessHeap (),\r
362                                     0,\r
363                                     cchReferencedDomain *\r
364                                     sizeof (TCHAR))) == NULL)\r
365         return FALSE;\r
366 \r
367     /* Obtain the SID of the specified account on the specified system. */\r
368         while (!GNLookupAccountName(SystemName, /* machine to lookup account on */\r
369                    AccountName,                                                                                         /* account to lookup */\r
370                    *Sid,                                                                                                                        /* SID of interest */\r
371                    &cbSid,                                                                                                              /* size of SID */\r
372                    ReferencedDomain,                                                                    /* domain account was found on */\r
373                    &cchReferencedDomain, &peUse))\r
374         {\r
375                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)\r
376                 {\r
377                         /* reallocate memory */\r
378                         if ((*Sid = HeapReAlloc (GetProcessHeap (), 0, *Sid, cbSid)) == NULL)\r
379                                 return FALSE;\r
380 \r
381                         if ((ReferencedDomain = (LPTSTR) HeapReAlloc (GetProcessHeap (),\r
382                                               0,\r
383                                               ReferencedDomain,\r
384                                               cchReferencedDomain\r
385                                               * sizeof (TCHAR))) == NULL)\r
386                                 return FALSE;\r
387       }\r
388       else\r
389         goto end;\r
390   }\r
391 \r
392   /* Indicate success. */\r
393   bSuccess = TRUE;\r
394 \r
395 end:\r
396   /* Cleanup and indicate failure, if appropriate. */\r
397   HeapFree (GetProcessHeap (), 0, ReferencedDomain);\r
398 \r
399   if (!bSuccess)\r
400   {\r
401     if (*Sid != NULL)\r
402     {\r
403                 HeapFree (GetProcessHeap (), 0, *Sid);\r
404                 *Sid = NULL;\r
405     }\r
406   }\r
407 \r
408   return bSuccess;\r
409 }\r
410 \r
411 /**\r
412  * @author Scott Field, Microsoft\r
413  * @see http://support.microsoft.com/?scid=kb;en-us;132958\r
414  * @date 12-Jul-95\r
415  */\r
416 NTSTATUS _SetPrivilegeOnAccount(LSA_HANDLE PolicyHandle,/* open policy handle */\r
417                                PSID AccountSid,                         /* SID to grant privilege to */\r
418                                LPWSTR PrivilegeName,    /* privilege to grant (Unicode) */\r
419                                BOOL bEnable                                             /* enable or disable */\r
420   )\r
421 {\r
422   LSA_UNICODE_STRING PrivilegeString;\r
423 \r
424   /* Create a LSA_UNICODE_STRING for the privilege name. */\r
425   _InitLsaString(&PrivilegeString, PrivilegeName);\r
426 \r
427   /* grant or revoke the privilege, accordingly */\r
428   if(bEnable)\r
429   {\r
430     NTSTATUS i;\r
431 \r
432     i = GNLsaAddAccountRights(PolicyHandle,                             /* open policy handle */\r
433                                AccountSid,                              /* target SID */\r
434                                &PrivilegeString,        /* privileges */\r
435                                1                                                                                        /* privilege count */\r
436       );\r
437   }\r
438   else\r
439   {\r
440     return GNLsaRemoveAccountRights(PolicyHandle,                       /* open policy handle */\r
441                                   AccountSid,                           /* target SID */\r
442                                   FALSE,                                                        /* do not disable all rights */\r
443                                   &PrivilegeString,             /* privileges */\r
444                                   1                                                                             /* privilege count */\r
445       );\r
446   }\r
447 }\r
448 \r
449 /**\r
450  * @brief Create a Windows service account\r
451  * @return 0 on success, > 0 otherwise\r
452  * @param pszName the name of the account\r
453  * @param pszDesc description of the account\r
454  */\r
455 int CreateServiceAccount(const char *pszName, const char *pszDesc)\r
456 {\r
457   USER_INFO_1 ui;\r
458   USER_INFO_1008 ui2;\r
459   NET_API_STATUS nStatus;\r
460   wchar_t wszName[MAX_NAME_LENGTH], wszDesc[MAX_NAME_LENGTH];\r
461   DWORD dwErr;\r
462   LSA_HANDLE hPolicy;\r
463   PSID pSID;\r
464 \r
465   if (! GNNetUserAdd)\r
466         return 1;\r
467   mbstowcs(wszName, pszName, strlen(pszName) + 1);\r
468   mbstowcs(wszDesc, pszDesc, strlen(pszDesc) + 1);\r
469 \r
470   memset(&ui, 0, sizeof(ui));\r
471   ui.usri1_name = wszName;\r
472   ui.usri1_password = wszName; /* account is locked anyway */\r
473   ui.usri1_priv = USER_PRIV_USER;\r
474   ui.usri1_comment = wszDesc;\r
475   ui.usri1_flags = UF_SCRIPT;\r
476 \r
477   nStatus = GNNetUserAdd(NULL, 1, (LPBYTE)&ui, NULL);\r
478 \r
479   if (nStatus != NERR_Success && nStatus != NERR_UserExists)\r
480         return 2;\r
481 \r
482   ui2.usri1008_flags = UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD;\r
483   GNNetUserSetInfo(NULL, wszName, 1008, (LPBYTE)&ui2, NULL);\r
484 \r
485   if (_OpenPolicy(NULL, POLICY_ALL_ACCESS, &hPolicy) !=\r
486                                                                                 STATUS_SUCCESS)\r
487         return 3;\r
488 \r
489   _GetAccountSid(NULL, (LPCTSTR) pszName, &pSID);\r
490 \r
491   if (_SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeServiceLogonRight", TRUE) != STATUS_SUCCESS)\r
492         return 4;\r
493 \r
494   _SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeDenyInteractiveLogonRight", TRUE);\r
495   _SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeDenyBatchLogonRight", TRUE);\r
496   _SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeDenyNetworkLogonRight", TRUE);\r
497 \r
498   GNLsaClose(hPolicy);\r
499 \r
500   return 0;\r
501 }\r
502 \r
503 /**\r
504  * @brief Grant permission to a file\r
505  * @param lpszFileName the name of the file or directory\r
506  * @param lpszAccountName the user account\r
507  * @param dwAccessMask the desired access (e.g. GENERIC_ALL)\r
508  * @return TRUE on success\r
509  * @remark based on http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q102102&\r
510  */\r
511 BOOL AddPathAccessRights(char *lpszFileName, char *lpszAccountName,\r
512       DWORD dwAccessMask)\r
513 {\r
514   /* SID variables. */\r
515   SID_NAME_USE   snuType;\r
516   TCHAR *        szDomain       = NULL;\r
517   DWORD          cbDomain       = 0;\r
518   LPVOID         pUserSID       = NULL;\r
519   DWORD          cbUserSID      = 0;\r
520 \r
521   /* File SD variables. */\r
522   PSECURITY_DESCRIPTOR pFileSD  = NULL;\r
523   DWORD          cbFileSD       = 0;\r
524 \r
525   /* New SD variables. */\r
526   SECURITY_DESCRIPTOR  newSD;\r
527 \r
528   /* ACL variables. */\r
529   PACL           pACL           = NULL;\r
530   BOOL           fDaclPresent;\r
531   BOOL           fDaclDefaulted;\r
532   ACL_SIZE_INFORMATION AclInfo;\r
533 \r
534   /* New ACL variables. */\r
535   PACL           pNewACL        = NULL;\r
536   DWORD          cbNewACL       = 0;\r
537 \r
538   /* Temporary ACE. */\r
539   LPVOID         pTempAce       = NULL;\r
540   UINT           CurrentAceIndex = 0;\r
541 \r
542   UINT           newAceIndex = 0;\r
543 \r
544   /* Assume function will fail. */\r
545   BOOL           fResult        = FALSE;\r
546   BOOL           fAPISuccess;\r
547 \r
548   SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;\r
549 \r
550   /**\r
551    * STEP 1: Get SID of the account name specified.\r
552    */\r
553   fAPISuccess = GNLookupAccountName(NULL, (LPCTSTR) lpszAccountName,\r
554         pUserSID, &cbUserSID, (LPTSTR) szDomain, &cbDomain, &snuType);\r
555 \r
556   /* API should have failed with insufficient buffer. */\r
557   if (fAPISuccess)\r
558      goto end;\r
559   else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {\r
560      goto end;\r
561   }\r
562 \r
563   pUserSID = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbUserSID);\r
564   if (!pUserSID) {\r
565      goto end;\r
566   }\r
567 \r
568   szDomain = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbDomain * sizeof(TCHAR));\r
569   if (!szDomain) {\r
570      goto end;\r
571   }\r
572 \r
573   fAPISuccess = GNLookupAccountName(NULL, (LPCTSTR) lpszAccountName,\r
574         pUserSID, &cbUserSID, (LPTSTR) szDomain, &cbDomain, &snuType);\r
575   if (!fAPISuccess) {\r
576      goto end;\r
577   }\r
578 \r
579   /**\r
580    *  STEP 2: Get security descriptor (SD) of the file specified.\r
581    */\r
582   fAPISuccess = GNGetFileSecurity((LPCTSTR) lpszFileName,\r
583         secInfo, pFileSD, 0, &cbFileSD);\r
584 \r
585   /* API should have failed with insufficient buffer. */\r
586   if (fAPISuccess)\r
587      goto end;\r
588   else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {\r
589      goto end;\r
590   }\r
591 \r
592   pFileSD = (PSECURITY_DESCRIPTOR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,\r
593         cbFileSD);\r
594   if (!pFileSD) {\r
595      goto end;\r
596   }\r
597 \r
598   fAPISuccess = GNGetFileSecurity((LPCTSTR) lpszFileName,\r
599         secInfo, pFileSD, cbFileSD, &cbFileSD);\r
600   if (!fAPISuccess) {\r
601      goto end;\r
602   }\r
603 \r
604   /**\r
605    * STEP 3: Initialize new SD.\r
606    */\r
607   if (!GNInitializeSecurityDescriptor(&newSD,\r
608         SECURITY_DESCRIPTOR_REVISION)) {\r
609      goto end;\r
610   }\r
611 \r
612   /**\r
613    * STEP 4: Get DACL from the old SD.\r
614    */\r
615   if (!GNGetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL,\r
616         &fDaclDefaulted)) {\r
617      goto end;\r
618   }\r
619 \r
620   /**\r
621    * STEP 5: Get size information for DACL.\r
622    */\r
623   AclInfo.AceCount = 0; // Assume NULL DACL.\r
624   AclInfo.AclBytesFree = 0;\r
625   AclInfo.AclBytesInUse = sizeof(ACL);\r
626 \r
627   if (pACL == NULL)\r
628      fDaclPresent = FALSE;\r
629 \r
630   /* If not NULL DACL, gather size information from DACL. */\r
631   if (fDaclPresent) {\r
632 \r
633      if (!GNGetAclInformation(pACL, &AclInfo,\r
634            sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) {\r
635         goto end;\r
636      }\r
637   }\r
638 \r
639   /**\r
640    * STEP 6: Compute size needed for the new ACL.\r
641    */\r
642   cbNewACL = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE)\r
643         + GetLengthSid(pUserSID) - sizeof(DWORD);\r
644 \r
645   /**\r
646    * STEP 7: Allocate memory for new ACL.\r
647    */\r
648   pNewACL = (PACL) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbNewACL);\r
649   if (!pNewACL) {\r
650      goto end;\r
651   }\r
652 \r
653   /**\r
654    * STEP 8: Initialize the new ACL.\r
655    */\r
656   if (!GNInitializeAcl(pNewACL, cbNewACL, ACL_REVISION2)) {\r
657      goto end;\r
658   }\r
659 \r
660   /**\r
661    * STEP 9 If DACL is present, copy all the ACEs from the old DACL\r
662    * to the new DACL.\r
663    *\r
664    * The following code assumes that the old DACL is\r
665    * already in Windows 2000 preferred order.  To conform\r
666    * to the new Windows 2000 preferred order, first we will\r
667    * copy all non-inherited ACEs from the old DACL to the\r
668    * new DACL, irrespective of the ACE type.\r
669    */\r
670 \r
671   newAceIndex = 0;\r
672 \r
673   if (fDaclPresent && AclInfo.AceCount) {\r
674 \r
675      for (CurrentAceIndex = 0;\r
676            CurrentAceIndex < AclInfo.AceCount;\r
677            CurrentAceIndex++) {\r
678 \r
679         /**\r
680          * TEP 10: Get an ACE.\r
681          */\r
682         if (!GNGetAce(pACL, CurrentAceIndex, &pTempAce)) {\r
683            goto end;\r
684         }\r
685 \r
686         /**\r
687          * STEP 11: Check if it is a non-inherited ACE.\r
688          * If it is an inherited ACE, break from the loop so\r
689          * that the new access allowed non-inherited ACE can\r
690          * be added in the correct position, immediately after\r
691          * all non-inherited ACEs.\r
692          */\r
693         if (((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags\r
694            & INHERITED_ACE)\r
695            break;\r
696 \r
697         /**\r
698          * STEP 12: Skip adding the ACE, if the SID matches\r
699          * with the account specified, as we are going to\r
700          * add an access allowed ACE with a different access\r
701          * mask.\r
702          */\r
703         if (GNEqualSid(pUserSID,\r
704            &(((ACCESS_ALLOWED_ACE *)pTempAce)->SidStart)))\r
705            continue;\r
706 \r
707         /**\r
708          * STEP 13: Add the ACE to the new ACL.\r
709          */\r
710         if (!GNAddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,\r
711               ((PACE_HEADER) pTempAce)->AceSize)) {\r
712            goto end;\r
713         }\r
714 \r
715         newAceIndex++;\r
716      }\r
717   }\r
718 \r
719   /**\r
720    * STEP 14: Add the access-allowed ACE to the new DACL.\r
721    * The new ACE added here will be in the correct position,\r
722    * immediately after all existing non-inherited ACEs.\r
723    */\r
724   if (!GNAddAccessAllowedAce(pNewACL, ACL_REVISION2, dwAccessMask,\r
725         pUserSID)) {\r
726      goto end;\r
727   }\r
728 \r
729   /**\r
730    * STEP 14.5: Make new ACE inheritable\r
731    */\r
732   if (!GetAce(pNewACL, newAceIndex, &pTempAce))\r
733     goto end;\r
734   ((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags |=\r
735     (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);\r
736 \r
737   /**\r
738    * STEP 15: To conform to the new Windows 2000 preferred order,\r
739    * we will now copy the rest of inherited ACEs from the\r
740    * old DACL to the new DACL.\r
741    */\r
742   if (fDaclPresent && AclInfo.AceCount) {\r
743 \r
744      for (;\r
745           CurrentAceIndex < AclInfo.AceCount;\r
746           CurrentAceIndex++) {\r
747 \r
748         /**\r
749          * STEP 16: Get an ACE.\r
750          */\r
751         if (!GNGetAce(pACL, CurrentAceIndex, &pTempAce)) {\r
752            goto end;\r
753         }\r
754 \r
755         /**\r
756          * STEP 17: Add the ACE to the new ACL.\r
757          */\r
758         if (!GNAddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,\r
759               ((PACE_HEADER) pTempAce)->AceSize)) {\r
760            goto end;\r
761         }\r
762      }\r
763   }\r
764 \r
765   /**\r
766    * STEP 18: Set permissions\r
767    */\r
768   if (GNSetNamedSecurityInfo((LPTSTR) lpszFileName, SE_FILE_OBJECT,\r
769     DACL_SECURITY_INFORMATION, NULL, NULL, pNewACL, NULL) != ERROR_SUCCESS) {\r
770         goto end;\r
771   }\r
772 \r
773   fResult = TRUE;\r
774 \r
775 end:\r
776 \r
777   /**\r
778    * STEP 19: Free allocated memory\r
779    */\r
780   if (pUserSID)\r
781      HeapFree(GetProcessHeap(), 0, pUserSID);\r
782 \r
783   if (szDomain)\r
784      HeapFree(GetProcessHeap(), 0, szDomain);\r
785 \r
786   if (pFileSD)\r
787      HeapFree(GetProcessHeap(), 0, pFileSD);\r
788 \r
789   if (pNewACL)\r
790      HeapFree(GetProcessHeap(), 0, pNewACL);\r
791 \r
792   return fResult;\r
793 }\r
794 \r
795 char *winErrorStr(const char *prefix, int dwErr)\r
796 {\r
797   char *err, *ret;\r
798   int mem;\r
799 \r
800   if (! FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,\r
801     NULL, (DWORD) dwErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &err,\r
802     0, NULL ))\r
803   {\r
804     err = (char *) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, 1);\r
805   }\r
806 \r
807   mem = strlen(err) + strlen(prefix) + 20;\r
808   ret = (char *) malloc(mem);\r
809 \r
810   snprintf(ret, mem, "%s: %s (#%u)", prefix, err, dwErr);\r
811 \r
812   LocalFree(err);\r
813 \r
814   return ret;\r
815 }\r
816 \r
817 } /* extern "C" */\r
818 \r
819 #endif\r