gnu: Add hostapd.
[jackhill/guix/guix.git] / gnu / system / pam.scm
CommitLineData
0ded70f3 1;;; GNU Guix --- Functional package management for GNU
fbc31dc1 2;;; Copyright © 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
0ded70f3
LC
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
6e828634 19(define-module (gnu system pam)
0ded70f3
LC
20 #:use-module (guix records)
21 #:use-module (guix derivations)
b5f4e686 22 #:use-module (guix gexp)
0adfe95a 23 #:use-module (gnu services)
0ded70f3
LC
24 #:use-module (ice-9 match)
25 #:use-module (srfi srfi-1)
909147e4 26 #:use-module (srfi srfi-9)
12c00bca 27 #:use-module (srfi srfi-11)
0ded70f3
LC
28 #:use-module (srfi srfi-26)
29 #:use-module ((guix utils) #:select (%current-system))
30 #:export (pam-service
d7bce31c
LC
31 pam-service-name
32 pam-service-account
33 pam-service-auth
34 pam-service-password
35 pam-service-session
36
0ded70f3 37 pam-entry
d7bce31c
LC
38 pam-entry-control
39 pam-entry-module
40 pam-entry-arguments
41
909147e4
RW
42 pam-limits-entry
43 pam-limits-entry-domain
44 pam-limits-entry-type
45 pam-limits-entry-item
46 pam-limits-entry-value
47 pam-limits-entry->string
48
0ded70f3 49 pam-services->directory
09e028f4 50 unix-pam-service
0adfe95a
LC
51 base-pam-services
52
fbc31dc1
LC
53 session-environment-service
54 session-environment-service-type
55
0adfe95a
LC
56 pam-root-service-type
57 pam-root-service))
0ded70f3
LC
58
59;;; Commentary:
60;;;
6e828634 61;;; Configuration of the pluggable authentication modules (PAM).
0ded70f3
LC
62;;;
63;;; Code:
64
65;; PAM services (see
66;; <http://www.linux-pam.org/Linux-PAM-html/sag-configuration-file.html>.)
67(define-record-type* <pam-service> pam-service
68 make-pam-service
69 pam-service?
70 (name pam-service-name) ; string
71
72 ;; The four "management groups".
73 (account pam-service-account ; list of <pam-entry>
74 (default '()))
75 (auth pam-service-auth
76 (default '()))
77 (password pam-service-password
78 (default '()))
79 (session pam-service-session
80 (default '())))
81
82(define-record-type* <pam-entry> pam-entry
83 make-pam-entry
84 pam-entry?
b5f4e686
LC
85 (control pam-entry-control) ; string
86 (module pam-entry-module) ; file name
87 (arguments pam-entry-arguments ; list of string-valued g-expressions
0ded70f3
LC
88 (default '())))
89
909147e4
RW
90;; PAM limits entries are used by the pam_limits PAM module to set or override
91;; limits on system resources for user sessions. The format is specified
92;; here: http://linux-pam.org/Linux-PAM-html/sag-pam_limits.html
93(define-record-type <pam-limits-entry>
94 (make-pam-limits-entry domain type item value)
95 pam-limits-entry?
96 (domain pam-limits-entry-domain) ; string
97 (type pam-limits-entry-type) ; symbol
98 (item pam-limits-entry-item) ; symbol
99 (value pam-limits-entry-value)) ; symbol or number
100
101(define (pam-limits-entry domain type item value)
102 "Construct a pam-limits-entry ensuring that the provided values are valid."
103 (define (valid? value)
104 (case item
105 ((priority) (number? value))
106 ((nice) (and (number? value)
107 (>= value -20)
108 (<= value 19)))
109 (else (or (and (number? value)
110 (>= value -1))
111 (member value '(unlimited infinity))))))
112 (define items
113 (list 'core 'data 'fsize
114 'memlock 'nofile 'rss
115 'stack 'cpu 'nproc
116 'as 'maxlogins 'maxsyslogins
117 'priority 'locks 'sigpending
118 'msgqueue 'nice 'rtprio))
119 (when (not (member type '(hard soft both)))
120 (error "invalid limit type" type))
121 (when (not (member item items))
122 (error "invalid limit item" item))
123 (when (not (valid? value))
124 (error "invalid limit value" value))
125 (make-pam-limits-entry domain type item value))
126
127(define (pam-limits-entry->string entry)
128 "Convert a pam-limits-entry record to a string."
129 (match entry
130 (($ <pam-limits-entry> domain type item value)
131 (string-join (list domain
132 (if (eq? type 'both)
133 "-"
134 (symbol->string type))
135 (symbol->string item)
136 (cond
137 ((symbol? value)
138 (symbol->string value))
139 (else
140 (number->string value))))
141 " "))))
142
0ded70f3 143(define (pam-service->configuration service)
b5f4e686
LC
144 "Return the derivation building the configuration file for SERVICE, to be
145dumped in /etc/pam.d/NAME, where NAME is the name of SERVICE."
146 (define (entry->gexp type entry)
0ded70f3
LC
147 (match entry
148 (($ <pam-entry> control module (arguments ...))
b5f4e686
LC
149 #~(format #t "~a ~a ~a ~a~%"
150 #$type #$control #$module
151 (string-join (list #$@arguments))))))
0ded70f3
LC
152
153 (match service
154 (($ <pam-service> name account auth password session)
b5f4e686
LC
155 (define builder
156 #~(begin
157 (with-output-to-file #$output
158 (lambda ()
159 #$@(append (map (cut entry->gexp "account" <>) account)
160 (map (cut entry->gexp "auth" <>) auth)
161 (map (cut entry->gexp "password" <>) password)
162 (map (cut entry->gexp "session" <>) session))
163 #t))))
164
23afe939 165 (computed-file name builder))))
0ded70f3 166
d9f0a237 167(define (pam-services->directory services)
0ded70f3
LC
168 "Return the derivation to build the configuration directory to be used as
169/etc/pam.d for SERVICES."
23afe939
LC
170 (let ((names (map pam-service-name services))
171 (files (map pam-service->configuration services)))
0ded70f3 172 (define builder
b5f4e686 173 #~(begin
11dddd8a
LC
174 (use-modules (ice-9 match)
175 (srfi srfi-1))
0ded70f3 176
b5f4e686
LC
177 (mkdir #$output)
178 (for-each (match-lambda
0adfe95a
LC
179 ((name file)
180 (symlink file (string-append #$output "/" name))))
11dddd8a
LC
181
182 ;; Since <pam-service> objects cannot be compared with
183 ;; 'equal?' since they contain gexps, which contain
184 ;; closures, use 'delete-duplicates' on the build-side
185 ;; instead. See <http://bugs.gnu.org/20037>.
186 (delete-duplicates '#$(zip names files)))))
0ded70f3 187
23afe939 188 (computed-file "pam.d" builder)))
0ded70f3
LC
189
190(define %pam-other-services
191 ;; The "other" PAM configuration, which denies everything (see
192 ;; <http://www.linux-pam.org/Linux-PAM-html/sag-configuration-example.html>.)
193 (let ((deny (pam-entry
194 (control "required")
195 (module "pam_deny.so"))))
196 (pam-service
197 (name "other")
198 (account (list deny))
199 (auth (list deny))
200 (password (list deny))
201 (session (list deny)))))
202
203(define unix-pam-service
204 (let ((unix (pam-entry
205 (control "required")
af9908ff
SB
206 (module "pam_unix.so")))
207 (env (pam-entry ; to honor /etc/environment.
208 (control "required")
209 (module "pam_env.so"))))
e586257b 210 (lambda* (name #:key allow-empty-passwords? (allow-root? #f) motd)
0ded70f3 211 "Return a standard Unix-style PAM service for NAME. When
e586257b
RW
212ALLOW-EMPTY-PASSWORDS? is true, allow empty passwords. When ALLOW-ROOT? is
213true, allow root to run the command without authentication. When MOTD is
214true, it should be a file-like object used as the message-of-the-day."
0ded70f3
LC
215 ;; See <http://www.linux-pam.org/Linux-PAM-html/sag-configuration-example.html>.
216 (let ((name* name))
217 (pam-service
218 (name name*)
219 (account (list unix))
e586257b
RW
220 (auth (append (if allow-root?
221 (list (pam-entry
222 (control "sufficient")
223 (module "pam_rootok.so")))
224 '())
225 (list (if allow-empty-passwords?
226 (pam-entry
227 (control "required")
228 (module "pam_unix.so")
229 (arguments '("nullok")))
230 unix))))
9297065a
SB
231 (password (list (pam-entry
232 (control "required")
233 (module "pam_unix.so")
234 ;; Store SHA-512 encrypted passwords in /etc/shadow.
235 (arguments '("sha512" "shadow")))))
43a27798 236 (session (if motd
af9908ff 237 (list env unix
43a27798
LC
238 (pam-entry
239 (control "optional")
240 (module "pam_motd.so")
b5f4e686
LC
241 (arguments
242 (list #~(string-append "motd=" #$motd)))))
af9908ff 243 (list env unix))))))))
0ded70f3 244
da417ffe
LC
245(define (rootok-pam-service command)
246 "Return a PAM service for COMMAND such that 'root' does not need to
247authenticate to run COMMAND."
248 (let ((unix (pam-entry
249 (control "required")
250 (module "pam_unix.so"))))
251 (pam-service
252 (name command)
253 (account (list unix))
254 (auth (list (pam-entry
255 (control "sufficient")
256 (module "pam_rootok.so"))))
257 (password (list unix))
258 (session (list unix)))))
259
09e028f4
LC
260(define* (base-pam-services #:key allow-empty-passwords?)
261 "Return the list of basic PAM services everyone would want."
da417ffe
LC
262 ;; TODO: Add other Shadow programs?
263 (append (list %pam-other-services)
264
265 ;; These programs are setuid-root.
266 (map (cut unix-pam-service <>
267 #:allow-empty-passwords? allow-empty-passwords?)
e586257b
RW
268 '("passwd" "sudo"))
269 ;; This is setuid-root, as well. Allow root to run "su" without
270 ;; authenticating.
271 (list (unix-pam-service "su"
272 #:allow-empty-passwords? allow-empty-passwords?
273 #:allow-root? #t))
da417ffe
LC
274
275 ;; These programs are not setuid-root, and we want root to be able
276 ;; to run them without having to authenticate (notably because
277 ;; 'useradd' and 'groupadd' are run during system activation.)
278 (map rootok-pam-service
279 '("useradd" "userdel" "usermod"
280 "groupadd" "groupdel" "groupmod"))))
09e028f4 281
0adfe95a 282\f
fbc31dc1
LC
283;;;
284;;; System-wide environment variables.
285;;;
286
287(define (environment-variables->environment-file vars)
288 "Return a file for pam_env(8) that contains environment variables VARS."
289 (apply mixed-text-file "environment"
290 (append-map (match-lambda
291 ((key . value)
292 (list key "=" value "\n")))
293 vars)))
294
295(define session-environment-service-type
296 (service-type
297 (name 'session-environment)
298 (extensions
299 (list (service-extension
300 etc-service-type
301 (lambda (vars)
302 (list `("environment"
303 ,(environment-variables->environment-file vars)))))))
304 (compose concatenate)
305 (extend append)
306 (description
307 "Populate @file{/etc/environment}, which is honored by @code{pam_env},
308with the specified environment variables. The value of this service is a list
309of name/value pairs for environments variables, such as:
310
311@example
312'((\"TZ\" . \"Canada/Pacific\"))
313@end example\n")))
314
315(define (session-environment-service vars)
316 "Return a service that builds the @file{/etc/environment}, which can be read
317by PAM-aware applications to set environment variables for sessions.
318
319VARS should be an association list in which both the keys and the values are
320strings or string-valued gexps."
321 (service session-environment-service-type vars))
322
323
324\f
0adfe95a
LC
325;;;
326;;; PAM root service.
327;;;
328
12c00bca
LC
329;; Overall PAM configuration: a list of services, plus a procedure that takes
330;; one <pam-service> and returns a <pam-service>. The procedure is used to
331;; implement cross-cutting concerns such as the use of the 'elogind.so'
332;; session module that keeps track of logged-in users.
333(define-record-type* <pam-configuration>
334 pam-configuration make-pam-configuration? pam-configuration?
335 (services pam-configuration-services) ;list of <pam-service>
336 (transform pam-configuration-transform)) ;procedure
337
338(define (/etc-entry config)
339 "Return the /etc/pam.d entry corresponding to CONFIG."
340 (match config
341 (($ <pam-configuration> services transform)
342 (let ((services (map transform services)))
343 `(("pam.d" ,(pam-services->directory services)))))))
344
345(define (extend-configuration initial extensions)
346 "Extend INITIAL with NEW."
347 (let-values (((services procs)
348 (partition pam-service? extensions)))
349 (pam-configuration
350 (services (append (pam-configuration-services initial)
351 services))
352 (transform (apply compose
353 (pam-configuration-transform initial)
354 procs)))))
0adfe95a
LC
355
356(define pam-root-service-type
357 (service-type (name 'pam)
358 (extensions (list (service-extension etc-service-type
359 /etc-entry)))
12c00bca
LC
360
361 ;; Arguments include <pam-service> as well as procedures.
0adfe95a 362 (compose concatenate)
12c00bca 363 (extend extend-configuration)))
0adfe95a 364
12c00bca 365(define* (pam-root-service base #:key (transform identity))
0adfe95a 366 "The \"root\" PAM service, which collects <pam-service> instance and turns
12c00bca
LC
367them into a /etc/pam.d directory, including the <pam-service> listed in BASE.
368TRANSFORM is a procedure that takes a <pam-service> and returns a
369<pam-service>. It can be used to implement cross-cutting concerns that affect
370all the PAM services."
371 (service pam-root-service-type
372 (pam-configuration (services base)
373 (transform transform))))
0adfe95a 374
290ad224 375