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