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