@@ -59,6 +59,8 @@ var /global StdOutHandleSet
5959!include " x64.nsh"
6060
6161!include " FileFunc.nsh"
62+ !include " StrFunc.nsh"
63+ $ {Using:StrFunc} StrStr
6264!insertmacro GetParameters
6365!insertmacro GetOptions
6466
@@ -133,18 +135,22 @@ var /global InstMode # 0 = Just Me, 1 = All Users.
133135!define ALL_USERS 1
134136
135137var /global CMD_EXE
138+ var /global ICACLS_EXE
136139
137- !macro FindCmdExe
140+ !macro FindWindowsBinaries
138141 # Find cmd.exe
139142 ReadEnvStr $R0 SystemRoot
140143 ReadEnvStr $R1 windir
141144 $ {If} $ {FileExists} " $R0"
142145 StrCpy $CMD_EXE " $R0\S ystem32\c md.exe"
146+ StrCpy $ICACLS_EXE " $R0\S ystem32\i cacls.exe"
143147 $ {ElseIf} $ {FileExists} " $R1"
144148 StrCpy $CMD_EXE " $R1\S ystem32\c md.exe"
149+ StrCpy $ICACLS_EXE " $R1\S ystem32\i cacls.exe"
145150 $ {Else}
146- # Cross our fingers CMD is in PATH
151+ # Cross our fingers binaries are in PATH
147152 StrCpy $CMD_EXE " cmd.exe"
153+ StrCpy $ICACLS_EXE " icacls.exe"
148154 $ {EndIf}
149155!macroend
150156
@@ -1235,18 +1241,88 @@ FunctionEnd
12351241!insertmacro AbortRetryNSExecWaitMacro " "
12361242!insertmacro AbortRetryNSExecWaitMacro " un."
12371243
1244+ !macro setInstdirPermissions
1245+ # To address CVE-2022-26526.
1246+ # Revoke the write permission on directory " $INSTDIR" for Users. Users are:
1247+ # AU - authenticated users (NT AUTHORITY\Authenticated Users)
1248+ # BU - built-in (local) users (BUILTIN\Users)
1249+ # DU - domain users (<domain name>\DOMAIN USERS)
1250+ # This also applies for single-user installations to avoid giving other users
1251+ # full access on shared drives.
1252+ $ {If} $ {UAC_IsAdmin}
1253+ StrCpy $0 " (AU) (BU) (DU)"
1254+ $ {Else}
1255+ # Not every directory grants write access to Users (e.g. , %USERPROFILE%),
1256+ # so test whether user groups have the necessary rights.
1257+ nsExec::ExecToStack '" $ICACLS_EXE" " $INSTDIR" '
1258+ Pop $R0
1259+ Pop $R1
1260+ $ {If} $R0 != " 0"
1261+ StrCpy $R1 \
1262+ " Unable to determine the defaults permissions of the installation directory. " \
1263+ " Ensure that you have read access to $INSTDIR and icacls.exe is in your PATH."
1264+ $ {Print} $R1
1265+ Abort
1266+ $ {EndIf}
1267+ StrCpy $0 " "
1268+ StrCpy $R2 " NT AUTHORITY\A uthenticated Users|BUILTIN\Users|\D omain Users"
1269+ StrCpy $R3 " (AU)|(BU)|(DU)"
1270+ StrCpy $R4 1
1271+ loop_single_user_default_access:
1272+ $ {WordFind} $R2 " |" " E+$R4" $R5
1273+ $ {WordFind} $R3 " |" " E+$R4" $R6
1274+ IfErrors endloop_single_user_default_access
1275+ $ {StrStr} $R7 $R1 $R5
1276+ $ {If} $R7 == " "
1277+ goto increment_loop_single_user_default_access
1278+ $ {EndIf}
1279+ # If the user group has a deny permission directive, do not change permissions.
1280+ # Granting (RX) permissions may increase the permissions that are inherited and
1281+ # it is very unlikely that a user is granted write permissions but denied others.
1282+ $ {StrStr} $R7 $R1 " $R5:(D)"
1283+ $ {If} $R7 != " "
1284+ goto increment_loop_single_user_default_access
1285+ $ {EndIf}
1286+ StrCpy $0 " $0 $R6"
1287+ increment_loop_single_user_default_access:
1288+ IntOp $R4 $R4 + 1
1289+ goto loop_single_user_default_access
1290+ endloop_single_user_default_access:
1291+ $ {EndIf}
1292+ AccessControl::DisableFileInheritance " $INSTDIR"
1293+ $ {If} $0 != " "
1294+ StrCpy $1 1
1295+ loop_access:
1296+ $ {WordFind} $0 " " " E+$1" $2
1297+ IfErrors endloop_access
1298+ AccessControl::RevokeOnFile " $INSTDIR" " $2" " GenericWrite"
1299+ AccessControl::SetOnFile " $INSTDIR" " $2" " GenericRead + GenericExecute"
1300+ IntOp $1 $1 + 1
1301+ goto loop_access
1302+ endloop_access:
1303+ $ {EndIf}
1304+ $ {IfNot} $ {UAC_IsAdmin}
1305+ # Ensure that creator has full access
1306+ ReadEnvStr $R0 USERDOMAIN
1307+ ReadEnvStr $R1 USERNAME
1308+ $ {If} $R0 == " "
1309+ AccessControl::SetOnFile " $INSTDIR" " $R1" " FullAccess"
1310+ $ {Else}
1311+ AccessControl::SetOnFile " $INSTDIR" " $R0\$ R1" " FullAccess"
1312+ $ {EndIf}
1313+ $ {EndIf}
1314+ !macroend
1315+
12381316# Installer sections
12391317Section " Install"
12401318 $ {LogSet} on
12411319 $ {If} $ {Silent}
12421320 call OnDirectoryLeave
12431321 $ {EndIf}
12441322
1245- !insertmacro FindCmdExe
1323+ !insertmacro FindWindowsBinaries
12461324
1247- SetOutPath " $INSTDIR\L ib"
1248- File " {{ NSIS_DIR }}\_ nsis.py"
1249- File " {{ NSIS_DIR }}\_ system_path.py"
1325+ SetOutPath " $INSTDIR"
12501326
12511327 # Resolve INSTDIR so that paths and registry keys do not contain '.. ' or similar strings.
12521328 # $0 is empty if the directory doesn't exist, but the File commands should have created it already.
@@ -1257,6 +1333,15 @@ Section "Install"
12571333 $ {EndIf}
12581334 StrCpy $INSTDIR $0
12591335
1336+ # Restrict permissions immediately after creating $INSTDIR
1337+ # If not , the installation directory may inherit write-permissions
1338+ # for users even during an all-users installation.
1339+ !insertmacro setInstdirPermissions
1340+
1341+ SetOutPath " $INSTDIR\L ib"
1342+ File " {{ NSIS_DIR }}\_ nsis.py"
1343+ File " {{ NSIS_DIR }}\_ system_path.py"
1344+
12601345{%- if has_license %}
12611346 SetOutPath " $INSTDIR"
12621347 File {{ licensefile }}
@@ -1498,37 +1583,14 @@ Section "Install"
14981583
14991584 WriteUninstaller " $INSTDIR\Uninstall-${NAME}.exe"
15001585
1501- # To address CVE-2022-26526.
1502- # Revoke the write permission on directory " $INSTDIR" for Users if this is
1503- # being run with administrative privileges. Users are:
1504- # AU - authenticated users
1505- # BU - built-in (local) users
1506- # DU - domain users
1507- $ {If} $ {UAC_IsAdmin}
1508- $ {Print} " Setting installation directory permissions..."
1509- AccessControl::DisableFileInheritance " $INSTDIR"
1510- # Enable inheritance on all files inside $INSTDIR .
1511- # Use icacls because it is much faster than custom NSIS solutions.
1512- # We continue on error because icacls fails on broken links.
1513- ReadEnvStr $0 SystemRoot
1514- ReadEnvStr $1 windir
1515- $ {If} $ {FileExists} " $0"
1516- push '" $0\S ystem32\i cacls.exe" " $INSTDIR\* " /inheritance:e /T /C /Q'
1517- $ {ElseIf} $ {FileExists} " $1"
1518- push '" $1\S ystem32\i cacls.exe" " $INSTDIR\* " /inheritance:e /T /C /Q'
1519- $ {Else}
1520- # Cross our fingers icacls is in PATH
1521- push 'icacls.exe " $INSTDIR\* " /inheritance:e /T /C /Q'
1522- $ {EndIf}
1523- push 'Failed to enable inheritance for all files in the installation directory. '
1524- push 'NoLog'
1525- call AbortRetryNSExecWait
1526- AccessControl::RevokeOnFile " $INSTDIR" " (AU)" " GenericWrite"
1527- AccessControl::RevokeOnFile " $INSTDIR" " (DU)" " GenericWrite"
1528- AccessControl::RevokeOnFile " $INSTDIR" " (BU)" " GenericWrite"
1529- AccessControl::SetOnFile " $INSTDIR" " (BU)" " GenericRead + GenericExecute"
1530- AccessControl::SetOnFile " $INSTDIR" " (DU)" " GenericRead + GenericExecute"
1531- $ {EndIf}
1586+ $ {Print} " Setting installation directory permissions..."
1587+ # Enable inheritance on all files inside $INSTDIR .
1588+ # Use icacls because it is much faster than custom NSIS solutions.
1589+ # We continue on error because icacls fails on broken links.
1590+ push '" $ICACLS_EXE" " $INSTDIR\* " /inheritance:e /T /C /Q'
1591+ push 'Failed to enable inheritance for all files in the installation directory. '
1592+ push 'NoLog'
1593+ call AbortRetryNSExecWait
15321594 $ {Print} " Done!"
15331595SectionEnd
15341596
@@ -1553,7 +1615,7 @@ Section "Uninstall"
15531615 !insertmacro un.ParseCommandLineArgs
15541616 $ {EndIf}
15551617
1556- !insertmacro FindCmdExe
1618+ !insertmacro FindWindowsBinaries
15571619
15581620 System::Call 'kernel32::SetEnvironmentVariable(t,t)i(" CONDA_ROOT_PREFIX" , " $INSTDIR" )" .r0'
15591621
0 commit comments