From 6d40104f116abdf9740c60846320ce6d36f03edd Mon Sep 17 00:00:00 2001 From: John Starks Date: Fri, 13 May 2016 18:50:19 +0000 Subject: [PATCH] Windows: revendor go-winio This fixes a variety of small bugs in layer handling and adds a new API for acquiring privileges for the whole process. Fixes #22404 (but only for new images -- existing images will need to be re-pushed). Signed-off-by: John Starks --- hack/vendor.sh | 2 +- .../github.com/Microsoft/go-winio/.gitignore | 1 + .../Microsoft/go-winio/backuptar/tar.go | 34 +++++--- .../Microsoft/go-winio/privilege.go | 79 ++++++++++++++----- .../github.com/Microsoft/go-winio/reparse.go | 2 +- .../github.com/Microsoft/go-winio/zsyscall.go | 12 ++- 6 files changed, 93 insertions(+), 37 deletions(-) create mode 100644 vendor/src/github.com/Microsoft/go-winio/.gitignore diff --git a/hack/vendor.sh b/hack/vendor.sh index 2c9c286b8b..8d91832bb0 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -8,7 +8,7 @@ source 'hack/.vendor-helpers.sh' # the following lines are in sorted order, FYI clone git github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62 clone git github.com/Microsoft/hcsshim v0.2.2 -clone git github.com/Microsoft/go-winio v0.3.0 +clone git github.com/Microsoft/go-winio v0.3.4 clone git github.com/Sirupsen/logrus v0.9.0 # logrus is a common dependency among multiple deps clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a clone git github.com/go-check/check 03a4d9dcf2f92eae8e90ed42aa2656f63fdd0b14 https://github.com/cpuguy83/check.git diff --git a/vendor/src/github.com/Microsoft/go-winio/.gitignore b/vendor/src/github.com/Microsoft/go-winio/.gitignore new file mode 100644 index 0000000000..b883f1fdc6 --- /dev/null +++ b/vendor/src/github.com/Microsoft/go-winio/.gitignore @@ -0,0 +1 @@ +*.exe diff --git a/vendor/src/github.com/Microsoft/go-winio/backuptar/tar.go b/vendor/src/github.com/Microsoft/go-winio/backuptar/tar.go index 96069b0daa..c454c4c054 100644 --- a/vendor/src/github.com/Microsoft/go-winio/backuptar/tar.go +++ b/vendor/src/github.com/Microsoft/go-winio/backuptar/tar.go @@ -1,6 +1,7 @@ package backuptar import ( + "encoding/base64" "errors" "fmt" "io" @@ -29,9 +30,10 @@ const ( ) const ( - hdrFileAttributes = "fileattr" - hdrSecurityDescriptor = "sd" - hdrMountPoint = "mountpoint" + hdrFileAttributes = "fileattr" + hdrSecurityDescriptor = "sd" + hdrRawSecurityDescriptor = "rawsd" + hdrMountPoint = "mountpoint" ) func writeZeroes(w io.Writer, count int64) error { @@ -108,7 +110,7 @@ func BasicInfoHeader(name string, size int64, fileInfo *winio.FileBasicInfo) *ta // // MSWINDOWS.fileattr: The Win32 file attributes, as a decimal value // -// MSWINDOWS.sd: The Win32 security descriptor, in SDDL (string) format +// MSWINDOWS.rawsd: The Win32 security descriptor, in raw binary format // // MSWINDOWS.mountpoint: If present, this is a mount point and not a symlink, even though the type is '2' (symlink) func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size int64, fileInfo *winio.FileBasicInfo) error { @@ -133,11 +135,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size if err != nil { return err } - sddl, err := winio.SecurityDescriptorToSddl(sd) - if err != nil { - return err - } - hdr.Winheaders[hdrSecurityDescriptor] = sddl + hdr.Winheaders[hdrRawSecurityDescriptor] = base64.StdEncoding.EncodeToString(sd) case winio.BackupReparseData: hdr.Mode |= c_ISLNK @@ -263,16 +261,28 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win // tar file that was not processed, or io.EOF is there are no more. func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (*tar.Header, error) { bw := winio.NewBackupStreamWriter(w) + var sd []byte + var err error + // Maintaining old SDDL-based behavior for backward compatibility. All new tar headers written + // by this library will have raw binary for the security descriptor. if sddl, ok := hdr.Winheaders[hdrSecurityDescriptor]; ok { - sd, err := winio.SddlToSecurityDescriptor(sddl) + sd, err = winio.SddlToSecurityDescriptor(sddl) if err != nil { return nil, err } + } + if sdraw, ok := hdr.Winheaders[hdrRawSecurityDescriptor]; ok { + sd, err = base64.StdEncoding.DecodeString(sdraw) + if err != nil { + return nil, err + } + } + if len(sd) != 0 { bhdr := winio.BackupHeader{ Id: winio.BackupSecurity, Size: int64(len(sd)), } - err = bw.WriteHeader(&bhdr) + err := bw.WriteHeader(&bhdr) if err != nil { return nil, err } @@ -284,7 +294,7 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) ( if hdr.Typeflag == tar.TypeSymlink { _, isMountPoint := hdr.Winheaders[hdrMountPoint] rp := winio.ReparsePoint{ - Target: hdr.Linkname, + Target: filepath.FromSlash(hdr.Linkname), IsMountPoint: isMountPoint, } reparse := winio.EncodeReparsePoint(&rp) diff --git a/vendor/src/github.com/Microsoft/go-winio/privilege.go b/vendor/src/github.com/Microsoft/go-winio/privilege.go index 81f9af7b70..3d59412c76 100644 --- a/vendor/src/github.com/Microsoft/go-winio/privilege.go +++ b/vendor/src/github.com/Microsoft/go-winio/privilege.go @@ -5,14 +5,17 @@ import ( "encoding/binary" "fmt" "runtime" + "sync" "syscall" "unicode/utf16" + + "golang.org/x/sys/windows" ) -//sys adjustTokenPrivileges(token syscall.Handle, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges +//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges //sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf //sys revertToSelf() (err error) = advapi32.RevertToSelf -//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *syscall.Handle) (err error) = advapi32.OpenThreadToken +//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken //sys getCurrentThread() (h syscall.Handle) = GetCurrentThread //sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW //sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW @@ -34,6 +37,12 @@ const ( securityDelegation ) +var ( + privNames = make(map[string]uint64) + privNameMutex sync.Mutex +) + +// PrivilegeError represents an error enabling privileges. type PrivilegeError struct { privileges []uint64 } @@ -56,19 +65,16 @@ func (e *PrivilegeError) Error() string { return s } +// RunWithPrivilege enables a single privilege for a function call. func RunWithPrivilege(name string, fn func() error) error { return RunWithPrivileges([]string{name}, fn) } +// RunWithPrivileges enables privileges for a function call. func RunWithPrivileges(names []string, fn func() error) error { - var privileges []uint64 - for _, name := range names { - p := uint64(0) - err := lookupPrivilegeValue("", name, &p) - if err != nil { - return err - } - privileges = append(privileges, p) + privileges, err := mapPrivileges(names) + if err != nil { + return err } runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -84,7 +90,43 @@ func RunWithPrivileges(names []string, fn func() error) error { return fn() } -func adjustPrivileges(token syscall.Handle, privileges []uint64) error { +func mapPrivileges(names []string) ([]uint64, error) { + var privileges []uint64 + privNameMutex.Lock() + defer privNameMutex.Unlock() + for _, name := range names { + p, ok := privNames[name] + if !ok { + err := lookupPrivilegeValue("", name, &p) + if err != nil { + return nil, err + } + privNames[name] = p + } + privileges = append(privileges, p) + } + return privileges, nil +} + +// EnableProcessPrivileges enables privileges globally for the process. +func EnableProcessPrivileges(names []string) error { + privileges, err := mapPrivileges(names) + if err != nil { + return err + } + + p, _ := windows.GetCurrentProcess() + var token windows.Token + err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token) + if err != nil { + return err + } + + defer token.Close() + return adjustPrivileges(token, privileges) +} + +func adjustPrivileges(token windows.Token, privileges []uint64) error { var b bytes.Buffer binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) for _, p := range privileges { @@ -113,23 +155,22 @@ func getPrivilegeName(luid uint64) string { var displayNameBuffer [256]uint16 displayBufSize := uint32(len(displayNameBuffer)) - var langId uint32 - err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langId) + var langID uint32 + err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID) if err != nil { - return fmt.Sprintf("", utf16.Decode(nameBuffer[:bufSize])) + return fmt.Sprintf("", string(utf16.Decode(nameBuffer[:bufSize]))) } return string(utf16.Decode(displayNameBuffer[:displayBufSize])) } -func newThreadToken() (syscall.Handle, error) { +func newThreadToken() (windows.Token, error) { err := impersonateSelf(securityImpersonation) if err != nil { - panic(err) return 0, err } - var token syscall.Handle + var token windows.Token err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token) if err != nil { rerr := revertToSelf() @@ -141,10 +182,10 @@ func newThreadToken() (syscall.Handle, error) { return token, nil } -func releaseThreadToken(h syscall.Handle) { +func releaseThreadToken(h windows.Token) { err := revertToSelf() if err != nil { panic(err) } - syscall.Close(h) + h.Close() } diff --git a/vendor/src/github.com/Microsoft/go-winio/reparse.go b/vendor/src/github.com/Microsoft/go-winio/reparse.go index 9425711ed4..fc1ee4d3a3 100644 --- a/vendor/src/github.com/Microsoft/go-winio/reparse.go +++ b/vendor/src/github.com/Microsoft/go-winio/reparse.go @@ -80,7 +80,7 @@ func EncodeReparsePoint(rp *ReparsePoint) []byte { var ntTarget string relative := false if strings.HasPrefix(rp.Target, `\\?\`) { - ntTarget = rp.Target + ntTarget = `\??\` + rp.Target[4:] } else if strings.HasPrefix(rp.Target, `\\`) { ntTarget = `\??\UNC\` + rp.Target[2:] } else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' { diff --git a/vendor/src/github.com/Microsoft/go-winio/zsyscall.go b/vendor/src/github.com/Microsoft/go-winio/zsyscall.go index 74b6e97a66..6d047d3690 100644 --- a/vendor/src/github.com/Microsoft/go-winio/zsyscall.go +++ b/vendor/src/github.com/Microsoft/go-winio/zsyscall.go @@ -2,8 +2,12 @@ package winio -import "unsafe" -import "syscall" +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) var _ unsafe.Pointer @@ -300,7 +304,7 @@ func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, si return } -func adjustTokenPrivileges(token syscall.Handle, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { +func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { var _p0 uint32 if releaseAll { _p0 = 1 @@ -343,7 +347,7 @@ func revertToSelf() (err error) { return } -func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *syscall.Handle) (err error) { +func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) { var _p0 uint32 if openAsSelf { _p0 = 1