2 * Copyright (c) 2006, 2008 Thorsten Kukuk <kukuk@thkukuk.de>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, and the entire permission notice in its entirety,
9 * including the disclaimer of warranties.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote
14 * products derived from this software without specific prior
17 * ALTERNATIVELY, this product may be distributed under the terms of
18 * the GNU Public License, in which case the provisions of the GPL are
19 * required INSTEAD OF the above restrictions. (This clause is
20 * necessary due to a potential bad interaction between the GPL and
21 * the restrictions contained in a BSD-style copyright.)
23 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
24 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33 * OF THE POSSIBILITY OF SUCH DAMAGE.
36 #if defined(HAVE_CONFIG_H)
50 #include <sys/types.h>
54 #define PAM_SM_ACCOUNT
55 #define PAM_SM_SESSION
56 #define PAM_SM_PASSWORD
58 #include <security/pam_modules.h>
59 #include <security/pam_modutil.h>
60 #include <security/pam_ext.h>
61 #include <security/_pam_macros.h>
63 #define ENV_ITEM(n) { (n), #n }
68 ENV_ITEM(PAM_SERVICE),
75 /* move_fd_to_non_stdio copies the given file descriptor to something other
76 * than stdin, stdout, or stderr. Assumes that the caller will close all
77 * unwanted fds after calling. */
79 move_fd_to_non_stdio (pam_handle_t *pamh, int fd)
87 pam_syslog (pamh, LOG_ERR, "dup failed: %m");
95 call_exec (const char *pam_type, pam_handle_t *pamh,
96 int argc, const char **argv)
101 int expose_authtok = 0;
104 const char *logfile = NULL;
105 const char *authtok = NULL;
109 FILE *stdout_file = NULL;
112 pam_syslog (pamh, LOG_ERR,
113 "This module needs at least one argument");
114 return PAM_SERVICE_ERR;
117 for (optargc = 0; optargc < argc; optargc++)
119 if (argv[optargc][0] == '/') /* paths starts with / */
122 if (strcasecmp (argv[optargc], "debug") == 0)
124 else if (strcasecmp (argv[optargc], "stdout") == 0)
126 else if (strncasecmp (argv[optargc], "log=", 4) == 0)
127 logfile = &argv[optargc][4];
128 else if (strncasecmp (argv[optargc], "type=", 5) == 0)
130 if (strcmp (pam_type, &argv[optargc][5]) != 0)
133 else if (strcasecmp (argv[optargc], "seteuid") == 0)
135 else if (strcasecmp (argv[optargc], "quiet") == 0)
137 else if (strcasecmp (argv[optargc], "expose_authtok") == 0)
140 break; /* Unknown option, assume program to execute. */
143 if (expose_authtok == 1)
145 if (strcmp (pam_type, "auth") != 0)
147 pam_syslog (pamh, LOG_ERR,
148 "expose_authtok not supported for type %s", pam_type);
153 const void *void_pass;
156 retval = pam_get_item (pamh, PAM_AUTHTOK, &void_pass);
157 if (retval != PAM_SUCCESS)
160 pam_syslog (pamh, LOG_DEBUG,
161 "pam_get_item (PAM_AUTHTOK) failed, return %d",
165 else if (void_pass == NULL)
169 retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF,
170 &resp, _("Password: "));
172 if (retval != PAM_SUCCESS)
175 if (retval == PAM_CONV_AGAIN)
176 retval = PAM_INCOMPLETE;
180 pam_set_item (pamh, PAM_AUTHTOK, resp);
181 authtok = strdupa (resp);
189 pam_syslog (pamh, LOG_ERR, "Could not create pipe: %m");
190 return PAM_SYSTEM_ERR;
197 if (pipe(stdout_fds) != 0)
199 pam_syslog (pamh, LOG_ERR, "Could not create pipe: %m");
200 return PAM_SYSTEM_ERR;
202 stdout_file = fdopen(stdout_fds[0], "r");
205 pam_syslog (pamh, LOG_ERR, "Could not fdopen pipe: %m");
206 return PAM_SYSTEM_ERR;
210 if (optargc >= argc) {
211 pam_syslog (pamh, LOG_ERR, "No path given as argument");
212 return PAM_SERVICE_ERR;
217 return PAM_SYSTEM_ERR;
218 if (pid > 0) /* parent */
223 if (expose_authtok) /* send the password to the child */
226 { /* send the password to the child */
228 pam_syslog (pamh, LOG_DEBUG, "send password to child");
229 if (write(fds[1], authtok, strlen(authtok)+1) == -1)
230 pam_syslog (pamh, LOG_ERR,
231 "sending password to child failed: %m");
236 if (write(fds[1], "", 1) == -1) /* blank password */
237 pam_syslog (pamh, LOG_ERR,
238 "sending password to child failed: %m");
240 close(fds[0]); /* close here to avoid possible SIGPIPE above */
247 close(stdout_fds[1]);
248 while (fgets(buf, sizeof(buf), stdout_file) != NULL)
252 if (buf[len-1] == '\n')
254 pam_info(pamh, "%s", buf);
259 while ((retval = waitpid (pid, &status, 0)) == -1 &&
261 if (retval == (pid_t)-1)
263 pam_syslog (pamh, LOG_ERR, "waitpid returns with -1: %m");
264 return PAM_SYSTEM_ERR;
266 else if (status != 0)
268 if (WIFEXITED(status))
270 pam_syslog (pamh, LOG_ERR, "%s failed: exit code %d",
271 argv[optargc], WEXITSTATUS(status));
273 pam_error (pamh, _("%s failed: exit code %d"),
274 argv[optargc], WEXITSTATUS(status));
276 else if (WIFSIGNALED(status))
278 pam_syslog (pamh, LOG_ERR, "%s failed: caught signal %d%s",
279 argv[optargc], WTERMSIG(status),
280 WCOREDUMP(status) ? " (core dumped)" : "");
282 pam_error (pamh, _("%s failed: caught signal %d%s"),
283 argv[optargc], WTERMSIG(status),
284 WCOREDUMP(status) ? " (core dumped)" : "");
288 pam_syslog (pamh, LOG_ERR, "%s failed: unknown status 0x%x",
289 argv[optargc], status);
291 pam_error (pamh, _("%s failed: unknown status 0x%x"),
292 argv[optargc], status);
294 return PAM_SYSTEM_ERR;
302 char **envlist, **tmp;
305 enum pam_modutil_redirect_fd redirect_stdin =
306 expose_authtok ? PAM_MODUTIL_IGNORE_FD : PAM_MODUTIL_PIPE_FD;
307 enum pam_modutil_redirect_fd redirect_stdout =
308 (use_stdout || logfile) ? PAM_MODUTIL_IGNORE_FD : PAM_MODUTIL_NULL_FD;
310 /* First, move all the pipes off of stdin, stdout, and stderr, to ensure
311 * that calls to dup2 won't close them. */
315 fds[0] = move_fd_to_non_stdio(pamh, fds[0]);
321 stdout_fds[1] = move_fd_to_non_stdio(pamh, stdout_fds[1]);
322 close(stdout_fds[0]);
329 /* reopen stdin as pipe */
330 if (dup2(fds[0], STDIN_FILENO) == -1)
333 pam_syslog (pamh, LOG_ERR, "dup2 of STDIN failed: %m");
342 if (dup2(stdout_fds[1], STDOUT_FILENO) == -1)
345 pam_syslog (pamh, LOG_ERR, "dup2 to stdout failed: %m");
351 time_t tm = time (NULL);
354 close (STDOUT_FILENO);
355 if ((i = open (logfile, O_CREAT|O_APPEND|O_WRONLY,
356 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1)
359 pam_syslog (pamh, LOG_ERR, "open of %s failed: %m",
363 if (asprintf (&buffer, "*** %s", ctime (&tm)) > 0)
365 pam_modutil_write (i, buffer, strlen (buffer));
370 if ((use_stdout || logfile) &&
371 dup2 (STDOUT_FILENO, STDERR_FILENO) == -1)
374 pam_syslog (pamh, LOG_ERR, "dup2 failed: %m");
378 if (pam_modutil_sanitize_helper_fds(pamh, redirect_stdin,
379 redirect_stdout, redirect_stdout) < 0)
383 if (setuid (geteuid ()) == -1)
386 pam_syslog (pamh, LOG_ERR, "setuid(%lu) failed: %m",
387 (unsigned long) geteuid ());
394 pam_syslog (pamh, LOG_ERR, "setsid failed: %m");
398 arggv = calloc (argc + 4, sizeof (char *));
402 for (i = 0; i < (argc - optargc); i++)
403 arggv[i] = strdup(argv[i+optargc]);
407 * Set up the child's environment list. It consists of the PAM
408 * environment, plus a few hand-picked PAM items.
410 envlist = pam_getenvlist(pamh);
411 for (envlen = 0; envlist[envlen] != NULL; ++envlen)
413 nitems = sizeof(env_items) / sizeof(*env_items);
414 /* + 2 because of PAM_TYPE and NULL entry */
415 tmp = realloc(envlist, (envlen + nitems + 2) * sizeof(*envlist));
419 pam_syslog (pamh, LOG_ERR, "realloc environment failed: %m");
423 for (i = 0; i < nitems; ++i)
427 if (pam_get_item(pamh, env_items[i].item, &item) != PAM_SUCCESS || item == NULL)
429 if (asprintf(&envstr, "%s=%s", env_items[i].name, (const char *)item) < 0)
432 pam_syslog (pamh, LOG_ERR, "prepare environment failed: %m");
435 envlist[envlen++] = envstr;
436 envlist[envlen] = NULL;
439 if (asprintf(&envstr, "PAM_TYPE=%s", pam_type) < 0)
442 pam_syslog (pamh, LOG_ERR, "prepare environment failed: %m");
445 envlist[envlen++] = envstr;
446 envlist[envlen] = NULL;
449 pam_syslog (pamh, LOG_DEBUG, "Calling %s ...", arggv[0]);
451 execve (arggv[0], arggv, envlist);
453 pam_syslog (pamh, LOG_ERR, "execve(%s,...) failed: %m", arggv[0]);
457 return PAM_SYSTEM_ERR; /* will never be reached. */
461 pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
462 int argc, const char **argv)
464 return call_exec ("auth", pamh, argc, argv);
468 pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED,
469 int argc UNUSED, const char **argv UNUSED)
474 /* password updating functions */
477 pam_sm_chauthtok(pam_handle_t *pamh, int flags,
478 int argc, const char **argv)
480 if (flags & PAM_PRELIM_CHECK)
482 return call_exec ("password", pamh, argc, argv);
486 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
487 int argc, const char **argv)
489 return call_exec ("account", pamh, argc, argv);
493 pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
494 int argc, const char **argv)
496 return call_exec ("open_session", pamh, argc, argv);
500 pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
501 int argc, const char **argv)
503 return call_exec ("close_session", pamh, argc, argv);
507 struct pam_module _pam_exec_modstruct = {
513 pam_sm_close_session,