pam.scm 15 KB

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