* gnu/home/services/ssh.scm (serialize-address-family): Use the public API of
the maybe infrastructure.
* gnu/services/file-sharing.scm (serialize-maybe-string): Use maybe-value.
(serialize-maybe-file-object): Use maybe-value-set?.
* gnu/services/getmail.scm (getmail-retriever-configuration): Don't use
internals in unset field declarations.
(getmail-destination-configuration): Ditto.
* gnu/services/messaging.scm (raw-content?): Use maybe-value-set?.
(prosody-configuration): Use %unset-value.
* gnu/services/telephony.scm (jami-shepherd-services): Use maybe-value-set?.
(archive-name->username): Use maybe-value-set?.
* tests/services/configuration.scm ("maybe type, no default"): Use
%unset-value.
Modified-by: Maxim Cournoyer <maxim.cournoyer@gmail.com>
Signed-off-by: Maxim Cournoyer <maxim.cournoyer@gmail.com>
		
	
			
		
			
				
	
	
		
			465 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			Scheme
		
	
	
	
	
	
			
		
		
	
	
			465 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			Scheme
		
	
	
	
	
	
| ;;; GNU Guix --- Functional package management for GNU
 | ||
| ;;; Copyright © 2016 John Darrington <jmd@gnu.org>
 | ||
| ;;;
 | ||
| ;;; 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 kerberos)
 | ||
|   #:use-module (gnu services)
 | ||
|   #:use-module (gnu services configuration)
 | ||
|   #:use-module (gnu system pam)
 | ||
|   #:use-module (guix gexp)
 | ||
|   #:use-module (guix records)
 | ||
|   #:use-module (srfi srfi-1)
 | ||
|   #:use-module (srfi srfi-34)
 | ||
|   #:use-module (srfi srfi-35)
 | ||
|   #:use-module (ice-9 match)
 | ||
|   #:export (pam-krb5-configuration
 | ||
|             pam-krb5-configuration?
 | ||
|             pam-krb5-service-type
 | ||
| 
 | ||
|             krb5-realm
 | ||
|             krb5-realm?
 | ||
| 
 | ||
|             krb5-configuration
 | ||
|             krb5-configuration?
 | ||
|             krb5-service-type))
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| ;; TODO Use %unset-value and the define-maybe infrastructure.
 | ||
| (define unset-field (list 'unset-field))
 | ||
| 
 | ||
| (define (predicate/unset pred)
 | ||
|   (lambda (x) (or (eq? x unset-field) (pred x))))
 | ||
| 
 | ||
| (define string/unset? (predicate/unset string?))
 | ||
| (define boolean/unset? (predicate/unset boolean?))
 | ||
| (define integer/unset? (predicate/unset integer?))
 | ||
| 
 | ||
| (define (uglify-field-name field-name)
 | ||
|   "Return FIELD-NAME with all instances of '-' replaced by '_' and any
 | ||
| trailing '?' removed."
 | ||
|   (let ((str (symbol->string field-name)))
 | ||
|     (string-join (string-split (if (string-suffix? "?" str)
 | ||
|                                    (substring str 0 (1- (string-length str)))
 | ||
|                                    str)
 | ||
|                                #\-)
 | ||
|                  "_")))
 | ||
| 
 | ||
| (define (serialize-field* field-name val)
 | ||
|   (format #t "~a = ~a\n" (uglify-field-name field-name) val))
 | ||
| 
 | ||
| (define (serialize-string/unset field-name val)
 | ||
|   (unless (eq? val unset-field)
 | ||
|       (serialize-field* field-name val)))
 | ||
| 
 | ||
| (define (serialize-integer/unset field-name val)
 | ||
|   (unless (eq? val unset-field)
 | ||
|       (serialize-field* field-name val)))
 | ||
| 
 | ||
| (define (serialize-boolean/unset field-name val)
 | ||
|   (unless (eq? val unset-field)
 | ||
|       (serialize-field* field-name
 | ||
|                         (if val "true" "false"))))
 | ||
| 
 | ||
| 
 | ||
| ;; An end-point is an address such as "192.168.0.1"
 | ||
| ;; or an address port pair ("foobar.example.com" . 109)
 | ||
| (define (end-point? val)
 | ||
|   (match val
 | ||
|     ((? string?) #t)
 | ||
|     (((? string?) . (? integer?)) #t)
 | ||
|     (_ #f)))
 | ||
| 
 | ||
| (define (serialize-end-point field-name val)
 | ||
|   (serialize-field* field-name
 | ||
|                     (match val
 | ||
|                       ((host . port)
 | ||
|                        ;; The [] are needed in the case of IPv6 addresses
 | ||
|                        (format #f "[~a]:~a" host port))
 | ||
|                       (host
 | ||
|                        (format #f "~a" host)))))
 | ||
| 
 | ||
| (define (serialize-space-separated-string-list/unset field-name val)
 | ||
|   (unless (eq? val unset-field)
 | ||
|       (serialize-field* field-name (string-join val " "))))
 | ||
| 
 | ||
| (define (space-separated-string-list? val)
 | ||
|   (and (list? val)
 | ||
|        (and-map (lambda (x)
 | ||
|                   (and (string? x) (not (string-index x #\space))))
 | ||
|                 val)))
 | ||
| 
 | ||
| (define space-separated-string-list/unset?
 | ||
|   (predicate/unset space-separated-string-list?))
 | ||
| 
 | ||
| (define comma-separated-integer-list/unset?
 | ||
|   (predicate/unset (lambda (val)
 | ||
|                      (and (list? val)
 | ||
|                           (and-map (lambda (x) (integer? x))
 | ||
|                                    val)))))
 | ||
| 
 | ||
| (define (serialize-comma-separated-integer-list/unset field-name val)
 | ||
|   (unless (eq? val unset-field)
 | ||
|       (serialize-field* field-name
 | ||
|                        (string-drop ; Drop the leading comma
 | ||
|                         (fold
 | ||
|                          (lambda (i prev)
 | ||
|                            (string-append prev "," (number->string i)))
 | ||
|                          "" val) 1))))
 | ||
| 
 | ||
| (define file-name? (predicate/unset
 | ||
|                     (lambda (val)
 | ||
|                       (string-prefix? "/" val))))
 | ||
| 
 | ||
| (define (serialize-field field-name val)
 | ||
|   (format #t "~a ~a\n" (uglify-field-name field-name) val))
 | ||
| 
 | ||
| (define (serialize-string field-name val)
 | ||
|   (serialize-field field-name val))
 | ||
| 
 | ||
| (define (serialize-file-name field-name val)
 | ||
|   (unless (eq? val unset-field)
 | ||
|     (serialize-string field-name val)))
 | ||
| 
 | ||
| (define (serialize-space-separated-string-list field-name val)
 | ||
|   (serialize-field field-name (string-join val " ")))
 | ||
| 
 | ||
| (define (non-negative-integer? val)
 | ||
|   (and (exact-integer? val) (not (negative? val))))
 | ||
| 
 | ||
| (define (serialize-non-negative-integer/unset field-name val)
 | ||
|   (unless (eq? val unset-field)
 | ||
|     (serialize-field* field-name val)))
 | ||
| 
 | ||
| (define (free-form-fields? val)
 | ||
|   (match val
 | ||
|     (() #t)
 | ||
|     ((((? symbol?) . (? string)) . val) (free-form-fields? val))
 | ||
|     (_ #f)))
 | ||
| 
 | ||
| (define (serialize-free-form-fields field-name val)
 | ||
|   (for-each (match-lambda ((k . v) (serialize-field* k v))) val))
 | ||
| 
 | ||
| (define non-negative-integer/unset? (predicate/unset non-negative-integer?))
 | ||
| 
 | ||
| (define (realm-list? val)
 | ||
|   (and (list? val)
 | ||
|        (and-map (lambda (x) (krb5-realm? x)) val)))
 | ||
| 
 | ||
| (define (serialize-realm-list field-name val)
 | ||
|   (format #t "\n[~a]\n" field-name)
 | ||
|   (for-each (lambda (realm)
 | ||
|               (format #t "\n~a = {\n" (krb5-realm-name realm))
 | ||
|               (for-each (lambda (field)
 | ||
|                           (unless (eq? 'name (configuration-field-name field))
 | ||
|                             ((configuration-field-serializer field)
 | ||
|                              (configuration-field-name field)
 | ||
|                              ((configuration-field-getter field)
 | ||
|                               realm)))) krb5-realm-fields)
 | ||
| 
 | ||
|               (format #t "}\n")) val))
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| ;; For a more detailed explanation of these fields see man 5 krb5.conf
 | ||
| (define-configuration krb5-realm
 | ||
|   (name
 | ||
|    (string/unset unset-field)
 | ||
|    "The name of the realm.")
 | ||
| 
 | ||
|   (kdc
 | ||
|    (end-point unset-field)
 | ||
|    "The host and port on which the realm's Key Distribution Server listens.")
 | ||
| 
 | ||
|   (admin-server
 | ||
|    (string/unset unset-field)
 | ||
|    "The Host running the administration server for the realm.")
 | ||
| 
 | ||
|   (master-kdc
 | ||
|    (string/unset unset-field)
 | ||
|    "If an attempt to get credentials fails because of an invalid password, 
 | ||
| the client software will attempt to contact the master KDC.")
 | ||
| 
 | ||
|   (kpasswd-server
 | ||
|    (string/unset unset-field)
 | ||
|    "The server where password changes are performed.")
 | ||
| 
 | ||
|   (auth-to-local
 | ||
|    (free-form-fields '())
 | ||
|    "Rules to map between principals and local users.")
 | ||
| 
 | ||
|   (auth-to-local-names
 | ||
|    (free-form-fields '())
 | ||
|    "Explicit mappings between principal names and local user names.")
 | ||
| 
 | ||
|   (http-anchors
 | ||
|    (free-form-fields '())
 | ||
|    "Useful only when http proxy is used to access KDC or KPASSWD.")
 | ||
| 
 | ||
|   ;; The following are useful only for working with V4 services
 | ||
|   (default-domain
 | ||
|     (string/unset unset-field)
 | ||
|     "The domain used to expand host names when translating Kerberos 4 service
 | ||
| principals to Kerberos 5 principals")
 | ||
| 
 | ||
|   (v4-instance-convert
 | ||
|    (free-form-fields '())
 | ||
|    "Exceptions to the default-domain mapping rule.")
 | ||
| 
 | ||
|   (v4-realm
 | ||
|    (string/unset unset-field)
 | ||
|    "Used  when the V4 realm name and the V5 realm name are not the same, but
 | ||
| still share the same principal names and passwords"))
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| ;; For a more detailed explanation of these fields see man 5 krb5.conf
 | ||
| (define-configuration krb5-configuration
 | ||
|   (allow-weak-crypto?
 | ||
|    (boolean/unset unset-field)
 | ||
|    "If true, permits access to services which only offer weak encryption.")
 | ||
| 
 | ||
|   (ap-req-checksum-type
 | ||
|    (non-negative-integer/unset unset-field)
 | ||
|    "The type of the AP-REQ checksum.")
 | ||
| 
 | ||
|   (canonicalize?
 | ||
|    (boolean/unset unset-field)
 | ||
|    "Should principals in initial ticket requests be canonicalized?")
 | ||
| 
 | ||
|   (ccache-type
 | ||
|    (non-negative-integer/unset unset-field)
 | ||
|    "The format of the credential cache type.")
 | ||
| 
 | ||
|   (clockskew
 | ||
|    (non-negative-integer/unset unset-field)
 | ||
|    "Maximum allowable clock skew in seconds (default 300).")
 | ||
| 
 | ||
|   (default-ccache-name
 | ||
|     (file-name unset-field)
 | ||
|     "The name of the default credential cache.")
 | ||
| 
 | ||
|   (default-client-keytab-name
 | ||
|     (file-name unset-field)
 | ||
|     "The name of the default keytab for client credentials.")
 | ||
| 
 | ||
|   (default-keytab-name
 | ||
|     (file-name unset-field)
 | ||
|     "The name of the default keytab file.")
 | ||
| 
 | ||
|   (default-realm
 | ||
|     (string/unset unset-field)
 | ||
|     "The realm to be accessed if not explicitly specified by clients.")
 | ||
| 
 | ||
|   (default-tgs-enctypes
 | ||
|     (free-form-fields '())
 | ||
|     "Session key encryption types when making TGS-REQ requests.")
 | ||
| 
 | ||
|   (default-tkt-enctypes
 | ||
|     (free-form-fields '())
 | ||
|     "Session key encryption types when making AS-REQ requests.")
 | ||
| 
 | ||
|   (dns-canonicalize-hostname?
 | ||
|    (boolean/unset  unset-field)
 | ||
|    "Whether name lookups will be used to canonicalize host names for use in 
 | ||
| service principal names.")
 | ||
| 
 | ||
|   (dns-lookup-kdc?
 | ||
|    (boolean/unset unset-field)
 | ||
|  "Should DNS SRV records should be used to locate the KDCs and other servers 
 | ||
| not appearing in the realm specification")
 | ||
| 
 | ||
|   (err-fmt
 | ||
|    (string/unset unset-field)
 | ||
|    "Custom error message formatting. If not #f error messages will be formatted 
 | ||
| by substituting a normal error message for %M and an error code for %C in the 
 | ||
| value.")
 | ||
| 
 | ||
|   (forwardable?
 | ||
|    (boolean/unset unset-field)
 | ||
|    "Should initial tickets be forwardable by default?")
 | ||
| 
 | ||
|   (ignore-acceptor-hostname?
 | ||
|    (boolean/unset unset-field)
 | ||
|    "When accepting GSSAPI or krb5 security contexts for host-based service 
 | ||
| principals, ignore any hostname passed by the calling application, and allow 
 | ||
| clients to authenticate to any service principal in the keytab matching the 
 | ||
| service name and realm name.")
 | ||
| 
 | ||
|   (k5login-authoritative?
 | ||
|    (boolean/unset unset-field)
 | ||
|    "If this flag is true, principals must be listed in a local user's k5login
 | ||
| file to be granted login access, if a ~/.k5login file exists.")
 | ||
| 
 | ||
|   (k5login-directory
 | ||
|    (string/unset unset-field)
 | ||
|    "If not #f, the library will look for a local user's @file{k5login} file 
 | ||
| within the named directory (instead of the user's home directory), with a 
 | ||
| file name corresponding to the local user name.")
 | ||
| 
 | ||
|   (kcm-mach-service
 | ||
|    (string/unset unset-field)
 | ||
|    "The name of the bootstrap service used to contact the KCM daemon for the 
 | ||
| KCM credential cache type.")
 | ||
| 
 | ||
|   (kcm-socket
 | ||
|    (file-name unset-field)
 | ||
|  "Path to the Unix domain socket used to access the KCM daemon for the KCM 
 | ||
| credential cache type.")
 | ||
| 
 | ||
|   (kdc-default-options
 | ||
|    (non-negative-integer/unset unset-field)
 | ||
|    "Default KDC options (logored for multiple values) when requesting initial 
 | ||
| tickets.")
 | ||
| 
 | ||
|   (kdc-timesync
 | ||
|    (non-negative-integer/unset unset-field)
 | ||
|    "Attempt to compensate for clock skew between the KDC and client.")
 | ||
| 
 | ||
|   (kdc-req-checksum-type
 | ||
|    (non-negative-integer/unset unset-field)
 | ||
|    "The type of checksum to use for the KDC requests. Relevant only for DES 
 | ||
| keys")
 | ||
| 
 | ||
|   (noaddresses?
 | ||
|    (boolean/unset unset-field)
 | ||
|    "If true, initial ticket requests will not be made with address restrictions.
 | ||
| This enables their use across NATs.")
 | ||
| 
 | ||
|   (permitted-enctypes
 | ||
|    (space-separated-string-list/unset unset-field)
 | ||
|    "All encryption types that are permitted for use in session key encryption.")
 | ||
| 
 | ||
|   (plugin-base-dir
 | ||
|    (file-name unset-field)
 | ||
|    "The directory where krb5 plugins are located.")
 | ||
| 
 | ||
|   (preferred-preauth-types
 | ||
|    (comma-separated-integer-list/unset unset-field)
 | ||
|    "The preferred pre-authentication types which the client will attempt before 
 | ||
| others.")
 | ||
| 
 | ||
|   (proxiable?
 | ||
|    (boolean/unset unset-field)
 | ||
|    "Should initial tickets be proxiable by default?")
 | ||
| 
 | ||
|   (rdns?
 | ||
|    (boolean/unset unset-field)
 | ||
|    "Should reverse DNS lookup be used in addition to forward name lookup to 
 | ||
| canonicalize host names for use in service principal names.")
 | ||
| 
 | ||
|   (realm-try-domains
 | ||
|    (integer/unset unset-field)
 | ||
|    "Should a host's domain components should be used to determine the Kerberos 
 | ||
| realm of the host.")
 | ||
| 
 | ||
|   (renew-lifetime
 | ||
|    (non-negative-integer/unset unset-field)
 | ||
|    "The default renewable lifetime for initial ticket requests.")
 | ||
| 
 | ||
|   (safe-checksum-type
 | ||
|    (non-negative-integer/unset unset-field)
 | ||
|    "The type of checksum to use for the KRB-SAFE requests.")
 | ||
| 
 | ||
|   (ticket-lifetime
 | ||
|    (non-negative-integer/unset unset-field)
 | ||
|    "The default lifetime for initial ticket requests.")
 | ||
| 
 | ||
|   (udp-preference-limit
 | ||
|    (non-negative-integer/unset unset-field)
 | ||
|    "When sending messages to the KDC, the library will try using TCP
 | ||
| before UDP if the size of the message greater than this limit.")
 | ||
| 
 | ||
|   (verify-ap-rereq-nofail?
 | ||
|    (boolean/unset unset-field)
 | ||
|  "If true, then attempts to verify initial credentials will fail if the client
 | ||
| machine does not have a keytab.")
 | ||
| 
 | ||
|   (realms
 | ||
|    (realm-list '())
 | ||
|    "The list of realms which clients may access."))
 | ||
| 
 | ||
| 
 | ||
| (define (krb5-configuration-file config)
 | ||
|   "Create a Kerberos 5 configuration file based on CONFIG"
 | ||
|   (mixed-text-file "krb5.conf"
 | ||
|                    "[libdefaults]\n\n"
 | ||
|                    (with-output-to-string
 | ||
|                      (lambda ()
 | ||
|                        (serialize-configuration config
 | ||
|                                                 krb5-configuration-fields)))))
 | ||
| 
 | ||
| (define (krb5-etc-service config)
 | ||
|   (list `("krb5.conf" ,(krb5-configuration-file config))))
 | ||
| 
 | ||
| 
 | ||
| (define krb5-service-type
 | ||
|   (service-type (name 'krb5)
 | ||
|                 (extensions
 | ||
|                  (list (service-extension etc-service-type
 | ||
|                                           krb5-etc-service)))
 | ||
|                 (description "Programs using a Kerberos client library
 | ||
| normally expect a configuration file in @file{/etc/krb5.conf}.  This service
 | ||
| generates such a file.  It does not cause any daemon to be started.")))
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| (define-record-type* <pam-krb5-configuration>
 | ||
|   pam-krb5-configuration  make-pam-krb5-configuration
 | ||
|   pam-krb5-configuration?
 | ||
|   (pam-krb5               pam-krb5-configuration-pam-krb5
 | ||
|                           (default pam-krb5))
 | ||
|   (minimum-uid            pam-krb5-configuration-minimum-uid
 | ||
|                           (default 1000)))
 | ||
| 
 | ||
| (define (pam-krb5-pam-service config)
 | ||
|   "Return a PAM service for Kerberos authentication."
 | ||
|   (lambda (pam)
 | ||
|     (define pam-krb5-module
 | ||
|       #~(string-append #$(pam-krb5-configuration-pam-krb5 config)
 | ||
|                        "/lib/security/pam_krb5.so"))
 | ||
| 
 | ||
|     (let ((pam-krb5-sufficient
 | ||
|            (pam-entry
 | ||
|             (control "sufficient")
 | ||
|             (module pam-krb5-module)
 | ||
|             (arguments
 | ||
|              (list
 | ||
|               (format #f "minimum_uid=~a"
 | ||
|                       (pam-krb5-configuration-minimum-uid config)))))))
 | ||
|       (pam-service
 | ||
|        (inherit pam)
 | ||
|        (auth (cons* pam-krb5-sufficient
 | ||
|                     (pam-service-auth pam)))
 | ||
|        (session (cons* pam-krb5-sufficient
 | ||
|                        (pam-service-session pam)))
 | ||
|        (account (cons* pam-krb5-sufficient
 | ||
|                        (pam-service-account pam)))))))
 | ||
| 
 | ||
| (define (pam-krb5-pam-services config)
 | ||
|   (list (pam-krb5-pam-service config)))
 | ||
| 
 | ||
| (define pam-krb5-service-type
 | ||
|   (service-type (name 'pam-krb5)
 | ||
|                 (extensions
 | ||
|                  (list
 | ||
|                   (service-extension pam-root-service-type
 | ||
|                                      pam-krb5-pam-services)))
 | ||
|                 (description "The @code{pam-krb5} service allows for login
 | ||
| authentication and password management via Kerberos.  You will need this
 | ||
| service if you want PAM-enabled applications to authenticate users using
 | ||
| Kerberos.")))
 |