pam_exec.c revision 164154
134192Sjdp/*- 255687Sjdp * Copyright (c) 2001,2003 Networks Associates Technology, Inc. 334192Sjdp * All rights reserved. 434192Sjdp * 534192Sjdp * This software was developed for the FreeBSD Project by ThinkSec AS and 634192Sjdp * NAI Labs, the Security Research Division of Network Associates, Inc. 734192Sjdp * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 834192Sjdp * DARPA CHATS research program. 934192Sjdp * 1034192Sjdp * Redistribution and use in source and binary forms, with or without 1134192Sjdp * modification, are permitted provided that the following conditions 1234192Sjdp * are met: 1334192Sjdp * 1. Redistributions of source code must retain the above copyright 1434192Sjdp * notice, this list of conditions and the following disclaimer. 1534192Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1634192Sjdp * notice, this list of conditions and the following disclaimer in the 1734192Sjdp * documentation and/or other materials provided with the distribution. 1834192Sjdp * 3. The name of the author may not be used to endorse or promote 1934192Sjdp * products derived from this software without specific prior written 2034192Sjdp * permission. 2134192Sjdp * 2234192Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2334192Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2434192Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2550476Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2634192Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2734192Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2834192Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2934192Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3034192Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3134192Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3250608Sjdp * SUCH DAMAGE. 3334192Sjdp */ 3435529Sdfr 3534192Sjdp#include <sys/cdefs.h> 3634192Sjdp__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_exec/pam_exec.c 164154 2006-11-10 23:33:25Z des $"); 3734192Sjdp 3845501Sjdp#include <sys/types.h> 3945501Sjdp#include <sys/wait.h> 4034192Sjdp 4134192Sjdp#include <errno.h> 4234192Sjdp#include <stdlib.h> 4334192Sjdp#include <string.h> 4434192Sjdp#include <unistd.h> 4534192Sjdp 4634192Sjdp#include <security/pam_appl.h> 4734192Sjdp#include <security/pam_modules.h> 4834192Sjdp#include <security/openpam.h> 4934192Sjdp 5034192Sjdp#define ENV_ITEM(n) { (n), #n } 5134192Sjdpstatic struct { 5250609Sjdp int item; 5334192Sjdp const char *name; 5434192Sjdp} env_items[] = { 5555687Sjdp ENV_ITEM(PAM_SERVICE), 5650608Sjdp ENV_ITEM(PAM_USER), 5760938Sjake ENV_ITEM(PAM_TTY), 5850608Sjdp ENV_ITEM(PAM_RHOST), 5950608Sjdp ENV_ITEM(PAM_RUSER), 6050608Sjdp}; 6160938Sjake 6250608Sjdpstatic int 6355687Sjdp_pam_exec(pam_handle_t *pamh __unused, int flags __unused, 6455687Sjdp int argc, const char *argv[]) 6555687Sjdp{ 6655687Sjdp int envlen, i, nitems, pam_err, status; 6760938Sjake char *env, **envlist, **tmp; 6855687Sjdp volatile int childerr; 6955687Sjdp pid_t pid; 7055687Sjdp 7160938Sjake if (argc < 1) 7255687Sjdp return (PAM_SERVICE_ERR); 7355687Sjdp 7434192Sjdp /* 7534192Sjdp * XXX For additional credit, divert child's stdin/stdout/stderr 7634192Sjdp * to the conversation function. 7734192Sjdp */ 7834192Sjdp 7934192Sjdp /* 8062801Sjdp * Set up the child's environment list. It consists of the PAM 8162801Sjdp * environment, plus a few hand-picked PAM items. 8262801Sjdp */ 8362801Sjdp envlist = pam_getenvlist(pamh); 8462801Sjdp for (envlen = 0; envlist[envlen] != NULL; ++envlen) 8562801Sjdp /* nothing */ ; 8662801Sjdp nitems = sizeof(env_items) / sizeof(*env_items); 8762801Sjdp tmp = realloc(envlist, (envlen + nitems + 1) * sizeof(*envlist)); 8862801Sjdp if (tmp == NULL) { 8962801Sjdp openpam_free_envlist(envlist); 9062801Sjdp return (PAM_BUF_ERR); 9162801Sjdp } 9262801Sjdp envlist = tmp; 9362801Sjdp for (i = 0; i < nitems; ++i) { 9462801Sjdp const void *item; 9562801Sjdp char *envstr; 9662801Sjdp 9734192Sjdp pam_err = pam_get_item(pamh, env_items[i].item, &item); 9834192Sjdp if (pam_err != PAM_SUCCESS || item == NULL) 9934192Sjdp continue; 10034192Sjdp asprintf(&envstr, "%s=%s", env_items[i].name, item); 10134192Sjdp if (envstr == NULL) { 10250977Sjdp openpam_free_envlist(envlist); 10350977Sjdp return (PAM_BUF_ERR); 10450977Sjdp } 10550977Sjdp envlist[envlen++] = envstr; 10634192Sjdp envlist[envlen] = NULL; 10734192Sjdp } 10834192Sjdp 10934192Sjdp /* 11034192Sjdp * Fork and run the command. By using vfork() instead of fork(), 11134192Sjdp * we can distinguish between an execve() failure and a non-zero 11238467Sjb * exit code from the command. 11338467Sjb */ 11434192Sjdp childerr = 0; 11534192Sjdp if ((pid = vfork()) == 0) { 11634192Sjdp execve(argv[0], (char * const *)argv, (char * const *)envlist); 11734192Sjdp childerr = errno; 11834192Sjdp _exit(1); 11934192Sjdp } 12034192Sjdp openpam_free_envlist(envlist); 12134192Sjdp if (pid == -1) { 12234192Sjdp openpam_log(PAM_LOG_ERROR, "vfork(): %m"); 12334192Sjdp return (PAM_SYSTEM_ERR); 12438467Sjb } 12534192Sjdp if (waitpid(pid, &status, 0) == -1) { 12638467Sjb openpam_log(PAM_LOG_ERROR, "waitpid(): %m"); 12734192Sjdp return (PAM_SYSTEM_ERR); 12838467Sjb } 12934192Sjdp if (childerr != 0) { 13050610Sjdp openpam_log(PAM_LOG_ERROR, "execve(): %m"); 13134192Sjdp return (PAM_SYSTEM_ERR); 13234192Sjdp } 13345501Sjdp if (WIFSIGNALED(status)) { 13438467Sjb openpam_log(PAM_LOG_ERROR, "%s caught signal %d%s", 13534192Sjdp argv[0], WTERMSIG(status), 13638816Sdfr WCOREDUMP(status) ? " (core dumped)" : ""); 13738816Sdfr return (PAM_SYSTEM_ERR); 13838467Sjb } 13934192Sjdp if (!WIFEXITED(status)) { 14038816Sdfr openpam_log(PAM_LOG_ERROR, "unknown status 0x%x", status); 14138816Sdfr return (PAM_SYSTEM_ERR); 14238467Sjb } 14334192Sjdp if (WEXITSTATUS(status) != 0) { 14434192Sjdp openpam_log(PAM_LOG_ERROR, "%s returned code %d", 14534192Sjdp argv[0], WEXITSTATUS(status)); 14638816Sdfr return (PAM_SYSTEM_ERR); 14734192Sjdp } 14838816Sdfr return (PAM_SUCCESS); 14934192Sjdp} 15034192Sjdp 15134192SjdpPAM_EXTERN int 15234192Sjdppam_sm_authenticate(pam_handle_t *pamh, int flags, 15334192Sjdp int argc, const char *argv[]) 15455687Sjdp{ 15555687Sjdp 15634192Sjdp return (_pam_exec(pamh, flags, argc, argv)); 15734192Sjdp} 15834192Sjdp 15934192SjdpPAM_EXTERN int 16034192Sjdppam_sm_setcred(pam_handle_t *pamh, int flags, 16138740Sjdp int argc, const char *argv[]) 16256780Sjdp{ 16335529Sdfr 16435529Sdfr return (_pam_exec(pamh, flags, argc, argv)); 16550977Sjdp} 16650977Sjdp 16750977SjdpPAM_EXTERN int 16850977Sjdppam_sm_acct_mgmt(pam_handle_t *pamh, int flags, 16934192Sjdp int argc, const char *argv[]) 17034192Sjdp{ 17134192Sjdp 17234192Sjdp return (_pam_exec(pamh, flags, argc, argv)); 17334192Sjdp} 17448871Sjdp 17550609SjdpPAM_EXTERN int 17634192Sjdppam_sm_open_session(pam_handle_t *pamh, int flags, 17734192Sjdp int argc, const char *argv[]) 17834192Sjdp{ 17938816Sdfr 18034192Sjdp return (_pam_exec(pamh, flags, argc, argv)); 18138816Sdfr} 18238816Sdfr 18338816SdfrPAM_EXTERN int 18438816Sdfrpam_sm_close_session(pam_handle_t *pamh, int flags, 18538816Sdfr int argc, const char *argv[]) 18650608Sjdp{ 18750608Sjdp 18845501Sjdp return (_pam_exec(pamh, flags, argc, argv)); 18962801Sjdp} 19050608Sjdp 19150608SjdpPAM_EXTERN int 19245501Sjdppam_sm_chauthtok(pam_handle_t *pamh, int flags, 19356780Sjdp int argc, const char *argv[]) 19456780Sjdp{ 19545501Sjdp 19638816Sdfr return (_pam_exec(pamh, flags, argc, argv)); 19738816Sdfr} 19838816Sdfr 19934192SjdpPAM_MODULE_ENTRY("pam_exec"); 200