1
0
mirror of https://git.savannah.gnu.org/git/guix.git synced 2026-05-28 12:01:49 +02:00

activation: Fix TOCTTOU in mkdir-p/perms.

Fixes <https://issues.guix.gnu.org/47584>.

I removed the 'Based upon mkdir-p from (guix build utils)'
comment because it's quite a bit different now.

* gnu/build/activation.scm (verify-not-symbolic): Delete.
(mkdir-p/perms): Rewrite in terms of 'openat'.

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
Change-Id: Id2f5bcbb903283afd45f6109190210d02eb383c7
This commit is contained in:
Maxime Devos
2022-10-28 18:04:09 +02:00
committed by Ludovic Courtès
parent 571c605f17
commit c1283e2039
+57 -33
View File
@@ -5,7 +5,7 @@
;;; Copyright © 2015, 2018 Mark H Weaver <mhw@netris.org> ;;; Copyright © 2015, 2018 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2018 Arun Isaac <arunisaac@systemreboot.net> ;;; Copyright © 2018 Arun Isaac <arunisaac@systemreboot.net>
;;; Copyright © 2018, 2019 Ricardo Wurmus <rekado@elephly.net> ;;; Copyright © 2018, 2019 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be> ;;; Copyright © 2021, 2022 Maxime Devos <maximedevos@telenet.be>
;;; Copyright © 2020 Christine Lemmer-Webber <cwebber@dustycloud.org> ;;; Copyright © 2020 Christine Lemmer-Webber <cwebber@dustycloud.org>
;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re> ;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re>
;;; Copyright © 2022 Tobias Geerinckx-Rice <me@tobias.gr> ;;; Copyright © 2022 Tobias Geerinckx-Rice <me@tobias.gr>
@@ -66,46 +66,70 @@
(define (dot-or-dot-dot? file) (define (dot-or-dot-dot? file)
(member file '("." ".."))) (member file '("." "..")))
;; Based upon mkdir-p from (guix build utils) (define (mkdir-p/perms directory owner bits)
(define (verify-not-symbolic dir) "Create directory DIRECTORY and all its ancestors.
"Verify DIR or its ancestors aren't symbolic links."
Additionally, verify no component of DIRECTORY is a symbolic link,
without TOCTTOU races. However, if OWNER differs from the the current
(process) uid/gid, there is a small window in which DIRECTORY is set to the
current (process) uid/gid instead of OWNER. This is not expected to be
a problem in practice.
The permission bits and owner of DIRECTORY are set to BITS and OWNER.
Anything above DIRECTORY that already exists keeps
its old owner and bits. For components that do not exist yet, the owner
and bits are set according to the default behaviour of 'mkdir'."
(define absolute? (define absolute?
(string-prefix? "/" dir)) (string-prefix? "/" directory))
(define not-slash (define not-slash
(char-set-complement (char-set #\/))) (char-set-complement (char-set #\/)))
(define (verify-component file) ;; By combining O_NOFOLLOW and O_DIRECTORY, this procedure automatically
(unless (eq? 'directory (stat:type (lstat file))) ;; verifies that no components are symlinks.
(error "file name component is not a directory" dir))) (define open-flags (logior O_CLOEXEC ; don't pass the port on to subprocesses
O_NOFOLLOW ; don't follow symlinks
O_DIRECTORY)) ; reject anything not a directory
(let loop ((components (string-tokenize dir not-slash)) (let loop ((components (string-tokenize directory not-slash))
(root (if absolute? (root (open (if absolute? "/" ".") open-flags)))
""
".")))
(match components (match components
((head tail ...) ((head tail ...)
(let ((file (string-append root "/" head))) (let retry ()
(catch 'system-error ;; In the usual case, we expect HEAD to already exist.
(lambda () (match (catch 'system-error
(verify-component file) (lambda ()
(loop tail file)) (openat root head open-flags))
(lambda args (lambda args
(if (= ENOENT (system-error-errno args)) (if (= ENOENT (system-error-errno args))
#t #false
(apply throw args)))))) (begin
(() #t)))) (close-port root)
(apply throw args)))))
;; TODO: the TOCTTOU race can be addressed once guile has bindings ((? port? new-root)
;; for fstatat, openat and friends. (close root)
(define (mkdir-p/perms directory owner bits) (loop tail new-root))
"Create the directory DIRECTORY and all its ancestors. (#false
Verify no component of DIRECTORY is a symbolic link. ;; If not, create it.
Warning: this is currently suspect to a TOCTTOU race!" (catch 'system-error
(verify-not-symbolic directory) (lambda _
(mkdir-p directory) (mkdirat root head))
(chown directory (passwd:uid owner) (passwd:gid owner)) (lambda args
(chmod directory bits)) ;; Someone else created the directory. Unexpected but fine.
(unless (= EEXIST (system-error-errno args))
(close-port root)
(apply throw args))))
(retry)))))
(()
(catch 'system-error
(lambda ()
(chown root (passwd:uid owner) (passwd:gid owner))
(chmod root bits))
(lambda args
(close-port root)
(apply throw args)))
(close-port root)
(values)))))
(define* (copy-account-skeletons home (define* (copy-account-skeletons home
#:key #:key