pam.scm 15 KB

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