activation.scm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
  4. ;;; Copyright © 2013 Andreas Enge <andreas@enge.fr>
  5. ;;; Copyright © 2015, 2018 Mark H Weaver <mhw@netris.org>
  6. ;;; Copyright © 2018 Arun Isaac <arunisaac@systemreboot.net>
  7. ;;; Copyright © 2018, 2019 Ricardo Wurmus <rekado@elephly.net>
  8. ;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
  9. ;;; Copyright © 2020 Christine Lemmer-Webber <cwebber@dustycloud.org>
  10. ;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re>
  11. ;;;
  12. ;;; This file is part of GNU Guix.
  13. ;;;
  14. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  15. ;;; under the terms of the GNU General Public License as published by
  16. ;;; the Free Software Foundation; either version 3 of the License, or (at
  17. ;;; your option) any later version.
  18. ;;;
  19. ;;; GNU Guix is distributed in the hope that it will be useful, but
  20. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  21. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. ;;; GNU General Public License for more details.
  23. ;;;
  24. ;;; You should have received a copy of the GNU General Public License
  25. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  26. (define-module (gnu build activation)
  27. #:use-module (gnu system accounts)
  28. #:use-module (gnu system setuid)
  29. #:use-module (gnu build accounts)
  30. #:use-module (gnu build linux-boot)
  31. #:use-module (guix build utils)
  32. #:use-module ((guix build syscalls) #:select (with-file-lock))
  33. #:use-module (ice-9 ftw)
  34. #:use-module (ice-9 match)
  35. #:use-module (ice-9 vlist)
  36. #:use-module (srfi srfi-1)
  37. #:use-module (srfi srfi-11)
  38. #:use-module (srfi srfi-26)
  39. #:export (activate-users+groups
  40. activate-user-home
  41. activate-etc
  42. activate-setuid-programs
  43. activate-special-files
  44. activate-modprobe
  45. activate-firmware
  46. activate-ptrace-attach
  47. activate-current-system
  48. mkdir-p/perms))
  49. ;;; Commentary:
  50. ;;;
  51. ;;; This module provides "activation" helpers. Activation is the process that
  52. ;;; consists in setting up system-wide files and directories so that an
  53. ;;; 'operating-system' configuration becomes active.
  54. ;;;
  55. ;;; Code:
  56. (define %skeleton-directory
  57. ;; Directory containing skeleton files for new accounts.
  58. ;; Note: keep the trailing '/' so that 'scandir' enters it.
  59. "/etc/skel/")
  60. (define (dot-or-dot-dot? file)
  61. (member file '("." "..")))
  62. ;; Based upon mkdir-p from (guix build utils)
  63. (define (verify-not-symbolic dir)
  64. "Verify DIR or its ancestors aren't symbolic links."
  65. (define absolute?
  66. (string-prefix? "/" dir))
  67. (define not-slash
  68. (char-set-complement (char-set #\/)))
  69. (define (verify-component file)
  70. (unless (eq? 'directory (stat:type (lstat file)))
  71. (error "file name component is not a directory" dir)))
  72. (let loop ((components (string-tokenize dir not-slash))
  73. (root (if absolute?
  74. ""
  75. ".")))
  76. (match components
  77. ((head tail ...)
  78. (let ((file (string-append root "/" head)))
  79. (catch 'system-error
  80. (lambda ()
  81. (verify-component file)
  82. (loop tail file))
  83. (lambda args
  84. (if (= ENOENT (system-error-errno args))
  85. #t
  86. (apply throw args))))))
  87. (() #t))))
  88. ;; TODO: the TOCTTOU race can be addressed once guile has bindings
  89. ;; for fstatat, openat and friends.
  90. (define (mkdir-p/perms directory owner bits)
  91. "Create the directory DIRECTORY and all its ancestors.
  92. Verify no component of DIRECTORY is a symbolic link.
  93. Warning: this is currently suspect to a TOCTTOU race!"
  94. (verify-not-symbolic directory)
  95. (mkdir-p directory)
  96. (chown directory (passwd:uid owner) (passwd:gid owner))
  97. (chmod directory bits))
  98. (define* (copy-account-skeletons home
  99. #:key
  100. (directory %skeleton-directory)
  101. uid gid)
  102. "Copy the account skeletons from DIRECTORY to HOME. When UID is an integer,
  103. make it the owner of all the files created except the home directory; likewise
  104. for GID."
  105. (define (set-owner file)
  106. (when (or uid gid)
  107. (chown file (or uid -1) (or gid -1))))
  108. (let ((files (scandir directory (negate dot-or-dot-dot?)
  109. string<?)))
  110. (mkdir-p home)
  111. (for-each (lambda (file)
  112. (let ((target (string-append home "/" file)))
  113. (copy-recursively (string-append directory "/" file)
  114. target
  115. #:log (%make-void-port "w"))
  116. (for-each set-owner
  117. (find-files target (const #t)
  118. #:directories? #t))
  119. (make-file-writable target)))
  120. files)))
  121. (define* (make-skeletons-writable home
  122. #:optional (directory %skeleton-directory))
  123. "Make sure that the files that have been copied from DIRECTORY to HOME are
  124. owner-writable in HOME."
  125. (let ((files (scandir directory (negate dot-or-dot-dot?)
  126. string<?)))
  127. (for-each (lambda (file)
  128. (let ((target (string-append home "/" file)))
  129. (when (file-exists? target)
  130. (make-file-writable target))))
  131. files)))
  132. (define (duplicates lst)
  133. "Return elements from LST present more than once in LST."
  134. (let loop ((lst lst)
  135. (seen vlist-null)
  136. (result '()))
  137. (match lst
  138. (()
  139. (reverse result))
  140. ((head . tail)
  141. (loop tail
  142. (vhash-cons head #t seen)
  143. (if (vhash-assoc head seen)
  144. (cons head result)
  145. result))))))
  146. (define (activate-users+groups users groups)
  147. "Make sure USERS (a list of user account records) and GROUPS (a list of user
  148. group records) are all available."
  149. (define (make-home-directory user)
  150. (let ((home (user-account-home-directory user))
  151. (pwd (getpwnam (user-account-name user))))
  152. (mkdir-p home)
  153. ;; Always set ownership and permissions for home directories of system
  154. ;; accounts. If a service needs looser permissions on its home
  155. ;; directories, it can always chmod it in an activation snippet.
  156. (chown home (passwd:uid pwd) (passwd:gid pwd))
  157. (chmod home #o700)))
  158. (define system-accounts
  159. (filter (lambda (user)
  160. (and (user-account-system? user)
  161. (user-account-create-home-directory? user)))
  162. users))
  163. ;; Allow home directories to be created under /var/lib.
  164. (mkdir-p "/var/lib")
  165. ;; Take same lock as libc's 'lckpwdf' (but without a timeout) while we read
  166. ;; and write the databases. This ensures there's no race condition with
  167. ;; other tools that might be accessing it at the same time.
  168. (with-file-lock %password-lock-file
  169. (let-values (((groups passwd shadow)
  170. (user+group-databases users groups)))
  171. (write-group groups)
  172. (write-passwd passwd)
  173. (write-shadow shadow)))
  174. ;; Home directories of non-system accounts are created by
  175. ;; 'activate-user-home'.
  176. (for-each make-home-directory system-accounts)
  177. ;; Turn shared home directories, such as /var/empty, into root-owned,
  178. ;; read-only places.
  179. (for-each (lambda (directory)
  180. (chown directory 0 0)
  181. (chmod directory #o555))
  182. (duplicates (map user-account-home-directory system-accounts))))
  183. (define (activate-user-home users)
  184. "Create and populate the home directory of USERS, a list of tuples, unless
  185. they already exist."
  186. (define ensure-user-home
  187. (lambda (user)
  188. (let ((name (user-account-name user))
  189. (home (user-account-home-directory user))
  190. (create-home? (user-account-create-home-directory? user))
  191. (system? (user-account-system? user)))
  192. ;; The home directories of system accounts are created during
  193. ;; activation, not here.
  194. (unless (or (not home) (not create-home?) system?
  195. (directory-exists? home))
  196. (let* ((pw (getpwnam name))
  197. (uid (passwd:uid pw))
  198. (gid (passwd:gid pw)))
  199. (mkdir-p home)
  200. (chmod home #o700)
  201. (copy-account-skeletons home
  202. #:uid uid #:gid gid)
  203. ;; It is important 'chown' be called after
  204. ;; 'copy-account-skeletons'. Otherwise, a malicious user with
  205. ;; good timing could create a symlink in HOME that would be
  206. ;; dereferenced by 'copy-account-skeletons'.
  207. (chown home uid gid))))))
  208. (for-each ensure-user-home users))
  209. (define (activate-etc etc)
  210. "Install ETC, a directory in the store, as the source of static files for
  211. /etc."
  212. ;; /etc is a mixture of static and dynamic settings. Here is where we
  213. ;; initialize it from the static part.
  214. (define (rm-f file)
  215. (false-if-exception (delete-file file)))
  216. (format #t "populating /etc from ~a...~%" etc)
  217. (mkdir-p "/etc")
  218. ;; Create the /etc/ssl -> /run/current-system/profile/etc/ssl symlink. This
  219. ;; symlink, to a target outside of the store, probably doesn't belong in the
  220. ;; static 'etc' store directory. However, if it were to be put there,
  221. ;; beware that if /run/current-system/profile/etc/ssl doesn't exist at the
  222. ;; time of activation (e.g. when installing a fresh system), the call to
  223. ;; 'file-is-directory?' below will fail because it uses 'stat', not 'lstat'.
  224. (rm-f "/etc/ssl")
  225. (symlink "/run/current-system/profile/etc/ssl" "/etc/ssl")
  226. (rm-f "/etc/static")
  227. (symlink etc "/etc/static")
  228. (for-each (lambda (file)
  229. (let ((target (string-append "/etc/" file))
  230. (source (string-append "/etc/static/" file)))
  231. (rm-f target)
  232. ;; Things such as /etc/sudoers must be regular files, not
  233. ;; symlinks; furthermore, they could be modified behind our
  234. ;; back---e.g., with 'visudo'. Thus, make a copy instead of
  235. ;; symlinking them.
  236. (if (file-is-directory? source)
  237. (symlink source target)
  238. (copy-file source target))
  239. ;; XXX: Dirty hack to meet sudo's expectations.
  240. (when (string=? (basename target) "sudoers")
  241. (chmod target #o440))))
  242. (scandir etc (negate dot-or-dot-dot?)
  243. ;; The default is 'string-locale<?', but we don't have
  244. ;; it when run from the initrd's statically-linked
  245. ;; Guile.
  246. string<?)))
  247. (define %setuid-directory
  248. ;; Place where setuid programs are stored.
  249. "/run/setuid-programs")
  250. (define (activate-setuid-programs programs)
  251. "Turn PROGRAMS, a list of file setuid-programs record, into setuid programs
  252. stored under %SETUID-DIRECTORY."
  253. (define (make-setuid-program program setuid? setgid? uid gid)
  254. (let ((target (string-append %setuid-directory
  255. "/" (basename program)))
  256. (mode (+ #o0555 ; base permissions
  257. (if setuid? #o4000 0) ; setuid bit
  258. (if setgid? #o2000 0)))) ; setgid bit
  259. (copy-file program target)
  260. (chown target uid gid)
  261. (chmod target mode)))
  262. (format #t "setting up setuid programs in '~a'...~%"
  263. %setuid-directory)
  264. (if (file-exists? %setuid-directory)
  265. (for-each (compose delete-file
  266. (cut string-append %setuid-directory "/" <>))
  267. (scandir %setuid-directory
  268. (lambda (file)
  269. (not (member file '("." ".."))))
  270. string<?))
  271. (mkdir-p %setuid-directory))
  272. (for-each (lambda (program)
  273. (catch 'system-error
  274. (lambda ()
  275. (let* ((program-name (setuid-program-program program))
  276. (setuid? (setuid-program-setuid? program))
  277. (setgid? (setuid-program-setgid? program))
  278. (user (setuid-program-user program))
  279. (group (setuid-program-group program))
  280. (uid (match user
  281. ((? string?) (passwd:uid (getpwnam user)))
  282. ((? integer?) user)))
  283. (gid (match group
  284. ((? string?) (group:gid (getgrnam group)))
  285. ((? integer?) group))))
  286. (make-setuid-program program-name setuid? setgid? uid gid)))
  287. (lambda args
  288. ;; If we fail to create a setuid program, better keep going
  289. ;; so that we don't leave %SETUID-DIRECTORY empty or
  290. ;; half-populated. This can happen if PROGRAMS contains
  291. ;; incorrect file names: <https://bugs.gnu.org/38800>.
  292. (format (current-error-port)
  293. "warning: failed to make ~s setuid/setgid: ~a~%"
  294. (setuid-program-program program)
  295. (strerror (system-error-errno args))))))
  296. programs))
  297. (define (activate-special-files special-files)
  298. "Install the files listed in SPECIAL-FILES. Each element of SPECIAL-FILES
  299. is a pair where the first element is the name of the special file and the
  300. second element is the name it should appear at, such as:
  301. ((\"/bin/sh\" \"/gnu/store/…-bash/bin/sh\")
  302. (\"/usr/bin/env\" \"/gnu/store/…-coreutils/bin/env\"))
  303. "
  304. (define install-special-file
  305. (match-lambda
  306. ((target file)
  307. (let ((pivot (string-append target ".new")))
  308. (mkdir-p (dirname target))
  309. (symlink file pivot)
  310. (rename-file pivot target)))))
  311. (for-each install-special-file special-files))
  312. (define (activate-modprobe modprobe)
  313. "Tell the kernel to use MODPROBE to load modules."
  314. ;; If the kernel was built without loadable module support, this file is
  315. ;; unavailable, so check for its existence first.
  316. (when (file-exists? "/proc/sys/kernel/modprobe")
  317. (call-with-output-file "/proc/sys/kernel/modprobe"
  318. (lambda (port)
  319. (display modprobe port)))))
  320. (define (activate-firmware directory)
  321. "Tell the kernel to look for device firmware under DIRECTORY. This
  322. mechanism bypasses udev: it allows Linux to handle firmware loading directly
  323. by itself, without having to resort to a \"user helper\"."
  324. (call-with-output-file "/sys/module/firmware_class/parameters/path"
  325. (lambda (port)
  326. (display directory port))))
  327. (define (activate-ptrace-attach)
  328. "Allow users to PTRACE_ATTACH their own processes.
  329. This works around a regression introduced in the default \"security\" policy
  330. found in Linux 3.4 onward that prevents users from attaching to their own
  331. processes--see Yama.txt in the Linux source tree for the rationale. This
  332. sounds like an unacceptable restriction for little or no security
  333. improvement."
  334. (let ((file "/proc/sys/kernel/yama/ptrace_scope"))
  335. (when (file-exists? file)
  336. (call-with-output-file file
  337. (lambda (port)
  338. (display 0 port))))))
  339. (define %current-system
  340. ;; The system that is current (a symlink.) This is not necessarily the same
  341. ;; as the system we booted (aka. /run/booted-system) because we can re-build
  342. ;; a new system configuration and activate it, without rebooting.
  343. "/run/current-system")
  344. (define (boot-time-system)
  345. "Return the '--system' argument passed on the kernel command line."
  346. (find-long-option "--system" (if (string-contains %host-type "linux-gnu")
  347. (linux-command-line)
  348. (command-line))))
  349. (define* (activate-current-system
  350. #:optional (system (or (getenv "GUIX_NEW_SYSTEM")
  351. (boot-time-system))))
  352. "Atomically make SYSTEM the current system."
  353. ;; The 'GUIX_NEW_SYSTEM' environment variable is used as a way for 'guix
  354. ;; system reconfigure' to pass the file name of the new system.
  355. (format #t "making '~a' the current system...~%" system)
  356. ;; Atomically make SYSTEM current.
  357. (let ((new (string-append %current-system ".new")))
  358. (symlink system new)
  359. (rename-file new %current-system)))
  360. ;;; activation.scm ends here