diff mbox series

[bug#72316,3/3] Add a guile-pam-module service.

Message ID dc90d610c63d9ea32622ad87092c4fca64be3016.1722032727.git.felix.lechner@lease-up.com
State New
Headers show
Series Switch to Guile-PAM. | expand

Commit Message

Felix Lechner July 26, 2024, 10:39 p.m. UTC
Change-Id: I1da0fe25f542cf9d8c22d26a7434f952585119e6
---
 doc/guix.texi        |  89 ++++++++++++++++++++++++++++++++++++
 gnu/local.mk         |   1 +
 gnu/services/pam.scm | 105 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 195 insertions(+)
 create mode 100644 gnu/services/pam.scm

Comments

pelzflorian (Florian Pelz) July 29, 2024, 10:22 a.m. UTC | #1
Hi Felix.  I don’t know linux-pam much but had been wary of its design,
but now that I started reading your guile-pam info manual, it is less of
a riddle.

By the way, guile-pam docs reference guile-wtut, which presumably
should be guile-tut without w.

About this doc/guix.texi addition, it is okay in my opinion, but it
would be better giving one or two functional examples rather than only
calling the (format) procedure.  This would showcase to the
uninitiated what PAM can do and how it looks in Guile.

> +  (foreign-library-path
> +   maybe-list-of-packages
> +   "Search path for shared objects and libraries.") )
> […]
> +                        (foreign-library-path (if (eq? %unset-value foreign-library-path)
> +                                                  '()
> +                                                  foreign-library-path)))))

It is repetitive that foreign-library-path must be set now everywhere
for non-guile pam modules.  Even though a foreign-library-path is not
always needed, would it be better to always set it as default even when
unneeded, then patch 2/3 “Switch to Guile-PAM.” could be dropped?

Disclaimer; I do not know PAM.  I may well be wrong.

Regards,
Florian
Felix Lechner July 30, 2024, 5 p.m. UTC | #2
Hi Florian,

On Mon, Jul 29 2024, pelzflorian (Florian Pelz) wrote:

> guile-pam docs reference guile-wtut, which presumably should be
> guile-tut without w.

Thank you for your review!

A baby has been typing extra letters.  The typo was fixed.  You were
credited in the commit message. [1]

> About this doc/guix.texi addition [...] it would be better giving one
> or two functional examples rather than only calling the (format)
> procedure.  This would showcase to the uninitiated what PAM can do and
> how it looks in Guile.

I personally think that it would turn off new readers.

Guix System configures PAM already.  Only people hoping to accomplish
something non-standard will look into Guile-PAM.  Unfortunately, those
readers have little in common.  That's why I illustrated the way
Guile-PAM works with a simple example.

You are now saying we should instead solve a specialist case, but I
believe that's likely to distract the diverse group of readers by
drawing too much attention to what the module does, as opposed to how
Guile-PAM works.

The example was supposed to draw readers to Guile-PAM's Texinfo manual,
which I mentioned nearby.  Should we strike the example instead?

> It is repetitive that foreign-library-path must be set now everywhere
> for non-guile pam modules.

The foreign-library-path only looks repetitive.  It is the absolute path
to each module.  The modules just happen to be in the same place.

Guix traditionally relied on a special feature in Linux-PAM: One can use
absolute paths but, as many long-timer Guixers know, that is likely to
cause stability issues.

Guile-PAM solves that issue for Guix by separating the load path so a
running process won't reload a newer version of the same shared object.
Since the change has a logic to it, I have trouble relating to your
observation that the load paths look repetitive.

Please note that the foreign-library-path isn't actually needed for
modules that ship with Linux-PAM.  The Linux-PAM load path is added by
default near the comment regarding "courtesy for historical usage" in
the patch.  It is being offered only for user customizations of the
operating-system record, however, and may go away.

The right thing is always list the load path for a module.  That is what
the patch does.

> Even though a foreign-library-path is not always needed, would it be
> better to always set it as default even when unneeded

As I hoped to explain above, the load path is always needed.  In my
estimation, is not better to offer a default even though I did so for
the time being in the interest of a smooth transition.

Ultimately, the matter rests with the Guix maintainers.  They will (or
will not) decide if, when, and how to offer Guile-PAM to their users.

Because Guile-PAM is a new and lightly tested package that strives to
become an integral part of every Guix system, the decision will likely
involve a lot more questions than the ones you and I are discussing in
this thread here.

At the same time, Guile-PAM is only 541 lines of code (in Scheme, not
counting the examples) so maybe someone will get around to taking a
look.

> then patch 2/3 “Switch to Guile-PAM.” could be dropped?

No, the patch does other things.  It switches all PAM configurations
from Linux-PAM to Guile-PAM.  The configured system will use Guile-PAM's
stack implementation.

Guile-PAM should be attractive to Guix for several reasons.  One is that
it may simplify Guix's existing PAM machinery, which is complex, because
the same things can be accomplished better with quoted S-expressions (or
G-expressions, depending on the context).

There are also philosophical considerations which I hope will encourage
Guix to adopt Guile-PAM.  The code is short, written in Scheme, and
licensed under the GPL.

> Disclaimer; I do not know PAM.  I may well be wrong.

No worries, please, and thanks again for your review.  Linux-PAM is
arcane and complicated.  I wrote Guile-PAM for you!

Kind regards
Felix

[1] https://codeberg.org/lechner/guile-pam/commit/2f0f20a0a44f7672bfd93470c0562d19eb8ec511
diff mbox series

Patch

diff --git a/doc/guix.texi b/doc/guix.texi
index 41814042f5..a9bf00f0bb 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -403,6 +403,7 @@  Top
 * Telephony Services::          Telephony services.
 * File-Sharing Services::       File-sharing services.
 * Monitoring Services::         Monitoring services.
+* Guile-PAM Services::          Guile-PAM services.
 * Kerberos Services::           Kerberos services.
 * LDAP Services::               LDAP services.
 * Web Services::                Web servers.
@@ -18991,6 +18992,7 @@  Services
 * Telephony Services::          Telephony services.
 * File-Sharing Services::       File-sharing services.
 * Monitoring Services::         Monitoring services.
+* Guile-PAM Services::          Guile-PAM services.
 * Kerberos Services::           Kerberos services.
 * LDAP Services::               LDAP services.
 * Web Services::                Web servers.
@@ -30932,6 +30934,93 @@  Monitoring Services
 @end deftp
 
 
+@c %end of fragment
+
+@node Guile-PAM Services
+@subsection Guile-PAM Services
+@cindex Guile-PAM
+
+The @code{(gnu services pam)} module provides services related to the
+authentication mechanism @dfn{Guile-PAM}.
+
+Guile-PAM is a reimplementation in GNU Guile of the venerable Linux-PAM
+authentication system.  For details, please have a look at the Texinfo
+manual in the @code{guile-pam} package.
+
+@defvar guile-pam-module-service-type
+A service type for Guile-PAM modules.
+@end defvar
+
+@noindent
+Here is an example of its use:
+@lisp
+(define welcome-pamda-file
+  (scheme-file
+   "welcome-pamda-file"
+   #~(begin
+       (use-modules (ice-9 format))
+
+       (lambda (action handle flags options)
+         (case action
+           ;; authentication management
+           ((pam_sm_authenticate)
+            (format #t "In a working module, we would now identify you.~%"))
+           ((pam_sm_setcred)
+            (format #t "In a working module, we would now help you manage additional credentials.~%"))
+           ;; account management
+           ((pam_sm_acct_mgmt)
+            (format #t "In a working module, we would now confirm your access rights.~%"))
+           ;; password management
+           ((pam_sm_chauthtok)
+            (format #t "In a working module, we would now change your password.~%"))
+           ;; session management
+           ((pam_sm_open_session)
+            (format #t "In a working module, we would now open a session for you.~%"))
+           ((pam_sm_close_session)
+            (format #t "In a working module, we would now close your session.~%"))
+           (else
+            (format #t "In a working module, we would not know what to do about action '~s'.~%"
+                    action)))
+         'PAM_SUCCESS))))
+
+(service guile-pam-module-service-type
+         (guile-pam-module-configuration
+          (rules "optional")
+          (module welcome-pamda-file)
+          (services '("login"
+                      "greetd"
+                      "su"
+                      "slim"
+                      "gdm-password"
+                      "sddm"))))
+@end lisp
+
+@c %start of fragment
+
+@deftp {Data Type} guile-pam-module-configuration
+Available @code{guile-pam-module-configuration} fields are:
+
+@table @asis
+@item @code{rules} (type: maybe-string)
+Determines how the module's return value is evaluated.
+
+@item @code{module} (type: maybe-file-like)
+A Guile-PAM pamda file or a classical PAM module.
+
+@item @code{services} (type: maybe-list-of-strings)
+List of PAM service names for which to install the module.
+
+@item @code{guile-inputs} (type: maybe-list-of-packages)
+Guile inputs available in the PAM module
+
+@item @code{foreign-library-path} (type: maybe-list-of-packages)
+Search path for shared objects and libraries.
+
+@end table
+
+@end deftp
+
+
 @c %end of fragment
 
 @node Kerberos Services
diff --git a/gnu/local.mk b/gnu/local.mk
index fac7b5973b..30551971ac 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -733,6 +733,7 @@  GNU_SYSTEM_MODULES =				\
   %D%/services/networking.scm			\
   %D%/services/nix.scm				\
   %D%/services/nfs.scm			\
+  %D%/services/pam.scm				\
   %D%/services/pam-mount.scm			\
   %D%/services/science.scm			\
   %D%/services/security.scm			\
diff --git a/gnu/services/pam.scm b/gnu/services/pam.scm
new file mode 100644
index 0000000000..a242067e38
--- /dev/null
+++ b/gnu/services/pam.scm
@@ -0,0 +1,105 @@ 
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2024 Felix Lechner <felix.lechner@lease-up.com>
+;;;
+;;; 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 pam)
+  #:use-module (gnu packages guile)
+  #:use-module (gnu packages guile-xyz)
+  #:use-module (gnu packages linux)
+  #:use-module (gnu packages mes)
+  #:use-module (gnu services)
+  #:use-module (gnu services configuration)
+  #:use-module (gnu system pam)
+  #:use-module (guix gexp)
+  #:use-module (guix packages)
+  #:use-module (guix records)
+  #:use-module (guix utils)
+  #:use-module (srfi srfi-1)
+  #:export (guile-pam-module-configuration))
+
+(define-maybe string)
+(define-maybe list-of-strings)
+(define-maybe file-like)
+
+(define-maybe string-or-file-like)
+(define (string-or-file-like? val)
+  (or (string? val) (file-like? val)))
+
+(define-maybe list-of-packages)
+(define (list-of-packages? val)
+  (and (list? val) (map package? val)))
+
+(define-configuration/no-serialization guile-pam-module-configuration
+  (rules
+   maybe-string
+   "Determines how the module's return value is evaluated.")
+  (module
+   maybe-file-like
+   "A Guile-PAM pamda file or a classical PAM module.")
+  (services
+   maybe-list-of-strings
+   "List of PAM service names for which to install the module.")
+  (guile-inputs
+   maybe-list-of-packages
+   "Guile inputs available in the PAM module")
+  (foreign-library-path
+   maybe-list-of-packages
+   "Search path for shared objects and libraries.") )
+
+(define (guile-pam-module-service config)
+  "Return a list of <shepherd-service> for guile-pam-module for CONFIG."
+  (match-record
+      config <guile-pam-module-configuration> (foreign-library-path
+                                               guile-inputs
+                                               module
+                                               rules
+                                               services)
+      (list
+       (pam-extension
+        (transformer
+         (lambda (pam)
+           (if (member (pam-service-name pam) services)
+               (let* ((new-entry
+                       (pam-entry
+                        (control rules)
+                        (module module)
+                        (guile-inputs (if (eq? %unset-value guile-inputs)
+                                          '()
+                                          guile-inputs))
+                        (foreign-library-path (if (eq? %unset-value foreign-library-path)
+                                                  '()
+                                                  foreign-library-path)))))
+                 (pam-service
+                  (inherit pam)
+                  (auth (append (pam-service-auth pam)
+                                (list new-entry)))
+                  (account (append (pam-service-account pam)
+                                   (list new-entry)))
+                  (session (append (pam-service-session pam)
+                                   (list new-entry)))
+                  (password (append (pam-service-password pam)
+                                    (list new-entry)))))
+               pam)))))))
+
+(define-public guile-pam-module-service-type
+  (service-type
+   (name 'guile-pam-module)
+   (extensions (list (service-extension pam-root-service-type
+                                        guile-pam-module-service)))
+   (compose concatenate)
+   (default-value (guile-pam-module-configuration))
+   (description "Load Guile code as part of Linux-PAM.")))