package dockerfile // import "github.com/docker/docker/builder/dockerfile" import ( "fmt" "os" "path/filepath" "strings" winio "github.com/Microsoft/go-winio" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/system" "github.com/pkg/errors" "golang.org/x/sys/windows" ) var pathDenyList = map[string]bool{ "c:\\": true, "c:\\windows": true, } func init() { reexec.Register("windows-fix-permissions", fixPermissionsReexec) } func fixPermissions(source, destination string, identity idtools.Identity, _ bool) error { if identity.SID == "" { return nil } cmd := reexec.Command("windows-fix-permissions", source, destination, identity.SID) output, err := cmd.CombinedOutput() return errors.Wrapf(err, "failed to exec windows-fix-permissions: %s", output) } func fixPermissionsReexec() { err := fixPermissionsWindows(os.Args[1], os.Args[2], os.Args[3]) if err != nil { fmt.Fprint(os.Stderr, err) os.Exit(1) } } func fixPermissionsWindows(source, destination, SID string) error { privileges := []string{winio.SeRestorePrivilege, idtools.SeTakeOwnershipPrivilege} err := winio.EnableProcessPrivileges(privileges) if err != nil { return err } defer winio.DisableProcessPrivileges(privileges) sid, err := windows.StringToSid(SID) if err != nil { return err } // Owners on *nix have read/write/delete/read control and write DAC. // Add an ACE that grants this to the user/group specified with the // chown option. Currently Windows is not honoring the owner change, // however, they are aware of this and it should be fixed at some // point. sddlString := system.SddlAdministratorsLocalSystem sddlString += "(A;OICI;GRGWGXRCWDSD;;;" + SID + ")" securityDescriptor, err := windows.SecurityDescriptorFromString(sddlString) if err != nil { return err } dacl, _, err := securityDescriptor.DACL() if err != nil { return err } return windows.SetNamedSecurityInfo(destination, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION, sid, nil, dacl, nil) } // normalizeDest normalises the destination of a COPY/ADD command in a // platform semantically consistent way. func normalizeDest(workingDir, requested string) (string, error) { dest := filepath.FromSlash(requested) endsInSlash := strings.HasSuffix(dest, string(os.PathSeparator)) // We are guaranteed that the working directory is already consistent, // However, Windows also has, for now, the limitation that ADD/COPY can // only be done to the system drive, not any drives that might be present // as a result of a bind mount. // // So... if the path requested is Linux-style absolute (/foo or \\foo), // we assume it is the system drive. If it is a Windows-style absolute // (DRIVE:\\foo), error if DRIVE is not C. And finally, ensure we // strip any configured working directories drive letter so that it // can be subsequently legitimately converted to a Windows volume-style // pathname. // Not a typo - filepath.IsAbs, not system.IsAbs on this next check as // we only want to validate where the DriveColon part has been supplied. if filepath.IsAbs(dest) { if strings.ToUpper(string(dest[0])) != "C" { return "", fmt.Errorf("Windows does not support destinations not on the system drive (C:)") } dest = dest[2:] // Strip the drive letter } // Cannot handle relative where WorkingDir is not the system drive. if len(workingDir) > 0 { if ((len(workingDir) > 1) && !system.IsAbs(workingDir[2:])) || (len(workingDir) == 1) { return "", fmt.Errorf("Current WorkingDir %s is not platform consistent", workingDir) } if !system.IsAbs(dest) { if string(workingDir[0]) != "C" { return "", fmt.Errorf("Windows does not support relative paths when WORKDIR is not the system drive") } dest = filepath.Join(string(os.PathSeparator), workingDir[2:], dest) // Make sure we preserve any trailing slash if endsInSlash { dest += string(os.PathSeparator) } } } return dest, nil } func containsWildcards(name string) bool { for i := 0; i < len(name); i++ { ch := name[i] if ch == '*' || ch == '?' || ch == '[' { return true } } return false } func validateCopySourcePath(imageSource *imageMount, origPath string) error { if imageSource == nil { return nil } origPath = filepath.FromSlash(origPath) p := strings.ToLower(filepath.Clean(origPath)) if !filepath.IsAbs(p) { if filepath.VolumeName(p) != "" { if p[len(p)-2:] == ":." { // case where clean returns weird c:. paths p = p[:len(p)-1] } p += `\` } else { p = filepath.Join(`c:\`, p) } } if _, ok := pathDenyList[p]; ok { return errors.New(`copy from c:\ or c:\windows is not allowed on windows`) } return nil }