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:
parent
875cccfb44
commit
2a060adbf8
|
@ -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}} > %SYSTEMROOT%\Temp\{{.TaskName}}.out 2>&1</Arguments>
|
||||
<Arguments>/c {{.XMLEscapedCommand}} > %SYSTEMROOT%\Temp\{{.TaskName}}.out 2>&1</Arguments>
|
||||
</Exec>
|
||||
</Actions>
|
||||
</Task>
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue