summary refs log tree commit diff
path: root/gnu/build/activation.scm
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/build/activation.scm')
-rw-r--r--gnu/build/activation.scm95
1 files changed, 89 insertions, 6 deletions
diff --git a/gnu/build/activation.scm b/gnu/build/activation.scm
index 909e971833..352e736050 100644
--- a/gnu/build/activation.scm
+++ b/gnu/build/activation.scm
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org>
 ;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
 ;;;
 ;;; This file is part of GNU Guix.
@@ -30,6 +30,7 @@
             activate-/bin/sh
             activate-modprobe
             activate-firmware
+            activate-ptrace-attach
             activate-current-system))
 
 ;;; Commentary:
@@ -40,6 +41,24 @@
 ;;;
 ;;; Code:
 
+(define (enumerate thunk)
+  "Return the list of values returned by THUNK until it returned #f."
+  (let loop ((entry  (thunk))
+             (result '()))
+    (if (not entry)
+        (reverse result)
+        (loop (thunk) (cons entry result)))))
+
+(define (current-users)
+  "Return the passwd entries for all the currently defined user accounts."
+  (setpw)
+  (enumerate getpwent))
+
+(define (current-groups)
+  "Return the group entries for all the currently defined user groups."
+  (setgr)
+  (enumerate getgrent))
+
 (define* (add-group name #:key gid password system?
                     (log-port (current-error-port)))
   "Add NAME as a user group, with the given numeric GID if specified."
@@ -59,6 +78,11 @@
 (define (dot-or-dot-dot? file)
   (member file '("." "..")))
 
+(define (make-file-writable file)
+  "Make FILE writable for its owner.."
+  (let ((stat (lstat file)))                      ;XXX: symlinks
+    (chmod file (logior #o600 (stat:perms stat)))))
+
 (define* (copy-account-skeletons home
                                  #:optional (directory %skeleton-directory))
   "Copy the account skeletons from DIRECTORY to HOME."
@@ -66,8 +90,21 @@
                         string<?)))
     (mkdir-p home)
     (for-each (lambda (file)
-                (copy-file (string-append directory "/" file)
-                           (string-append home "/" file)))
+                (let ((target (string-append home "/" file)))
+                  (copy-file (string-append directory "/" file) target)
+                  (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))))
               files)))
 
 (define* (add-user name group
@@ -109,7 +146,14 @@ properties.  Return #t on success."
                     ,@(if password `("-p" ,password) '())
                     ,@(if system? '("--system") '())
                     ,name)))
-        (zero? (apply system* "useradd" args)))))
+        (and (zero? (apply system* "useradd" args))
+             (begin
+               ;; Since /etc/skel is a link to a directory in the store where
+               ;; all files have the writable bit cleared, and since 'useradd'
+               ;; preserves permissions when it copies them, explicitly make
+               ;; them writable.
+               (make-skeletons-writable home)
+               #t)))))
 
 (define* (modify-user name group
                       #:key uid comment home shell password system?
@@ -128,6 +172,17 @@ properties.  Return #t on success."
                 ,name)))
     (zero? (apply system* "usermod" args))))
 
+(define* (delete-user name #:key (log-port (current-error-port)))
+  "Remove user account NAME.  Return #t on success.  This may fail if NAME is
+logged in."
+  (format log-port "deleting user '~a'...~%" name)
+  (zero? (system* "userdel" name)))
+
+(define* (delete-group name #:key (log-port (current-error-port)))
+  "Remove group NAME.  Return #t on success."
+  (format log-port "deleting group '~a'...~%" name)
+  (zero? (system* "groupdel" name)))
+
 (define* (ensure-user name group
                       #:key uid comment home shell password system?
                       (supplementary-groups '())
@@ -186,8 +241,22 @@ numeric gid or #f."
                            #:system? system?))))
             groups)
 
-  ;; Finally create the other user accounts.
-  (for-each activate-user users))
+  ;; Create the other user accounts.
+  (for-each activate-user users)
+
+  ;; Finally, delete extra user accounts and groups.
+  (for-each delete-user
+            (lset-difference string=?
+                             (map passwd:name (current-users))
+                             (match users
+                               (((names . _) ...)
+                                names))))
+  (for-each delete-group
+            (lset-difference string=?
+                             (map group:name (current-groups))
+                             (match groups
+                               (((names . _) ...)
+                                names)))))
 
 (define (activate-etc etc)
   "Install ETC, a directory in the store, as the source of static files for
@@ -292,6 +361,20 @@ by itself, without having to resort to a \"user helper\"."
     (lambda (port)
       (display directory port))))
 
+(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."
+  (let ((file "/proc/sys/kernel/yama/ptrace_scope"))
+    (when (file-exists? file)
+      (call-with-output-file file
+        (lambda (port)
+          (display 0 port))))))
+
 
 (define %current-system
   ;; The system that is current (a symlink.)  This is not necessarily the same