From 87aef1a9b8cc794c7d32336f23a99b58c05be5a5 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Tue, 9 Jan 2024 12:15:02 +0100 Subject: services: Support running Exim with setuid/setgid. In a typical configuration, Exim binary is setuid root and the Exim daemon process listens for connections under a non-root system account (usually `exim`). Upon receiving a message, it forks into a child process which re-executes the binary to regain privileges and deliver the mail to its destination (e.g. a Maildir inside user's home directory). Besides the setuid binary itself, such setup also requires the Exim configuration file to live at the path Exim considers safe. It defaults to /etc/exim.conf and changing it requires rebuilding the Exim daemon. If a configuration at unsafe path is used instead, Exim drops its privileges before reading it and becomes unable to perform certain kinds of email delivery. * gnu/services/mail.scm ()[setuid-user]: New field. ()[setgid-group]: New field. (exim-computed-config-file): Delete variable. (exim-shepherd-service)[start]: Use Exim's default config at /etc/exim.conf. (exim-activation): Atomically put Exim's current config at /etc/exim.conf and verify its syntactic correctness. (exim-setuids): New variable. (exim-service-type)[extensions]: Extend `setuid-program-service-type`. Change-Id: Ie6153baac80180d3d48f6b5a6959895df06aef0b --- gnu/services/mail.scm | 67 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 23 deletions(-) (limited to 'gnu') diff --git a/gnu/services/mail.scm b/gnu/services/mail.scm index afe1bb6016..d5c5e5d35e 100644 --- a/gnu/services/mail.scm +++ b/gnu/services/mail.scm @@ -7,6 +7,9 @@ ;;; Copyright © 2020 Jonathan Brielmaier ;;; Copyright © 2023 Thomas Ieong ;;; Copyright © 2023 Saku Laesvuori +;;; Copyright © 2023, 2024 Wojtek Kosior +;;; Additions and modifications by Wojtek Kosior are additionally +;;; dual-licensed under the Creative Commons Zero v1.0. ;;; ;;; This file is part of GNU Guix. ;;; @@ -1816,10 +1819,14 @@ database---computed from the given alias list."))) (define-record-type* exim-configuration make-exim-configuration exim-configuration? - (package exim-configuration-package ;file-like - (default exim)) - (config-file exim-configuration-config-file ;file-like - (default #f))) + (package exim-configuration-package ;file-like + (default exim)) + (config-file exim-configuration-config-file ;file-like + (default #f)) + (setuid-user exim-configuration-setuid-user + (default #f)) + (setgid-group exim-configuration-setgid-group + (default #f))) (define %exim-accounts (list (user-group @@ -1833,17 +1840,6 @@ database---computed from the given alias list."))) (home-directory "/var/empty") (shell (file-append shadow "/sbin/nologin"))))) -(define (exim-computed-config-file package config-file) - (computed-file "exim.conf" - #~(call-with-output-file #$output - (lambda (port) - (format port " -exim_user = exim -exim_group = exim -.include ~a" - #$(or config-file - (file-append package "/etc/exim.conf"))))))) - (define exim-shepherd-service (match-lambda (($ package config-file) @@ -1852,29 +1848,53 @@ exim_group = exim (documentation "Run the exim daemon.") (requirement '(networking)) (start #~(make-forkexec-constructor - '(#$(file-append package "/bin/exim") - "-bd" "-v" "-C" - #$(exim-computed-config-file package config-file)))) + '(#$(file-append package "/bin/exim") "-bd" "-v"))) (stop #~(make-kill-destructor))))))) (define exim-activation (match-lambda - (($ package config-file) + (($ package config-file setuid-user setgid-group) (with-imported-modules '((guix build utils)) #~(begin - (use-modules (guix build utils)) + (use-modules (guix build utils) + (ice-9 format)) (let ((uid (passwd:uid (getpw "exim"))) (gid (group:gid (getgr "exim")))) (mkdir-p "/var/spool/exim") (chown "/var/spool/exim" uid gid)) - (zero? (system* #$(file-append package "/bin/exim") - "-bV" "-C" #$(exim-computed-config-file package config-file)))))))) + ;; Exim often rereads its config file. Let's substitute it + ;; atomically. + (with-output-to-file "/etc/exim.conf.new" + (lambda _ + (format #t " +~:[~;exim_path = /run/setuid-programs/exim~%~] +.include ~a" + (or #$setuid-user #$setgid-group) + #$(or config-file + (file-append package "/etc/exim.conf"))))) + + (chmod "/etc/exim.conf.new" #o444) + (rename-file "/etc/exim.conf.new" "/etc/exim.conf") + + (zero? (system* #$(file-append package "/bin/exim") "-bV"))))))) (define exim-profile (compose list exim-configuration-package)) +(define exim-setuids + (match-lambda + (($ package config-file setuid-user setgid-group) + (if (or setuid-user setgid-group) + (list (setuid-program + (program (file-append package "/bin/exim")) + (setuid? #t) + (user (or setuid-user "exim")) + (setgid? (not (not setgid-group))) + (group (or setgid-group 0)))) + '())))) + (define exim-service-type (service-type (name 'exim) @@ -1883,7 +1903,8 @@ exim_group = exim (service-extension account-service-type (const %exim-accounts)) (service-extension activation-service-type exim-activation) (service-extension profile-service-type exim-profile) - (service-extension mail-aliases-service-type (const '())))) + (service-extension mail-aliases-service-type (const '())) + (service-extension setuid-program-service-type exim-setuids))) (description "Run the Exim mail transfer agent (MTA)."))) -- cgit v1.2.3