]> ruderich.org/simon Gitweb - safcm/safcm.git/commitdiff
fixperms: don't follow symlinks
authorSimon Ruderich <simon@ruderich.org>
Sun, 4 Apr 2021 06:25:57 +0000 (08:25 +0200)
committerSimon Ruderich <simon@ruderich.org>
Sun, 4 Apr 2021 06:25:57 +0000 (08:25 +0200)
This prevents a possible TOCTOU when chmodding files.

cmd/safcm/fixperms.go

index 6770934f14d176f7ab15f413ea1d9ce8f0accbd3..47e4fb96484db645402ad9cbdb60676e04a041bc 100644 (file)
@@ -23,6 +23,7 @@ import (
        "log"
        "os"
        "path/filepath"
+       "syscall"
 
        "ruderich.org/simon/safcm/cmd/safcm/config"
 )
@@ -78,7 +79,7 @@ func fixpermsWalkDirFunc(path string, d fs.DirEntry, err error) error {
                        // This is safe because perm does not include
                        // setuid/setgid/sticky which use different values in
                        // FileMode.
-                       err := os.Chmod(path, fs.FileMode(perm))
+                       err := chmodNoFollow(path, fs.FileMode(perm))
                        if err != nil {
                                return err
                        }
@@ -87,7 +88,7 @@ func fixpermsWalkDirFunc(path string, d fs.DirEntry, err error) error {
                if perm != 0755 {
                        perm = 0755
                        log.Printf("chmodding %q to %#o", path, perm)
-                       err := os.Chmod(path, fs.FileMode(perm))
+                       err := chmodNoFollow(path, fs.FileMode(perm))
                        if err != nil {
                                return err
                        }
@@ -97,3 +98,20 @@ func fixpermsWalkDirFunc(path string, d fs.DirEntry, err error) error {
 
        return nil
 }
+
+// chmodNoFollow works like os.Chmod but doesn't follow symlinks.
+func chmodNoFollow(path string, mode fs.FileMode) error {
+       x, err := os.OpenFile(path,
+               os.O_RDONLY|syscall.O_NOFOLLOW|syscall.O_NONBLOCK, 0)
+       if err != nil {
+               return err
+       }
+       defer x.Close()
+
+       err = x.Chmod(mode)
+       if err != nil {
+               return err
+       }
+
+       return nil
+}