@echo off setlocal enableextensions enabledelayedexpansion title setup-windows-xp @rem --------------------------------------------------------------------------- @rem setup-windows-xp.bat - provision a Windows XP VM with an SSH server + Python @rem --------------------------------------------------------------------------- @rem Companion to setup-windows.bat, for the throwaway XP VM that the XP-toolset @rem builds get tested on. Almost none of the modern machinery exists here: no @rem winget, no PowerShell, no where.exe, no setx.exe, no curl, no tar. The one @rem that shapes this whole script is TLS: XP's SChannel stops at TLS 1.0, so the @rem VM cannot download anything from python.org, sourceforge or github over @rem HTTPS. So nothing is fetched at run time. Stage the installers on the HOST, @rem into the folder this script lives in (or a vendor-xp\ subfolder of it, which @rem is what the repo gitignores), and run the script from the share. Run it with @rem the payload missing and it prints the exact shopping list, then stops. @rem @rem Usage - run it from your NORMAL account first; it stages a local copy and @rem hands back the runas line to elevate with: @rem @rem setup-windows-xp.bat --user your-normal-account @rem @rem Options: @rem --user NAME account that will log in over SSH (default: you) @rem --vendor DIR where the staged installers are (default: vendor-xp\) @rem --port N sshd port (default 22) @rem --stage-dir DIR local copy used for elevation (default C:\xp-setup) @rem --python27 also install Python 2.7.18 (last of the 2.x line) @rem --no-python skip Python entirely @rem --no-ssh skip the SSH server @rem --keep-forceguest leave the LSA network-logon policy alone (see below) @rem --help print this commentary and exit @rem @rem Everything is idempotent: an installed service, an existing Python and a @rem PATH entry that is already there are all detected and skipped, so re-running @rem after fixing one broken step is cheap. @rem --------------------------------------------------------------------------- set "SCRIPT=%~f0" set "SCRIPT_DIR=%~dp0" set "SCRIPT_NAME=%~nx0" set "TARGET_USER=%USERNAME%" set "VENDOR=" set "SSH_PORT=22" set "STAGE_DIR=C:\xp-setup" set "DO_SSH=1" set "DO_PY34=1" set "DO_PY27=0" set "DO_LSA=1" set "WARNINGS=0" set "PATH_CHANGED=0" :parse if "%~1"=="" goto parsed if /i "%~1"=="--user" (set "TARGET_USER=%~2"& shift& shift& goto parse) if /i "%~1"=="--vendor" (set "VENDOR=%~2"& shift& shift& goto parse) if /i "%~1"=="--port" (set "SSH_PORT=%~2"& shift& shift& goto parse) if /i "%~1"=="--stage-dir" (set "STAGE_DIR=%~2"& shift& shift& goto parse) if /i "%~1"=="--python27" (set "DO_PY27=1"& shift& goto parse) if /i "%~1"=="--no-python" (set "DO_PY34=0"& set "DO_PY27=0"& shift& goto parse) if /i "%~1"=="--no-ssh" (set "DO_SSH=0"& shift& goto parse) if /i "%~1"=="--keep-forceguest" (set "DO_LSA=0"& shift& goto parse) if /i "%~1"=="--help" goto usage if /i "%~1"=="-h" goto usage if /i "%~1"=="/?" goto usage echo Unknown option: %~1 echo Run "%SCRIPT_NAME% --help" for usage. exit /b 64 :usage @rem Print the commentary block at the top of this file, stopping at the first @rem line of real code. goto out of a for loop is legal and is the only way to @rem break one early. for /f "usebackq delims=" %%L in ("%SCRIPT%") do ( set "L=%%L" if "!L!"=="@rem" ( echo. ) else if "!L:~0,4!"=="@rem" ( echo !L:~5! ) else ( if not "!L:~0,1!"=="@" if /i not "!L:~0,8!"=="setlocal" if /i not "!L:~0,5!"=="title" goto usage_done ) ) :usage_done exit /b 0 :parsed if not defined VENDOR set "VENDOR=%SCRIPT_DIR%vendor-xp" @rem cmd.exe refuses a UNC working directory and this script is meant to run @rem straight off the \\VBOXSVR share, so pushd maps a temporary drive for it. pushd "%SCRIPT_DIR%" 2>nul if errorlevel 1 ( echo [setup-xp] FATAL: cannot enter "%SCRIPT_DIR%". exit /b 1 ) @rem --- Log: next to the script if the share is writable, otherwise TEMP ------- set "LOG=%SCRIPT_DIR%setup-windows-xp.log" echo.> "%LOG%" 2>nul if not exist "%LOG%" ( set "LOG=%TEMP%\setup-windows-xp.log" echo.> "!LOG!" 2>nul ) set "TMPOUT=%TEMP%\setup-xp-out.tmp" call :say "===========================================================" call :say " setup-windows-xp" call :say "===========================================================" call :say "" call :say "Script : %SCRIPT%" call :say "Payload : %VENDOR%" call :say "Log : %LOG%" call :say "Running as : %USERDOMAIN%\%USERNAME%" call :say "SSH user : %TARGET_USER%" call :say "SSH port : %SSH_PORT%" for /f "delims=" %%V in ('ver ^| findstr /r "."') do call :say "OS : %%V" call :say "Arch : %PROCESSOR_ARCHITECTURE%" call :say "" @rem --- Sanity: this should be XP (5.1) or XP x64 / 2003 (5.2) ----------------- ver | findstr /c:"5.1." >nul if errorlevel 1 ( ver | findstr /c:"5.2." >nul if errorlevel 1 ( call :warn "This does not look like Windows XP. The payload below is XP-specific," call :warn "and a modern OS has far better options - see setup-windows.bat." ) ) @rem --------------------------------------------------------------------------- @rem Administrator check @rem --------------------------------------------------------------------------- @rem XP has no UAC, so there is nothing to elevate into - the script simply has @rem to be started by an administrator. Reading the ACL of the SYSTEM hive is @rem the dependable test: "net session" needs the Server service, which is a coin @rem flip on a stripped-down VM, and a probe write leaks "Access is denied" onto @rem the console past 2>nul. If we are not admin we stage a local copy and @rem print the runas line: a VirtualBox share mounted under YOUR account is not @rem visible to the Administrator account, which is the whole reason for the copy. set "IS_ADMIN=0" cacls "%SystemRoot%\system32\config\system" >nul 2>&1 if not errorlevel 1 set "IS_ADMIN=1" if "%IS_ADMIN%"=="0" goto not_admin call :say "[ok] Running with administrator rights." call :say "" @rem --------------------------------------------------------------------------- @rem Locate the staged payload @rem --------------------------------------------------------------------------- call :find_payload "setupssh381-20040709.exe" SSH_EXE call :find_payload "python-3.4.4.msi" PY34_MSI call :find_payload "python-3.4.4.amd64.msi" PY34_MSI64 call :find_payload "python-2.7.18.msi" PY27_MSI call :find_payload "get-pip.py" GETPIP call :find_payload "authorized_keys" AUTHKEYS if /i "%PROCESSOR_ARCHITECTURE%"=="AMD64" if defined PY34_MSI64 set "PY34_MSI=%PY34_MSI64%" set "MISSING=0" if "%DO_SSH%"=="1" if not defined SSH_EXE set "MISSING=1" if "%DO_PY34%"=="1" if not defined PY34_MSI set "MISSING=1" if "%DO_PY27%"=="1" if not defined PY27_MSI set "MISSING=1" if "%MISSING%"=="1" goto payload_missing @rem --------------------------------------------------------------------------- @rem SSH server: OpenSSH for Windows 3.8.1p1-1 (the sshwindows build) @rem --------------------------------------------------------------------------- @rem Why this one: a single 2004-vintage NSIS installer that registers a real @rem service, authenticates against local Windows accounts, needs no network and @rem no runtime, and installs unattended. The price is its crypto - SSH-2, but @rem with 2004 algorithms - so a current OpenSSH client has to be told to @rem re-enable diffie-hellman-group1-sha1, ssh-rsa and a CBC cipher. The summary @rem at the end prints the exact client incantation and a ~/.ssh/config block. @rem If you would rather have modern crypto, the two other workable XP options @rem are Bitvise SSH Server 6.x (last XP-capable line, free for personal use) and @rem Cygwin 2.5.2 from the Cygwin Time Machine (OpenSSH 7.x plus rsync, served @rem over plain HTTP so XP can actually fetch it). Both want more hand-holding @rem than a batch file can give, which is why neither is the default here. if "%DO_SSH%"=="0" ( call :say "[skip] SSH server, because of --no-ssh." goto python ) set "OSSH_DIR=%ProgramFiles%\OpenSSH" set "OSSH_BIN=%OSSH_DIR%\bin" set "OSSH_ETC=%OSSH_DIR%\etc" call :say "--- OpenSSH for Windows -----------------------------------" if exist "%OSSH_BIN%\mkpasswd.exe" ( call :say "[skip] Already installed at %OSSH_DIR%." ) else ( call :say "Installing %SSH_EXE% ..." call :run "%SSH_EXE%" /S if not exist "%OSSH_BIN%\mkpasswd.exe" ( call :warn "The silent install produced nothing. Falling back to the interactive" call :warn "installer - click through it, keeping the default location." start /wait "OpenSSH" "%SSH_EXE%" ) ) if not exist "%OSSH_BIN%\mkpasswd.exe" call :die "OpenSSH did not install - no %OSSH_BIN%\mkpasswd.exe. See the log." 3 @rem --- Account database ------------------------------------------------------- @rem sshd here is a Cygwin program: it will not authenticate anyone who is not in @rem its own etc\passwd and etc\group. Both are rewritten (not appended to) on @rem every run, so repeated runs cannot pile up duplicate entries. call :say "Generating %OSSH_ETC%\group and %OSSH_ETC%\passwd ..." "%OSSH_BIN%\mkgroup.exe" -l > "%OSSH_ETC%\group" 2>>"%LOG%" "%OSSH_BIN%\mkpasswd.exe" -l -u "%TARGET_USER%" > "%OSSH_ETC%\passwd" 2>>"%LOG%" if /i not "%TARGET_USER%"=="%USERNAME%" "%OSSH_BIN%\mkpasswd.exe" -l -u "%USERNAME%" >> "%OSSH_ETC%\passwd" 2>>"%LOG%" findstr /b /i /c:"%TARGET_USER%:" "%OSSH_ETC%\passwd" >nul 2>&1 if errorlevel 1 ( call :say "No entry yet - retrying mkpasswd without -u, for every local account ..." "%OSSH_BIN%\mkpasswd.exe" -l > "%OSSH_ETC%\passwd" 2>>"%LOG%" ) findstr /b /i /c:"%TARGET_USER%:" "%OSSH_ETC%\passwd" >nul 2>&1 if errorlevel 1 ( call :warn "No passwd entry for '%TARGET_USER%' - is it a local account? Check" call :warn "'net user', then re-run with --user NAME. Without an entry, sshd" call :warn "will refuse the login no matter what the password is." ) type "%OSSH_ETC%\passwd" >> "%LOG%" 2>&1 @rem --- sshd_config ------------------------------------------------------------ @rem Rebuilt from a pristine copy on each run: strip the directives we own, then @rem append our own block. StrictModes has to go: it judges Windows ACLs by POSIX @rem rules and rejects an authorized_keys that is perfectly fine here. set "CFG=%OSSH_ETC%\sshd_config" if exist "%CFG%" ( if not exist "%CFG%.orig" copy /y "%CFG%" "%CFG%.orig" >nul 2>&1 findstr /v /b /i /c:"Port " /c:"#Port " /c:"PasswordAuthentication" /c:"#PasswordAuthentication" /c:"PubkeyAuthentication" /c:"#PubkeyAuthentication" /c:"StrictModes" /c:"#StrictModes" "%CFG%.orig" > "%CFG%.new" >>"%CFG%.new" echo. >>"%CFG%.new" echo # --- added by setup-windows-xp.bat --- >>"%CFG%.new" echo Port %SSH_PORT% >>"%CFG%.new" echo PasswordAuthentication yes >>"%CFG%.new" echo PubkeyAuthentication yes >>"%CFG%.new" echo StrictModes no move /y "%CFG%.new" "%CFG%" >nul call :say "[ok] sshd_config: port %SSH_PORT%, password + pubkey auth, StrictModes off." ) else ( call :warn "No sshd_config at %CFG% - keeping the installer defaults." ) @rem --- authorized_keys, if a public key was staged ---------------------------- @rem The home directory is read out of the passwd entry we just generated, in @rem Cygwin notation, and translated back to a Windows path rather than guessed. if defined AUTHKEYS ( set "CYGHOME=" for /f "tokens=6 delims=:" %%H in ('findstr /b /i /c:"%TARGET_USER%:" "%OSSH_ETC%\passwd"') do set "CYGHOME=%%H" if defined CYGHOME ( set "WINHOME=" if /i "!CYGHOME:~0,6!"=="/home/" set "WINHOME=%OSSH_DIR%\home\!CYGHOME:~6!" if /i "!CYGHOME:~0,10!"=="/cygdrive/" ( set "REST=!CYGHOME:~10!" set "WINHOME=!REST:~0,1!:!REST:~1!" ) if defined WINHOME ( set "WINHOME=!WINHOME:/=\!" call :say "Installing authorized_keys into !WINHOME!\.ssh ..." if not exist "!WINHOME!\.ssh" mkdir "!WINHOME!\.ssh" 2>nul copy /y "%AUTHKEYS%" "!WINHOME!\.ssh\authorized_keys" >nul 2>>"%LOG%" if exist "!WINHOME!\.ssh\authorized_keys" ( cacls "!WINHOME!\.ssh" /E /G "%TARGET_USER%":F >nul 2>>"%LOG%" call :say "[ok] Key installed - passwd says home is !CYGHOME!" ) else ( call :warn "Could not write !WINHOME!\.ssh\authorized_keys." ) ) else ( call :warn "Unrecognised home '!CYGHOME!' in passwd; install the key by hand." ) ) else ( call :warn "No passwd entry for %TARGET_USER%; skipped authorized_keys." ) ) else ( call :say "[skip] No authorized_keys staged - password auth only for now." ) @rem --- Firewall --------------------------------------------------------------- @rem SP2 and later only; on an unpatched XP the netsh firewall context does not @rem exist at all and the failure is harmless. call :say "Opening TCP %SSH_PORT% in the Windows Firewall ..." call :run netsh firewall add portopening protocol=TCP port=%SSH_PORT% name=OpenSSH mode=ENABLE scope=ALL if errorlevel 1 call :warn "netsh firewall failed - open TCP %SSH_PORT% by hand if you cannot connect." @rem --- Network logon policy --------------------------------------------------- @rem Two XP defaults break SSH password auth, both under the LSA key, because @rem sshd authenticates with a NETWORK logon: @rem forceguest=1 every network logon collapses to Guest. This is @rem the default in a workgroup, which a VM always is. @rem limitblankpassworduse=1 an account with an empty password cannot log on @rem over the network at all. @rem The first is switched to Classic here, because password auth cannot work @rem with it on; --keep-forceguest opts out. The second is only reported: give @rem the account a password rather than weakening that policy. if "%DO_LSA%"=="1" ( set "LSA=HKLM\SYSTEM\CurrentControlSet\Control\Lsa" set "FG=" for /f "tokens=2,*" %%A in ('reg query "!LSA!" /v forceguest 2^>nul ^| findstr /i /c:"forceguest"') do set "FG=%%B" if "!FG!"=="0x1" ( call :say "Setting LSA forceguest=0 - Classic logon - so SSH sees the real account." call :run reg add "!LSA!" /v forceguest /t REG_DWORD /d 0 /f call :say " to revert: reg add HKLM\SYSTEM\CurrentControlSet\Control\Lsa /v forceguest /t REG_DWORD /d 1 /f" ) else if "!FG!"=="0x0" ( call :say "[ok] LSA forceguest already off - Classic logon." ) else ( call :warn "Could not read LSA forceguest; if password auth logs you in as" call :warn "Guest, set it to 0 by hand." ) set "BLANK=" for /f "tokens=2,*" %%A in ('reg query "!LSA!" /v limitblankpassworduse 2^>nul ^| findstr /i /c:"limitblank"') do set "BLANK=%%B" if "!BLANK!"=="0x1" ( call :warn "limitblankpassworduse=1, so an account with a BLANK password cannot" call :warn "log in over SSH. Give %TARGET_USER% a password: net user %TARGET_USER% *" ) ) else ( call :say "[skip] LSA policy untouched, because of --keep-forceguest." ) @rem --- Service ---------------------------------------------------------------- call :say "Starting the OpenSSHd service ..." call :run sc config OpenSSHd start= auto net start OpenSSHd >>"%LOG%" 2>&1 sc query OpenSSHd | findstr /c:"RUNNING" >nul if errorlevel 1 ( call :warn "OpenSSHd is not running." call :run sc query OpenSSHd call :warn "Check %OSSH_DIR%\var\log\OpenSSHd.log for the reason." ) else ( call :say "[ok] OpenSSHd is running, and set to start automatically." ) call :say "" @rem --------------------------------------------------------------------------- @rem Python @rem --------------------------------------------------------------------------- @rem 3.4.4 (December 2015) is the last CPython that supports XP - 3.5 raised the @rem floor to Vista. 2.7.18 closes out the 2.x line and still runs here, worth @rem having if anything under test is 2.x. The 3.4 MSI has no "add to PATH" @rem feature (that arrived with the 3.5 installer), so PATH is edited directly in @rem the registry below. :python if "%DO_PY34%"=="0" if "%DO_PY27%"=="0" ( call :say "[skip] Python, because of --no-python." goto verify ) call :say "--- Python ------------------------------------------------" set "PY34_DIR=C:\Python34" set "PY27_DIR=C:\Python27" if "%DO_PY34%"=="1" ( if exist "%PY34_DIR%\python.exe" ( call :say "[skip] Python 3.4 already at %PY34_DIR%." ) else ( call :say "Installing %PY34_MSI% to %PY34_DIR% ..." call :run msiexec /i "%PY34_MSI%" /qn /norestart ALLUSERS=1 TARGETDIR="%PY34_DIR%" /l*v "%TEMP%\python34-msi.log" if not exist "%PY34_DIR%\python.exe" call :warn "Python 3.4 install failed; see %TEMP%\python34-msi.log." ) if exist "%PY34_DIR%\python.exe" ( call :add_syspath "%PY34_DIR%" call :add_syspath "%PY34_DIR%\Scripts" call :bootstrap_pip "%PY34_DIR%" ) ) if "%DO_PY27%"=="1" ( if exist "%PY27_DIR%\python.exe" ( call :say "[skip] Python 2.7 already at %PY27_DIR%." ) else ( call :say "Installing %PY27_MSI% to %PY27_DIR% ..." call :run msiexec /i "%PY27_MSI%" /qn /norestart ALLUSERS=1 TARGETDIR="%PY27_DIR%" /l*v "%TEMP%\python27-msi.log" if not exist "%PY27_DIR%\python.exe" call :warn "Python 2.7 install failed; see %TEMP%\python27-msi.log." ) ) call :say "" @rem A service reads its environment when it starts, so sshd is still holding the @rem pre-Python PATH. Bounce it, or the first SSH session cannot find python.exe @rem and it looks like the PATH edit never took. if "%DO_SSH%"=="1" if "%PATH_CHANGED%"=="1" ( call :say "Restarting OpenSSHd so SSH sessions inherit the new PATH ..." net stop OpenSSHd >>"%LOG%" 2>&1 net start OpenSSHd >>"%LOG%" 2>&1 ) @rem --------------------------------------------------------------------------- @rem Verify @rem --------------------------------------------------------------------------- :verify call :say "--- Verification ------------------------------------------" if "%DO_SSH%"=="1" ( call :run sc query OpenSSHd call :say "Sockets listening on port %SSH_PORT%:" netstat -an | findstr /c:":%SSH_PORT% " | findstr /c:"LISTENING" if errorlevel 1 call :warn "Nothing is listening on port %SSH_PORT%." netstat -an | findstr /c:":%SSH_PORT% " | findstr /c:"LISTENING" >> "%LOG%" ) if exist "%PY34_DIR%\python.exe" call :run "%PY34_DIR%\python.exe" -V if exist "%PY34_DIR%\python.exe" call :run "%PY34_DIR%\python.exe" -m pip --version if exist "%PY27_DIR%\python.exe" call :run "%PY27_DIR%\python.exe" -V call :say "" set "VMIP=" for /f "tokens=2 delims=:" %%I in ('ipconfig ^| findstr /c:"IP Address" /c:"IPv4 Address"') do if not defined VMIP set "VMIP=%%I" if defined VMIP set "VMIP=%VMIP: =%" if not defined VMIP set "VMIP=vm-ip" call :say "===========================================================" call :say " Done. Warnings: %WARNINGS%" call :say " Log: %LOG%" call :say "===========================================================" call :say "" if "%DO_SSH%"=="1" ( call :say "Connecting from a modern client means re-enabling the 2004" call :say "algorithms, or you get 'no matching key exchange method found':" call :say "" call :say " ssh -p %SSH_PORT% -o KexAlgorithms=+diffie-hellman-group1-sha1,diffie-hellman-group14-sha1 -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa -c aes128-cbc %TARGET_USER%@%VMIP%" call :say "" call :say "Or drop this into ~/.ssh/config on the host and just 'ssh xpvm':" call :say "" call :say " Host xpvm" call :say " HostName %VMIP%" call :say " Port %SSH_PORT%" call :say " User %TARGET_USER%" call :say " KexAlgorithms +diffie-hellman-group1-sha1,diffie-hellman-group14-sha1" call :say " HostKeyAlgorithms +ssh-rsa" call :say " PubkeyAcceptedAlgorithms +ssh-rsa" call :say " Ciphers +aes128-cbc" call :say " MACs +hmac-sha1" call :say "" call :say "The account needs a real password - a blank one cannot log on over" call :say "the network. A NAT-only VM needs a host port forwarded to %SSH_PORT%." ) if "%PATH_CHANGED%"=="1" ( call :say "" call :say "PATH was changed in the registry: log off and back on, or reboot," call :say "before an interactive console sees python." ) popd endlocal exit /b 0 @rem =========================================================================== @rem Subroutines @rem =========================================================================== @rem Console and log in one call. Messages are passed as ONE quoted argument, @rem which constrains what can go in them - all of these were found the hard way: @rem no ! ......... delayed expansion eats it @rem no < or > .... echo re-parses the expanded value and redirects. A caret @rem does NOT help: by then the quotes are gone. @rem no ( or ) .... inside a NESTED if-block these close the block early, even @rem quoted, and even carets do not save them. @rem Use commas and dashes instead. A line that genuinely needs quotes or one of @rem these characters is echoed twice inline instead, once to each destination. :say if "%~1"=="" (echo.& >>"%LOG%" echo.) else (echo %~1& >>"%LOG%" echo %~1) goto :eof :warn set /a WARNINGS+=1 echo [warn] %~1 >>"%LOG%" echo [warn] %~1 goto :eof :die call :say "" call :say "[FATAL] %~1" popd endlocal exit /b %~2 @rem Run a command, showing its output and copying it to the log. Redirection @rem cannot be passed through %*, so anything needing a > of its own runs inline @rem instead of through here. :run echo run: %* >>"%LOG%" echo run: %* %* > "%TMPOUT%" 2>&1 set "RC=%ERRORLEVEL%" if exist "%TMPOUT%" ( type "%TMPOUT%" type "%TMPOUT%" >> "%LOG%" del "%TMPOUT%" >nul 2>&1 ) exit /b %RC% @rem :find_payload - vendor dir first, then beside the script :find_payload set "%~2=" if exist "%VENDOR%\%~1" (set "%~2=%VENDOR%\%~1"& goto :eof) if exist "%SCRIPT_DIR%%~1" (set "%~2=%SCRIPT_DIR%%~1"& goto :eof) goto :eof @rem :add_syspath - append to the machine PATH, once. @rem setx.exe is a Support Tools extra on XP, so the registry is edited directly. @rem Nothing broadcasts WM_SETTINGCHANGE afterwards, which is why the summary @rem asks for a logoff: running shells and explorer keep the old value. :add_syspath set "ENVKEY=HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" set "SYSPATH=" set "PATHTYPE=REG_EXPAND_SZ" for /f "tokens=2,*" %%A in ('reg query "%ENVKEY%" /v Path 2^>nul ^| findstr /i /r /c:"Path.*REG_"') do ( set "PATHTYPE=%%A" set "SYSPATH=%%B" ) if not defined SYSPATH ( call :warn "Could not read the machine PATH; add %~1 to it by hand." goto :eof ) echo ;%SYSPATH%;| findstr /i /c:";%~1;" >nul if not errorlevel 1 ( call :say "[ok] %~1 already in the machine PATH." goto :eof ) >>"%LOG%" echo [path] previous value: %SYSPATH% reg add "%ENVKEY%" /v Path /t %PATHTYPE% /d "%SYSPATH%;%~1" /f >>"%LOG%" 2>&1 if errorlevel 1 ( call :warn "Failed to append %~1 to the machine PATH." ) else ( call :say "[ok] Added %~1 to the machine PATH." set "PATH=%PATH%;%~1" set "PATH_CHANGED=1" ) goto :eof @rem :bootstrap_pip @rem ensurepip is offline and always works, but 3.4.4 carries pip 6, which @rem today's PyPI turns away. A staged get-pip.py - the bootstrap.pypa.io/pip/3.4 @rem one - carries pip 19.1.1, the last release supporting 3.4, so prefer it. @rem Either way, installing FROM PyPI later is its own problem: expect to stage @rem wheels on the host and use pip install --no-index --find-links. :bootstrap_pip if exist "%~1\Scripts\pip.exe" ( call :say "[skip] pip already present in %~1." goto :eof ) if defined GETPIP ( call :say "Bootstrapping pip from %GETPIP% ..." call :run "%~1\python.exe" "%GETPIP%" ) else ( call :say "Bootstrapping pip with ensurepip - no get-pip.py staged ..." call :run "%~1\python.exe" -m ensurepip --default-pip ) if not exist "%~1\Scripts\pip.exe" call :warn "pip bootstrap failed for %~1." goto :eof @rem =========================================================================== @rem Exits @rem =========================================================================== :not_admin @rem Stage the script and its payload somewhere the Administrator account can @rem actually reach - a share mounted under your account is not it - and hand @rem back the command line to run. call :say "[--] Not running as an administrator." call :say "" if /i "%SCRIPT_DIR%"=="%STAGE_DIR%\" goto not_admin_hint call :say "Staging a local copy in %STAGE_DIR% ..." if not exist "%STAGE_DIR%" mkdir "%STAGE_DIR%" 2>nul if not exist "%STAGE_DIR%" ( set "STAGE_DIR=%TEMP%\xp-setup" if not exist "!STAGE_DIR!" mkdir "!STAGE_DIR!" 2>nul ) copy /y "%SCRIPT%" "%STAGE_DIR%\" >nul 2>&1 if exist "%VENDOR%" ( if not exist "%STAGE_DIR%\vendor-xp" mkdir "%STAGE_DIR%\vendor-xp" 2>nul xcopy "%VENDOR%\*.*" "%STAGE_DIR%\vendor-xp\" /y /i >nul 2>&1 ) if exist "%SCRIPT_DIR%authorized_keys" copy /y "%SCRIPT_DIR%authorized_keys" "%STAGE_DIR%\" >nul 2>&1 call :say "[ok] Copied to %STAGE_DIR%." :not_admin_hint call :say "" call :say "Now run it as the admin account. From this same window:" call :say "" echo runas /user:%COMPUTERNAME%\Administrator "cmd /k %STAGE_DIR%\%SCRIPT_NAME% --user %USERNAME%" >>"%LOG%" echo runas /user:%COMPUTERNAME%\Administrator "cmd /k %STAGE_DIR%\%SCRIPT_NAME% --user %USERNAME%" call :say "" call :say "Substitute your own admin account name; runas needs the Secondary" call :say "Logon service. Right-clicking the .bat and picking 'Run as...' works" call :say "just as well." call :say "" call :say "The --user %USERNAME% part matters: the elevated run has to be told" call :say "which account you will actually be logging in as over SSH." popd endlocal exit /b 2 :payload_missing call :say "" call :say "[--] The installers are not staged yet." call :say "" call :say "XP cannot fetch them itself - its TLS stops at 1.0 and every one of" call :say "these hosts requires TLS 1.2. Download them on the HOST, drop them in" call :say " %VENDOR%" call :say "and run this again." call :say "" call :say "Required:" if not defined SSH_EXE ( call :say " setupssh381-20040709.exe OpenSSH for Windows 3.8.1p1-1" call :say " https://sourceforge.net/projects/sshwindows/files/" call :say " Binaries, then Release 3.8.1p1-1" ) if not defined PY34_MSI call :say " python-3.4.4.msi https://www.python.org/ftp/python/3.4.4/python-3.4.4.msi" if "%DO_PY27%"=="1" if not defined PY27_MSI call :say " python-2.7.18.msi https://www.python.org/ftp/python/2.7.18/python-2.7.18.msi" call :say "" call :say "Optional:" call :say " get-pip.py https://bootstrap.pypa.io/pip/3.4/get-pip.py" call :say " pip 19.1.1, the last release for 3.4" call :say " authorized_keys your host public key, for key auth" call :say "" call :say "On the host, in this folder:" call :say "" echo powershell -NoProfile -Command "New-Item -ItemType Directory -Force vendor-xp; iwr https://www.python.org/ftp/python/3.4.4/python-3.4.4.msi -OutFile vendor-xp\python-3.4.4.msi; iwr https://bootstrap.pypa.io/pip/3.4/get-pip.py -OutFile vendor-xp\get-pip.py" call :say "" call :say "SourceForge hands out a browser interstitial rather than the file, so" call :say "grab setupssh381-20040709.exe by hand." popd endlocal exit /b 4