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