# Standalone SFTP/FTP login test. Nothing is read from a config file - you type # the details in - so it isolates the account from whatever a script is doing # with it. Called by test-login.bat. $scriptRoot = Split-Path -Parent $PSCommandPath $winscpDll = Join-Path $scriptRoot "WinSCP\WinSCPnet.dll" function Read-Answer($question, $default) { while ($true) { if ($default) { Write-Host "$question " -NoNewline -ForegroundColor White Write-Host "[$default]" -NoNewline -ForegroundColor DarkGray Write-Host ": " -NoNewline -ForegroundColor White } else { Write-Host "${question}: " -NoNewline -ForegroundColor White } $answer = Read-Host if ([string]::IsNullOrWhiteSpace($answer)) { if ($default) { return $default } Write-Host " (required)" -ForegroundColor Yellow continue } return $answer } } Write-Host "" Write-Host ("=" * 64) -ForegroundColor Cyan Write-Host " Login test" -ForegroundColor Cyan Write-Host ("=" * 64) -ForegroundColor Cyan Write-Host "" Write-Host "Type the details exactly as they work in WinSCP or FileZilla." -ForegroundColor Gray if (-not (Test-Path $winscpDll)) { Write-Host "" Write-Host "WinSCPnet.dll not found at $winscpDll" -ForegroundColor Red Write-Host "Run setup.bat first, or copy this script next to the WinSCP folder." -ForegroundColor Yellow exit 1 } Get-ChildItem -Path (Join-Path $scriptRoot "WinSCP") -Recurse -File | Unblock-File -ErrorAction SilentlyContinue try { Add-Type -Path $winscpDll } catch { Write-Host "Could not load WinSCPnet.dll - $($_.Exception.Message)" -ForegroundColor Red exit 1 } Write-Host "" $serverHost = Read-Answer "Server" $null $serverHost = ($serverHost -replace '^\w+://', '').Split('/')[0].Trim() $protoChoice = 0 Write-Host "" Write-Host "Protocol:" -ForegroundColor White Write-Host " 1. SFTP" Write-Host " 2. FTPS" Write-Host " 3. FTP" while ($protoChoice -lt 1 -or $protoChoice -gt 3) { Write-Host "Choose 1-3 " -NoNewline -ForegroundColor White Write-Host "[1]" -NoNewline -ForegroundColor DarkGray Write-Host ": " -NoNewline -ForegroundColor White $a = Read-Host if ([string]::IsNullOrWhiteSpace($a)) { $a = "1" } [int]::TryParse($a.Trim(), [ref]$protoChoice) | Out-Null } $protocol = @("sftp", "ftps", "ftp")[$protoChoice - 1] $defaultPort = if ($protocol -eq "sftp") { 22 } else { 21 } $port = [int](Read-Answer "Port" $defaultPort) $username = Read-Answer "Username" $null Write-Host "" Write-Host "Paste the password. It is not echoed and not saved anywhere." -ForegroundColor DarkGray Write-Host "Password: " -NoNewline -ForegroundColor White $secure = Read-Host -AsSecureString $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure) $password = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) # -- What the credentials actually contain ------------------------------------ Write-Host "" Write-Host ("-" * 64) Write-Host "What was entered" -ForegroundColor Cyan Write-Host ("-" * 64) Write-Host " Server : [$serverHost] port $port over $($protocol.ToUpper())" Write-Host " Username : [$username] ($($username.Length) characters)" Write-Host " Password : $($password.Length) characters" $warned = $false if ($username -ne $username.Trim()) { Write-Host " !! The username has a leading or trailing space." -ForegroundColor Yellow $warned = $true } if ($password -ne $password.Trim()) { Write-Host " !! The password has a leading or trailing space." -ForegroundColor Yellow $warned = $true } if ($password -match '[\$`]') { Write-Host " !! The password contains a `$ or a backtick." -ForegroundColor Yellow Write-Host " In a PowerShell script these MUST be in single quotes:" -ForegroundColor Yellow Write-Host " `$Password = 'the`$password' <- correct" -ForegroundColor Yellow Write-Host " `$Password = `"the`$password`" <- WRONG, PowerShell eats `$password" -ForegroundColor Yellow Write-Host " This is the most common reason a login works in the WinSCP" -ForegroundColor Yellow Write-Host " window but fails from a script with the same credentials." -ForegroundColor Yellow $warned = $true } if ($password -match '[^\x20-\x7E]') { Write-Host " !! The password contains a non-standard character." -ForegroundColor Yellow $warned = $true } if (-not $warned) { Write-Host " Nothing unusual about either string." -ForegroundColor Green } # -- Try it ------------------------------------------------------------------- $logFile = Join-Path $scriptRoot "login-test.log" if (Test-Path $logFile) { Remove-Item $logFile -Force } $opts = New-Object WinSCP.SessionOptions $opts.HostName = $serverHost $opts.PortNumber = $port $opts.UserName = $username $opts.Password = $password if ($protocol -eq "sftp") { $opts.Protocol = [WinSCP.Protocol]::Sftp $opts.GiveUpSecurityAndAcceptAnySshHostKey = $true } else { $opts.Protocol = [WinSCP.Protocol]::Ftp if ($protocol -eq "ftps") { $opts.FtpSecure = [WinSCP.FtpSecure]::Explicit $opts.GiveUpSecurityAndAcceptAnyTlsHostCertificate = $true } else { $opts.FtpSecure = [WinSCP.FtpSecure]::None } } Write-Host "" Write-Host ("-" * 64) Write-Host "Connecting" -ForegroundColor Cyan Write-Host ("-" * 64) $session = New-Object WinSCP.Session $session.SessionLogPath = $logFile $connected = $false try { $session.Open($opts) $connected = $true Write-Host " CONNECTED" -ForegroundColor Green } catch { Write-Host " FAILED" -ForegroundColor Red Write-Host "" Write-Host " $($_.Exception.Message)" -ForegroundColor Red } if ($connected) { try { Write-Host "" Write-Host " Home directory: $($session.HomePath)" -ForegroundColor Gray Write-Host " Folders visible at the top level:" -ForegroundColor Gray $listing = $session.ListDirectory($session.HomePath) $shown = 0 foreach ($item in $listing.Files) { if ($item.Name -eq "." -or $item.Name -eq "..") { continue } $kind = if ($item.IsDirectory) { "[dir] " } else { " " } Write-Host " $kind$($item.Name)" $shown++ if ($shown -ge 25) { Write-Host " ... (more)"; break } } if ($shown -eq 0) { Write-Host " (nothing - the account may be locked to an empty folder)" } } catch { Write-Host " Connected, but could not list the folder: $($_.Exception.Message)" -ForegroundColor Yellow } Write-Host "" $checkPath = Read-Answer "Remote folder to check (or . to skip)" "." if ($checkPath -ne ".") { try { if ($session.FileExists($checkPath)) { Write-Host " $checkPath ... found" -ForegroundColor Green } else { Write-Host " $checkPath ... NOT THERE" -ForegroundColor Red } } catch { Write-Host " Could not check: $($_.Exception.Message)" -ForegroundColor Yellow } } } $session.Dispose() # -- The authentication conversation ------------------------------------------ if (-not $connected -and (Test-Path $logFile)) { Write-Host "" Write-Host ("-" * 64) Write-Host "What the server actually said" -ForegroundColor Cyan Write-Host ("-" * 64) $interesting = Select-String -Path $logFile -Pattern "authentic|password|keyboard|publickey|banner|denied|method" -AllMatches if ($interesting) { foreach ($line in ($interesting | Select-Object -First 30)) { Write-Host " $($line.Line.Trim())" -ForegroundColor Gray } Write-Host "" Write-Host " Look for the line listing which methods the server offers." -ForegroundColor Yellow Write-Host " If it offers only 'publickey', a password will never work and" -ForegroundColor Yellow Write-Host " the partner has to either enable password auth or issue a key." -ForegroundColor Yellow } else { Write-Host " (nothing recognisable - see the full log)" -ForegroundColor Gray } Write-Host "" Write-Host " Full log: $logFile" -ForegroundColor Cyan Write-Host " It contains no password. Send it to Brad." -ForegroundColor Cyan } Write-Host "" Write-Host ("=" * 64) -ForegroundColor Cyan if ($connected) { Write-Host " Login works. The account is fine - so if a script using these" -ForegroundColor Green Write-Host " same details still fails, the script is mangling one of them." -ForegroundColor Green } else { Write-Host " Login failed. Compare against the WinSCP window: File > Session" -ForegroundColor Yellow Write-Host " > Generate Session URL/Code > .NET assembly code. That shows the" -ForegroundColor Yellow Write-Host " exact settings the working connection uses." -ForegroundColor Yellow } Write-Host ("=" * 64) -ForegroundColor Cyan Write-Host ""