activation.scm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
  4. ;;;
  5. ;;; This file is part of GNU Guix.
  6. ;;;
  7. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  8. ;;; under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 3 of the License, or (at
  10. ;;; your option) any later version.
  11. ;;;
  12. ;;; GNU Guix is distributed in the hope that it will be useful, but
  13. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  19. (define-module (gnu build activation)
  20. #:use-module (gnu build linux-boot)
  21. #:use-module (guix build utils)
  22. #:use-module (ice-9 ftw)
  23. #:use-module (ice-9 match)
  24. #:use-module (srfi srfi-1)
  25. #:use-module (srfi srfi-26)
  26. #:export (activate-users+groups
  27. activate-user-home
  28. activate-etc
  29. activate-setuid-programs
  30. activate-special-files
  31. activate-modprobe
  32. activate-firmware
  33. activate-ptrace-attach
  34. activate-current-system))
  35. ;;; Commentary:
  36. ;;;
  37. ;;; This module provides "activation" helpers. Activation is the process that
  38. ;;; consists in setting up system-wide files and directories so that an
  39. ;;; 'operating-system' configuration becomes active.
  40. ;;;
  41. ;;; Code:
  42. (define (enumerate thunk)
  43. "Return the list of values returned by THUNK until it returned #f."
  44. (let loop ((entry (thunk))
  45. (result '()))
  46. (if (not entry)
  47. (reverse result)
  48. (loop (thunk) (cons entry result)))))
  49. (define (current-users)
  50. "Return the passwd entries for all the currently defined user accounts."
  51. (setpw)
  52. (enumerate getpwent))
  53. (define (current-groups)
  54. "Return the group entries for all the currently defined user groups."
  55. (setgr)
  56. (enumerate getgrent))
  57. (define* (add-group name #:key gid password system?
  58. (log-port (current-error-port)))
  59. "Add NAME as a user group, with the given numeric GID if specified."
  60. ;; Use 'groupadd' from the Shadow package.
  61. (format log-port "adding group '~a'...~%" name)
  62. (let ((args `(,@(if gid `("-g" ,(number->string gid)) '())
  63. ,@(if password `("-p" ,password) '())
  64. ,@(if system? `("--system") '())
  65. ,name)))
  66. (zero? (apply system* "groupadd" args))))
  67. (define %skeleton-directory
  68. ;; Directory containing skeleton files for new accounts.
  69. ;; Note: keep the trailing '/' so that 'scandir' enters it.
  70. "/etc/skel/")
  71. (define (dot-or-dot-dot? file)
  72. (member file '("." "..")))
  73. (define* (copy-account-skeletons home
  74. #:key
  75. (directory %skeleton-directory)
  76. uid gid)
  77. "Copy the account skeletons from DIRECTORY to HOME. When UID is an integer,
  78. make it the owner of all the files created; likewise for GID."
  79. (define (set-owner file)
  80. (when (or uid gid)
  81. (chown file (or uid -1) (or gid -1))))
  82. (let ((files (scandir directory (negate dot-or-dot-dot?)
  83. string<?)))
  84. (mkdir-p home)
  85. (set-owner home)
  86. (for-each (lambda (file)
  87. (let ((target (string-append home "/" file)))
  88. (copy-recursively (string-append directory "/" file)
  89. target
  90. #:log (%make-void-port "w"))
  91. (for-each set-owner
  92. (find-files target (const #t)
  93. #:directories? #t))
  94. (make-file-writable target)))
  95. files)))
  96. (define* (make-skeletons-writable home
  97. #:optional (directory %skeleton-directory))
  98. "Make sure that the files that have been copied from DIRECTORY to HOME are
  99. owner-writable in HOME."
  100. (let ((files (scandir directory (negate dot-or-dot-dot?)
  101. string<?)))
  102. (for-each (lambda (file)
  103. (let ((target (string-append home "/" file)))
  104. (when (file-exists? target)
  105. (make-file-writable target))))
  106. files)))
  107. (define* (add-user name group
  108. #:key uid comment home create-home?
  109. shell password system?
  110. (supplementary-groups '())
  111. (log-port (current-error-port)))
  112. "Create an account for user NAME part of GROUP, with the specified
  113. properties. Return #t on success."
  114. (format log-port "adding user '~a'...~%" name)
  115. (if (and uid (zero? uid))
  116. ;; 'useradd' fails with "Cannot determine your user name" if the root
  117. ;; account doesn't exist. Thus, for bootstrapping purposes, create that
  118. ;; one manually.
  119. (let ((home (or home "/root")))
  120. (call-with-output-file "/etc/shadow"
  121. (cut format <> "~a::::::::~%" name))
  122. (call-with-output-file "/etc/passwd"
  123. (cut format <> "~a:x:~a:~a:~a:~a:~a~%"
  124. name "0" "0" comment home shell))
  125. (chmod "/etc/shadow" #o600)
  126. (copy-account-skeletons home)
  127. (chmod home #o700)
  128. #t)
  129. ;; Use 'useradd' from the Shadow package.
  130. (let ((args `(,@(if uid `("-u" ,(number->string uid)) '())
  131. "-g" ,(if (number? group) (number->string group) group)
  132. ,@(if (pair? supplementary-groups)
  133. `("-G" ,(string-join supplementary-groups ","))
  134. '())
  135. ,@(if comment `("-c" ,comment) '())
  136. ,@(if (and home create-home?)
  137. (if (file-exists? home)
  138. `("-d" ,home) ; avoid warning from 'useradd'
  139. `("-d" ,home "--create-home"))
  140. '())
  141. ,@(if shell `("-s" ,shell) '())
  142. ,@(if password `("-p" ,password) '())
  143. ,@(if system? '("--system") '())
  144. ,name)))
  145. (and (zero? (apply system* "useradd" args))
  146. (begin
  147. ;; Since /etc/skel is a link to a directory in the store where
  148. ;; all files have the writable bit cleared, and since 'useradd'
  149. ;; preserves permissions when it copies them, explicitly make
  150. ;; them writable.
  151. (make-skeletons-writable home)
  152. #t)))))
  153. (define* (modify-user name group
  154. #:key uid comment home create-home?
  155. shell password system?
  156. (supplementary-groups '())
  157. (log-port (current-error-port)))
  158. "Modify user account NAME to have all the given settings."
  159. ;; Use 'usermod' from the Shadow package.
  160. (let ((args `(,@(if uid `("-u" ,(number->string uid)) '())
  161. "-g" ,(if (number? group) (number->string group) group)
  162. ,@(if (pair? supplementary-groups)
  163. `("-G" ,(string-join supplementary-groups ","))
  164. '())
  165. ,@(if comment `("-c" ,comment) '())
  166. ;; Don't use '--move-home', so ignore HOME.
  167. ,@(if shell `("-s" ,shell) '())
  168. ,name)))
  169. (zero? (apply system* "usermod" args))))
  170. (define* (delete-user name #:key (log-port (current-error-port)))
  171. "Remove user account NAME. Return #t on success. This may fail if NAME is
  172. logged in."
  173. (format log-port "deleting user '~a'...~%" name)
  174. (zero? (system* "userdel" name)))
  175. (define* (delete-group name #:key (log-port (current-error-port)))
  176. "Remove group NAME. Return #t on success."
  177. (format log-port "deleting group '~a'...~%" name)
  178. (zero? (system* "groupdel" name)))
  179. (define* (ensure-user name group
  180. #:key uid comment home create-home?
  181. shell password system?
  182. (supplementary-groups '())
  183. (log-port (current-error-port))
  184. #:rest rest)
  185. "Make sure user NAME exists and has the relevant settings."
  186. (if (false-if-exception (getpwnam name))
  187. (apply modify-user name group rest)
  188. (apply add-user name group rest)))
  189. (define (activate-users+groups users groups)
  190. "Make sure the accounts listed in USERS and the user groups listed in GROUPS
  191. are all available.
  192. Each item in USERS is a list of all the characteristics of a user account;
  193. each item in GROUPS is a tuple with the group name, group password or #f, and
  194. numeric gid or #f."
  195. (define (touch file)
  196. (close-port (open-file file "a0b")))
  197. (define activate-user
  198. (match-lambda
  199. ((name uid group supplementary-groups comment home create-home?
  200. shell password system?)
  201. (let ((profile-dir (string-append "/var/guix/profiles/per-user/"
  202. name)))
  203. (ensure-user name group
  204. #:uid uid
  205. #:system? system?
  206. #:supplementary-groups supplementary-groups
  207. #:comment comment
  208. #:home home
  209. ;; Home directories of non-system accounts are created by
  210. ;; 'activate-user-home'.
  211. #:create-home? (and create-home? system?)
  212. #:shell shell
  213. #:password password)
  214. (unless system?
  215. ;; Create the profile directory for the new account.
  216. (let ((pw (getpwnam name)))
  217. (mkdir-p profile-dir)
  218. (chown profile-dir (passwd:uid pw) (passwd:gid pw))))))))
  219. ;; 'groupadd' aborts if the file doesn't already exist.
  220. (touch "/etc/group")
  221. ;; Allow home directories to be created under /var/lib.
  222. (mkdir-p "/var/lib")
  223. ;; Create the root account so we can use 'useradd' and 'groupadd'.
  224. (activate-user (find (match-lambda
  225. ((name (? zero?) _ ...) #t)
  226. (_ #f))
  227. users))
  228. ;; Then create the groups.
  229. (for-each (match-lambda
  230. ((name password gid system?)
  231. (unless (false-if-exception (getgrnam name))
  232. (add-group name
  233. #:gid gid #:password password
  234. #:system? system?))))
  235. groups)
  236. ;; Create the other user accounts.
  237. (for-each activate-user users)
  238. ;; Finally, delete extra user accounts and groups.
  239. (for-each delete-user
  240. (lset-difference string=?
  241. (map passwd:name (current-users))
  242. (match users
  243. (((names . _) ...)
  244. names))))
  245. (for-each delete-group
  246. (lset-difference string=?
  247. (map group:name (current-groups))
  248. (match groups
  249. (((names . _) ...)
  250. names)))))
  251. (define (activate-user-home users)
  252. "Create and populate the home directory of USERS, a list of tuples, unless
  253. they already exist."
  254. (define ensure-user-home
  255. (match-lambda
  256. ((name uid group supplementary-groups comment home create-home?
  257. shell password system?)
  258. ;; The home directories of system accounts are created during
  259. ;; activation, not here.
  260. (unless (or (not home) (not create-home?) system?
  261. (directory-exists? home))
  262. (let* ((pw (getpwnam name))
  263. (uid (passwd:uid pw))
  264. (gid (passwd:gid pw)))
  265. (mkdir-p home)
  266. (chown home uid gid)
  267. (unless system?
  268. (copy-account-skeletons home
  269. #:uid uid #:gid gid)))))))
  270. (for-each ensure-user-home users))
  271. (define (activate-etc etc)
  272. "Install ETC, a directory in the store, as the source of static files for
  273. /etc."
  274. ;; /etc is a mixture of static and dynamic settings. Here is where we
  275. ;; initialize it from the static part.
  276. (define (rm-f file)
  277. (false-if-exception (delete-file file)))
  278. (format #t "populating /etc from ~a...~%" etc)
  279. (mkdir-p "/etc")
  280. ;; Create the /etc/ssl -> /run/current-system/profile/etc/ssl symlink. This
  281. ;; symlink, to a target outside of the store, probably doesn't belong in the
  282. ;; static 'etc' store directory. However, if it were to be put there,
  283. ;; beware that if /run/current-system/profile/etc/ssl doesn't exist at the
  284. ;; time of activation (e.g. when installing a fresh system), the call to
  285. ;; 'file-is-directory?' below will fail because it uses 'stat', not 'lstat'.
  286. (rm-f "/etc/ssl")
  287. (symlink "/run/current-system/profile/etc/ssl" "/etc/ssl")
  288. (rm-f "/etc/static")
  289. (symlink etc "/etc/static")
  290. (for-each (lambda (file)
  291. (let ((target (string-append "/etc/" file))
  292. (source (string-append "/etc/static/" file)))
  293. (rm-f target)
  294. ;; Things such as /etc/sudoers must be regular files, not
  295. ;; symlinks; furthermore, they could be modified behind our
  296. ;; back---e.g., with 'visudo'. Thus, make a copy instead of
  297. ;; symlinking them.
  298. (if (file-is-directory? source)
  299. (symlink source target)
  300. (copy-file source target))
  301. ;; XXX: Dirty hack to meet sudo's expectations.
  302. (when (string=? (basename target) "sudoers")
  303. (chmod target #o440))))
  304. (scandir etc (negate dot-or-dot-dot?)
  305. ;; The default is 'string-locale<?', but we don't have
  306. ;; it when run from the initrd's statically-linked
  307. ;; Guile.
  308. string<?)))
  309. (define %setuid-directory
  310. ;; Place where setuid programs are stored.
  311. "/run/setuid-programs")
  312. (define (activate-setuid-programs programs)
  313. "Turn PROGRAMS, a list of file names, into setuid programs stored under
  314. %SETUID-DIRECTORY."
  315. (define (make-setuid-program prog)
  316. (let ((target (string-append %setuid-directory
  317. "/" (basename prog))))
  318. (copy-file prog target)
  319. (chown target 0 0)
  320. (chmod target #o6555)))
  321. (format #t "setting up setuid programs in '~a'...~%"
  322. %setuid-directory)
  323. (if (file-exists? %setuid-directory)
  324. (for-each (compose delete-file
  325. (cut string-append %setuid-directory "/" <>))
  326. (scandir %setuid-directory
  327. (lambda (file)
  328. (not (member file '("." ".."))))
  329. string<?))
  330. (mkdir-p %setuid-directory))
  331. (for-each make-setuid-program programs))
  332. (define (activate-special-files special-files)
  333. "Install the files listed in SPECIAL-FILES. Each element of SPECIAL-FILES
  334. is a pair where the first element is the name of the special file and the
  335. second element is the name it should appear at, such as:
  336. ((\"/bin/sh\" \"/gnu/store/…-bash/bin/sh\")
  337. (\"/usr/bin/env\" \"/gnu/store/…-coreutils/bin/env\"))
  338. "
  339. (define install-special-file
  340. (match-lambda
  341. ((target file)
  342. (let ((pivot (string-append target ".new")))
  343. (mkdir-p (dirname target))
  344. (symlink file pivot)
  345. (rename-file pivot target)))))
  346. (for-each install-special-file special-files))
  347. (define (activate-modprobe modprobe)
  348. "Tell the kernel to use MODPROBE to load modules."
  349. (call-with-output-file "/proc/sys/kernel/modprobe"
  350. (lambda (port)
  351. (display modprobe port))))
  352. (define (activate-firmware directory)
  353. "Tell the kernel to look for device firmware under DIRECTORY. This
  354. mechanism bypasses udev: it allows Linux to handle firmware loading directly
  355. by itself, without having to resort to a \"user helper\"."
  356. (call-with-output-file "/sys/module/firmware_class/parameters/path"
  357. (lambda (port)
  358. (display directory port))))
  359. (define (activate-ptrace-attach)
  360. "Allow users to PTRACE_ATTACH their own processes.
  361. This works around a regression introduced in the default \"security\" policy
  362. found in Linux 3.4 onward that prevents users from attaching to their own
  363. processes--see Yama.txt in the Linux source tree for the rationale. This
  364. sounds like an unacceptable restriction for little or no security
  365. improvement."
  366. (let ((file "/proc/sys/kernel/yama/ptrace_scope"))
  367. (when (file-exists? file)
  368. (call-with-output-file file
  369. (lambda (port)
  370. (display 0 port))))))
  371. (define %current-system
  372. ;; The system that is current (a symlink.) This is not necessarily the same
  373. ;; as the system we booted (aka. /run/booted-system) because we can re-build
  374. ;; a new system configuration and activate it, without rebooting.
  375. "/run/current-system")
  376. (define (boot-time-system)
  377. "Return the '--system' argument passed on the kernel command line."
  378. (find-long-option "--system" (linux-command-line)))
  379. (define* (activate-current-system
  380. #:optional (system (or (getenv "GUIX_NEW_SYSTEM")
  381. (boot-time-system))))
  382. "Atomically make SYSTEM the current system."
  383. ;; The 'GUIX_NEW_SYSTEM' environment variable is used as a way for 'guix
  384. ;; system reconfigure' to pass the file name of the new system.
  385. (format #t "making '~a' the current system...~%" system)
  386. ;; Atomically make SYSTEM current.
  387. (let ((new (string-append %current-system ".new")))
  388. (symlink system new)
  389. (rename-file new %current-system)))
  390. ;;; activation.scm ends here