Subject: [PATCH v3] file-systems: Allow specifying CIFS credentials in a file.

From: vicvbcun <[email-masked]>
Date: Wed, 26 Jun 2024 14:15:28 +0200

As files in the store and /etc/fstab are world readable, specifying the
password in the file-system record is suboptimal.  To mitigate this,
`mount.cifs' supports reading `username', `password' and `domain' options from
a file named by the `credentials' or `cred' option.

* gnu/build/file-systems.scm (mount-file-system): Read mount options from the
file specified via the `credentials' or `cred' option if specified.

Change-Id: I786c5da373fc26d45fe7a876c56a8c4854d18532
---
Changes since v2:
  - Add an implementation note to `read-cifs-credential-file'.

Changes since v1:

  - rename `read-credential-file' to `read-cifs-credential-file' and rewrite
    using `match'

  - break lines earlier

 gnu/build/file-systems.scm | 42 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm
index ae29b36c4e..58e8170c0d 100644
--- a/gnu/build/file-systems.scm
+++ b/gnu/build/file-systems.scm
@@ -39,6 +39,7 @@ (define-module (gnu build file-systems)
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
   #:use-module (ice-9 regex)
+  #:use-module (ice-9 string-fun)
   #:use-module (system foreign)
   #:autoload   (system repl repl) (start-repl)
   #:use-module (srfi srfi-1)
@@ -1186,6 +1187,39 @@ (define* (mount-file-system fs #:key (root "/root")
                                 (string-append "," options)
                                 "")))))
 
+  (define (read-cifs-credential-file file)
+    ;; Read password, user and domain options from file
+    ;;
+    ;; XXX: As of version 7.0, mount.cifs strips all lines of leading
+    ;; whitespace, parses those starting with "pass", "user" and "dom" into
+    ;; "pass=", "user=" and "domain=" options respectively and ignores
+    ;; everything else.  To simplify the implementation, we pass those lines
+    ;; as is.  As a consequence, the "password2" option can be specified in a
+    ;; credential file with the expected semantics (see:
+    ;; https://issues.guix.gnu.org/71594#3).
+    (with-input-from-file file
+      (lambda ()
+        (let loop
+            ((next-line (read-line))
+             (lines '()))
+          (match next-line
+            ((? eof-object?)
+             lines)
+            ((= string-trim line)
+             (loop (read-line)
+                   (cond
+                    ((string-prefix? "pass" line)
+                     ;; mount.cifs escapes commas in the password by doubling
+                     ;; them
+                     (cons (string-replace-substring line "," ",,")
+                           lines))
+                    ((or (string-prefix? "user" line)
+                         (string-prefix? "dom" line))
+                     (cons line lines))
+                    ;; Ignore all other lines.
+                    (else
+                     lines)))))))))
+
   (define (mount-cifs source mount-point type flags options)
     ;; Source is of form "//<server-ip-or-host>/<service>"
     (let* ((regex-match (string-match "//([^/]+)/(.+)" source))
@@ -1194,6 +1228,9 @@ (define* (mount-file-system fs #:key (root "/root")
            ;; Match ",guest,", ",guest$", "^guest,", or "^guest$," not
            ;; e.g. user=foo,pass=notaguest
            (guest? (string-match "(^|,)(guest)($|,)" options))
+           (credential-file (and=> (string-match "(^|,)(credentials|cred)=([^,]+)(,|$)"
+                                                 options)
+                                   (cut match:substring <> 3)))
            ;; Perform DNS resolution now instead of attempting kernel dns
            ;; resolver upcalling. /sbin/request-key does not exist and the
            ;; kernel hardcodes the path.
@@ -1218,6 +1255,11 @@ (define* (mount-file-system fs #:key (root "/root")
                                 ;; ignores it. Also, avoiding excess commas
                                 ;; when deleting is a pain.
                                 (string-append "," options)
+                                "")
+                            (if credential-file
+                                ;; The "credentials" option is ignored too.
+                                (string-join (read-cifs-credential-file credential-file)
+                                             "," 'prefix)
                                 "")))))
 
   (let* ((type    (file-system-type fs))

base-commit: 2195f70936b7aeec123d4e95345f1007d3a7bb06
-- 
2.45.1