pam-mount.scm 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2019 Guillaume Le Vaillant <glv@posteo.net>
  3. ;;; Copyright © 2023 Brian Cully <bjc@spork.org>
  4. ;;;
  5. ;;; This file is part of GNU Guix.
  6. ;;;
  7. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  8. ;;; under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 3 of the License, or (at
  10. ;;; your option) any later version.
  11. ;;;
  12. ;;; GNU Guix is distributed in the hope that it will be useful, but
  13. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  19. (define-module (gnu services pam-mount)
  20. #:use-module (gnu packages admin)
  21. #:use-module (gnu services)
  22. #:use-module (gnu services configuration)
  23. #:use-module (gnu system pam)
  24. #:use-module (guix gexp)
  25. #:use-module (guix records)
  26. #:use-module (ice-9 match)
  27. #:use-module (srfi srfi-1)
  28. #:export (pam-mount-configuration
  29. pam-mount-configuration?
  30. pam-mount-service-type
  31. pam-mount-volume
  32. pam-mount-volume?
  33. pam-mount-volume-service-type))
  34. (define %pam-mount-default-configuration
  35. `((debug (@ (enable "0")))
  36. (mntoptions (@ (allow ,(string-join
  37. '("nosuid" "nodev" "loop"
  38. "encryption" "fsck" "nonempty"
  39. "allow_root" "allow_other")
  40. ","))))
  41. (mntoptions (@ (require "nosuid,nodev")))
  42. (logout (@ (wait "0")
  43. (hup "0")
  44. (term "no")
  45. (kill "no")))
  46. (mkmountpoint (@ (enable "1")
  47. (remove "true")))))
  48. (define (make-pam-mount-configuration-file config)
  49. (computed-file
  50. "pam_mount.conf.xml"
  51. #~(begin
  52. (use-modules (sxml simple))
  53. (call-with-output-file #$output
  54. (lambda (port)
  55. (sxml->xml
  56. '(*TOP*
  57. (*PI* xml "version='1.0' encoding='utf-8'")
  58. (pam_mount
  59. #$@(pam-mount-configuration-rules config)
  60. (pmvarrun
  61. #$(file-append pam-mount
  62. "/sbin/pmvarrun -u '%(USER)' -o '%(OPERATION)'"))
  63. (cryptmount
  64. #$(file-append pam-mount
  65. (string-append
  66. "/sbin/mount.crypt"
  67. " '%(if %(CIPHER),-ocipher=%(CIPHER))'"
  68. " '%(if %(FSKEYCIPHER),"
  69. "-ofsk_cipher=%(FSKEYCIPHER))'"
  70. " '%(if %(FSKEYHASH),-ofsk_hash=%(FSKEYHASH))'"
  71. " '%(if %(FSKEYPATH),-okeyfile=%(FSKEYPATH))'"
  72. " '%(if %(OPTIONS),-o%(OPTIONS))'"
  73. " '%(VOLUME)' '%(MNTPT)'")))
  74. (cryptumount
  75. #$(file-append pam-mount "/sbin/umount.crypt '%(MNTPT)'"))))
  76. port))))))
  77. (define-record-type* <pam-mount-configuration>
  78. pam-mount-configuration
  79. make-pam-mount-configuration
  80. pam-mount-configuration?
  81. (rules pam-mount-configuration-rules
  82. (default %pam-mount-default-configuration)))
  83. (define (pam-mount-etc-service config)
  84. `(("security/pam_mount.conf.xml"
  85. ,(make-pam-mount-configuration-file config))))
  86. (define (pam-mount-pam-service config)
  87. (define optional-pam-mount
  88. (pam-entry
  89. (control "optional")
  90. (module (file-append pam-mount "/lib/security/pam_mount.so"))))
  91. (list
  92. (pam-extension
  93. (transformer
  94. (lambda (pam)
  95. (if (member (pam-service-name pam)
  96. '("login" "greetd" "su" "slim" "gdm-password" "sddm"))
  97. (pam-service
  98. (inherit pam)
  99. (auth (append (pam-service-auth pam)
  100. (list optional-pam-mount)))
  101. (session (append (pam-service-session pam)
  102. (list optional-pam-mount))))
  103. pam))))))
  104. (define (extend-pam-mount-configuration initial extensions)
  105. "Extends INITIAL with EXTENSIONS."
  106. (pam-mount-configuration (rules (append (pam-mount-configuration-rules
  107. initial) extensions))))
  108. (define pam-mount-service-type
  109. (service-type
  110. (name 'pam-mount)
  111. (extensions (list (service-extension etc-service-type
  112. pam-mount-etc-service)
  113. (service-extension pam-root-service-type
  114. pam-mount-pam-service)))
  115. (compose concatenate)
  116. (extend extend-pam-mount-configuration)
  117. (default-value (pam-mount-configuration))
  118. (description "Activate PAM-Mount support. It allows mounting volumes for
  119. specific users when they log in.")))
  120. (define (field-name->tag field-name)
  121. "Convert FIELD-NAME to its tag used by the configuration XML."
  122. (match field-name
  123. ('user-name 'user)
  124. ('user-id 'uid)
  125. ('primary-group 'pgrp)
  126. ('group-id 'gid)
  127. ('secondary-group 'sgrp)
  128. ('file-system-type 'fstype)
  129. ('no-mount-as-root? 'noroot)
  130. ('file-name 'path)
  131. ('mount-point 'mountpoint)
  132. ('ssh? 'ssh)
  133. ('file-system-key-cipher 'fskeycipher)
  134. ('file-system-key-hash 'fskeyhash)
  135. ('file-system-key-file-name 'fskeypath)
  136. (_ field-name)))
  137. (define-maybe string)
  138. (define (serialize-string field-name value)
  139. (list (field-name->tag field-name) value))
  140. (define (integer-or-range? value)
  141. (match value
  142. ((start . end) (and (integer? start)
  143. (integer? end)))
  144. (_ (number? value))))
  145. (define-maybe integer-or-range)
  146. (define (serialize-integer-or-range field-name value)
  147. (let ((value-string (match value
  148. ((start . end) (format #f "~a-~a" start end))
  149. (_ (number->string value)))))
  150. (list (field-name->tag field-name) value-string)))
  151. (define-maybe boolean)
  152. (define (serialize-boolean field-name value)
  153. (let ((value-string (if value "1" "0")))
  154. (list (field-name->tag field-name) value-string)))
  155. (define-configuration pam-mount-volume
  156. (user-name maybe-string "User name to match.")
  157. (user-id maybe-integer-or-range
  158. "User ID, or range of user IDs, in the form of @code{(start . end)} to\nmatch.")
  159. (primary-group maybe-string "Primary group name to match.")
  160. (group-id maybe-integer-or-range
  161. "Group ID, or range of group IDs, in the form of @code{(start . end)} to\nmatch.")
  162. (secondary-group maybe-string
  163. "Match users who belong to this group name as either a primary or secondary\ngroup.")
  164. (file-system-type maybe-string "File system type of volume being mounted.")
  165. (no-mount-as-root? maybe-boolean
  166. "Do not use super user privileges to mount this volume.")
  167. (server maybe-string "Remote server this volume resides on.")
  168. (file-name maybe-string "Location of the volume to be mounted.")
  169. (mount-point maybe-string
  170. "Where to mount the volume in the local file system.")
  171. (options maybe-string "Options to pass to the underlying mount program.")
  172. (ssh? maybe-boolean "Whether to pass the login password to SSH.")
  173. (cipher maybe-string "Cryptsetup cipher named used by volume.")
  174. (file-system-key-cipher maybe-string
  175. "Cipher name used by the target volume.")
  176. (file-system-key-hash maybe-string
  177. "SSL hash name used by the target volume.")
  178. (file-system-key-file-name maybe-string
  179. "File name for the file system key used by the target volume."))
  180. (define (pam-mount-volume->sxml volume)
  181. ;; Convert a list of configuration fields into an SXML-compatible attribute
  182. ;; list.
  183. (define xml-attrs
  184. (filter-map (lambda (field)
  185. (let* ((accessor (configuration-field-getter field))
  186. (value (accessor volume)))
  187. (and (not (eq? value %unset-value))
  188. (list (field-name->tag (configuration-field-name
  189. field)) value))))
  190. pam-mount-volume-fields))
  191. `(volume (@ ,@xml-attrs)))
  192. (define (pam-mount-volume-rules volumes)
  193. (map pam-mount-volume->sxml volumes))
  194. (define pam-mount-volume-service-type
  195. (service-type (name 'pam-mount-volume)
  196. (extensions (list (service-extension pam-mount-service-type
  197. pam-mount-volume-rules)))
  198. (compose concatenate)
  199. (extend append)
  200. (default-value '())
  201. (description
  202. "Mount remote volumes such as CIFS shares @i{via}
  203. @acronym{PAM, Pluggable Authentication Modules} when logging in, using login
  204. credentials.")))