123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 |
- ;;; GNU Guix --- Functional package management for GNU
- ;;; Copyright © 2018 Danny Milosavljevic <dannym@scratchpost.org>
- ;;; Copyright © 2018, 2019 Ricardo Wurmus <rekado@elephly.net>
- ;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
- ;;;
- ;;; This file is part of GNU Guix.
- ;;;
- ;;; GNU Guix is free software; you can redistribute it and/or modify it
- ;;; under the terms of the GNU General Public License as published by
- ;;; the Free Software Foundation; either version 3 of the License, or (at
- ;;; your option) any later version.
- ;;;
- ;;; GNU Guix is distributed in the hope that it will be useful, but
- ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;;; GNU General Public License for more details.
- ;;;
- ;;; You should have received a copy of the GNU General Public License
- ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
- (define-module (gnu services authentication)
- #:use-module (gnu services)
- #:use-module (gnu services base)
- #:use-module (gnu services configuration)
- #:use-module (gnu services dbus)
- #:use-module (gnu services shepherd)
- #:use-module (gnu system pam)
- #:use-module (gnu system shadow)
- #:use-module (gnu packages admin)
- #:use-module (gnu packages freedesktop)
- #:use-module (gnu packages openldap)
- #:use-module (guix gexp)
- #:use-module (guix records)
- #:use-module (guix packages)
- #:use-module (guix modules)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-26)
- #:export (fprintd-configuration
- fprintd-configuration?
- fprintd-service-type
- nslcd-configuration
- nslcd-configuration?
- nslcd-service-type))
- (define-configuration fprintd-configuration
- (fprintd (package fprintd)
- "The fprintd package"))
- (define (fprintd-dbus-service config)
- (list (fprintd-configuration-fprintd config)))
- (define fprintd-service-type
- (service-type (name 'fprintd)
- (extensions
- (list (service-extension dbus-root-service-type
- fprintd-dbus-service)
- (service-extension polkit-service-type
- fprintd-dbus-service)))
- (default-value (fprintd-configuration))
- (description
- "Run fprintd, a fingerprint management daemon.")))
- ;;;
- ;;; NSS Pam LDAP service (nslcd)
- ;;;
- (define (uglify-field-name name)
- (match name
- ('filters "filter")
- ('maps "map")
- (_ (string-map (match-lambda
- (#\- #\_)
- (chr chr))
- (symbol->string name)))))
- (define (value->string val)
- (cond
- ((boolean? val)
- (if val "on" "off"))
- ((number? val)
- (number->string val))
- ((symbol? val)
- (string-map (match-lambda
- (#\- #\_)
- (chr chr))
- (symbol->string val)))
- (else val)))
- (define (serialize-field field-name val)
- (if (eq? field-name 'pam-services)
- #t
- (format #t "~a ~a\n"
- (uglify-field-name field-name)
- (value->string val))))
- (define serialize-string serialize-field)
- (define serialize-boolean serialize-field)
- (define serialize-number serialize-field)
- (define (serialize-list field-name val)
- (map (cut serialize-field field-name <>) val))
- (define-maybe string)
- (define-maybe boolean)
- (define-maybe number)
- (define (ssl-option? val)
- (or (boolean? val)
- (eq? val 'start-tls)))
- (define serialize-ssl-option serialize-field)
- (define-maybe ssl-option)
- (define (tls-reqcert-option? val)
- (member val '(never allow try demand hard)))
- (define serialize-tls-reqcert-option serialize-field)
- (define-maybe tls-reqcert-option)
- (define (deref-option? val)
- (member val '(never searching finding always)))
- (define serialize-deref-option serialize-field)
- (define-maybe deref-option)
- (define (comma-separated-list-of-strings? val)
- (and (list? val)
- (every string? val)))
- (define (ignore-users-option? val)
- (or (comma-separated-list-of-strings? val)
- (eq? 'all-local val)))
- (define (serialize-ignore-users-option field-name val)
- (serialize-field field-name (if (eq? 'all-local val)
- val
- (string-join val ","))))
- (define-maybe ignore-users-option)
- (define (log-option? val)
- (let ((valid-scheme? (lambda (scheme)
- (or (string? scheme)
- (member scheme '(none syslog))))))
- (match val
- ((scheme level)
- (and (valid-scheme? scheme)
- (member level '(crit error warning notice info debug))))
- ((scheme)
- (valid-scheme? scheme)))))
- (define (serialize-log-option field-name val)
- (serialize-field field-name
- (string-join (map (cut format #f "~a" <>) val))))
- (define (valid-map? val)
- "Is VAL a supported map name?"
- (member val
- '(alias aliases ether ethers group host hosts netgroup network networks
- passwd protocol protocols rpc service services shadow)))
- (define (scope-option? val)
- (let ((valid-scopes '(subtree onelevel base children)))
- (match val
- ((map-name scope)
- (and (valid-map? map-name)
- (member scope valid-scopes)))
- ((scope)
- (member scope valid-scopes)))))
- (define (serialize-scope-option field-name val)
- (serialize-field field-name
- (string-join (map (cut format #f "~a" <>) val))))
- (define (map-entry? val)
- (match val
- (((? valid-map? map-name)
- (? string? attribute)
- (? string? new-attribute)) #t)
- (_ #f)))
- (define (list-of-map-entries? val)
- (and (list? val)
- (every map-entry? val)))
- (define (filter-entry? val)
- (match val
- (((? valid-map? map-name)
- (? string? filter-expression)) #t)
- (_ #f)))
- (define (list-of-filter-entries? val)
- (and (list? val)
- (every filter-entry? val)))
- (define (serialize-filter-entry field-name val)
- (serialize-field 'filter
- (match val
- (((? valid-map? map-name)
- (? string? filter-expression))
- (string-append (symbol->string map-name)
- " " filter-expression)))))
- (define (serialize-list-of-filter-entries field-name val)
- (for-each (cut serialize-filter-entry field-name <>) val))
- (define (serialize-map-entry field-name val)
- (serialize-field 'map
- (match val
- (((? valid-map? map-name)
- (? string? attribute)
- (? string? new-attribute))
- (string-append (symbol->string map-name)
- " " attribute
- " " new-attribute)))))
- (define (serialize-list-of-map-entries field-name val)
- (for-each (cut serialize-map-entry field-name <>) val))
- (define-configuration nslcd-configuration
- (nss-pam-ldapd
- (package nss-pam-ldapd)
- "The NSS-PAM-LDAPD package to use.")
- ;; Runtime options
- (threads
- (maybe-number 'disabled)
- "The number of threads to start that can handle requests and perform LDAP
- queries. Each thread opens a separate connection to the LDAP server. The
- default is to start 5 threads.")
- (uid
- (string "nslcd")
- "This specifies the user id with which the daemon should be run.")
- (gid
- (string "nslcd")
- "This specifies the group id with which the daemon should be run.")
- (log
- (log-option '("/var/log/nslcd" info))
- "This option controls the way logging is done via a list containing SCHEME
- and LEVEL. The SCHEME argument may either be the symbols \"none\" or
- \"syslog\", or an absolute file name. The LEVEL argument is optional and
- specifies the log level. The log level may be one of the following symbols:
- \"crit\", \"error\", \"warning\", \"notice\", \"info\" or \"debug\". All
- messages with the specified log level or higher are logged.")
- ;; LDAP connection settings
- (uri
- (list '("ldap://localhost:389/"))
- "The list of LDAP server URIs. Normally, only the first server will be
- used with the following servers as fall-back.")
- (ldap-version
- (maybe-string 'disabled)
- "The version of the LDAP protocol to use. The default is to use the
- maximum version supported by the LDAP library.")
- (binddn
- (maybe-string 'disabled)
- "Specifies the distinguished name with which to bind to the directory
- server for lookups. The default is to bind anonymously.")
- (bindpw
- (maybe-string 'disabled)
- "Specifies the credentials with which to bind. This option is only
- applicable when used with binddn.")
- (rootpwmoddn
- (maybe-string 'disabled)
- "Specifies the distinguished name to use when the root user tries to modify
- a user's password using the PAM module.")
- (rootpwmodpw
- (maybe-string 'disabled)
- "Specifies the credentials with which to bind if the root user tries to
- change a user's password. This option is only applicable when used with
- rootpwmoddn")
- ;; SASL authentication options
- (sasl-mech
- (maybe-string 'disabled)
- "Specifies the SASL mechanism to be used when performing SASL
- authentication.")
- (sasl-realm
- (maybe-string 'disabled)
- "Specifies the SASL realm to be used when performing SASL authentication.")
- (sasl-authcid
- (maybe-string 'disabled)
- "Specifies the authentication identity to be used when performing SASL
- authentication.")
- (sasl-authzid
- (maybe-string 'disabled)
- "Specifies the authorization identity to be used when performing SASL
- authentication.")
- (sasl-canonicalize?
- (maybe-boolean 'disabled)
- "Determines whether the LDAP server host name should be canonicalised. If
- this is enabled the LDAP library will do a reverse host name lookup. By
- default, it is left up to the LDAP library whether this check is performed or
- not.")
- ;; Kerberos authentication options
- (krb5-ccname
- (maybe-string 'disabled)
- "Set the name for the GSS-API Kerberos credentials cache.")
- ;; Search / mapping options
- (base
- (string "dc=example,dc=com")
- "The directory search base.")
- (scope
- (scope-option '(subtree))
- "Specifies the search scope (subtree, onelevel, base or children). The
- default scope is subtree; base scope is almost never useful for name service
- lookups; children scope is not supported on all servers.")
- (deref
- (maybe-deref-option 'disabled)
- "Specifies the policy for dereferencing aliases. The default policy is to
- never dereference aliases.")
- (referrals
- (maybe-boolean 'disabled)
- "Specifies whether automatic referral chasing should be enabled. The
- default behaviour is to chase referrals.")
- (maps
- (list-of-map-entries '())
- "This option allows for custom attributes to be looked up instead of the
- default RFC 2307 attributes. It is a list of maps, each consisting of the
- name of a map, the RFC 2307 attribute to match and the query expression for
- the attribute as it is available in the directory.")
- (filters
- (list-of-filter-entries '())
- "A list of filters consisting of the name of a map to which the filter
- applies and an LDAP search filter expression.")
- ;; Timing / reconnect options
- (bind-timelimit
- (maybe-number 'disabled)
- "Specifies the time limit in seconds to use when connecting to the
- directory server. The default value is 10 seconds.")
- (timelimit
- (maybe-number 'disabled)
- "Specifies the time limit (in seconds) to wait for a response from the LDAP
- server. A value of zero, which is the default, is to wait indefinitely for
- searches to be completed.")
- (idle-timelimit
- (maybe-number 'disabled)
- "Specifies the period if inactivity (in seconds) after which the con‐
- nection to the LDAP server will be closed. The default is not to time out
- connections.")
- (reconnect-sleeptime
- (maybe-number 'disabled)
- "Specifies the number of seconds to sleep when connecting to all LDAP
- servers fails. By default one second is waited between the first failure and
- the first retry.")
- (reconnect-retrytime
- (maybe-number 'disabled)
- "Specifies the time after which the LDAP server is considered to be
- permanently unavailable. Once this time is reached retries will be done only
- once per this time period. The default value is 10 seconds.")
- ;; TLS options
- (ssl
- (maybe-ssl-option 'disabled)
- "Specifies whether to use SSL/TLS or not (the default is not to). If
- 'start-tls is specified then StartTLS is used rather than raw LDAP over SSL.")
- (tls-reqcert
- (maybe-tls-reqcert-option 'disabled)
- "Specifies what checks to perform on a server-supplied certificate.
- The meaning of the values is described in the ldap.conf(5) manual page.")
- (tls-cacertdir
- (maybe-string 'disabled)
- "Specifies the directory containing X.509 certificates for peer authen‐
- tication. This parameter is ignored when using GnuTLS.")
- (tls-cacertfile
- (maybe-string 'disabled)
- "Specifies the path to the X.509 certificate for peer authentication.")
- (tls-randfile
- (maybe-string 'disabled)
- "Specifies the path to an entropy source. This parameter is ignored when
- using GnuTLS.")
- (tls-ciphers
- (maybe-string 'disabled)
- "Specifies the ciphers to use for TLS as a string.")
- (tls-cert
- (maybe-string 'disabled)
- "Specifies the path to the file containing the local certificate for client
- TLS authentication.")
- (tls-key
- (maybe-string 'disabled)
- "Specifies the path to the file containing the private key for client TLS
- authentication.")
- ;; Other options
- (pagesize
- (maybe-number 'disabled)
- "Set this to a number greater than 0 to request paged results from the LDAP
- server in accordance with RFC2696. The default (0) is to not request paged
- results.")
- (nss-initgroups-ignoreusers
- (maybe-ignore-users-option 'disabled)
- "This option prevents group membership lookups through LDAP for the
- specified users. Alternatively, the value 'all-local may be used. With that
- value nslcd builds a full list of non-LDAP users on startup.")
- (nss-min-uid
- (maybe-number 'disabled)
- "This option ensures that LDAP users with a numeric user id lower than the
- specified value are ignored.")
- (nss-uid-offset
- (maybe-number 'disabled)
- "This option specifies an offset that is added to all LDAP numeric user
- ids. This can be used to avoid user id collisions with local users.")
- (nss-gid-offset
- (maybe-number 'disabled)
- "This option specifies an offset that is added to all LDAP numeric group
- ids. This can be used to avoid user id collisions with local groups.")
- (nss-nested-groups
- (maybe-boolean 'disabled)
- "If this option is set, the member attribute of a group may point to
- another group. Members of nested groups are also returned in the higher level
- group and parent groups are returned when finding groups for a specific user.
- The default is not to perform extra searches for nested groups.")
- (nss-getgrent-skipmembers
- (maybe-boolean 'disabled)
- "If this option is set, the group member list is not retrieved when looking
- up groups. Lookups for finding which groups a user belongs to will remain
- functional so the user will likely still get the correct groups assigned on
- login.")
- (nss-disable-enumeration
- (maybe-boolean 'disabled)
- "If this option is set, functions which cause all user/group entries to be
- loaded from the directory will not succeed in doing so. This can dramatically
- reduce LDAP server load in situations where there are a great number of users
- and/or groups. This option is not recommended for most configurations.")
- (validnames
- (maybe-string 'disabled)
- "This option can be used to specify how user and group names are verified
- within the system. This pattern is used to check all user and group names
- that are requested and returned from LDAP.")
- (ignorecase
- (maybe-boolean 'disabled)
- "This specifies whether or not to perform searches using case-insensitive
- matching. Enabling this could open up the system to authorization bypass
- vulnerabilities and introduce nscd cache poisoning vulnerabilities which allow
- denial of service.")
- (pam-authc-ppolicy
- (maybe-boolean 'disabled)
- "This option specifies whether password policy controls are requested and
- handled from the LDAP server when performing user authentication.")
- (pam-authc-search
- (maybe-string 'disabled)
- "By default nslcd performs an LDAP search with the user's credentials after
- BIND (authentication) to ensure that the BIND operation was successful. The
- default search is a simple check to see if the user's DN exists. A search
- filter can be specified that will be used instead. It should return at least
- one entry.")
- (pam-authz-search
- (maybe-string 'disabled)
- "This option allows flexible fine tuning of the authorisation check that
- should be performed. The search filter specified is executed and if any
- entries match, access is granted, otherwise access is denied.")
- (pam-password-prohibit-message
- (maybe-string 'disabled)
- "If this option is set password modification using pam_ldap will be denied
- and the specified message will be presented to the user instead. The message
- can be used to direct the user to an alternative means of changing their
- password.")
- ;; Options for extension of pam-root-service-type.
- (pam-services
- (list '())
- "List of pam service names for which LDAP authentication should suffice."))
- (define %nslcd-accounts
- (list (user-group
- (name "nslcd")
- (system? #t))
- (user-account
- (name "nslcd")
- (group "nslcd")
- (comment "NSLCD service account")
- (home-directory "/var/empty")
- (shell (file-append shadow "/sbin/nologin"))
- (system? #t))))
- (define (nslcd-config-file config)
- "Return an NSLCD configuration file."
- (plain-file "nslcd.conf"
- (with-output-to-string
- (lambda ()
- (serialize-configuration config nslcd-configuration-fields)
- ;; The file must end with a newline character.
- (format #t "\n")))))
- ;; XXX: The file should only be readable by root if it contains a "bindpw"
- ;; declaration. Unfortunately, this etc-service-type extension does not
- ;; support setting file modes, so we do this in the activation service.
- (define (nslcd-etc-service config)
- `(("nslcd.conf" ,(nslcd-config-file config))))
- (define (nslcd-shepherd-service config)
- (list (shepherd-service
- (documentation "Run the nslcd service for resolving names from LDAP.")
- (provision '(nslcd))
- (requirement '(networking user-processes))
- (start #~(make-forkexec-constructor
- (list (string-append #$(nslcd-configuration-nss-pam-ldapd config)
- "/sbin/nslcd")
- "--nofork")
- #:pid-file "/var/run/nslcd/nslcd.pid"
- #:environment-variables
- (list (string-append "LD_LIBRARY_PATH="
- #$(nslcd-configuration-nss-pam-ldapd config)
- "/lib"))))
- (stop #~(make-kill-destructor)))))
- (define (pam-ldap-pam-service config)
- "Return a PAM service for LDAP authentication."
- (define pam-ldap-module
- #~(string-append #$(nslcd-configuration-nss-pam-ldapd config)
- "/lib/security/pam_ldap.so"))
- (lambda (pam)
- (if (member (pam-service-name pam)
- (nslcd-configuration-pam-services config))
- (let ((sufficient
- (pam-entry
- (control "sufficient")
- (module pam-ldap-module))))
- (pam-service
- (inherit pam)
- (auth (cons sufficient (pam-service-auth pam)))
- (session (cons sufficient (pam-service-session pam)))
- (account (cons sufficient (pam-service-account pam)))))
- pam)))
- (define (pam-ldap-pam-services config)
- (list (pam-ldap-pam-service config)))
- (define %nslcd-activation
- (with-imported-modules (source-module-closure '((gnu build activation)))
- #~(begin
- (use-modules (gnu build activation))
- (let ((rundir "/var/run/nslcd")
- (user (getpwnam "nslcd")))
- (mkdir-p/perms rundir user #o755)
- (when (file-exists? "/etc/nslcd.conf")
- (chmod "/etc/nslcd.conf" #o400))))))
- (define nslcd-service-type
- (service-type
- (name 'nslcd)
- (description "Run the NSLCD service for looking up names from LDAP.")
- (extensions
- (list (service-extension account-service-type
- (const %nslcd-accounts))
- (service-extension etc-service-type
- nslcd-etc-service)
- (service-extension activation-service-type
- (const %nslcd-activation))
- (service-extension pam-root-service-type
- pam-ldap-pam-services)
- (service-extension nscd-service-type
- (const (list nss-pam-ldapd)))
- (service-extension shepherd-root-service-type
- nslcd-shepherd-service)))
- (default-value (nslcd-configuration))))
- (define (generate-nslcd-documentation)
- (generate-documentation
- `((nslcd-configuration ,nslcd-configuration-fields))
- 'nslcd-configuration))
|