authentication.scm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2018 Danny Milosavljevic <dannym@scratchpost.org>
  3. ;;; Copyright © 2018, 2019 Ricardo Wurmus <rekado@elephly.net>
  4. ;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
  5. ;;;
  6. ;;; This file is part of GNU Guix.
  7. ;;;
  8. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  9. ;;; under the terms of the GNU General Public License as published by
  10. ;;; the Free Software Foundation; either version 3 of the License, or (at
  11. ;;; your option) any later version.
  12. ;;;
  13. ;;; GNU Guix is distributed in the hope that it will be useful, but
  14. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;;; GNU General Public License for more details.
  17. ;;;
  18. ;;; You should have received a copy of the GNU General Public License
  19. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  20. (define-module (gnu services authentication)
  21. #:use-module (gnu services)
  22. #:use-module (gnu services base)
  23. #:use-module (gnu services configuration)
  24. #:use-module (gnu services dbus)
  25. #:use-module (gnu services shepherd)
  26. #:use-module (gnu system pam)
  27. #:use-module (gnu system shadow)
  28. #:use-module (gnu packages admin)
  29. #:use-module (gnu packages freedesktop)
  30. #:use-module (gnu packages openldap)
  31. #:use-module (guix gexp)
  32. #:use-module (guix records)
  33. #:use-module (guix packages)
  34. #:use-module (guix modules)
  35. #:use-module (ice-9 match)
  36. #:use-module (srfi srfi-1)
  37. #:use-module (srfi srfi-26)
  38. #:export (fprintd-configuration
  39. fprintd-configuration?
  40. fprintd-service-type
  41. nslcd-configuration
  42. nslcd-configuration?
  43. nslcd-service-type))
  44. (define-configuration fprintd-configuration
  45. (fprintd (package fprintd)
  46. "The fprintd package"))
  47. (define (fprintd-dbus-service config)
  48. (list (fprintd-configuration-fprintd config)))
  49. (define fprintd-service-type
  50. (service-type (name 'fprintd)
  51. (extensions
  52. (list (service-extension dbus-root-service-type
  53. fprintd-dbus-service)
  54. (service-extension polkit-service-type
  55. fprintd-dbus-service)))
  56. (default-value (fprintd-configuration))
  57. (description
  58. "Run fprintd, a fingerprint management daemon.")))
  59. ;;;
  60. ;;; NSS Pam LDAP service (nslcd)
  61. ;;;
  62. (define (uglify-field-name name)
  63. (match name
  64. ('filters "filter")
  65. ('maps "map")
  66. (_ (string-map (match-lambda
  67. (#\- #\_)
  68. (chr chr))
  69. (symbol->string name)))))
  70. (define (value->string val)
  71. (cond
  72. ((boolean? val)
  73. (if val "on" "off"))
  74. ((number? val)
  75. (number->string val))
  76. ((symbol? val)
  77. (string-map (match-lambda
  78. (#\- #\_)
  79. (chr chr))
  80. (symbol->string val)))
  81. (else val)))
  82. (define (serialize-field field-name val)
  83. (if (eq? field-name 'pam-services)
  84. #t
  85. (format #t "~a ~a\n"
  86. (uglify-field-name field-name)
  87. (value->string val))))
  88. (define serialize-string serialize-field)
  89. (define serialize-boolean serialize-field)
  90. (define serialize-number serialize-field)
  91. (define (serialize-list field-name val)
  92. (map (cut serialize-field field-name <>) val))
  93. (define-maybe string)
  94. (define-maybe boolean)
  95. (define-maybe number)
  96. (define (ssl-option? val)
  97. (or (boolean? val)
  98. (eq? val 'start-tls)))
  99. (define serialize-ssl-option serialize-field)
  100. (define-maybe ssl-option)
  101. (define (tls-reqcert-option? val)
  102. (member val '(never allow try demand hard)))
  103. (define serialize-tls-reqcert-option serialize-field)
  104. (define-maybe tls-reqcert-option)
  105. (define (deref-option? val)
  106. (member val '(never searching finding always)))
  107. (define serialize-deref-option serialize-field)
  108. (define-maybe deref-option)
  109. (define (comma-separated-list-of-strings? val)
  110. (and (list? val)
  111. (every string? val)))
  112. (define (ignore-users-option? val)
  113. (or (comma-separated-list-of-strings? val)
  114. (eq? 'all-local val)))
  115. (define (serialize-ignore-users-option field-name val)
  116. (serialize-field field-name (if (eq? 'all-local val)
  117. val
  118. (string-join val ","))))
  119. (define-maybe ignore-users-option)
  120. (define (log-option? val)
  121. (let ((valid-scheme? (lambda (scheme)
  122. (or (string? scheme)
  123. (member scheme '(none syslog))))))
  124. (match val
  125. ((scheme level)
  126. (and (valid-scheme? scheme)
  127. (member level '(crit error warning notice info debug))))
  128. ((scheme)
  129. (valid-scheme? scheme)))))
  130. (define (serialize-log-option field-name val)
  131. (serialize-field field-name
  132. (string-join (map (cut format #f "~a" <>) val))))
  133. (define (valid-map? val)
  134. "Is VAL a supported map name?"
  135. (member val
  136. '(alias aliases ether ethers group host hosts netgroup network networks
  137. passwd protocol protocols rpc service services shadow)))
  138. (define (scope-option? val)
  139. (let ((valid-scopes '(subtree onelevel base children)))
  140. (match val
  141. ((map-name scope)
  142. (and (valid-map? map-name)
  143. (member scope valid-scopes)))
  144. ((scope)
  145. (member scope valid-scopes)))))
  146. (define (serialize-scope-option field-name val)
  147. (serialize-field field-name
  148. (string-join (map (cut format #f "~a" <>) val))))
  149. (define (map-entry? val)
  150. (match val
  151. (((? valid-map? map-name)
  152. (? string? attribute)
  153. (? string? new-attribute)) #t)
  154. (_ #f)))
  155. (define (list-of-map-entries? val)
  156. (and (list? val)
  157. (every map-entry? val)))
  158. (define (filter-entry? val)
  159. (match val
  160. (((? valid-map? map-name)
  161. (? string? filter-expression)) #t)
  162. (_ #f)))
  163. (define (list-of-filter-entries? val)
  164. (and (list? val)
  165. (every filter-entry? val)))
  166. (define (serialize-filter-entry field-name val)
  167. (serialize-field 'filter
  168. (match val
  169. (((? valid-map? map-name)
  170. (? string? filter-expression))
  171. (string-append (symbol->string map-name)
  172. " " filter-expression)))))
  173. (define (serialize-list-of-filter-entries field-name val)
  174. (for-each (cut serialize-filter-entry field-name <>) val))
  175. (define (serialize-map-entry field-name val)
  176. (serialize-field 'map
  177. (match val
  178. (((? valid-map? map-name)
  179. (? string? attribute)
  180. (? string? new-attribute))
  181. (string-append (symbol->string map-name)
  182. " " attribute
  183. " " new-attribute)))))
  184. (define (serialize-list-of-map-entries field-name val)
  185. (for-each (cut serialize-map-entry field-name <>) val))
  186. (define-configuration nslcd-configuration
  187. (nss-pam-ldapd
  188. (package nss-pam-ldapd)
  189. "The NSS-PAM-LDAPD package to use.")
  190. ;; Runtime options
  191. (threads
  192. (maybe-number 'disabled)
  193. "The number of threads to start that can handle requests and perform LDAP
  194. queries. Each thread opens a separate connection to the LDAP server. The
  195. default is to start 5 threads.")
  196. (uid
  197. (string "nslcd")
  198. "This specifies the user id with which the daemon should be run.")
  199. (gid
  200. (string "nslcd")
  201. "This specifies the group id with which the daemon should be run.")
  202. (log
  203. (log-option '("/var/log/nslcd" info))
  204. "This option controls the way logging is done via a list containing SCHEME
  205. and LEVEL. The SCHEME argument may either be the symbols \"none\" or
  206. \"syslog\", or an absolute file name. The LEVEL argument is optional and
  207. specifies the log level. The log level may be one of the following symbols:
  208. \"crit\", \"error\", \"warning\", \"notice\", \"info\" or \"debug\". All
  209. messages with the specified log level or higher are logged.")
  210. ;; LDAP connection settings
  211. (uri
  212. (list '("ldap://localhost:389/"))
  213. "The list of LDAP server URIs. Normally, only the first server will be
  214. used with the following servers as fall-back.")
  215. (ldap-version
  216. (maybe-string 'disabled)
  217. "The version of the LDAP protocol to use. The default is to use the
  218. maximum version supported by the LDAP library.")
  219. (binddn
  220. (maybe-string 'disabled)
  221. "Specifies the distinguished name with which to bind to the directory
  222. server for lookups. The default is to bind anonymously.")
  223. (bindpw
  224. (maybe-string 'disabled)
  225. "Specifies the credentials with which to bind. This option is only
  226. applicable when used with binddn.")
  227. (rootpwmoddn
  228. (maybe-string 'disabled)
  229. "Specifies the distinguished name to use when the root user tries to modify
  230. a user's password using the PAM module.")
  231. (rootpwmodpw
  232. (maybe-string 'disabled)
  233. "Specifies the credentials with which to bind if the root user tries to
  234. change a user's password. This option is only applicable when used with
  235. rootpwmoddn")
  236. ;; SASL authentication options
  237. (sasl-mech
  238. (maybe-string 'disabled)
  239. "Specifies the SASL mechanism to be used when performing SASL
  240. authentication.")
  241. (sasl-realm
  242. (maybe-string 'disabled)
  243. "Specifies the SASL realm to be used when performing SASL authentication.")
  244. (sasl-authcid
  245. (maybe-string 'disabled)
  246. "Specifies the authentication identity to be used when performing SASL
  247. authentication.")
  248. (sasl-authzid
  249. (maybe-string 'disabled)
  250. "Specifies the authorization identity to be used when performing SASL
  251. authentication.")
  252. (sasl-canonicalize?
  253. (maybe-boolean 'disabled)
  254. "Determines whether the LDAP server host name should be canonicalised. If
  255. this is enabled the LDAP library will do a reverse host name lookup. By
  256. default, it is left up to the LDAP library whether this check is performed or
  257. not.")
  258. ;; Kerberos authentication options
  259. (krb5-ccname
  260. (maybe-string 'disabled)
  261. "Set the name for the GSS-API Kerberos credentials cache.")
  262. ;; Search / mapping options
  263. (base
  264. (string "dc=example,dc=com")
  265. "The directory search base.")
  266. (scope
  267. (scope-option '(subtree))
  268. "Specifies the search scope (subtree, onelevel, base or children). The
  269. default scope is subtree; base scope is almost never useful for name service
  270. lookups; children scope is not supported on all servers.")
  271. (deref
  272. (maybe-deref-option 'disabled)
  273. "Specifies the policy for dereferencing aliases. The default policy is to
  274. never dereference aliases.")
  275. (referrals
  276. (maybe-boolean 'disabled)
  277. "Specifies whether automatic referral chasing should be enabled. The
  278. default behaviour is to chase referrals.")
  279. (maps
  280. (list-of-map-entries '())
  281. "This option allows for custom attributes to be looked up instead of the
  282. default RFC 2307 attributes. It is a list of maps, each consisting of the
  283. name of a map, the RFC 2307 attribute to match and the query expression for
  284. the attribute as it is available in the directory.")
  285. (filters
  286. (list-of-filter-entries '())
  287. "A list of filters consisting of the name of a map to which the filter
  288. applies and an LDAP search filter expression.")
  289. ;; Timing / reconnect options
  290. (bind-timelimit
  291. (maybe-number 'disabled)
  292. "Specifies the time limit in seconds to use when connecting to the
  293. directory server. The default value is 10 seconds.")
  294. (timelimit
  295. (maybe-number 'disabled)
  296. "Specifies the time limit (in seconds) to wait for a response from the LDAP
  297. server. A value of zero, which is the default, is to wait indefinitely for
  298. searches to be completed.")
  299. (idle-timelimit
  300. (maybe-number 'disabled)
  301. "Specifies the period if inactivity (in seconds) after which the con‐
  302. nection to the LDAP server will be closed. The default is not to time out
  303. connections.")
  304. (reconnect-sleeptime
  305. (maybe-number 'disabled)
  306. "Specifies the number of seconds to sleep when connecting to all LDAP
  307. servers fails. By default one second is waited between the first failure and
  308. the first retry.")
  309. (reconnect-retrytime
  310. (maybe-number 'disabled)
  311. "Specifies the time after which the LDAP server is considered to be
  312. permanently unavailable. Once this time is reached retries will be done only
  313. once per this time period. The default value is 10 seconds.")
  314. ;; TLS options
  315. (ssl
  316. (maybe-ssl-option 'disabled)
  317. "Specifies whether to use SSL/TLS or not (the default is not to). If
  318. 'start-tls is specified then StartTLS is used rather than raw LDAP over SSL.")
  319. (tls-reqcert
  320. (maybe-tls-reqcert-option 'disabled)
  321. "Specifies what checks to perform on a server-supplied certificate.
  322. The meaning of the values is described in the ldap.conf(5) manual page.")
  323. (tls-cacertdir
  324. (maybe-string 'disabled)
  325. "Specifies the directory containing X.509 certificates for peer authen‐
  326. tication. This parameter is ignored when using GnuTLS.")
  327. (tls-cacertfile
  328. (maybe-string 'disabled)
  329. "Specifies the path to the X.509 certificate for peer authentication.")
  330. (tls-randfile
  331. (maybe-string 'disabled)
  332. "Specifies the path to an entropy source. This parameter is ignored when
  333. using GnuTLS.")
  334. (tls-ciphers
  335. (maybe-string 'disabled)
  336. "Specifies the ciphers to use for TLS as a string.")
  337. (tls-cert
  338. (maybe-string 'disabled)
  339. "Specifies the path to the file containing the local certificate for client
  340. TLS authentication.")
  341. (tls-key
  342. (maybe-string 'disabled)
  343. "Specifies the path to the file containing the private key for client TLS
  344. authentication.")
  345. ;; Other options
  346. (pagesize
  347. (maybe-number 'disabled)
  348. "Set this to a number greater than 0 to request paged results from the LDAP
  349. server in accordance with RFC2696. The default (0) is to not request paged
  350. results.")
  351. (nss-initgroups-ignoreusers
  352. (maybe-ignore-users-option 'disabled)
  353. "This option prevents group membership lookups through LDAP for the
  354. specified users. Alternatively, the value 'all-local may be used. With that
  355. value nslcd builds a full list of non-LDAP users on startup.")
  356. (nss-min-uid
  357. (maybe-number 'disabled)
  358. "This option ensures that LDAP users with a numeric user id lower than the
  359. specified value are ignored.")
  360. (nss-uid-offset
  361. (maybe-number 'disabled)
  362. "This option specifies an offset that is added to all LDAP numeric user
  363. ids. This can be used to avoid user id collisions with local users.")
  364. (nss-gid-offset
  365. (maybe-number 'disabled)
  366. "This option specifies an offset that is added to all LDAP numeric group
  367. ids. This can be used to avoid user id collisions with local groups.")
  368. (nss-nested-groups
  369. (maybe-boolean 'disabled)
  370. "If this option is set, the member attribute of a group may point to
  371. another group. Members of nested groups are also returned in the higher level
  372. group and parent groups are returned when finding groups for a specific user.
  373. The default is not to perform extra searches for nested groups.")
  374. (nss-getgrent-skipmembers
  375. (maybe-boolean 'disabled)
  376. "If this option is set, the group member list is not retrieved when looking
  377. up groups. Lookups for finding which groups a user belongs to will remain
  378. functional so the user will likely still get the correct groups assigned on
  379. login.")
  380. (nss-disable-enumeration
  381. (maybe-boolean 'disabled)
  382. "If this option is set, functions which cause all user/group entries to be
  383. loaded from the directory will not succeed in doing so. This can dramatically
  384. reduce LDAP server load in situations where there are a great number of users
  385. and/or groups. This option is not recommended for most configurations.")
  386. (validnames
  387. (maybe-string 'disabled)
  388. "This option can be used to specify how user and group names are verified
  389. within the system. This pattern is used to check all user and group names
  390. that are requested and returned from LDAP.")
  391. (ignorecase
  392. (maybe-boolean 'disabled)
  393. "This specifies whether or not to perform searches using case-insensitive
  394. matching. Enabling this could open up the system to authorization bypass
  395. vulnerabilities and introduce nscd cache poisoning vulnerabilities which allow
  396. denial of service.")
  397. (pam-authc-ppolicy
  398. (maybe-boolean 'disabled)
  399. "This option specifies whether password policy controls are requested and
  400. handled from the LDAP server when performing user authentication.")
  401. (pam-authc-search
  402. (maybe-string 'disabled)
  403. "By default nslcd performs an LDAP search with the user's credentials after
  404. BIND (authentication) to ensure that the BIND operation was successful. The
  405. default search is a simple check to see if the user's DN exists. A search
  406. filter can be specified that will be used instead. It should return at least
  407. one entry.")
  408. (pam-authz-search
  409. (maybe-string 'disabled)
  410. "This option allows flexible fine tuning of the authorisation check that
  411. should be performed. The search filter specified is executed and if any
  412. entries match, access is granted, otherwise access is denied.")
  413. (pam-password-prohibit-message
  414. (maybe-string 'disabled)
  415. "If this option is set password modification using pam_ldap will be denied
  416. and the specified message will be presented to the user instead. The message
  417. can be used to direct the user to an alternative means of changing their
  418. password.")
  419. ;; Options for extension of pam-root-service-type.
  420. (pam-services
  421. (list '())
  422. "List of pam service names for which LDAP authentication should suffice."))
  423. (define %nslcd-accounts
  424. (list (user-group
  425. (name "nslcd")
  426. (system? #t))
  427. (user-account
  428. (name "nslcd")
  429. (group "nslcd")
  430. (comment "NSLCD service account")
  431. (home-directory "/var/empty")
  432. (shell (file-append shadow "/sbin/nologin"))
  433. (system? #t))))
  434. (define (nslcd-config-file config)
  435. "Return an NSLCD configuration file."
  436. (plain-file "nslcd.conf"
  437. (with-output-to-string
  438. (lambda ()
  439. (serialize-configuration config nslcd-configuration-fields)
  440. ;; The file must end with a newline character.
  441. (format #t "\n")))))
  442. ;; XXX: The file should only be readable by root if it contains a "bindpw"
  443. ;; declaration. Unfortunately, this etc-service-type extension does not
  444. ;; support setting file modes, so we do this in the activation service.
  445. (define (nslcd-etc-service config)
  446. `(("nslcd.conf" ,(nslcd-config-file config))))
  447. (define (nslcd-shepherd-service config)
  448. (list (shepherd-service
  449. (documentation "Run the nslcd service for resolving names from LDAP.")
  450. (provision '(nslcd))
  451. (requirement '(networking user-processes))
  452. (start #~(make-forkexec-constructor
  453. (list (string-append #$(nslcd-configuration-nss-pam-ldapd config)
  454. "/sbin/nslcd")
  455. "--nofork")
  456. #:pid-file "/var/run/nslcd/nslcd.pid"
  457. #:environment-variables
  458. (list (string-append "LD_LIBRARY_PATH="
  459. #$(nslcd-configuration-nss-pam-ldapd config)
  460. "/lib"))))
  461. (stop #~(make-kill-destructor)))))
  462. (define (pam-ldap-pam-service config)
  463. "Return a PAM service for LDAP authentication."
  464. (define pam-ldap-module
  465. #~(string-append #$(nslcd-configuration-nss-pam-ldapd config)
  466. "/lib/security/pam_ldap.so"))
  467. (lambda (pam)
  468. (if (member (pam-service-name pam)
  469. (nslcd-configuration-pam-services config))
  470. (let ((sufficient
  471. (pam-entry
  472. (control "sufficient")
  473. (module pam-ldap-module))))
  474. (pam-service
  475. (inherit pam)
  476. (auth (cons sufficient (pam-service-auth pam)))
  477. (session (cons sufficient (pam-service-session pam)))
  478. (account (cons sufficient (pam-service-account pam)))))
  479. pam)))
  480. (define (pam-ldap-pam-services config)
  481. (list (pam-ldap-pam-service config)))
  482. (define %nslcd-activation
  483. (with-imported-modules (source-module-closure '((gnu build activation)))
  484. #~(begin
  485. (use-modules (gnu build activation))
  486. (let ((rundir "/var/run/nslcd")
  487. (user (getpwnam "nslcd")))
  488. (mkdir-p/perms rundir user #o755)
  489. (when (file-exists? "/etc/nslcd.conf")
  490. (chmod "/etc/nslcd.conf" #o400))))))
  491. (define nslcd-service-type
  492. (service-type
  493. (name 'nslcd)
  494. (description "Run the NSLCD service for looking up names from LDAP.")
  495. (extensions
  496. (list (service-extension account-service-type
  497. (const %nslcd-accounts))
  498. (service-extension etc-service-type
  499. nslcd-etc-service)
  500. (service-extension activation-service-type
  501. (const %nslcd-activation))
  502. (service-extension pam-root-service-type
  503. pam-ldap-pam-services)
  504. (service-extension nscd-service-type
  505. (const (list nss-pam-ldapd)))
  506. (service-extension shepherd-root-service-type
  507. nslcd-shepherd-service)))
  508. (default-value (nslcd-configuration))))
  509. (define (generate-nslcd-documentation)
  510. (generate-documentation
  511. `((nslcd-configuration ,nslcd-configuration-fields))
  512. 'nslcd-configuration))