pam_exec.c revision 141102
197182Sdes/*- 2110448Sdes * Copyright (c) 2001,2003 Networks Associates Technology, Inc. 397182Sdes * All rights reserved. 497182Sdes * 597182Sdes * This software was developed for the FreeBSD Project by ThinkSec AS and 697182Sdes * NAI Labs, the Security Research Division of Network Associates, Inc. 797182Sdes * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 897182Sdes * DARPA CHATS research program. 997182Sdes * 1097182Sdes * Redistribution and use in source and binary forms, with or without 1197182Sdes * modification, are permitted provided that the following conditions 1297182Sdes * are met: 1397182Sdes * 1. Redistributions of source code must retain the above copyright 1497182Sdes * notice, this list of conditions and the following disclaimer. 1597182Sdes * 2. Redistributions in binary form must reproduce the above copyright 1697182Sdes * notice, this list of conditions and the following disclaimer in the 1797182Sdes * documentation and/or other materials provided with the distribution. 1897182Sdes * 3. The name of the author may not be used to endorse or promote 1997182Sdes * products derived from this software without specific prior written 2097182Sdes * permission. 2197182Sdes * 2297182Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2397182Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2497182Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2597182Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2697182Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2797182Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2897182Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2997182Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3097182Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3197182Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3297182Sdes * SUCH DAMAGE. 3397182Sdes */ 3497182Sdes 3597182Sdes#include <sys/cdefs.h> 3697182Sdes__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_exec/pam_exec.c 141102 2005-02-01 10:37:07Z des $"); 3797182Sdes 3897182Sdes#include <sys/types.h> 3997182Sdes#include <sys/wait.h> 4097182Sdes 4197182Sdes#include <errno.h> 4297182Sdes#include <stdlib.h> 4397182Sdes#include <string.h> 4497182Sdes#include <unistd.h> 4597182Sdes 4697182Sdes#include <security/pam_appl.h> 4797182Sdes#include <security/pam_modules.h> 4897182Sdes#include <security/openpam.h> 4997182Sdes 50141102Sdes#define ENV_ITEM(n) { (n), #n } 51141102Sdesstatic struct { 52141102Sdes int item; 53141102Sdes const char *name; 54141102Sdes} env_items[] = { 55141102Sdes ENV_ITEM(PAM_SERVICE), 56141102Sdes ENV_ITEM(PAM_USER), 57141102Sdes ENV_ITEM(PAM_TTY), 58141102Sdes ENV_ITEM(PAM_RHOST), 59141102Sdes ENV_ITEM(PAM_RUSER), 60141102Sdes}; 61141102Sdes 6297182Sdesstatic int 6397182Sdes_pam_exec(pam_handle_t *pamh __unused, int flags __unused, 6497182Sdes int argc, const char *argv[]) 6597182Sdes{ 66141102Sdes int childerr, envlen, i, nitems, pam_err, status; 67141102Sdes char *env, **envlist, **tmp; 6897182Sdes pid_t pid; 6997182Sdes 7097182Sdes if (argc < 1) 7197182Sdes return (PAM_SERVICE_ERR); 7297182Sdes 7397182Sdes /* 7497182Sdes * XXX For additional credit, divert child's stdin/stdout/stderr 7597182Sdes * to the conversation function. 7697182Sdes */ 77141102Sdes 78141102Sdes /* 79141102Sdes * Set up the child's environment list. It consists of the PAM 80141102Sdes * environment, plus a few hand-picked PAM items. 81141102Sdes */ 82110446Sdes envlist = pam_getenvlist(pamh); 83141102Sdes for (envlen = 0; envlist[envlen] != NULL; ++envlen) 84141102Sdes /* nothing */ ; 85141102Sdes nitems = sizeof(env_items) / sizeof(*env_items); 86141102Sdes tmp = realloc(envlist, (envlen + nitems + 1) * sizeof **envlist); 87141102Sdes if (tmp == NULL) { 88141102Sdes openpam_free_envlist(envlist); 89141102Sdes return (PAM_BUF_ERR); 90141102Sdes } 91141102Sdes envlist = tmp; 92141102Sdes for (i = 0; i < nitems; ++i) { 93141102Sdes const void *item; 94141102Sdes char *envstr; 95141102Sdes 96141102Sdes pam_err = pam_get_item(pamh, env_items[i].item, &item); 97141102Sdes if (pam_err != PAM_SUCCESS || item == NULL) 98141102Sdes continue; 99141102Sdes asprintf(&envstr, "%s=%s", env_items[i].name, item); 100141102Sdes if (envstr == NULL) { 101141102Sdes openpam_free_envlist(envlist); 102141102Sdes return (PAM_BUF_ERR); 103141102Sdes } 104141102Sdes envlist[envlen++] = envstr; 105141102Sdes envlist[envlen] = NULL; 106141102Sdes } 107141102Sdes 108141102Sdes /* 109141102Sdes * Fork and run the command. By using vfork() instead of fork(), 110141102Sdes * we can distinguish between an execve() failure and a non-zero 111141102Sdes * exit code from the command. 112141102Sdes */ 11397182Sdes childerr = 0; 11497182Sdes if ((pid = vfork()) == 0) { 115141102Sdes execve(argv[0], (char * const *)argv, (char * const *)envlist); 11697182Sdes childerr = errno; 11797182Sdes _exit(1); 118110446Sdes } 119141102Sdes openpam_free_envlist(envlist); 120110446Sdes if (pid == -1) { 12197182Sdes openpam_log(PAM_LOG_ERROR, "vfork(): %m"); 12297182Sdes return (PAM_SYSTEM_ERR); 12397182Sdes } 12497182Sdes if (waitpid(pid, &status, 0) == -1) { 12597182Sdes openpam_log(PAM_LOG_ERROR, "waitpid(): %m"); 12697182Sdes return (PAM_SYSTEM_ERR); 12797182Sdes } 12897182Sdes if (childerr != 0) { 129141102Sdes openpam_log(PAM_LOG_ERROR, "execve(): %m"); 13097182Sdes return (PAM_SYSTEM_ERR); 13197182Sdes } 13297182Sdes if (WIFSIGNALED(status)) { 13397182Sdes openpam_log(PAM_LOG_ERROR, "%s caught signal %d%s", 13497182Sdes argv[0], WTERMSIG(status), 13597182Sdes WCOREDUMP(status) ? " (core dumped)" : ""); 13697182Sdes return (PAM_SYSTEM_ERR); 13797182Sdes } 13897182Sdes if (!WIFEXITED(status)) { 13997182Sdes openpam_log(PAM_LOG_ERROR, "unknown status 0x%x", status); 14097182Sdes return (PAM_SYSTEM_ERR); 14197182Sdes } 14297182Sdes if (WEXITSTATUS(status) != 0) { 14397182Sdes openpam_log(PAM_LOG_ERROR, "%s returned code %d", 14497182Sdes argv[0], WEXITSTATUS(status)); 14597182Sdes return (PAM_SYSTEM_ERR); 14697182Sdes } 14797182Sdes return (PAM_SUCCESS); 14897182Sdes} 14997182Sdes 15097182SdesPAM_EXTERN int 15197182Sdespam_sm_authenticate(pam_handle_t *pamh, int flags, 15297182Sdes int argc, const char *argv[]) 15397182Sdes{ 15497182Sdes 15597182Sdes return (_pam_exec(pamh, flags, argc, argv)); 15697182Sdes} 15797182Sdes 15897182SdesPAM_EXTERN int 15997182Sdespam_sm_setcred(pam_handle_t *pamh, int flags, 16097182Sdes int argc, const char *argv[]) 16197182Sdes{ 16297182Sdes 16397182Sdes return (_pam_exec(pamh, flags, argc, argv)); 16497182Sdes} 16597182Sdes 16697182SdesPAM_EXTERN int 16797182Sdespam_sm_acct_mgmt(pam_handle_t *pamh, int flags, 16897182Sdes int argc, const char *argv[]) 16997182Sdes{ 17097182Sdes 17197182Sdes return (_pam_exec(pamh, flags, argc, argv)); 17297182Sdes} 17397182Sdes 17497182SdesPAM_EXTERN int 17597182Sdespam_sm_open_session(pam_handle_t *pamh, int flags, 17697182Sdes int argc, const char *argv[]) 17797182Sdes{ 17897182Sdes 17997182Sdes return (_pam_exec(pamh, flags, argc, argv)); 18097182Sdes} 18197182Sdes 18297182SdesPAM_EXTERN int 18397182Sdespam_sm_close_session(pam_handle_t *pamh, int flags, 18497182Sdes int argc, const char *argv[]) 18597182Sdes{ 18697182Sdes 18797182Sdes return (_pam_exec(pamh, flags, argc, argv)); 18897182Sdes} 18997182Sdes 19097182SdesPAM_EXTERN int 19197182Sdespam_sm_chauthtok(pam_handle_t *pamh, int flags, 19297182Sdes int argc, const char *argv[]) 19397182Sdes{ 19497182Sdes 19597182Sdes return (_pam_exec(pamh, flags, argc, argv)); 19697182Sdes} 19797182Sdes 19897182SdesPAM_MODULE_ENTRY("pam_exec"); 199