pastebin

Paste Search Dynamic
Recent pastes
AccessControl.c
  1. * AccessControl plugin for NSIS
  2.  * Copyright (C) 2003 Mathias Hasselmann <mathias@taschenorakel.de>
  3.  *
  4.  * This software is provided 'as-is', without any express or implied
  5.  * warranty. In no event will the authors be held liable for any damages
  6.  * arising from the use of this software.
  7.  *
  8.  * Permission is granted to anyone to use this software for any purpose,
  9.  * including commercial applications, and to alter it and redistribute it
  10.  * freely, subject to the following restrictions:
  11.  *
  12.  *   1. The origin of this software must not be misrepresented; you must not
  13.  *      claim that you wrote the original software. if you use this software
  14.  *      in a product, an acknowledgment in the product documentation would be
  15.  *      appreciated but is not required.
  16.  *
  17.  *   2. Altered source versions must be plainly marked as such, and must not
  18.  *      be misrepresented as being the original software.
  19.  *
  20.  *   3. This notice may not be removed or altered from any source
  21.  *      distribution.
  22.  */
  23.  
  24. #define WIN32_LEAN_AND_MEAN
  25.  
  26. #include <windows.h>
  27. #include <aclapi.h>
  28. #include <sddl.h>
  29. #include <shlwapi.h>
  30.  
  31. #include "../exdll/exdll.h"
  32.  
  33. /*****************************************************************************
  34.  GLOBAL VARIABLES
  35.  *****************************************************************************/
  36.  
  37. #pragma message("Warning: Where is g_output?")
  38. //extern FILE * g_output;
  39. static HINSTANCE g_hInstance = null;
  40. static HWND g_hwndParent = null;
  41. static HWND g_hwndDialog = null;
  42. static HWND g_hwndProgress = null;
  43. static unsigned g_tempsize = 0;
  44. static char * g_temp = null;
  45.  
  46. /*****************************************************************************
  47.  TYPE DEFINITIONS
  48.  *****************************************************************************/
  49.  
  50. typedef struct
  51. {
  52.         const char * name;
  53.         SE_OBJECT_TYPE type;
  54.         BYTE defaultInheritance;
  55.         const char ** const permissionNames;
  56.         const DWORD * const permissionFlags;
  57.         const int permissionCount;
  58. }
  59. SchemeType;
  60.  
  61. typedef enum
  62. {
  63.         ChangeMode_Owner,
  64.         ChangeMode_Group
  65. }
  66. ChangeMode;
  67.  
  68. /*****************************************************************************
  69.  PLUG-IN HANDLING
  70.  *****************************************************************************/
  71.  
  72. #define PUBLIC_FUNCTION(Name) \
  73. void __declspec(dllexport) Name(HWND hwndParent, int string_size, \
  74.                                                                 char * variables, stack_t ** stacktop) \
  75. { \
  76.         EXDLL_INIT(); \
  77.         AllocGlobalTemp(); \
  78.         g_hwndParent = hwndParent; \
  79.         g_hwndDialog = FindWindowEx(g_hwndParent, null, WC_DIALOG, ""); \
  80.         g_hwndProgress = GetDlgItem(g_hwndDialog, 1006);
  81.  
  82. #define PUBLIC_FUNCTION_END \
  83. }
  84.  
  85. #define ABORT(Reason) \
  86.         do { ShowError Reason; goto cleanup; } while(0)
  87.  
  88. /** Resizes the global character buffer g_temp if needed. Information about
  89.  ** the need to do this is taken from g_stringsize. This function is useful
  90.  ** to ensure each argument passed to this plugin's API can be copied to
  91.  ** g_temp.
  92.  **/
  93. static void AllocGlobalTemp()
  94. {
  95.         if (g_stringsize >= g_tempsize)
  96.         {
  97.                 char * temp = (char *) LocalAlloc(LPTR, g_stringsize + 1);
  98.  
  99.                 if (null != temp)
  100.                 {
  101.                         LocalFree((HLOCAL) g_temp);
  102.                         g_tempsize = g_stringsize + 1;
  103.                         g_temp = temp;
  104.                 }
  105.         }
  106. }
  107.  
  108. BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
  109. {
  110.         g_hInstance = hInst;
  111.         return true;
  112. }
  113.  
  114. /*****************************************************************************
  115.  ARRAY UTILITIES
  116.  *****************************************************************************/
  117.  
  118. #define SIZE_OF_ARRAY(Array) \
  119.         (sizeof((Array)) / sizeof(*(Array)))
  120.  
  121. #define ARRAY_CONTAINS(Array, Index) \
  122.         ArrayContainsImpl(Index, SIZE_OF_ARRAY(Array))
  123.  
  124. /** Checks if an array of a given size contains a given index.
  125.  ** Used by the ARRAY_CONTAINS macro.
  126.  **/
  127. static int ArrayContainsImpl(int index, int size)
  128. {
  129.         return index >= 0 && index < size;
  130. }
  131.  
  132. /*****************************************************************************
  133.  FEEDBACK UTILITIES
  134.  *****************************************************************************/
  135.  
  136. static void ShowProgressV(const char * fmt, va_list args)
  137. {
  138.         char progress[1024];
  139.         wvnsprintf(progress, sizeof(progress), fmt, args);
  140.         SetWindowText(g_hwndProgress, progress);
  141. //      fprintf(g_output, "%s\r\n", progress);
  142. }
  143.  
  144. static void ShowProgress(const char * fmt, ...)
  145. {
  146.         va_list args;
  147.         va_start(args, fmt);
  148.         ShowProgressV(fmt, args);
  149.         va_end(args);
  150. }
  151.  
  152. static void ShowErrorV(const char * fmt, va_list args)
  153. {
  154.         char message[1024];
  155.         wvnsprintf(message, sizeof(message), fmt, args);
  156.         MessageBox(g_hwndParent, message, null, MB_OK | MB_ICONERROR | MB_TOPMOST);
  157.         pushstring(message);
  158. }
  159.  
  160. static void ShowError(const char * fmt, ...)
  161. {
  162.         va_list args;
  163.         va_start(args, fmt);
  164.         ShowErrorV(fmt, args);
  165.         va_end(args);
  166. }
  167.  
  168. /*****************************************************************************
  169.  STRING UTILITIES
  170.  *****************************************************************************/
  171.  
  172. /** Converts a string into an enumeration index. If the enumeration
  173.  ** contains the string the index of the string within the enumeration
  174.  ** is return. On error you'll receive -1.
  175.  **/
  176. static int ParseEnum(const char * keywords[], const char * str)
  177. {
  178.         const char ** key;
  179.  
  180.         for(key = keywords; *key; ++key)
  181.                 if (!lstrcmpi(str, *key))
  182.                         return (int)(key - keywords);
  183.  
  184.         return -1;
  185. }
  186.  
  187. /** Parses a trustee string. If encloded in curlies the string contains
  188.  ** a string SID. Otherwise it's assumed that the string contains a
  189.  ** trustee name. The caller has to make sure to release the string
  190.  ** returned with LocalFree.
  191.  **/
  192. static char * ParseTrustee(char * trustee, DWORD * trusteeForm)
  193. {
  194.         char * strend = trustee + strlen(trustee) - 1;
  195.  
  196.         if ('(' == *trustee && ')' == *strend)
  197.         {
  198.                 PSID pSid = null;
  199.                
  200.                 *strend = '\0';
  201.                 ++trustee;
  202.  
  203.                 if (!ConvertStringSidToSid(trustee, &pSid))
  204.                         pSid = null;
  205.  
  206.                 *trusteeForm = TRUSTEE_IS_SID;
  207.                 return (char *) pSid;
  208.         }
  209.  
  210.         *trusteeForm = TRUSTEE_IS_NAME;
  211.         return StrDup(trustee);
  212. }
  213.  
  214. static char * ParseSid(char * trustee)
  215. {
  216.         PSID pSid = null;
  217.         char * strend = trustee + strlen(trustee) - 1;
  218.  
  219.         if ('(' == *trustee && ')' == *strend)
  220.         {
  221.                 *strend = '\0';
  222.                 ++trustee;
  223.  
  224.                 if (!ConvertStringSidToSid(trustee, &pSid))
  225.                         pSid = null;
  226.         }
  227.         else
  228.         {
  229.                 DWORD sidLen = 0;
  230.                 DWORD domLen = 0;
  231.                 char * domain = null;
  232.                 SID_NAME_USE use;
  233.  
  234.                 if ((LookupAccountName(null, trustee,
  235.                          null, &sidLen, null, &domLen, &use) ||
  236.                          ERROR_INSUFFICIENT_BUFFER == GetLastError()) &&
  237.                     null != (domain = LocalAlloc(LPTR, domLen)) &&
  238.                         null != (pSid = LocalAlloc(LPTR, sidLen)))
  239.                 {
  240.                         if (!LookupAccountName(null, trustee,
  241.                                 pSid, &sidLen, domain, &domLen, &use))
  242.                         {
  243.                                 ShowError("uh..");
  244.                                 LocalFree(pSid);
  245.                                 pSid = null;
  246.                         }
  247.                 }
  248.  
  249.                 LocalFree(domain);
  250.         }
  251.  
  252.         return pSid;
  253. }
  254.  
  255. /* i know: this function is far to generious in accepting strings.
  256.  * but hey: this all is about code size, isn't it?
  257.  * so i can live with that pretty well.
  258.  */
  259. static DWORD ParsePermissions(const SchemeType * scheme, char * str)
  260. {
  261.         DWORD perms = 0;
  262.         char * first, * last;
  263.  
  264.         for(first = str; *first; first = last)
  265.         {
  266.                 int token;
  267.  
  268.                 while(*first && *first <= ' ') ++first;
  269.                 for(last = first; *last && *last > ' ' && *last != '|' &&
  270.                         *last != '+'; ++last);
  271.                 if (*last) *last++ = '\0';
  272.  
  273.                 token = ParseEnum(scheme->permissionNames, first);
  274.                 if (token >= 0 && token < scheme->permissionCount)
  275.                         perms|= scheme->permissionFlags[token];
  276.         }
  277.  
  278.         return perms;
  279. }
  280.  
  281. /*****************************************************************************
  282.  SYMBOL TABLES
  283.  *****************************************************************************/
  284.  
  285. static const char * g_filePermissionNames[] =
  286. {
  287.         "ReadData", "WriteData", "AppendData",
  288.         "ReadEA", "WriteEA", "Execute", "ReadAttributes", "WriteAttributes",
  289.         "Delete", "ReadControl", "WriteDAC", "WriteOwner", "Synchronize",
  290.         "FullAccess", "GenericRead", "GenericWrite", "GenericExecute", null
  291. };
  292.  
  293. static const DWORD g_filePermissionFlags[] =
  294. {
  295.         FILE_READ_DATA, FILE_WRITE_DATA, FILE_APPEND_DATA,
  296.         FILE_READ_EA, FILE_WRITE_EA, FILE_EXECUTE, FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES,
  297.         DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, SYNCHRONIZE,
  298.         FILE_ALL_ACCESS, FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE
  299. };
  300.  
  301. static const char * g_directoryPermissionNames[] =
  302. {
  303.         "ListDirectory", "AddFile", "AddSubdirectory",
  304.         "ReadEA", "WriteEA", "Traverse", "DeleteChild",
  305.         "ReadAttributes", "WriteAttributes",
  306.         "Delete", "ReadControl", "WriteDAC", "WriteOwner", "Synchronize",
  307.         "FullAccess", "GenericRead", "GenericWrite", "GenericExecute", null
  308. };
  309.  
  310. static const DWORD g_directoryPermissionFlags[] =
  311. {
  312.         FILE_LIST_DIRECTORY, FILE_ADD_FILE, FILE_ADD_SUBDIRECTORY,
  313.         FILE_READ_EA, FILE_WRITE_EA, FILE_TRAVERSE, FILE_DELETE_CHILD,
  314.         FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES,
  315.         DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, SYNCHRONIZE,
  316.         FILE_ALL_ACCESS, FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE
  317. };
  318.  
  319. static const char * g_registryPermissionNames[] =
  320. {
  321.         "QueryValue", "SetValue", "CreateSubKey",
  322.         "EnumerateSubKeys", "Notify", "CreateLink",
  323.         "Delete", "ReadControl", "WriteDAC", "WriteOwner", "Synchronize",
  324.         "GenericRead", "GenericWrite", "GenericExecute", "FullAccess", null
  325. };
  326.  
  327. static const DWORD g_registryPermissionFlags[] =
  328. {
  329.         KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY,
  330.         KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, KEY_CREATE_LINK,
  331.         DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, SYNCHRONIZE,
  332.         KEY_READ, KEY_WRITE, KEY_EXECUTE, KEY_ALL_ACCESS
  333. };
  334.  
  335. static const SchemeType g_fileScheme[] =
  336. {
  337.         "file", SE_FILE_OBJECT,
  338.         OBJECT_INHERIT_ACE,
  339.         g_filePermissionNames,
  340.         g_filePermissionFlags,
  341.         SIZE_OF_ARRAY(g_filePermissionFlags)
  342. };
  343.  
  344. static const SchemeType g_directoryScheme[] =
  345. {
  346.         "directory", SE_FILE_OBJECT,
  347.         OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE,
  348.         g_directoryPermissionNames,
  349.         g_directoryPermissionFlags,
  350.         SIZE_OF_ARRAY(g_directoryPermissionFlags)
  351. };
  352.  
  353. static const SchemeType g_registryScheme[] =
  354. {
  355.         "registry", SE_REGISTRY_KEY,
  356.         CONTAINER_INHERIT_ACE,
  357.         g_registryPermissionNames,
  358.         g_registryPermissionFlags,
  359.         SIZE_OF_ARRAY(g_registryPermissionFlags)
  360. };
  361.  
  362. static const char * g_rootKeyNames[] =
  363. {
  364.         "HKCR", "HKCU", "HKLM", "HKU", null
  365. };
  366.  
  367. static const char * g_rootKeyPrefixes[] =
  368. {
  369.         "CLASSES_ROOT\\", "CURRENT_USER\\", "MACHINE\\", "USERS\\"
  370. };
  371.  
  372. /*****************************************************************************
  373.  GENERIC ACL HANDLING
  374.  *****************************************************************************/
  375.  
  376. static void ChangeDACL(const SchemeType * scheme, char * path, DWORD mode)
  377. {
  378.         char * trusteeName = null;
  379.         DWORD trusteeForm = TRUSTEE_IS_NAME;
  380.         DWORD permissions = 0;
  381.  
  382.         PACL pOldAcl = null;
  383.         PACL pNewAcl = null;
  384.         EXPLICIT_ACCESS access;
  385.  
  386.         if (popstring(g_temp))
  387.                 ABORT(("Trustee is missing"));
  388.  
  389.         if (null == (trusteeName = ParseTrustee(g_temp, &trusteeForm)))
  390.                 ABORT(("Bad trustee (%s)", g_temp));
  391.  
  392.         if (popstring(g_temp))
  393.                 ABORT(("Permission flags are missing"));
  394.  
  395.         if (0 == (permissions = ParsePermissions(scheme, g_temp)))
  396.                 ABORT(("Bad permission flags (%s)", g_temp));
  397.  
  398.         ShowProgress("Adjusting %s permissions for %s", scheme->name, path);
  399.  
  400.         if (ERROR_SUCCESS != GetNamedSecurityInfo(path, scheme->type,
  401.                 DACL_SECURITY_INFORMATION, null, null, &pOldAcl, null, null))
  402.                 ABORT(("Cannot read access control list.\r\n"
  403.                         "Error code: %d", GetLastError()));
  404.  
  405.         BuildExplicitAccessWithName(&access, "", permissions, mode,
  406.                 scheme->defaultInheritance);
  407.  
  408.         access.Trustee.TrusteeForm = trusteeForm;
  409.         access.Trustee.ptstrName = trusteeName;
  410.  
  411.         if (ERROR_SUCCESS != SetEntriesInAcl(1, &access, pOldAcl, &pNewAcl))
  412.                 ABORT(("Cannot build new access control list.\r\n"
  413.                         "Error code: %d", GetLastError()));
  414.  
  415.         if (ERROR_SUCCESS != SetNamedSecurityInfo(path, scheme->type,
  416.                 DACL_SECURITY_INFORMATION, null, null, pNewAcl, null))
  417.                 ABORT(("Cannot apply new access control list.\r\n"
  418.                         "Error code: %d", GetLastError()));
  419.  
  420. cleanup:
  421.         LocalFree(pNewAcl);
  422.         LocalFree(pOldAcl);
  423.  
  424.         LocalFree(trusteeName);
  425. }
  426.  
  427. static void ChangeOwner(const SchemeType * scheme, char * path, ChangeMode mode)
  428. {
  429.         SECURITY_INFORMATION what;
  430.         PSID pSidOwner = null;
  431.         PSID pSidGroup = null;
  432.         PSID pSid = null;
  433.  
  434.         if (popstring(g_temp))
  435.                 ABORT(("Trustee is missing"));
  436.  
  437.         if (null == (pSid = ParseSid(g_temp)))
  438.                 ABORT(("Bad trustee (%s)", g_temp));
  439.  
  440.         switch(mode)
  441.         {
  442.         case ChangeMode_Owner:
  443.                 what = OWNER_SECURITY_INFORMATION;
  444.                 pSidOwner = pSid;
  445.                 break;
  446.  
  447.         case ChangeMode_Group:
  448.                 what = GROUP_SECURITY_INFORMATION;
  449.                 pSidGroup = pSid;
  450.                 break;
  451.  
  452.         default:
  453.                 ABORT(("Bug: Unsupported change mode: %d", mode));
  454.         }
  455.  
  456.         if (ERROR_SUCCESS != SetNamedSecurityInfo(path, scheme->type,
  457.                 what, pSidOwner, pSidGroup, null, null))
  458.                 ABORT(("Cannot apply new ownership.\r\n"
  459.                         "Error code: %d", GetLastError()));
  460.  
  461. cleanup:
  462.         ;
  463. }
  464.  
  465. /*****************************************************************************
  466.  FILESYSTEM BASED ACL HANDLING
  467.  *****************************************************************************/
  468.  
  469. static const SchemeType * PopFileArgs(char * pathbuf)
  470. {
  471.         if (!popstring(pathbuf))
  472.         {
  473.                 DWORD attr = GetFileAttributes(pathbuf);
  474.  
  475.                 if (INVALID_FILE_ATTRIBUTES != attr)
  476.                         return FILE_ATTRIBUTE_DIRECTORY & attr
  477.                                 ? g_directoryScheme
  478.                                 : g_fileScheme;
  479.                 else
  480.                         ABORT(("Invalid filesystem path missing"));
  481.         }
  482.         else
  483.                 ABORT(("Filesystem path missing"));
  484.  
  485. cleanup:
  486.         return null;
  487. }
  488.  
  489. static void ChangeFileDACL(DWORD mode)
  490. {
  491.         char path[MAX_PATH];
  492.         const SchemeType * scheme;
  493.  
  494.         if (null != (scheme = PopFileArgs(path)))
  495.                 ChangeDACL(scheme, path, mode);
  496. }
  497.  
  498. static void ChangeFileOwner(ChangeMode mode)
  499. {
  500.         char path[MAX_PATH];
  501.         const SchemeType * scheme;
  502.  
  503.         if (null != (scheme = PopFileArgs(path)))
  504.                 ChangeOwner(scheme, path, mode);
  505. }
  506.  
  507. /*****************************************************************************
  508.  REGISTRY BASED ACL HANDLING
  509.  *****************************************************************************/
  510.  
  511. static char * PopRegKeyArgs()
  512. {
  513.         size_t prefixLen, regkeyLen;
  514.         int iRootKey;
  515.  
  516.         const char * prefix;
  517.         char * path = null;
  518.  
  519.         if (popstring(g_temp))
  520.                 ABORT(("Root key name missing"));
  521.  
  522.         iRootKey = ParseEnum(g_rootKeyNames, g_temp);
  523.         if (!ARRAY_CONTAINS(g_rootKeyPrefixes, iRootKey))
  524.                 ABORT(("Bad root key name (%s)", g_temp));
  525.  
  526.         if (popstring(g_temp))
  527.                 ABORT(("Registry key name missing"));
  528.  
  529.         prefix = g_rootKeyPrefixes[iRootKey];
  530.         prefixLen = strlen(prefix);
  531.         regkeyLen = strlen(g_temp);
  532.  
  533.         path = LocalAlloc(LPTR, prefixLen + regkeyLen + 1);
  534.         memcpy(path, prefix, prefixLen);
  535.         memcpy(path + prefixLen, g_temp, regkeyLen);
  536.         path[prefixLen + regkeyLen] = '\0';
  537.  
  538.         return path;
  539.  
  540. cleanup:
  541.         return null;
  542. }
  543.  
  544. static void ChangeRegKeyDACL(DWORD mode)
  545. {
  546.         char * path = PopRegKeyArgs();
  547.  
  548.         if (null != path)
  549.         {
  550.                 ChangeDACL(g_registryScheme, path, mode);
  551.                 LocalFree(path);
  552.         }
  553. }
  554.  
  555. static void ChangeRegKeyOwner(ChangeMode mode)
  556. {
  557.         char * path = PopRegKeyArgs();
  558.  
  559.         if (null != path)
  560.         {
  561.                 ChangeOwner(g_registryScheme, path, mode);
  562.                 LocalFree(path);
  563.         }
  564. }
  565.  
  566. /*****************************************************************************
  567.  PUBLIC FILE RELATED FUNCTIONS
  568.  *****************************************************************************/
  569.  
  570. PUBLIC_FUNCTION(GrantOnFile)
  571.         ChangeFileDACL(GRANT_ACCESS);
  572. PUBLIC_FUNCTION_END
  573.  
  574. PUBLIC_FUNCTION(SetOnFile)
  575.         ChangeFileDACL(SET_ACCESS);
  576. PUBLIC_FUNCTION_END
  577.  
  578. PUBLIC_FUNCTION(DenyOnFile)
  579.         ChangeFileDACL(DENY_ACCESS);
  580. PUBLIC_FUNCTION_END
  581.  
  582. PUBLIC_FUNCTION(RevokeOnFile)
  583.         ChangeFileDACL(REVOKE_ACCESS);
  584. PUBLIC_FUNCTION_END
  585.  
  586. PUBLIC_FUNCTION(SetFileOwner)
  587.         ChangeFileOwner(ChangeMode_Owner);
  588. PUBLIC_FUNCTION_END
  589.  
  590. PUBLIC_FUNCTION(SetFileGroup)
  591.         ChangeFileOwner(ChangeMode_Group);
  592. PUBLIC_FUNCTION_END
  593.  
  594. /*****************************************************************************
  595.  PUBLIC REGISTRY RELATED FUNCTIONS
  596.  *****************************************************************************/
  597.  
  598. PUBLIC_FUNCTION(GrantOnRegKey)
  599.         ChangeRegKeyDACL(GRANT_ACCESS);
  600. PUBLIC_FUNCTION_END
  601.  
  602. PUBLIC_FUNCTION(SetOnRegKey)
  603.         ChangeRegKeyDACL(SET_ACCESS);
  604. PUBLIC_FUNCTION_END
  605.  
  606. PUBLIC_FUNCTION(DenyOnRegKey)
  607.         ChangeRegKeyDACL(DENY_ACCESS);
  608. PUBLIC_FUNCTION_END
  609.  
  610. PUBLIC_FUNCTION(RevokeOnRegKey)
  611.         ChangeRegKeyDACL(REVOKE_ACCESS);
  612. PUBLIC_FUNCTION_END
  613.  
  614. PUBLIC_FUNCTION(SetRegKeyOwner)
  615.         ChangeRegKeyOwner(ChangeMode_Owner);
  616. PUBLIC_FUNCTION_END
  617.  
  618. PUBLIC_FUNCTION(SetRegKeyGroup)
  619.         ChangeRegKeyOwner(ChangeMode_Group);
  620. PUBLIC_FUNCTION_END
Parsed in 0.141 seconds