Encode powershell using utf8

Fix a bug in the size of string that was returned when decoding a base64 string
Added tests around encoding and decoding powershell scripts. Used [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('powershell commands')) | clip to generate what base 64 strings should look like
This commit is contained in:
Taliesin Sisson 2016-07-13 00:28:14 +01:00
parent 17597b48e1
commit d61513bf77
3 changed files with 132 additions and 34 deletions

View File

@ -2,22 +2,33 @@ package powershell
import (
"encoding/base64"
"golang.org/x/text/encoding/unicode"
)
func powershellEncode(buffer []byte) string {
// 2 byte chars to make PowerShell happy
wideCmd := ""
for _, b := range buffer {
wideCmd += string(b) + "\x00"
func powershellUtf8(message string) (string, error) {
utf8 := unicode.UTF8
utfEncoder := utf8.NewEncoder()
utf8EncodedMessage, err := utfEncoder.String(message)
return utf8EncodedMessage, err
}
func powershellEncode(message string) (string, error) {
utf8EncodedMessage, err := powershellUtf8(message)
if err != nil {
return "", err
}
// Base64 encode the command
input := []uint8(wideCmd)
return base64.StdEncoding.EncodeToString(input)
input := []uint8(utf8EncodedMessage)
return base64.StdEncoding.EncodeToString(input), nil
}
func powershellDecode(message string) (retour string) {
base64Text := make([]byte, base64.StdEncoding.DecodedLen(len(message)))
base64.StdEncoding.Decode(base64Text, []byte(message))
return string(base64Text)
func powershellDecode(message string) (retour string, err error) {
data, err := base64.StdEncoding.DecodeString(message)
if err != nil {
return "", err
}
return string(data), nil
}

View File

@ -399,9 +399,25 @@ func (p *Provisioner) createCommandTextNonPrivileged() (command string, err erro
return "", fmt.Errorf("Error processing command: %s", err)
}
encodedCommand := "powershell -executionpolicy bypass -encodedCommand " + powershellEncode([]byte("if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; "+command+"; exit $LastExitCode"))
commandText, err := p.generateCommandLineRunner(command)
if err != nil {
return "", fmt.Errorf("Error generating command line runner: %s", err)
}
return encodedCommand, err
return commandText, err
}
func (p *Provisioner) generateCommandLineRunner(command string) (commandText string, err error) {
log.Printf("Building command line for: %s", command)
base64EncodedCommand, err := powershellEncode("if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; " + command + "; exit $LastExitCode")
if err != nil {
return "", fmt.Errorf("Error encoding command: %s", err)
}
commandText = "powershell -executionpolicy bypass -encodedCommand " + base64EncodedCommand
return commandText, nil
}
func (p *Provisioner) createCommandTextPrivileged() (command string, err error) {
@ -422,6 +438,9 @@ func (p *Provisioner) createCommandTextPrivileged() (command string, err error)
// OK so we need an elevated shell runner to wrap our command, this is going to have its own path
// generate the script and update the command runner in the process
path, err := p.generateElevatedRunner(command)
if err != nil {
return "", fmt.Errorf("Error generating elevated runner: %s", err)
}
// Return the path to the elevated shell wrapper
command = fmt.Sprintf("powershell -executionpolicy bypass -file \"%s\"", path)
@ -434,12 +453,18 @@ func (p *Provisioner) generateElevatedRunner(command string) (uploadedPath strin
// generate command
var buffer bytes.Buffer
base64EncodedCommand, err := powershellEncode("if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; " + command + "; exit $LastExitCode")
if err != nil {
return "", fmt.Errorf("Error encoding command: %s", err)
}
err = elevatedTemplate.Execute(&buffer, elevatedOptions{
User: p.config.ElevatedUser,
Password: p.config.ElevatedPassword,
TaskDescription: "Packer elevated task",
TaskName: fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()),
EncodedCommand: powershellEncode([]byte("if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; " + command + "; exit $LastExitCode")),
EncodedCommand: base64EncodedCommand,
})
if err != nil {

View File

@ -390,14 +390,27 @@ func TestProvisionerProvision_Inline(t *testing.T) {
}
expectedCommand := `if (Test-Path variable:global:ProgressPreference){$ProgressPreference="SilentlyContinue"}; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; c:/Windows/Temp/inlineScript.bat; exit $LastExitCode`
expectedCommandUtf8, err := powershellUtf8(expectedCommand)
if err != nil {
t.Fatal("should not have error when Utf 8 encoding")
}
expectedCommandBase64Encoded := `aWYgKFRlc3QtUGF0aCB2YXJpYWJsZTpnbG9iYWw6UHJvZ3Jlc3NQcmVmZXJlbmNlKXskUHJvZ3Jlc3NQcmVmZXJlbmNlPSJTaWxlbnRseUNvbnRpbnVlIn07ICRlbnY6UEFDS0VSX0JVSUxERVJfVFlQRT0iaXNvIjsgJGVudjpQQUNLRVJfQlVJTERfTkFNRT0idm13YXJlIjsgYzovV2luZG93cy9UZW1wL2lubGluZVNjcmlwdC5iYXQ7IGV4aXQgJExhc3RFeGl0Q29kZQ==`
expectedCommandPrefix := `powershell -executionpolicy bypass -encodedCommand `
expectedCommandEncoded := expectedCommandPrefix + powershellEncode([]byte(expectedCommand))
expectedCommandEncoded := expectedCommandPrefix + expectedCommandBase64Encoded
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded, err := powershellDecode(actualCommandWithoutPrefix)
if err != nil {
t.Fatal("should not have error when base64 decoding")
}
// Should run the command without alteration
if comm.StartCmd.Command != expectedCommandEncoded {
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded := powershellDecode(actualCommandWithoutPrefix)
t.Fatalf("Expect command to be: %s, got %s. Expected decoded: %s, got %s", expectedCommandEncoded, comm.StartCmd.Command, expectedCommand, actualCommandDecoded)
t.Fatalf("Expect command to be: %s, got %s.", expectedCommandEncoded, comm.StartCmd.Command)
}
if actualCommandDecoded != expectedCommandUtf8 {
t.Fatalf("Expected decoded:%s, %s, got %s", expectedCommandEncoded, len(expectedCommandUtf8), len(actualCommandDecoded))
}
envVars := make([]string, 2)
@ -413,14 +426,27 @@ func TestProvisionerProvision_Inline(t *testing.T) {
}
expectedCommand = `if (Test-Path variable:global:ProgressPreference){$ProgressPreference="SilentlyContinue"}; $env:BAR="BAZ"; $env:FOO="BAR"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; c:/Windows/Temp/inlineScript.bat; exit $LastExitCode`
expectedCommandUtf8, err = powershellUtf8(expectedCommand)
if err != nil {
t.Fatal("should not have error when Utf 8 encoding")
}
expectedCommandBase64Encoded = `aWYgKFRlc3QtUGF0aCB2YXJpYWJsZTpnbG9iYWw6UHJvZ3Jlc3NQcmVmZXJlbmNlKXskUHJvZ3Jlc3NQcmVmZXJlbmNlPSJTaWxlbnRseUNvbnRpbnVlIn07ICRlbnY6QkFSPSJCQVoiOyAkZW52OkZPTz0iQkFSIjsgJGVudjpQQUNLRVJfQlVJTERFUl9UWVBFPSJpc28iOyAkZW52OlBBQ0tFUl9CVUlMRF9OQU1FPSJ2bXdhcmUiOyBjOi9XaW5kb3dzL1RlbXAvaW5saW5lU2NyaXB0LmJhdDsgZXhpdCAkTGFzdEV4aXRDb2Rl`
expectedCommandPrefix = `powershell -executionpolicy bypass -encodedCommand `
expectedCommandEncoded = expectedCommandPrefix + powershellEncode([]byte(expectedCommand))
expectedCommandEncoded = expectedCommandPrefix + expectedCommandBase64Encoded
actualCommandWithoutPrefix = strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded, err = powershellDecode(actualCommandWithoutPrefix)
if err != nil {
t.Fatal("should not have error when base64 decoding")
}
// Should run the command without alteration
if comm.StartCmd.Command != expectedCommandEncoded {
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded := powershellDecode(actualCommandWithoutPrefix)
t.Fatalf("Expect command to be: %s, got %s. Expected decoded: %s, got %s", expectedCommandEncoded, comm.StartCmd.Command, expectedCommand, actualCommandDecoded)
t.Fatalf("Expect command to be: %s, got %s.", expectedCommandEncoded, comm.StartCmd.Command)
}
if actualCommandDecoded != expectedCommandUtf8 {
t.Fatalf("Expected decoded: %s, got %s", expectedCommandUtf8, actualCommandDecoded)
}
}
@ -443,14 +469,27 @@ func TestProvisionerProvision_Scripts(t *testing.T) {
}
expectedCommand := `if (Test-Path variable:global:ProgressPreference){$ProgressPreference="SilentlyContinue"}; $env:PACKER_BUILDER_TYPE="footype"; $env:PACKER_BUILD_NAME="foobuild"; c:/Windows/Temp/script.ps1; exit $LastExitCode`
expectedCommandUtf8, err := powershellUtf8(expectedCommand)
if err != nil {
t.Fatal("should not have error when Utf 8 encoding")
}
expectedCommandBase64Encoded := `aWYgKFRlc3QtUGF0aCB2YXJpYWJsZTpnbG9iYWw6UHJvZ3Jlc3NQcmVmZXJlbmNlKXskUHJvZ3Jlc3NQcmVmZXJlbmNlPSJTaWxlbnRseUNvbnRpbnVlIn07ICRlbnY6UEFDS0VSX0JVSUxERVJfVFlQRT0iZm9vdHlwZSI7ICRlbnY6UEFDS0VSX0JVSUxEX05BTUU9ImZvb2J1aWxkIjsgYzovV2luZG93cy9UZW1wL3NjcmlwdC5wczE7IGV4aXQgJExhc3RFeGl0Q29kZQ==`
expectedCommandPrefix := `powershell -executionpolicy bypass -encodedCommand `
expectedCommandEncoded := expectedCommandPrefix + powershellEncode([]byte(expectedCommand))
expectedCommandEncoded := expectedCommandPrefix + expectedCommandBase64Encoded
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded, err := powershellDecode(actualCommandWithoutPrefix)
if err != nil {
t.Fatal("should not have error when base64 decoding")
}
// Should run the command without alteration
if comm.StartCmd.Command != expectedCommandEncoded {
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded := powershellDecode(actualCommandWithoutPrefix)
t.Fatalf("Expect command to be: %s, got %s. Expected decoded: %s, got %s", expectedCommandEncoded, comm.StartCmd.Command, expectedCommand, actualCommandDecoded)
t.Fatalf("Expect command to be: %s, got %s.", expectedCommandEncoded, comm.StartCmd.Command)
}
if actualCommandDecoded != expectedCommandUtf8 {
t.Fatalf("Expected decoded: %n, got %n", len(expectedCommandUtf8), len(actualCommandDecoded))
}
}
@ -480,14 +519,27 @@ func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) {
}
expectedCommand := `if (Test-Path variable:global:ProgressPreference){$ProgressPreference="SilentlyContinue"}; $env:BAR="BAZ"; $env:FOO="BAR"; $env:PACKER_BUILDER_TYPE="footype"; $env:PACKER_BUILD_NAME="foobuild"; c:/Windows/Temp/script.ps1; exit $LastExitCode`
expectedCommandUtf8, err := powershellUtf8(expectedCommand)
if err != nil {
t.Fatal("should not have error when Utf 8 encoding")
}
expectedCommandBase64Encoded := `aWYgKFRlc3QtUGF0aCB2YXJpYWJsZTpnbG9iYWw6UHJvZ3Jlc3NQcmVmZXJlbmNlKXskUHJvZ3Jlc3NQcmVmZXJlbmNlPSJTaWxlbnRseUNvbnRpbnVlIn07ICRlbnY6QkFSPSJCQVoiOyAkZW52OkZPTz0iQkFSIjsgJGVudjpQQUNLRVJfQlVJTERFUl9UWVBFPSJmb290eXBlIjsgJGVudjpQQUNLRVJfQlVJTERfTkFNRT0iZm9vYnVpbGQiOyBjOi9XaW5kb3dzL1RlbXAvc2NyaXB0LnBzMTsgZXhpdCAkTGFzdEV4aXRDb2Rl`
expectedCommandPrefix := `powershell -executionpolicy bypass -encodedCommand `
expectedCommandEncoded := expectedCommandPrefix + powershellEncode([]byte(expectedCommand))
expectedCommandEncoded := expectedCommandPrefix + expectedCommandBase64Encoded
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded, err := powershellDecode(actualCommandWithoutPrefix)
if err != nil {
t.Fatal("should not have error when base64 decoding")
}
// Should run the command without alteration
if comm.StartCmd.Command != expectedCommandEncoded {
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded := powershellDecode(actualCommandWithoutPrefix)
t.Fatalf("Expect command to be: %s, got %s. Expected decoded: %s, got %s", expectedCommandEncoded, comm.StartCmd.Command, expectedCommand, actualCommandDecoded)
t.Fatalf("Expect command to be: %s, got %s.", expectedCommandEncoded, comm.StartCmd.Command)
}
if actualCommandDecoded != expectedCommandUtf8 {
t.Fatalf("Expected decoded: %s, got %s", expectedCommandUtf8, actualCommandDecoded)
}
}
@ -599,14 +651,24 @@ func TestProvision_createCommandText(t *testing.T) {
cmd, _ := p.createCommandText()
expectedCommand := `if (Test-Path variable:global:ProgressPreference){$ProgressPreference="SilentlyContinue"}; $env:PACKER_BUILDER_TYPE=""; $env:PACKER_BUILD_NAME=""; c:/Windows/Temp/script.ps1; exit $LastExitCode`
expectedCommandBase64Encoded := `aWYgKFRlc3QtUGF0aCB2YXJpYWJsZTpnbG9iYWw6UHJvZ3Jlc3NQcmVmZXJlbmNlKXskUHJvZ3Jlc3NQcmVmZXJlbmNlPSJTaWxlbnRseUNvbnRpbnVlIn07ICRlbnY6UEFDS0VSX0JVSUxERVJfVFlQRT0iIjsgJGVudjpQQUNLRVJfQlVJTERfTkFNRT0iIjsgYzovV2luZG93cy9UZW1wL3NjcmlwdC5wczE7IGV4aXQgJExhc3RFeGl0Q29kZQ==`
expectedCommandPrefix := `powershell -executionpolicy bypass -encodedCommand `
expectedCommandEncoded := expectedCommandPrefix + powershellEncode([]byte(expectedCommand))
expectedCommandEncoded := expectedCommandPrefix + expectedCommandBase64Encoded
actualCommandWithoutPrefix := strings.Replace(cmd, expectedCommandPrefix, "", -1)
actualCommandDecoded, err := powershellDecode(actualCommandWithoutPrefix)
if err != nil {
t.Fatal("should not have error when base64 decoding")
}
// Should run the command without alteration
if cmd != expectedCommandEncoded {
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded := powershellDecode(actualCommandWithoutPrefix)
t.Fatalf("Expect command to be: %s, got %s. Expected decoded: %s, got %s", expectedCommandEncoded, comm.StartCmd.Command, expectedCommand, actualCommandDecoded)
t.Fatalf("Expect command to be: %s, got %s.", expectedCommandEncoded, cmd)
}
if actualCommandDecoded != expectedCommand {
t.Fatalf("Expected decoded: %s, got %s", expectedCommand, actualCommandDecoded)
}
// Elevated