1 1.8 nia /* $NetBSD: pam_exec.c,v 1.8 2021/10/30 11:34:59 nia Exp $ */ 2 1.2 christos 3 1.1 christos /*- 4 1.1 christos * Copyright (c) 2001,2003 Networks Associates Technology, Inc. 5 1.1 christos * All rights reserved. 6 1.1 christos * 7 1.1 christos * This software was developed for the FreeBSD Project by ThinkSec AS and 8 1.1 christos * NAI Labs, the Security Research Division of Network Associates, Inc. 9 1.1 christos * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 10 1.1 christos * DARPA CHATS research program. 11 1.1 christos * 12 1.1 christos * Redistribution and use in source and binary forms, with or without 13 1.1 christos * modification, are permitted provided that the following conditions 14 1.1 christos * are met: 15 1.1 christos * 1. Redistributions of source code must retain the above copyright 16 1.1 christos * notice, this list of conditions and the following disclaimer. 17 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 18 1.1 christos * notice, this list of conditions and the following disclaimer in the 19 1.1 christos * documentation and/or other materials provided with the distribution. 20 1.1 christos * 3. The name of the author may not be used to endorse or promote 21 1.1 christos * products derived from this software without specific prior written 22 1.1 christos * permission. 23 1.1 christos * 24 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 1.1 christos * SUCH DAMAGE. 35 1.1 christos */ 36 1.1 christos 37 1.1 christos #include <sys/cdefs.h> 38 1.2 christos #ifdef __FreeBSD__ 39 1.3 thorpej __FBSDID("$FreeBSD: src/lib/libpam/modules/pam_exec/pam_exec.c,v 1.4 2005/02/01 10:37:07 des Exp $"); 40 1.2 christos #else 41 1.8 nia __RCSID("$NetBSD: pam_exec.c,v 1.8 2021/10/30 11:34:59 nia Exp $"); 42 1.2 christos #endif 43 1.1 christos 44 1.1 christos #include <sys/types.h> 45 1.1 christos #include <sys/wait.h> 46 1.1 christos 47 1.1 christos #include <errno.h> 48 1.1 christos #include <stdlib.h> 49 1.3 thorpej #include <stdio.h> 50 1.1 christos #include <string.h> 51 1.1 christos #include <unistd.h> 52 1.1 christos 53 1.1 christos #include <security/pam_appl.h> 54 1.1 christos #include <security/pam_modules.h> 55 1.1 christos #include <security/openpam.h> 56 1.1 christos 57 1.3 thorpej #define ENV_ITEM(n) { (n), #n } 58 1.3 thorpej static struct { 59 1.3 thorpej int item; 60 1.3 thorpej const char *name; 61 1.3 thorpej } env_items[] = { 62 1.3 thorpej ENV_ITEM(PAM_SERVICE), 63 1.3 thorpej ENV_ITEM(PAM_USER), 64 1.3 thorpej ENV_ITEM(PAM_TTY), 65 1.3 thorpej ENV_ITEM(PAM_RHOST), 66 1.3 thorpej ENV_ITEM(PAM_RUSER), 67 1.3 thorpej }; 68 1.3 thorpej 69 1.1 christos static int 70 1.1 christos _pam_exec(pam_handle_t *pamh __unused, int flags __unused, 71 1.1 christos int argc, const char *argv[]) 72 1.1 christos { 73 1.7 christos size_t envlen, i, nitems; 74 1.7 christos int pam_err, status; 75 1.8 nia char **envlist; 76 1.5 christos volatile int childerr; 77 1.1 christos pid_t pid; 78 1.1 christos 79 1.1 christos if (argc < 1) 80 1.1 christos return (PAM_SERVICE_ERR); 81 1.1 christos 82 1.1 christos /* 83 1.1 christos * XXX For additional credit, divert child's stdin/stdout/stderr 84 1.1 christos * to the conversation function. 85 1.1 christos */ 86 1.3 thorpej 87 1.3 thorpej /* 88 1.3 thorpej * Set up the child's environment list. It consists of the PAM 89 1.3 thorpej * environment, plus a few hand-picked PAM items. 90 1.3 thorpej */ 91 1.1 christos envlist = pam_getenvlist(pamh); 92 1.3 thorpej for (envlen = 0; envlist[envlen] != NULL; ++envlen) 93 1.3 thorpej /* nothing */ ; 94 1.3 thorpej nitems = sizeof(env_items) / sizeof(*env_items); 95 1.8 nia if (reallocarr(&envlist, envlen + nitems + 1, sizeof(*envlist)) != 0) { 96 1.3 thorpej openpam_free_envlist(envlist); 97 1.3 thorpej return (PAM_BUF_ERR); 98 1.3 thorpej } 99 1.3 thorpej for (i = 0; i < nitems; ++i) { 100 1.3 thorpej const void *item; 101 1.3 thorpej char *envstr; 102 1.3 thorpej 103 1.3 thorpej pam_err = pam_get_item(pamh, env_items[i].item, &item); 104 1.3 thorpej if (pam_err != PAM_SUCCESS || item == NULL) 105 1.3 thorpej continue; 106 1.3 thorpej asprintf(&envstr, "%s=%s", env_items[i].name, 107 1.3 thorpej (const char *)item); 108 1.3 thorpej if (envstr == NULL) { 109 1.3 thorpej openpam_free_envlist(envlist); 110 1.3 thorpej return (PAM_BUF_ERR); 111 1.3 thorpej } 112 1.3 thorpej envlist[envlen++] = envstr; 113 1.3 thorpej envlist[envlen] = NULL; 114 1.3 thorpej } 115 1.3 thorpej 116 1.3 thorpej /* 117 1.3 thorpej * Fork and run the command. By using vfork() instead of fork(), 118 1.3 thorpej * we can distinguish between an execve() failure and a non-zero 119 1.3 thorpej * exit code from the command. 120 1.3 thorpej */ 121 1.1 christos childerr = 0; 122 1.1 christos if ((pid = vfork()) == 0) { 123 1.2 christos /*LINTED const cast*/ 124 1.2 christos execve(argv[0], (char **)__UNCONST(argv), envlist); 125 1.1 christos childerr = errno; 126 1.1 christos _exit(1); 127 1.1 christos } 128 1.3 thorpej openpam_free_envlist(envlist); 129 1.1 christos if (pid == -1) { 130 1.6 christos openpam_log(PAM_LOG_ERROR, "vfork(): %s", strerror(errno)); 131 1.1 christos return (PAM_SYSTEM_ERR); 132 1.1 christos } 133 1.1 christos if (waitpid(pid, &status, 0) == -1) { 134 1.6 christos openpam_log(PAM_LOG_ERROR, "waitpid(): %s", strerror(errno)); 135 1.1 christos return (PAM_SYSTEM_ERR); 136 1.1 christos } 137 1.1 christos if (childerr != 0) { 138 1.6 christos openpam_log(PAM_LOG_ERROR, "execve(): %s", strerror(errno)); 139 1.1 christos return (PAM_SYSTEM_ERR); 140 1.1 christos } 141 1.1 christos if (WIFSIGNALED(status)) { 142 1.1 christos openpam_log(PAM_LOG_ERROR, "%s caught signal %d%s", 143 1.1 christos argv[0], WTERMSIG(status), 144 1.1 christos WCOREDUMP(status) ? " (core dumped)" : ""); 145 1.1 christos return (PAM_SYSTEM_ERR); 146 1.1 christos } 147 1.1 christos if (!WIFEXITED(status)) { 148 1.1 christos openpam_log(PAM_LOG_ERROR, "unknown status 0x%x", status); 149 1.1 christos return (PAM_SYSTEM_ERR); 150 1.1 christos } 151 1.1 christos if (WEXITSTATUS(status) != 0) { 152 1.1 christos openpam_log(PAM_LOG_ERROR, "%s returned code %d", 153 1.1 christos argv[0], WEXITSTATUS(status)); 154 1.1 christos return (PAM_SYSTEM_ERR); 155 1.1 christos } 156 1.1 christos return (PAM_SUCCESS); 157 1.1 christos } 158 1.1 christos 159 1.1 christos PAM_EXTERN int 160 1.1 christos pam_sm_authenticate(pam_handle_t *pamh, int flags, 161 1.1 christos int argc, const char *argv[]) 162 1.1 christos { 163 1.1 christos 164 1.1 christos return (_pam_exec(pamh, flags, argc, argv)); 165 1.1 christos } 166 1.1 christos 167 1.1 christos PAM_EXTERN int 168 1.1 christos pam_sm_setcred(pam_handle_t *pamh, int flags, 169 1.1 christos int argc, const char *argv[]) 170 1.1 christos { 171 1.1 christos 172 1.1 christos return (_pam_exec(pamh, flags, argc, argv)); 173 1.1 christos } 174 1.1 christos 175 1.1 christos PAM_EXTERN int 176 1.1 christos pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, 177 1.1 christos int argc, const char *argv[]) 178 1.1 christos { 179 1.1 christos 180 1.1 christos return (_pam_exec(pamh, flags, argc, argv)); 181 1.1 christos } 182 1.1 christos 183 1.1 christos PAM_EXTERN int 184 1.1 christos pam_sm_open_session(pam_handle_t *pamh, int flags, 185 1.1 christos int argc, const char *argv[]) 186 1.1 christos { 187 1.1 christos 188 1.1 christos return (_pam_exec(pamh, flags, argc, argv)); 189 1.1 christos } 190 1.1 christos 191 1.1 christos PAM_EXTERN int 192 1.1 christos pam_sm_close_session(pam_handle_t *pamh, int flags, 193 1.1 christos int argc, const char *argv[]) 194 1.1 christos { 195 1.1 christos 196 1.1 christos return (_pam_exec(pamh, flags, argc, argv)); 197 1.1 christos } 198 1.1 christos 199 1.1 christos PAM_EXTERN int 200 1.1 christos pam_sm_chauthtok(pam_handle_t *pamh, int flags, 201 1.1 christos int argc, const char *argv[]) 202 1.1 christos { 203 1.1 christos 204 1.1 christos return (_pam_exec(pamh, flags, argc, argv)); 205 1.1 christos } 206 1.1 christos 207 1.1 christos PAM_MODULE_ENTRY("pam_exec"); 208