block 373786 by 406942 merge 376165 406942 tags 376165 patch thanks Hi,
In both #376165 and #406942, you asked for PAM support in start-stop-daemon. The attached patch adds a --pam <service> option to start-stop-daemon. It only implements PAM session (i.e. no authentication, etc), which I guess is sufficient. Marc, does it work with the exim use case? (It would mean adding something like --pam exim-cron, and interested users should create /etc/pam.d/exim-cron). I tested it with the pam_tmpdir module and tested the --chuid and --background options. (s-s-d --pam test -S --chuid test -x /tmp/sh -- -c "echo \$TMPDIR") Russel, it would be nice if you could test the patch with the pam_namespace module. Kind Regards, -- Nekral
Index: utils/start-stop-daemon.c =================================================================== --- utils/start-stop-daemon.c (révision 650) +++ utils/start-stop-daemon.c (copie de travail) @@ -88,6 +88,9 @@ #include <limits.h> #include <assert.h> #include <ctype.h> +#include <sys/wait.h> +#include <security/pam_appl.h> +#include <security/pam_misc.h> #ifdef HAVE_SYS_CDEFS_H #include <sys/cdefs.h> @@ -140,6 +143,14 @@ static int nicelevel = 0; static int umask_value = -1; +static pam_handle_t *pamh = NULL; +static char *pam = NULL; + +static struct pam_conv conv = { + misc_conv, + NULL +}; + static struct stat exec_stat; #if defined(OSHURD) static struct proc_stat_list *procset = NULL; @@ -224,6 +235,10 @@ { va_list arglist; + if (pamh) { + int retcode = pam_close_session(pamh,0); + pam_end(pamh,retcode); + } fprintf(stderr, "%s: ", progname); va_start(arglist, format); vfprintf(stderr, format, arglist); @@ -306,6 +321,7 @@ " -r|--chroot <directory> chroot to <directory> before starting\n" " -d|--chdir <directory> change to <directory> (default is /)\n" " -N|--nicelevel <incr> add incr to the process's nice level\n" +" -P|--pam <service> open a session with this PAM service\n" " -k|--umask <mask> change the umask to <mask> before starting\n" " -b|--background force the process to detach\n" " -m|--make-pidfile create the pidfile before starting\n" @@ -512,6 +528,7 @@ { "make-pidfile", 0, NULL, 'm'}, { "retry", 1, NULL, 'R'}, { "chdir", 1, NULL, 'd'}, + { "pam", 1, NULL, 'P'}, { NULL, 0, NULL, 0} }; const char *umask_str = NULL; @@ -598,6 +615,9 @@ case 'd': /* --chdir /new/dir */ changedir = optarg; break; + case 'P': /* --pam <service> */ + pam = optarg; + break; default: badusage(NULL); /* message printed by getopt */ } @@ -1326,6 +1346,62 @@ #endif devnull_fd=open("/dev/null", O_RDWR); } + if (pam) { + int retcode; + char **envcp; + + char *pam_user; + struct passwd *pw; + pw = getpwuid((-1==runas_uid)?getuid():runas_uid); + if (!pw) { + fatal("user ID `%d' not found\n", + (-1==runas_uid)?getuid():runas_uid); + } + else { + pam_user = strdup(pw->pw_name); + if (!pam_user) + fatal("Unable to allocate memory: %s", strerror(errno)); + } + + retcode = pam_start (pam, pam_user, &conv, &pamh); + if (PAM_SUCCESS != retcode) { + fprintf(stderr, "%s\n", pam_strerror(pamh, retcode)); + pam_end(pamh, retcode); + exit(2); + } + + /* Some PAM modules may rely on PAM_RUSER */ + if (pam_user) { + int retcode = pam_set_item(pamh, PAM_RUSER, pam_user); + if (PAM_SUCCESS != retcode) { + fprintf(stderr, + "%s\n", + pam_strerror(pamh, retcode)); + pam_end(pamh, retcode); + exit(2); + } + } + /* FIXME: set some other PAM variables? + */ + + retcode = pam_open_session(pamh, 0); + if (PAM_SUCCESS != retcode) { + fprintf(stderr, "%s\n", pam_strerror(pamh, retcode)); + pam_end(pamh, retcode); + exit(2); + } + + /* Copy the environment variables set by the PAM modules. */ + envcp = pam_getenvlist (pamh); + if (envcp) { + while (*envcp) { + int err = putenv(*envcp); + if (err) + fatal("Unable to set the '%s' environment variable: %s", *envcp, strerror(errno)); + envcp++; + } + } + } if (nicelevel) { errno=0; if ((nice(nicelevel)==-1) && (errno!=0)) @@ -1386,6 +1462,30 @@ setpgid(0,0); #endif } + if (NULL != pam) { + int parent = fork(); + if (parent < 0) { + fatal("Unable to fork.\n"); + } + if (parent) { + /* parent: wait for child to finish, + * then cleanup the PAM session. + */ + int retcode; + int status = 1; + (void) wait(&status); + + retcode = pam_close_session(pamh,0); + pam_end(pamh,retcode); + + if (WIFSIGNALED(status)) + status = 1; + else + status = WEXITSTATUS(status); + exit(status); + } + /* Only child continue */ + } execv(startas, argv); fatal("Unable to start %s: %s", startas, strerror(errno)); } Index: utils/Makefile.am =================================================================== --- utils/Makefile.am (révision 650) +++ utils/Makefile.am (copie de travail) @@ -13,7 +13,7 @@ start_stop_daemon_SOURCES = \ start-stop-daemon.c - start_stop_daemon_LDADD = ../getopt/libopt.a $(SSD_LIBS) + start_stop_daemon_LDADD = ../getopt/libopt.a $(SSD_LIBS) -lpam -lpam_misc endif Index: man/start-stop-daemon.8 =================================================================== --- man/start-stop-daemon.8 (révision 650) +++ man/start-stop-daemon.8 (copie de travail) @@ -227,6 +227,9 @@ \fB\-N\fP|\fB\-\-nicelevel\fP \fIint\fP This alters the priority of the process before starting it. .TP +\fB\-P\fP|\fB\-\-pam\fP \fIpam_service\fP +Start a PAM session, using the given PAM service. +.TP \fB\-k\fP|\fB\-\-umask\fP \fImask\fP This sets the umask of the process before starting it. .TP