2014-04-30 09:44:59 -04:00
|
|
|
|
;;; GNU Guix --- Functional package management for GNU
|
2021-02-14 06:57:32 -05:00
|
|
|
|
;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
|
|
|
|
|
;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
|
|
|
|
|
;;; Copyright © 2013 Andreas Enge <andreas@enge.fr>
|
|
|
|
|
;;; Copyright © 2015, 2018 Mark H Weaver <mhw@netris.org>
|
|
|
|
|
;;; Copyright © 2018 Arun Isaac <arunisaac@systemreboot.net>
|
|
|
|
|
;;; Copyright © 2018, 2019 Ricardo Wurmus <rekado@elephly.net>
|
2022-10-28 12:04:09 -04:00
|
|
|
|
;;; Copyright © 2021, 2022 Maxime Devos <maximedevos@telenet.be>
|
2021-07-06 16:03:19 -04:00
|
|
|
|
;;; Copyright © 2020 Christine Lemmer-Webber <cwebber@dustycloud.org>
|
|
|
|
|
;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re>
|
2022-05-28 20:00:00 -04:00
|
|
|
|
;;; Copyright © 2022 Tobias Geerinckx-Rice <me@tobias.gr>
|
2024-02-03 21:19:55 -05:00
|
|
|
|
;;; Copyright © 2024 Nicolas Graves <ngraves@ngraves.fr>
|
2014-04-30 09:44:59 -04:00
|
|
|
|
;;;
|
|
|
|
|
;;; This file is part of GNU Guix.
|
|
|
|
|
;;;
|
|
|
|
|
;;; GNU Guix is free software; you can redistribute it and/or modify it
|
|
|
|
|
;;; under the terms of the GNU General Public License as published by
|
|
|
|
|
;;; the Free Software Foundation; either version 3 of the License, or (at
|
|
|
|
|
;;; your option) any later version.
|
|
|
|
|
;;;
|
|
|
|
|
;;; GNU Guix is distributed in the hope that it will be useful, but
|
|
|
|
|
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;;; GNU General Public License for more details.
|
|
|
|
|
;;;
|
|
|
|
|
;;; You should have received a copy of the GNU General Public License
|
|
|
|
|
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
2014-09-03 04:47:05 -04:00
|
|
|
|
(define-module (gnu build activation)
|
2019-03-03 15:57:26 -05:00
|
|
|
|
#:use-module (gnu system accounts)
|
2022-10-22 20:00:00 -04:00
|
|
|
|
#:use-module (gnu system privilege)
|
2019-03-03 17:16:41 -05:00
|
|
|
|
#:use-module (gnu build accounts)
|
2014-09-03 05:14:12 -04:00
|
|
|
|
#:use-module (gnu build linux-boot)
|
2014-04-30 16:17:56 -04:00
|
|
|
|
#:use-module (guix build utils)
|
2019-06-03 11:14:17 -04:00
|
|
|
|
#:use-module ((guix build syscalls) #:select (with-file-lock))
|
2014-04-30 09:44:59 -04:00
|
|
|
|
#:use-module (ice-9 ftw)
|
system: Make accounts and groups at activation time.
* gnu/services/base.scm (guix-build-accounts): Remove #:gid parameter;
add #:group. Remove 'password' and 'gid' fields in 'user-account'
form, and add 'group'.
(guix-service): Remove #:build-user-gid parameter. Remove 'id' field
in 'user-group' form.
* gnu/system.scm (etc-directory): Remove #:groups and #:accounts. No
longer produce files "passwd", "shadow", and "group". Adjust caller
accordingly.
(%root-account): New variable.
(operating-system-accounts): Add 'users' variable. Add %ROOT-ACCOUNT
only of 'operating-system-users' doesn't already contain a root
account.
(user-group->gexp, user-account->gexp): New procedures.
(operating-system-boot-script): Add calls to 'setenv' and
'activate-users+groups' in gexp.
* gnu/system/linux.scm (base-pam-services): Add PAM services for
"user{add,del,mode}" and "group{add,del,mod}".
* gnu/system/shadow.scm (<user-account>)[gid]: Rename to...
[group]: ... this.
[supplementary-groups]: New field.
[uid, password]: Default to #f.
(<user-group>)[id]: Default to #f.
(group-file, passwd-file): Remove.
* gnu/system/vm.scm (operating-system-default-contents)[user-directories]:
Remove. Add "/home" to the directives.
* guix/build/activation.scm (add-group, add-user,
activate-users+groups): New procedures.
2014-05-11 16:41:01 -04:00
|
|
|
|
#:use-module (ice-9 match)
|
2019-03-08 16:48:04 -05:00
|
|
|
|
#:use-module (ice-9 vlist)
|
system: Make accounts and groups at activation time.
* gnu/services/base.scm (guix-build-accounts): Remove #:gid parameter;
add #:group. Remove 'password' and 'gid' fields in 'user-account'
form, and add 'group'.
(guix-service): Remove #:build-user-gid parameter. Remove 'id' field
in 'user-group' form.
* gnu/system.scm (etc-directory): Remove #:groups and #:accounts. No
longer produce files "passwd", "shadow", and "group". Adjust caller
accordingly.
(%root-account): New variable.
(operating-system-accounts): Add 'users' variable. Add %ROOT-ACCOUNT
only of 'operating-system-users' doesn't already contain a root
account.
(user-group->gexp, user-account->gexp): New procedures.
(operating-system-boot-script): Add calls to 'setenv' and
'activate-users+groups' in gexp.
* gnu/system/linux.scm (base-pam-services): Add PAM services for
"user{add,del,mode}" and "group{add,del,mod}".
* gnu/system/shadow.scm (<user-account>)[gid]: Rename to...
[group]: ... this.
[supplementary-groups]: New field.
[uid, password]: Default to #f.
(<user-group>)[id]: Default to #f.
(group-file, passwd-file): Remove.
* gnu/system/vm.scm (operating-system-default-contents)[user-directories]:
Remove. Add "/home" to the directives.
* guix/build/activation.scm (add-group, add-user,
activate-users+groups): New procedures.
2014-05-11 16:41:01 -04:00
|
|
|
|
#:use-module (srfi srfi-1)
|
2019-03-03 17:16:41 -05:00
|
|
|
|
#:use-module (srfi srfi-11)
|
2014-05-03 18:18:46 -04:00
|
|
|
|
#:use-module (srfi srfi-26)
|
system: Make accounts and groups at activation time.
* gnu/services/base.scm (guix-build-accounts): Remove #:gid parameter;
add #:group. Remove 'password' and 'gid' fields in 'user-account'
form, and add 'group'.
(guix-service): Remove #:build-user-gid parameter. Remove 'id' field
in 'user-group' form.
* gnu/system.scm (etc-directory): Remove #:groups and #:accounts. No
longer produce files "passwd", "shadow", and "group". Adjust caller
accordingly.
(%root-account): New variable.
(operating-system-accounts): Add 'users' variable. Add %ROOT-ACCOUNT
only of 'operating-system-users' doesn't already contain a root
account.
(user-group->gexp, user-account->gexp): New procedures.
(operating-system-boot-script): Add calls to 'setenv' and
'activate-users+groups' in gexp.
* gnu/system/linux.scm (base-pam-services): Add PAM services for
"user{add,del,mode}" and "group{add,del,mod}".
* gnu/system/shadow.scm (<user-account>)[gid]: Rename to...
[group]: ... this.
[supplementary-groups]: New field.
[uid, password]: Default to #f.
(<user-group>)[id]: Default to #f.
(group-file, passwd-file): Remove.
* gnu/system/vm.scm (operating-system-default-contents)[user-directories]:
Remove. Add "/home" to the directives.
* guix/build/activation.scm (add-group, add-user,
activate-users+groups): New procedures.
2014-05-11 16:41:01 -04:00
|
|
|
|
#:export (activate-users+groups
|
2017-02-01 06:16:39 -05:00
|
|
|
|
activate-user-home
|
system: Make accounts and groups at activation time.
* gnu/services/base.scm (guix-build-accounts): Remove #:gid parameter;
add #:group. Remove 'password' and 'gid' fields in 'user-account'
form, and add 'group'.
(guix-service): Remove #:build-user-gid parameter. Remove 'id' field
in 'user-group' form.
* gnu/system.scm (etc-directory): Remove #:groups and #:accounts. No
longer produce files "passwd", "shadow", and "group". Adjust caller
accordingly.
(%root-account): New variable.
(operating-system-accounts): Add 'users' variable. Add %ROOT-ACCOUNT
only of 'operating-system-users' doesn't already contain a root
account.
(user-group->gexp, user-account->gexp): New procedures.
(operating-system-boot-script): Add calls to 'setenv' and
'activate-users+groups' in gexp.
* gnu/system/linux.scm (base-pam-services): Add PAM services for
"user{add,del,mode}" and "group{add,del,mod}".
* gnu/system/shadow.scm (<user-account>)[gid]: Rename to...
[group]: ... this.
[supplementary-groups]: New field.
[uid, password]: Default to #f.
(<user-group>)[id]: Default to #f.
(group-file, passwd-file): Remove.
* gnu/system/vm.scm (operating-system-default-contents)[user-directories]:
Remove. Add "/home" to the directives.
* guix/build/activation.scm (add-group, add-user,
activate-users+groups): New procedures.
2014-05-11 16:41:01 -04:00
|
|
|
|
activate-etc
|
2022-10-22 20:00:00 -04:00
|
|
|
|
activate-privileged-programs
|
2017-02-08 09:32:28 -05:00
|
|
|
|
activate-special-files
|
2014-11-02 17:06:17 -05:00
|
|
|
|
activate-modprobe
|
2014-11-11 16:42:15 -05:00
|
|
|
|
activate-firmware
|
2015-04-12 09:33:42 -04:00
|
|
|
|
activate-ptrace-attach
|
2021-02-14 06:57:32 -05:00
|
|
|
|
activate-current-system
|
|
|
|
|
mkdir-p/perms))
|
2014-04-30 09:44:59 -04:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
;;;
|
|
|
|
|
;;; This module provides "activation" helpers. Activation is the process that
|
|
|
|
|
;;; consists in setting up system-wide files and directories so that an
|
|
|
|
|
;;; 'operating-system' configuration becomes active.
|
|
|
|
|
;;;
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
2014-12-13 16:30:44 -05:00
|
|
|
|
(define %skeleton-directory
|
|
|
|
|
;; Directory containing skeleton files for new accounts.
|
|
|
|
|
;; Note: keep the trailing '/' so that 'scandir' enters it.
|
|
|
|
|
"/etc/skel/")
|
|
|
|
|
|
|
|
|
|
(define (dot-or-dot-dot? file)
|
|
|
|
|
(member file '("." "..")))
|
|
|
|
|
|
2022-10-28 12:04:09 -04:00
|
|
|
|
(define (mkdir-p/perms directory owner bits)
|
|
|
|
|
"Create directory DIRECTORY and all its ancestors.
|
|
|
|
|
|
|
|
|
|
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'."
|
2021-02-14 06:57:32 -05:00
|
|
|
|
(define absolute?
|
2022-10-28 12:04:09 -04:00
|
|
|
|
(string-prefix? "/" directory))
|
2021-02-14 06:57:32 -05:00
|
|
|
|
|
|
|
|
|
(define not-slash
|
|
|
|
|
(char-set-complement (char-set #\/)))
|
|
|
|
|
|
2022-10-28 12:04:09 -04:00
|
|
|
|
;; By combining O_NOFOLLOW and O_DIRECTORY, this procedure automatically
|
|
|
|
|
;; verifies that no components are symlinks.
|
|
|
|
|
(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
|
2021-02-14 06:57:32 -05:00
|
|
|
|
|
2022-10-28 12:04:09 -04:00
|
|
|
|
(let loop ((components (string-tokenize directory not-slash))
|
|
|
|
|
(root (open (if absolute? "/" ".") open-flags)))
|
2021-02-14 06:57:32 -05:00
|
|
|
|
(match components
|
|
|
|
|
((head tail ...)
|
2022-10-28 12:04:09 -04:00
|
|
|
|
(let retry ()
|
|
|
|
|
;; In the usual case, we expect HEAD to already exist.
|
|
|
|
|
(match (catch 'system-error
|
|
|
|
|
(lambda ()
|
|
|
|
|
(openat root head open-flags))
|
|
|
|
|
(lambda args
|
|
|
|
|
(if (= ENOENT (system-error-errno args))
|
|
|
|
|
#false
|
|
|
|
|
(begin
|
|
|
|
|
(close-port root)
|
|
|
|
|
(apply throw args)))))
|
|
|
|
|
((? port? new-root)
|
|
|
|
|
(close root)
|
|
|
|
|
(loop tail new-root))
|
|
|
|
|
(#false
|
|
|
|
|
;; If not, create it.
|
|
|
|
|
(catch 'system-error
|
|
|
|
|
(lambda _
|
|
|
|
|
(mkdirat root head))
|
|
|
|
|
(lambda args
|
|
|
|
|
;; 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)))))
|
2021-02-14 06:57:32 -05:00
|
|
|
|
|
2014-12-13 16:30:44 -05:00
|
|
|
|
(define* (copy-account-skeletons home
|
2017-02-03 03:50:09 -05:00
|
|
|
|
#:key
|
|
|
|
|
(directory %skeleton-directory)
|
|
|
|
|
uid gid)
|
|
|
|
|
"Copy the account skeletons from DIRECTORY to HOME. When UID is an integer,
|
2021-03-30 16:36:14 -04:00
|
|
|
|
make it the owner of all the files created except the home directory; likewise
|
|
|
|
|
for GID."
|
2017-02-03 03:50:09 -05:00
|
|
|
|
(define (set-owner file)
|
|
|
|
|
(when (or uid gid)
|
|
|
|
|
(chown file (or uid -1) (or gid -1))))
|
|
|
|
|
|
2014-12-13 16:30:44 -05:00
|
|
|
|
(let ((files (scandir directory (negate dot-or-dot-dot?)
|
|
|
|
|
string<?)))
|
|
|
|
|
(mkdir-p home)
|
|
|
|
|
(for-each (lambda (file)
|
2015-05-05 17:46:54 -04:00
|
|
|
|
(let ((target (string-append home "/" file)))
|
2016-03-20 10:02:38 -04:00
|
|
|
|
(copy-recursively (string-append directory "/" file)
|
2016-03-24 16:33:56 -04:00
|
|
|
|
target
|
|
|
|
|
#:log (%make-void-port "w"))
|
2017-02-03 03:50:09 -05:00
|
|
|
|
(for-each set-owner
|
|
|
|
|
(find-files target (const #t)
|
|
|
|
|
#:directories? #t))
|
2015-05-05 17:46:54 -04:00
|
|
|
|
(make-file-writable target)))
|
|
|
|
|
files)))
|
|
|
|
|
|
|
|
|
|
(define* (make-skeletons-writable home
|
|
|
|
|
#:optional (directory %skeleton-directory))
|
|
|
|
|
"Make sure that the files that have been copied from DIRECTORY to HOME are
|
|
|
|
|
owner-writable in HOME."
|
|
|
|
|
(let ((files (scandir directory (negate dot-or-dot-dot?)
|
|
|
|
|
string<?)))
|
|
|
|
|
(for-each (lambda (file)
|
|
|
|
|
(let ((target (string-append home "/" file)))
|
|
|
|
|
(when (file-exists? target)
|
|
|
|
|
(make-file-writable target))))
|
2014-12-13 16:30:44 -05:00
|
|
|
|
files)))
|
|
|
|
|
|
2019-03-08 16:48:04 -05:00
|
|
|
|
(define (duplicates lst)
|
|
|
|
|
"Return elements from LST present more than once in LST."
|
|
|
|
|
(let loop ((lst lst)
|
|
|
|
|
(seen vlist-null)
|
|
|
|
|
(result '()))
|
|
|
|
|
(match lst
|
|
|
|
|
(()
|
|
|
|
|
(reverse result))
|
|
|
|
|
((head . tail)
|
|
|
|
|
(loop tail
|
|
|
|
|
(vhash-cons head #t seen)
|
|
|
|
|
(if (vhash-assoc head seen)
|
|
|
|
|
(cons head result)
|
|
|
|
|
result))))))
|
|
|
|
|
|
system: Make accounts and groups at activation time.
* gnu/services/base.scm (guix-build-accounts): Remove #:gid parameter;
add #:group. Remove 'password' and 'gid' fields in 'user-account'
form, and add 'group'.
(guix-service): Remove #:build-user-gid parameter. Remove 'id' field
in 'user-group' form.
* gnu/system.scm (etc-directory): Remove #:groups and #:accounts. No
longer produce files "passwd", "shadow", and "group". Adjust caller
accordingly.
(%root-account): New variable.
(operating-system-accounts): Add 'users' variable. Add %ROOT-ACCOUNT
only of 'operating-system-users' doesn't already contain a root
account.
(user-group->gexp, user-account->gexp): New procedures.
(operating-system-boot-script): Add calls to 'setenv' and
'activate-users+groups' in gexp.
* gnu/system/linux.scm (base-pam-services): Add PAM services for
"user{add,del,mode}" and "group{add,del,mod}".
* gnu/system/shadow.scm (<user-account>)[gid]: Rename to...
[group]: ... this.
[supplementary-groups]: New field.
[uid, password]: Default to #f.
(<user-group>)[id]: Default to #f.
(group-file, passwd-file): Remove.
* gnu/system/vm.scm (operating-system-default-contents)[user-directories]:
Remove. Add "/home" to the directives.
* guix/build/activation.scm (add-group, add-user,
activate-users+groups): New procedures.
2014-05-11 16:41:01 -04:00
|
|
|
|
(define (activate-users+groups users groups)
|
2019-03-03 15:57:26 -05:00
|
|
|
|
"Make sure USERS (a list of user account records) and GROUPS (a list of user
|
|
|
|
|
group records) are all available."
|
2019-03-03 17:16:41 -05:00
|
|
|
|
(define (make-home-directory user)
|
|
|
|
|
(let ((home (user-account-home-directory user))
|
|
|
|
|
(pwd (getpwnam (user-account-name user))))
|
|
|
|
|
(mkdir-p home)
|
2019-03-08 16:48:04 -05:00
|
|
|
|
|
|
|
|
|
;; Always set ownership and permissions for home directories of system
|
2023-08-19 20:00:00 -04:00
|
|
|
|
;; accounts. If a service needs looser permissions on its home
|
|
|
|
|
;; directories, it can always chmod it in an activation snippet.
|
2019-03-03 17:16:41 -05:00
|
|
|
|
(chown home (passwd:uid pwd) (passwd:gid pwd))
|
2023-08-19 20:00:00 -04:00
|
|
|
|
(chmod home #o700)))
|
system: Make accounts and groups at activation time.
* gnu/services/base.scm (guix-build-accounts): Remove #:gid parameter;
add #:group. Remove 'password' and 'gid' fields in 'user-account'
form, and add 'group'.
(guix-service): Remove #:build-user-gid parameter. Remove 'id' field
in 'user-group' form.
* gnu/system.scm (etc-directory): Remove #:groups and #:accounts. No
longer produce files "passwd", "shadow", and "group". Adjust caller
accordingly.
(%root-account): New variable.
(operating-system-accounts): Add 'users' variable. Add %ROOT-ACCOUNT
only of 'operating-system-users' doesn't already contain a root
account.
(user-group->gexp, user-account->gexp): New procedures.
(operating-system-boot-script): Add calls to 'setenv' and
'activate-users+groups' in gexp.
* gnu/system/linux.scm (base-pam-services): Add PAM services for
"user{add,del,mode}" and "group{add,del,mod}".
* gnu/system/shadow.scm (<user-account>)[gid]: Rename to...
[group]: ... this.
[supplementary-groups]: New field.
[uid, password]: Default to #f.
(<user-group>)[id]: Default to #f.
(group-file, passwd-file): Remove.
* gnu/system/vm.scm (operating-system-default-contents)[user-directories]:
Remove. Add "/home" to the directives.
* guix/build/activation.scm (add-group, add-user,
activate-users+groups): New procedures.
2014-05-11 16:41:01 -04:00
|
|
|
|
|
2019-03-08 16:48:04 -05:00
|
|
|
|
(define system-accounts
|
|
|
|
|
(filter (lambda (user)
|
|
|
|
|
(and (user-account-system? user)
|
|
|
|
|
(user-account-create-home-directory? user)))
|
|
|
|
|
users))
|
|
|
|
|
|
2016-08-26 15:45:57 -04:00
|
|
|
|
;; Allow home directories to be created under /var/lib.
|
2016-09-06 14:39:35 -04:00
|
|
|
|
(mkdir-p "/var/lib")
|
2016-08-26 15:45:57 -04:00
|
|
|
|
|
2019-06-03 11:14:17 -04:00
|
|
|
|
;; Take same lock as libc's 'lckpwdf' (but without a timeout) while we read
|
|
|
|
|
;; and write the databases. This ensures there's no race condition with
|
|
|
|
|
;; other tools that might be accessing it at the same time.
|
|
|
|
|
(with-file-lock %password-lock-file
|
|
|
|
|
(let-values (((groups passwd shadow)
|
|
|
|
|
(user+group-databases users groups)))
|
|
|
|
|
(write-group groups)
|
|
|
|
|
(write-passwd passwd)
|
|
|
|
|
(write-shadow shadow)))
|
|
|
|
|
|
|
|
|
|
;; Home directories of non-system accounts are created by
|
|
|
|
|
;; 'activate-user-home'.
|
|
|
|
|
(for-each make-home-directory system-accounts)
|
|
|
|
|
|
|
|
|
|
;; Turn shared home directories, such as /var/empty, into root-owned,
|
|
|
|
|
;; read-only places.
|
|
|
|
|
(for-each (lambda (directory)
|
|
|
|
|
(chown directory 0 0)
|
|
|
|
|
(chmod directory #o555))
|
|
|
|
|
(duplicates (map user-account-home-directory system-accounts))))
|
system: Make accounts and groups at activation time.
* gnu/services/base.scm (guix-build-accounts): Remove #:gid parameter;
add #:group. Remove 'password' and 'gid' fields in 'user-account'
form, and add 'group'.
(guix-service): Remove #:build-user-gid parameter. Remove 'id' field
in 'user-group' form.
* gnu/system.scm (etc-directory): Remove #:groups and #:accounts. No
longer produce files "passwd", "shadow", and "group". Adjust caller
accordingly.
(%root-account): New variable.
(operating-system-accounts): Add 'users' variable. Add %ROOT-ACCOUNT
only of 'operating-system-users' doesn't already contain a root
account.
(user-group->gexp, user-account->gexp): New procedures.
(operating-system-boot-script): Add calls to 'setenv' and
'activate-users+groups' in gexp.
* gnu/system/linux.scm (base-pam-services): Add PAM services for
"user{add,del,mode}" and "group{add,del,mod}".
* gnu/system/shadow.scm (<user-account>)[gid]: Rename to...
[group]: ... this.
[supplementary-groups]: New field.
[uid, password]: Default to #f.
(<user-group>)[id]: Default to #f.
(group-file, passwd-file): Remove.
* gnu/system/vm.scm (operating-system-default-contents)[user-directories]:
Remove. Add "/home" to the directives.
* guix/build/activation.scm (add-group, add-user,
activate-users+groups): New procedures.
2014-05-11 16:41:01 -04:00
|
|
|
|
|
2017-02-01 06:16:39 -05:00
|
|
|
|
(define (activate-user-home users)
|
|
|
|
|
"Create and populate the home directory of USERS, a list of tuples, unless
|
|
|
|
|
they already exist."
|
|
|
|
|
(define ensure-user-home
|
2019-03-03 15:57:26 -05:00
|
|
|
|
(lambda (user)
|
|
|
|
|
(let ((name (user-account-name user))
|
|
|
|
|
(home (user-account-home-directory user))
|
|
|
|
|
(create-home? (user-account-create-home-directory? user))
|
|
|
|
|
(system? (user-account-system? user)))
|
|
|
|
|
;; The home directories of system accounts are created during
|
|
|
|
|
;; activation, not here.
|
|
|
|
|
(unless (or (not home) (not create-home?) system?
|
|
|
|
|
(directory-exists? home))
|
|
|
|
|
(let* ((pw (getpwnam name))
|
|
|
|
|
(uid (passwd:uid pw))
|
|
|
|
|
(gid (passwd:gid pw)))
|
|
|
|
|
(mkdir-p home)
|
|
|
|
|
(chmod home #o700)
|
|
|
|
|
(copy-account-skeletons home
|
2021-03-30 16:36:14 -04:00
|
|
|
|
#:uid uid #:gid gid)
|
|
|
|
|
|
|
|
|
|
;; It is important 'chown' be called after
|
|
|
|
|
;; 'copy-account-skeletons'. Otherwise, a malicious user with
|
|
|
|
|
;; good timing could create a symlink in HOME that would be
|
|
|
|
|
;; dereferenced by 'copy-account-skeletons'.
|
|
|
|
|
(chown home uid gid))))))
|
2017-02-01 06:16:39 -05:00
|
|
|
|
|
|
|
|
|
(for-each ensure-user-home users))
|
|
|
|
|
|
2014-04-30 09:44:59 -04:00
|
|
|
|
(define (activate-etc etc)
|
|
|
|
|
"Install ETC, a directory in the store, as the source of static files for
|
|
|
|
|
/etc."
|
|
|
|
|
|
|
|
|
|
;; /etc is a mixture of static and dynamic settings. Here is where we
|
|
|
|
|
;; initialize it from the static part.
|
|
|
|
|
|
2014-09-11 17:23:07 -04:00
|
|
|
|
(define (rm-f file)
|
|
|
|
|
(false-if-exception (delete-file file)))
|
|
|
|
|
|
2014-04-30 09:44:59 -04:00
|
|
|
|
(format #t "populating /etc from ~a...~%" etc)
|
2017-08-02 18:20:05 -04:00
|
|
|
|
(mkdir-p "/etc")
|
2014-09-11 17:23:07 -04:00
|
|
|
|
|
2015-03-03 02:14:14 -05:00
|
|
|
|
;; Create the /etc/ssl -> /run/current-system/profile/etc/ssl symlink. This
|
|
|
|
|
;; symlink, to a target outside of the store, probably doesn't belong in the
|
|
|
|
|
;; static 'etc' store directory. However, if it were to be put there,
|
|
|
|
|
;; beware that if /run/current-system/profile/etc/ssl doesn't exist at the
|
|
|
|
|
;; time of activation (e.g. when installing a fresh system), the call to
|
|
|
|
|
;; 'file-is-directory?' below will fail because it uses 'stat', not 'lstat'.
|
|
|
|
|
(rm-f "/etc/ssl")
|
|
|
|
|
(symlink "/run/current-system/profile/etc/ssl" "/etc/ssl")
|
|
|
|
|
|
2014-09-11 17:23:07 -04:00
|
|
|
|
(rm-f "/etc/static")
|
|
|
|
|
(symlink etc "/etc/static")
|
|
|
|
|
(for-each (lambda (file)
|
|
|
|
|
(let ((target (string-append "/etc/" file))
|
|
|
|
|
(source (string-append "/etc/static/" file)))
|
|
|
|
|
(rm-f target)
|
|
|
|
|
|
|
|
|
|
;; Things such as /etc/sudoers must be regular files, not
|
|
|
|
|
;; symlinks; furthermore, they could be modified behind our
|
|
|
|
|
;; back---e.g., with 'visudo'. Thus, make a copy instead of
|
|
|
|
|
;; symlinking them.
|
|
|
|
|
(if (file-is-directory? source)
|
|
|
|
|
(symlink source target)
|
|
|
|
|
(copy-file source target))
|
|
|
|
|
|
|
|
|
|
;; XXX: Dirty hack to meet sudo's expectations.
|
|
|
|
|
(when (string=? (basename target) "sudoers")
|
|
|
|
|
(chmod target #o440))))
|
2014-12-13 16:30:44 -05:00
|
|
|
|
(scandir etc (negate dot-or-dot-dot?)
|
2014-09-11 17:23:07 -04:00
|
|
|
|
|
|
|
|
|
;; The default is 'string-locale<?', but we don't have
|
|
|
|
|
;; it when run from the initrd's statically-linked
|
|
|
|
|
;; Guile.
|
2014-12-04 18:19:39 -05:00
|
|
|
|
string<?)))
|
2014-04-30 09:44:59 -04:00
|
|
|
|
|
2014-04-30 16:17:56 -04:00
|
|
|
|
(define %setuid-directory
|
2022-05-28 20:00:00 -04:00
|
|
|
|
;; Place where setuid programs used to be stored. It exists for backwards
|
|
|
|
|
;; compatibility & will be removed. Use %PRIVILEGED-PROGRAM-DIRECTORY instead.
|
2014-04-30 16:17:56 -04:00
|
|
|
|
"/run/setuid-programs")
|
|
|
|
|
|
2022-05-28 20:00:00 -04:00
|
|
|
|
(define %privileged-program-directory
|
|
|
|
|
;; Place where privileged copies of programs are stored.
|
|
|
|
|
"/run/privileged/bin")
|
|
|
|
|
|
2023-07-22 20:00:00 -04:00
|
|
|
|
(define (activate-privileged-programs programs libcap)
|
2022-10-22 20:00:00 -04:00
|
|
|
|
"Turn PROGRAMS, a list of file privileged-programs records, into privileged
|
2023-07-22 20:00:00 -04:00
|
|
|
|
copies stored under %PRIVILEGED-PROGRAM-DIRECTORY, using LIBCAP's setcap(8)
|
|
|
|
|
binary if needed."
|
2022-05-28 20:00:00 -04:00
|
|
|
|
(define (ensure-empty-directory directory)
|
|
|
|
|
(if (file-exists? directory)
|
|
|
|
|
(for-each (compose delete-file
|
|
|
|
|
(cut string-append directory "/" <>))
|
|
|
|
|
(scandir directory
|
|
|
|
|
(lambda (file)
|
|
|
|
|
(not (member file '("." ".."))))
|
|
|
|
|
string<?))
|
|
|
|
|
(mkdir-p directory)) )
|
|
|
|
|
|
2023-07-22 20:00:00 -04:00
|
|
|
|
(define (make-privileged-program program setuid? setgid? uid gid capabilities)
|
2022-05-28 20:00:00 -04:00
|
|
|
|
(let ((target (string-append %privileged-program-directory
|
2021-07-06 16:03:19 -04:00
|
|
|
|
"/" (basename program)))
|
|
|
|
|
(mode (+ #o0555 ; base permissions
|
|
|
|
|
(if setuid? #o4000 0) ; setuid bit
|
|
|
|
|
(if setgid? #o2000 0)))) ; setgid bit
|
|
|
|
|
(copy-file program target)
|
|
|
|
|
(chown target uid gid)
|
2023-07-22 20:00:00 -04:00
|
|
|
|
(chmod target mode)
|
|
|
|
|
(when (and capabilities libcap)
|
|
|
|
|
(system* (string-append libcap "/sbin/setcap")
|
|
|
|
|
"-q" capabilities target))))
|
2014-04-30 16:17:56 -04:00
|
|
|
|
|
2022-05-28 20:00:00 -04:00
|
|
|
|
(define (make-deprecated-wrapper program)
|
|
|
|
|
;; This will eventually become a script that warns on usage, then vanish.
|
|
|
|
|
(symlink (string-append %privileged-program-directory
|
|
|
|
|
"/" (basename program))
|
|
|
|
|
(string-append %setuid-directory
|
|
|
|
|
"/" (basename program))))
|
|
|
|
|
|
|
|
|
|
(format #t "setting up privileged programs in '~a'...~%"
|
|
|
|
|
%privileged-program-directory)
|
|
|
|
|
(ensure-empty-directory %privileged-program-directory)
|
|
|
|
|
(ensure-empty-directory %setuid-directory)
|
2014-04-30 16:17:56 -04:00
|
|
|
|
|
2020-01-02 12:29:00 -05:00
|
|
|
|
(for-each (lambda (program)
|
|
|
|
|
(catch 'system-error
|
|
|
|
|
(lambda ()
|
2022-10-22 20:00:00 -04:00
|
|
|
|
(let* ((program-name (privileged-program-program program))
|
|
|
|
|
(setuid? (privileged-program-setuid? program))
|
|
|
|
|
(setgid? (privileged-program-setgid? program))
|
|
|
|
|
(user (privileged-program-user program))
|
|
|
|
|
(group (privileged-program-group program))
|
2023-07-22 20:00:00 -04:00
|
|
|
|
(capabilities (privileged-program-capabilities program))
|
2021-07-06 16:03:19 -04:00
|
|
|
|
(uid (match user
|
|
|
|
|
((? string?) (passwd:uid (getpwnam user)))
|
|
|
|
|
((? integer?) user)))
|
|
|
|
|
(gid (match group
|
|
|
|
|
((? string?) (group:gid (getgrnam group)))
|
|
|
|
|
((? integer?) group))))
|
2023-07-22 20:00:00 -04:00
|
|
|
|
(make-privileged-program program-name
|
|
|
|
|
setuid? setgid? uid gid
|
|
|
|
|
capabilities)
|
2022-05-28 20:00:00 -04:00
|
|
|
|
(make-deprecated-wrapper program-name)))
|
2020-01-02 12:29:00 -05:00
|
|
|
|
(lambda args
|
2022-10-22 20:00:00 -04:00
|
|
|
|
;; If we fail to create a privileged program, better keep going
|
2022-05-28 20:00:00 -04:00
|
|
|
|
;; so that we don't leave %PRIVILEGED-PROGRAM-DIRECTORY empty
|
|
|
|
|
;; or half-populated. This can happen if PROGRAMS contains
|
2020-01-02 12:29:00 -05:00
|
|
|
|
;; incorrect file names: <https://bugs.gnu.org/38800>.
|
|
|
|
|
(format (current-error-port)
|
2022-10-22 20:00:00 -04:00
|
|
|
|
"warning: failed to privilege ~s: ~a~%"
|
|
|
|
|
(privileged-program-program program)
|
2021-07-06 16:03:19 -04:00
|
|
|
|
(strerror (system-error-errno args))))))
|
2020-01-02 12:29:00 -05:00
|
|
|
|
programs))
|
2014-04-30 16:17:56 -04:00
|
|
|
|
|
2017-02-08 09:32:28 -05:00
|
|
|
|
(define (activate-special-files special-files)
|
|
|
|
|
"Install the files listed in SPECIAL-FILES. Each element of SPECIAL-FILES
|
|
|
|
|
is a pair where the first element is the name of the special file and the
|
|
|
|
|
second element is the name it should appear at, such as:
|
|
|
|
|
|
|
|
|
|
((\"/bin/sh\" \"/gnu/store/…-bash/bin/sh\")
|
|
|
|
|
(\"/usr/bin/env\" \"/gnu/store/…-coreutils/bin/env\"))
|
|
|
|
|
"
|
|
|
|
|
(define install-special-file
|
|
|
|
|
(match-lambda
|
|
|
|
|
((target file)
|
|
|
|
|
(let ((pivot (string-append target ".new")))
|
|
|
|
|
(mkdir-p (dirname target))
|
|
|
|
|
(symlink file pivot)
|
|
|
|
|
(rename-file pivot target)))))
|
|
|
|
|
|
|
|
|
|
(for-each install-special-file special-files))
|
2014-09-11 16:18:52 -04:00
|
|
|
|
|
2014-11-02 17:06:17 -05:00
|
|
|
|
(define (activate-modprobe modprobe)
|
|
|
|
|
"Tell the kernel to use MODPROBE to load modules."
|
2020-01-02 07:13:45 -05:00
|
|
|
|
|
|
|
|
|
;; If the kernel was built without loadable module support, this file is
|
|
|
|
|
;; unavailable, so check for its existence first.
|
|
|
|
|
(when (file-exists? "/proc/sys/kernel/modprobe")
|
|
|
|
|
(call-with-output-file "/proc/sys/kernel/modprobe"
|
|
|
|
|
(lambda (port)
|
|
|
|
|
(display modprobe port)))))
|
2014-11-02 17:06:17 -05:00
|
|
|
|
|
2014-11-11 16:42:15 -05:00
|
|
|
|
(define (activate-firmware directory)
|
|
|
|
|
"Tell the kernel to look for device firmware under DIRECTORY. This
|
|
|
|
|
mechanism bypasses udev: it allows Linux to handle firmware loading directly
|
|
|
|
|
by itself, without having to resort to a \"user helper\"."
|
2022-12-15 10:28:14 -05:00
|
|
|
|
|
|
|
|
|
;; If the kernel was built without firmware loading support, this file
|
|
|
|
|
;; does not exist. Do nothing in that case.
|
|
|
|
|
(let ((firmware-path "/sys/module/firmware_class/parameters/path"))
|
|
|
|
|
(when (file-exists? firmware-path)
|
|
|
|
|
(call-with-output-file firmware-path
|
|
|
|
|
(lambda (port)
|
|
|
|
|
(display directory port))))))
|
2015-04-12 09:33:42 -04:00
|
|
|
|
|
|
|
|
|
(define (activate-ptrace-attach)
|
|
|
|
|
"Allow users to PTRACE_ATTACH their own processes.
|
|
|
|
|
|
|
|
|
|
This works around a regression introduced in the default \"security\" policy
|
|
|
|
|
found in Linux 3.4 onward that prevents users from attaching to their own
|
|
|
|
|
processes--see Yama.txt in the Linux source tree for the rationale. This
|
|
|
|
|
sounds like an unacceptable restriction for little or no security
|
|
|
|
|
improvement."
|
2015-05-09 12:57:36 -04:00
|
|
|
|
(let ((file "/proc/sys/kernel/yama/ptrace_scope"))
|
|
|
|
|
(when (file-exists? file)
|
|
|
|
|
(call-with-output-file file
|
|
|
|
|
(lambda (port)
|
|
|
|
|
(display 0 port))))))
|
2014-11-11 16:42:15 -05:00
|
|
|
|
|
|
|
|
|
|
2014-05-17 11:39:30 -04:00
|
|
|
|
(define %current-system
|
|
|
|
|
;; The system that is current (a symlink.) This is not necessarily the same
|
2014-05-24 09:51:57 -04:00
|
|
|
|
;; as the system we booted (aka. /run/booted-system) because we can re-build
|
|
|
|
|
;; a new system configuration and activate it, without rebooting.
|
2014-05-17 11:39:30 -04:00
|
|
|
|
"/run/current-system")
|
|
|
|
|
|
|
|
|
|
(define (boot-time-system)
|
2022-02-17 23:28:07 -05:00
|
|
|
|
"Return the 'gnu.system' argument passed on the kernel command line."
|
|
|
|
|
(find-long-option "gnu.system" (if (string-contains %host-type "linux-gnu")
|
2020-06-08 22:53:30 -04:00
|
|
|
|
(linux-command-line)
|
2020-05-03 10:32:09 -04:00
|
|
|
|
(command-line))))
|
2014-05-17 11:39:30 -04:00
|
|
|
|
|
2014-09-12 11:41:06 -04:00
|
|
|
|
(define* (activate-current-system
|
|
|
|
|
#:optional (system (or (getenv "GUIX_NEW_SYSTEM")
|
|
|
|
|
(boot-time-system))))
|
2014-05-24 09:51:57 -04:00
|
|
|
|
"Atomically make SYSTEM the current system."
|
2014-09-12 11:41:06 -04:00
|
|
|
|
;; The 'GUIX_NEW_SYSTEM' environment variable is used as a way for 'guix
|
|
|
|
|
;; system reconfigure' to pass the file name of the new system.
|
|
|
|
|
|
2014-05-17 11:39:30 -04:00
|
|
|
|
(format #t "making '~a' the current system...~%" system)
|
|
|
|
|
|
2024-02-03 21:19:55 -05:00
|
|
|
|
(mkdir-p "/run")
|
2014-05-17 11:39:30 -04:00
|
|
|
|
;; Atomically make SYSTEM current.
|
|
|
|
|
(let ((new (string-append %current-system ".new")))
|
|
|
|
|
(symlink system new)
|
|
|
|
|
(rename-file new %current-system)))
|
|
|
|
|
|
2014-04-30 09:44:59 -04:00
|
|
|
|
;;; activation.scm ends here
|