diff --git a/provisioner/powershell/elevated.go b/provisioner/powershell/elevated.go index d2c23f757..f8adb6cd1 100644 --- a/provisioner/powershell/elevated.go +++ b/provisioner/powershell/elevated.go @@ -5,11 +5,11 @@ import ( ) type elevatedOptions struct { - User string - Password string - TaskName string - TaskDescription string - EncodedCommand string + User string + Password string + TaskName string + TaskDescription string + XMLEscapedCommand string } var elevatedTemplate = template.Must(template.New("ElevatedCommand").Parse(` @@ -53,7 +53,7 @@ $t.XmlText = @' cmd - /c powershell.exe -EncodedCommand {{.EncodedCommand}} > %SYSTEMROOT%\Temp\{{.TaskName}}.out 2>&1 + /c {{.XMLEscapedCommand}} > %SYSTEMROOT%\Temp\{{.TaskName}}.out 2>&1 diff --git a/provisioner/powershell/powershell.go b/provisioner/powershell/powershell.go deleted file mode 100644 index 086e3e554..000000000 --- a/provisioner/powershell/powershell.go +++ /dev/null @@ -1,54 +0,0 @@ -package powershell - -import ( - "encoding/base64" - "encoding/binary" - "unicode/utf16" - "unicode/utf8" - - "golang.org/x/text/encoding/unicode" -) - -func convertUtf8ToUtf16LE(message string) (string, error) { - utf16le := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) - utfEncoder := utf16le.NewEncoder() - ut16LeEncodedMessage, err := utfEncoder.String(message) - - return ut16LeEncodedMessage, err -} - -// UTF16BytesToString converts UTF-16 encoded bytes, in big or little endian byte order, -// to a UTF-8 encoded string. -func UTF16BytesToString(b []byte, o binary.ByteOrder) string { - utf := make([]uint16, (len(b)+(2-1))/2) - for i := 0; i+(2-1) < len(b); i += 2 { - utf[i/2] = o.Uint16(b[i:]) - } - if len(b)/2 < len(utf) { - utf[len(utf)-1] = utf8.RuneError - } - return string(utf16.Decode(utf)) -} - -func powershellEncode(message string) (string, error) { - utf16LEEncodedMessage, err := convertUtf8ToUtf16LE(message) - if err != nil { - return "", err - } - - // Base64 encode the command - input := []uint8(utf16LEEncodedMessage) - return base64.StdEncoding.EncodeToString(input), nil -} - -func powershellDecode(messageBase64 string) (retour string, err error) { - messageUtf16LeByteArray, err := base64.StdEncoding.DecodeString(messageBase64) - - if err != nil { - return "", err - } - - message := UTF16BytesToString(messageUtf16LeByteArray, binary.LittleEndian) - - return message, nil -} diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index fd967ce86..1481d6ddc 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -5,6 +5,7 @@ package powershell import ( "bufio" "bytes" + "encoding/xml" "errors" "fmt" "io/ioutil" @@ -112,7 +113,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } if p.config.EnvVarFormat == "" { - p.config.EnvVarFormat = `$env:%s="%s"; ` + p.config.EnvVarFormat = `$env:%s=\"%s\"; ` } if p.config.ElevatedEnvVarFormat == "" { @@ -120,11 +121,11 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } if p.config.ExecuteCommand == "" { - p.config.ExecuteCommand = `if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};{{.Vars}}&'{{.Path}}';exit $LastExitCode` + p.config.ExecuteCommand = `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};{{.Vars}}&'{{.Path}}';exit $LastExitCode }"` } if p.config.ElevatedExecuteCommand == "" { - p.config.ElevatedExecuteCommand = `if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'}; . {{.Vars}}; &'{{.Path}}'; exit $LastExitCode` + p.config.ElevatedExecuteCommand = `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"` } if p.config.Inline != nil && len(p.config.Inline) == 0 { @@ -389,25 +390,8 @@ func (p *Provisioner) createCommandTextNonPrivileged() (command string, err erro return "", fmt.Errorf("Error processing command: %s", err) } - commandText, err := p.generateCommandLineRunner(command) - if err != nil { - return "", fmt.Errorf("Error generating command line runner: %s", 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(command) - if err != nil { - return "", fmt.Errorf("Error encoding command: %s", err) - } - - commandText = "powershell -executionpolicy bypass -encodedCommand " + base64EncodedCommand - - return commandText, nil + // Return the interpolated command + return command, nil } func (p *Provisioner) createCommandTextPrivileged() (command string, err error) { @@ -449,20 +433,26 @@ func (p *Provisioner) createCommandTextPrivileged() (command string, err error) func (p *Provisioner) generateElevatedRunner(command string) (uploadedPath string, err error) { log.Printf("Building elevated command wrapper for: %s", command) - // generate command var buffer bytes.Buffer - base64EncodedCommand, err := powershellEncode(command) + // elevatedTemplate wraps the command in a single quoted XML text + // string so we need to escape characters considered 'special' in XML. + err = xml.EscapeText(&buffer, []byte(command)) if err != nil { - return "", fmt.Errorf("Error encoding command: %s", err) + return "", fmt.Errorf("Error escaping characters special to XML in command %s: %s", command, err) } + escapedCommand := buffer.String() + log.Printf("Command [%s] converted to [%s] for use in XML string", command, escapedCommand) + + buffer.Reset() + // Generate command 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: base64EncodedCommand, + User: p.config.ElevatedUser, + Password: p.config.ElevatedPassword, + TaskDescription: "Packer elevated task", + TaskName: fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()), + XMLEscapedCommand: escapedCommand, }) if err != nil {