copy_windows.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package dockerfile // import "github.com/docker/docker/builder/dockerfile"
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "strings"
  7. winio "github.com/Microsoft/go-winio"
  8. "github.com/docker/docker/pkg/idtools"
  9. "github.com/docker/docker/pkg/reexec"
  10. "github.com/docker/docker/pkg/system"
  11. "github.com/pkg/errors"
  12. "golang.org/x/sys/windows"
  13. )
  14. var pathDenyList = map[string]bool{
  15. `c:\`: true,
  16. `c:\windows`: true,
  17. }
  18. func init() {
  19. reexec.Register("windows-fix-permissions", fixPermissionsReexec)
  20. }
  21. func fixPermissions(source, destination string, identity idtools.Identity, _ bool) error {
  22. if identity.SID == "" {
  23. return nil
  24. }
  25. cmd := reexec.Command("windows-fix-permissions", source, destination, identity.SID)
  26. output, err := cmd.CombinedOutput()
  27. return errors.Wrapf(err, "failed to exec windows-fix-permissions: %s", output)
  28. }
  29. func fixPermissionsReexec() {
  30. err := fixPermissionsWindows(os.Args[1], os.Args[2], os.Args[3])
  31. if err != nil {
  32. fmt.Fprint(os.Stderr, err)
  33. os.Exit(1)
  34. }
  35. }
  36. func fixPermissionsWindows(source, destination, SID string) error {
  37. privileges := []string{winio.SeRestorePrivilege, idtools.SeTakeOwnershipPrivilege}
  38. err := winio.EnableProcessPrivileges(privileges)
  39. if err != nil {
  40. return err
  41. }
  42. defer winio.DisableProcessPrivileges(privileges)
  43. sid, err := windows.StringToSid(SID)
  44. if err != nil {
  45. return err
  46. }
  47. // Owners on *nix have read/write/delete/read control and write DAC.
  48. // Add an ACE that grants this to the user/group specified with the
  49. // chown option. Currently Windows is not honoring the owner change,
  50. // however, they are aware of this and it should be fixed at some
  51. // point.
  52. sddlString := system.SddlAdministratorsLocalSystem
  53. sddlString += "(A;OICI;GRGWGXRCWDSD;;;" + SID + ")"
  54. securityDescriptor, err := windows.SecurityDescriptorFromString(sddlString)
  55. if err != nil {
  56. return err
  57. }
  58. dacl, _, err := securityDescriptor.DACL()
  59. if err != nil {
  60. return err
  61. }
  62. return windows.SetNamedSecurityInfo(destination, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION, sid, nil, dacl, nil)
  63. }
  64. // normalizeDest normalises the destination of a COPY/ADD command in a
  65. // platform semantically consistent way.
  66. func normalizeDest(workingDir, requested string) (string, error) {
  67. dest := filepath.FromSlash(requested)
  68. endsInSlash := strings.HasSuffix(dest, string(os.PathSeparator))
  69. // We are guaranteed that the working directory is already consistent,
  70. // However, Windows also has, for now, the limitation that ADD/COPY can
  71. // only be done to the system drive, not any drives that might be present
  72. // as a result of a bind mount.
  73. //
  74. // So... if the path requested is Linux-style absolute (/foo or \\foo),
  75. // we assume it is the system drive. If it is a Windows-style absolute
  76. // (DRIVE:\\foo), error if DRIVE is not C. And finally, ensure we
  77. // strip any configured working directories drive letter so that it
  78. // can be subsequently legitimately converted to a Windows volume-style
  79. // pathname.
  80. // Not a typo - filepath.IsAbs, not system.IsAbs on this next check as
  81. // we only want to validate where the DriveColon part has been supplied.
  82. if filepath.IsAbs(dest) {
  83. if strings.ToUpper(string(dest[0])) != "C" {
  84. return "", fmt.Errorf("Windows does not support destinations not on the system drive (C:)")
  85. }
  86. dest = dest[2:] // Strip the drive letter
  87. }
  88. // Cannot handle relative where WorkingDir is not the system drive.
  89. if len(workingDir) > 0 {
  90. if ((len(workingDir) > 1) && !system.IsAbs(workingDir[2:])) || (len(workingDir) == 1) {
  91. return "", fmt.Errorf("Current WorkingDir %s is not platform consistent", workingDir)
  92. }
  93. if !system.IsAbs(dest) {
  94. if string(workingDir[0]) != "C" {
  95. return "", fmt.Errorf("Windows does not support relative paths when WORKDIR is not the system drive")
  96. }
  97. dest = filepath.Join(string(os.PathSeparator), workingDir[2:], dest)
  98. // Make sure we preserve any trailing slash
  99. if endsInSlash {
  100. dest += string(os.PathSeparator)
  101. }
  102. }
  103. }
  104. return dest, nil
  105. }
  106. func containsWildcards(name string) bool {
  107. for i := 0; i < len(name); i++ {
  108. ch := name[i]
  109. if ch == '*' || ch == '?' || ch == '[' {
  110. return true
  111. }
  112. }
  113. return false
  114. }
  115. func validateCopySourcePath(imageSource *imageMount, origPath string) error {
  116. if imageSource == nil {
  117. return nil
  118. }
  119. origPath = filepath.FromSlash(origPath)
  120. p := strings.ToLower(filepath.Clean(origPath))
  121. if !filepath.IsAbs(p) {
  122. if filepath.VolumeName(p) != "" {
  123. if p[len(p)-2:] == ":." { // case where clean returns weird c:. paths
  124. p = p[:len(p)-1]
  125. }
  126. p += `\`
  127. } else {
  128. p = filepath.Join(`c:\`, p)
  129. }
  130. }
  131. if _, ok := pathDenyList[p]; ok {
  132. return errors.New(`copy from c:\ or c:\windows is not allowed on windows`)
  133. }
  134. return nil
  135. }