svn commit: r337732 - head/lib/libpam/modules/pam_exec

Dag-Erling Smørgrav des at FreeBSD.org
Tue Aug 14 00:14:19 UTC 2018


Author: des
Date: Tue Aug 14 00:14:17 2018
New Revision: 337732
URL: https://svnweb.freebsd.org/changeset/base/337732

Log:
  Add support for Linux-PAM's badly named expose_authtok option.
  
  Submitted by:	Thomas Munro <munro at ip9.org>
  MFC after:	1 week
  Differential Revision:	D16171

Modified:
  head/lib/libpam/modules/pam_exec/pam_exec.8
  head/lib/libpam/modules/pam_exec/pam_exec.c

Modified: head/lib/libpam/modules/pam_exec/pam_exec.8
==============================================================================
--- head/lib/libpam/modules/pam_exec/pam_exec.8	Mon Aug 13 23:53:11 2018	(r337731)
+++ head/lib/libpam/modules/pam_exec/pam_exec.8	Tue Aug 14 00:14:17 2018	(r337732)
@@ -1,5 +1,6 @@
 .\" Copyright (c) 2001,2003 Networks Associates Technology, Inc.
 .\" Copyright (c) 2017 Dag-Erling Smørgrav
+.\" Copyright (c) 2018 Thomas Munro
 .\" All rights reserved.
 .\"
 .\" Portions of this software were developed for the FreeBSD Project by
@@ -33,7 +34,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd March 22, 2017
+.Dd August 14, 2018
 .Dt PAM_EXEC 8
 .Os
 .Sh NAME
@@ -72,6 +73,8 @@ Ignored for compatibility reasons.
 .It Cm return_prog_exit_status
 Use the program exit status as the return code of the pam_sm_* function.
 It must be a valid return value for this function.
+.It Cm expose_authtok
+Write the authentication token to the program's standard input stream.
 .It Cm --
 Stop options parsing;
 program and its arguments follow.

Modified: head/lib/libpam/modules/pam_exec/pam_exec.c
==============================================================================
--- head/lib/libpam/modules/pam_exec/pam_exec.c	Mon Aug 13 23:53:11 2018	(r337731)
+++ head/lib/libpam/modules/pam_exec/pam_exec.c	Tue Aug 14 00:14:17 2018	(r337732)
@@ -3,6 +3,7 @@
  *
  * Copyright (c) 2001,2003 Networks Associates Technology, Inc.
  * Copyright (c) 2017 Dag-Erling Smørgrav
+ * Copyright (c) 2018 Thomas Munro
  * All rights reserved.
  *
  * This software was developed for the FreeBSD Project by ThinkSec AS and
@@ -108,6 +109,7 @@ struct pe_opts {
 	int	return_prog_exit_status;
 	int	capture_stdout;
 	int	capture_stderr;
+	int	expose_authtok;
 };
 
 static int
@@ -135,6 +137,8 @@ parse_options(const char *func, int *argc, const char 
 			options->capture_stderr = 1;
 		} else if (strcmp((*argv)[i], "return_prog_exit_status") == 0) {
 			options->return_prog_exit_status = 1;
+		} else if (strcmp((*argv)[i], "expose_authtok") == 0) {
+			options->expose_authtok = 1;
 		} else {
 			if (strcmp((*argv)[i], "--") == 0) {
 				(*argc)--;
@@ -158,19 +162,22 @@ _pam_exec(pam_handle_t *pamh,
     struct pe_opts *options)
 {
 	char buf[PAM_MAX_MSG_SIZE];
-	struct pollfd pfd[3];
+	struct pollfd pfd[4];
 	const void *item;
 	char **envlist, *envstr, *resp, **tmp;
-	ssize_t rlen;
+	ssize_t rlen, wlen;
 	int envlen, extralen, i;
 	int pam_err, serrno, status;
-	int chout[2], cherr[2], pd;
-	nfds_t nfds;
+	int chin[2], chout[2], cherr[2], pd;
+	nfds_t nfds, nreadfds;
 	pid_t pid;
+	const char *authtok;
+	size_t authtok_size;
+	int rc;
 
 	pd = -1;
 	pid = 0;
-	chout[0] = chout[1] = cherr[0] = cherr[1] = -1;
+	chin[0] = chin[1] = chout[0] = chout[1] = cherr[0] = cherr[1] = -1;
 	envlist = NULL;
 
 #define OUT(ret) do { pam_err = (ret); goto out; } while (0)
@@ -235,6 +242,25 @@ _pam_exec(pam_handle_t *pamh,
 	openpam_log(PAM_LOG_DEBUG, "envlen = %d extralen = %d envlist = %p",
 	    envlen, extralen, envlist);
 
+	/* set up pipe and get authtok if requested */
+	if (options->expose_authtok) {
+		if (pipe(chin) != 0) {
+			openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func);
+			OUT(PAM_SYSTEM_ERR);
+		}
+		if (fcntl(chin[1], F_SETFL, O_NONBLOCK)) {
+			openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func);
+			OUT(PAM_SYSTEM_ERR);
+		}
+		rc = pam_get_authtok(pamh, PAM_AUTHTOK, &authtok, NULL);
+		if (rc == PAM_SUCCESS) {
+			authtok_size = strlen(authtok);
+		} else {
+			openpam_log(PAM_LOG_ERROR, "%s: pam_get_authtok(): %s", func,
+						pam_strerror(pamh, rc));
+			OUT(PAM_SYSTEM_ERR);
+		}
+	}
 	/* set up pipes if capture was requested */
 	if (options->capture_stdout) {
 		if (pipe(chout) != 0) {
@@ -269,9 +295,13 @@ _pam_exec(pam_handle_t *pamh,
 
 	if ((pid = pdfork(&pd, 0)) == 0) {
 		/* child */
-		if ((chout[0] >= 0 && close(chout[0]) != 0) ||
+		if ((chin[1] >= 0 && close(chin[1]) != 0) ||
+			(chout[0] >= 0 && close(chout[0]) != 0) ||
 		    (cherr[0] >= 0 && close(cherr[0]) != 0)) {
 			openpam_log(PAM_LOG_ERROR, "%s: close(): %m", func);
+		} else if (chin[0] >= 0 &&
+			dup2(chin[0], STDIN_FILENO) != STDIN_FILENO) {
+			openpam_log(PAM_LOG_ERROR, "%s: dup2(): %m", func);
 		} else if (dup2(chout[1], STDOUT_FILENO) != STDOUT_FILENO ||
 		    dup2(cherr[1], STDERR_FILENO) != STDERR_FILENO) {
 			openpam_log(PAM_LOG_ERROR, "%s: dup2(): %m", func);
@@ -288,7 +318,9 @@ _pam_exec(pam_handle_t *pamh,
 		openpam_log(PAM_LOG_ERROR, "%s: pdfork(): %m", func);
 		OUT(PAM_SYSTEM_ERR);
 	}
-	/* use poll() to watch the process and stdout / stderr */
+	/* use poll() to watch the process and stdin / stdout / stderr */
+	if (chin[0] >= 0)
+		close(chin[0]);
 	if (chout[1] >= 0)
 		close(chout[1]);
 	if (cherr[1] >= 0)
@@ -297,16 +329,24 @@ _pam_exec(pam_handle_t *pamh,
 	pfd[0].fd = pd;
 	pfd[0].events = POLLHUP;
 	nfds = 1;
+	nreadfds = 0;
 	if (options->capture_stdout) {
 		pfd[nfds].fd = chout[0];
 		pfd[nfds].events = POLLIN|POLLERR|POLLHUP;
 		nfds++;
+		nreadfds++;
 	}
 	if (options->capture_stderr) {
 		pfd[nfds].fd = cherr[0];
 		pfd[nfds].events = POLLIN|POLLERR|POLLHUP;
 		nfds++;
+		nreadfds++;
 	}
+	if (options->expose_authtok) {
+		pfd[nfds].fd = chin[1];
+		pfd[nfds].events = POLLOUT|POLLERR|POLLHUP;
+		nfds++;
+	}
 
 	/* loop until the process exits */
 	do {
@@ -314,7 +354,8 @@ _pam_exec(pam_handle_t *pamh,
 			openpam_log(PAM_LOG_ERROR, "%s: poll(): %m", func);
 			OUT(PAM_SYSTEM_ERR);
 		}
-		for (i = 1; i < nfds; ++i) {
+		/* are the stderr / stdout pipes ready for reading? */
+		for (i = 1; i < 1 + nreadfds; ++i) {
 			if ((pfd[i].revents & POLLIN) == 0)
 				continue;
 			if ((rlen = read(pfd[i].fd, buf, sizeof(buf) - 1)) < 0) {
@@ -328,6 +369,26 @@ _pam_exec(pam_handle_t *pamh,
 			(void)pam_prompt(pamh, pfd[i].fd == chout[0] ?
 			    PAM_TEXT_INFO : PAM_ERROR_MSG, &resp, "%s", buf);
 		}
+		/* is the stdin pipe ready for writing? */
+		if (options->expose_authtok && authtok_size > 0 &&
+			(pfd[nfds - 1].revents & POLLOUT) != 0) {
+			if ((wlen = write(chin[1], authtok, authtok_size)) < 0) {
+				if (errno == EAGAIN)
+					continue;
+				openpam_log(PAM_LOG_ERROR, "%s: write(): %m",
+				    func);
+				OUT(PAM_SYSTEM_ERR);
+			} else {
+				authtok += wlen;
+				authtok_size -= wlen;
+				if (authtok_size == 0) {
+					/* finished writing; close and forget the pipe */
+					close(chin[1]);
+					chin[1] = -1;
+					nfds--;
+				}
+			}
+		}
 	} while (pfd[0].revents == 0);
 
 	/* the child process has exited */
@@ -364,6 +425,10 @@ out:
 	serrno = errno;
 	if (pd >= 0)
 		close(pd);
+	if (chin[0] >= 0)
+		close(chin[0]);
+	if (chin[1] >= 0)
+		close(chin[1]);
 	if (chout[0] >= 0)
 		close(chout[0]);
 	if (chout[1] >= 0)


More information about the svn-src-head mailing list