Don't use -EncodedCommand with PS as progress stream always leaks to stderr

* Setting $ProgressPreference to SilentlyContinue makes no difference
  when -EncodedCommand is used - any output to the progress stream
  still appears on stderr.
* Delete file containing encode/decode functions since we no longer
  need them.
* Fixes leak of output on progress streams for both normal and elevated
  commands.
* Since we no longer base64 encode, ensure any characters special to XML
  are correctly escaped in the elevated command. This ensures correct
  parsing once the command is wrapped within the elevatedTemplates XML
  based Task Scheduler definition.

Fixes #4322
This commit is contained in:
DanHam 2017-01-27 01:32:33 +00:00
parent 875cccfb44
commit 2a060adbf8
3 changed files with 26 additions and 90 deletions

View File

@ -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 = @'
<Actions Context="Author">
<Exec>
<Command>cmd</Command>
<Arguments>/c powershell.exe -EncodedCommand {{.EncodedCommand}} &gt; %SYSTEMROOT%\Temp\{{.TaskName}}.out 2&gt;&amp;1</Arguments>
<Arguments>/c {{.XMLEscapedCommand}} &gt; %SYSTEMROOT%\Temp\{{.TaskName}}.out 2&gt;&amp;1</Arguments>
</Exec>
</Actions>
</Task>

View File

@ -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
}

View File

@ -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 {