]> granicus.if.org Git - linux-pam/blob - modules/pam_exec/pam_exec.c
Relevant BUGIDs:
[linux-pam] / modules / pam_exec / pam_exec.c
1 /*
2  * Copyright (c) 2006 Thorsten Kukuk <kukuk@thkukuk.de>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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
15  *    written permission.
16  *
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.)
22  *
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.
34  */
35
36 #if defined(HAVE_CONFIG_H)
37 #include "config.h"
38 #endif
39
40 #include <time.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <syslog.h>
46 #include <unistd.h>
47 #include <stdlib.h>
48 #include <sys/wait.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
51
52
53 #define PAM_SM_AUTH
54 #define PAM_SM_ACCOUNT
55 #define PAM_SM_SESSION
56 #define PAM_SM_PASSWORD
57
58 #include <security/pam_modules.h>
59 #include <security/pam_modutil.h>
60 #include <security/pam_ext.h>
61
62 #define ENV_ITEM(n) { (n), #n }
63 static struct {
64   int item;
65   const char *name;
66 } env_items[] = {
67   ENV_ITEM(PAM_SERVICE),
68   ENV_ITEM(PAM_USER),
69   ENV_ITEM(PAM_TTY),
70   ENV_ITEM(PAM_RHOST),
71   ENV_ITEM(PAM_RUSER),
72 };
73
74 static int
75 call_exec (pam_handle_t *pamh, int argc, const char **argv)
76 {
77   int debug = 0;
78   int call_setuid = 0;
79   int quiet = 0;
80   int optargc;
81   const char *logfile = NULL;
82   pid_t pid;
83
84   if (argc < 1) {
85     pam_syslog (pamh, LOG_ERR,
86                 "This module needs at least one argument");
87     return PAM_SERVICE_ERR;
88   }
89
90   for (optargc = 0; optargc < argc; optargc++)
91     {
92       if (argv[optargc][0] == '/') /* paths starts with / */
93         break;
94
95       if (strcasecmp (argv[optargc], "debug") == 0)
96         debug = 1;
97       else if (strncasecmp (argv[optargc], "log=", 4) == 0)
98         logfile = &argv[optargc][4];
99       else if (strcasecmp (argv[optargc], "seteuid") == 0)
100         call_setuid = 1;
101       else if (strcasecmp (argv[optargc], "quiet") == 0)
102         quiet = 1;
103       else
104         break; /* Unknown option, assume program to execute. */
105     }
106
107
108   if (optargc >= argc) {
109     pam_syslog (pamh, LOG_ERR, "No path given as argument");
110     return PAM_SERVICE_ERR;
111   }
112
113   pid = fork();
114   if (pid == -1)
115     return PAM_SYSTEM_ERR;
116   if (pid > 0) /* parent */
117     {
118       int status = 0;
119       pid_t retval;
120       while ((retval = waitpid (pid, &status, 0)) == -1 &&
121              errno == EINTR);
122       if (retval == (pid_t)-1)
123         {
124           pam_syslog (pamh, LOG_ERR, "waitpid returns with -1: %m");
125           return PAM_SYSTEM_ERR;
126         }
127       else if (status != 0)
128         {
129           if (WIFEXITED(status))
130             {
131               pam_syslog (pamh, LOG_ERR, "%s failed: exit code %d",
132                           argv[optargc], WEXITSTATUS(status));
133                 if (!quiet)
134               pam_error (pamh, _("%s failed: exit code %d"),
135                          argv[optargc], WEXITSTATUS(status));
136             }
137           else if (WIFSIGNALED(status))
138             {
139               pam_syslog (pamh, LOG_ERR, "%s failed: caught signal %d%s",
140                           argv[optargc], WTERMSIG(status),
141                           WCOREDUMP(status) ? " (core dumped)" : "");
142                 if (!quiet)
143               pam_error (pamh, _("%s failed: caught signal %d%s"),
144                          argv[optargc], WTERMSIG(status),
145                          WCOREDUMP(status) ? " (core dumped)" : "");
146             }
147           else
148             {
149               pam_syslog (pamh, LOG_ERR, "%s failed: unknown status 0x%x",
150                           argv[optargc], status);
151                 if (!quiet)
152               pam_error (pamh, _("%s failed: unknown status 0x%x"),
153                          argv[optargc], status);
154             }
155           return PAM_SYSTEM_ERR;
156         }
157       return PAM_SUCCESS;
158     }
159   else /* child */
160     {
161       char **arggv;
162       int i;
163
164       for (i = 0; i < sysconf (_SC_OPEN_MAX); i++)
165         close (i);
166
167       /* New stdin.  */
168       if ((i = open ("/dev/null", O_RDWR)) < 0)
169         {
170           int err = errno;
171           pam_syslog (pamh, LOG_ERR, "open of /dev/null failed: %m");
172           exit (err);
173         }
174       /* New stdout and stderr.  */
175       if (logfile)
176         {
177           time_t tm = time (NULL);
178           char *buffer = NULL;
179
180           if ((i = open (logfile, O_CREAT|O_APPEND|O_WRONLY,
181                          S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1)
182             {
183               int err = errno;
184               pam_syslog (pamh, LOG_ERR, "open of %s failed: %m",
185                           logfile);
186               exit (err);
187             }
188           if (asprintf (&buffer, "*** %s", ctime (&tm)) > 0)
189             {
190               pam_modutil_write (i, buffer, strlen (buffer));
191               free (buffer);
192             }
193         }
194       else
195         if (dup (i) == -1)
196           {
197             int err = errno;
198             pam_syslog (pamh, LOG_ERR, "dup failed: %m");
199             exit (err);
200           }
201       if (dup (i) == -1)
202         {
203           int err = errno;
204           pam_syslog (pamh, LOG_ERR, "dup failed: %m");
205           exit (err);
206         }
207
208       if (call_setuid)
209         if (setuid (geteuid ()) == -1)
210           {
211             int err = errno;
212             pam_syslog (pamh, LOG_ERR, "setuid(%lu) failed: %m",
213                         (unsigned long) geteuid ());
214             exit (err);
215           }
216
217       if (setsid () == -1)
218         {
219           int err = errno;
220           pam_syslog (pamh, LOG_ERR, "setsid failed: %m");
221           exit (err);
222         }
223
224       arggv = calloc (argc + 4, sizeof (char *));
225       if (arggv == NULL)
226         exit (ENOMEM);
227
228       for (i = 0; i < (argc - optargc); i++)
229         arggv[i] = strdup(argv[i+optargc]);
230       arggv[i] = NULL;
231
232       char **envlist, **tmp;
233       int envlen, nitems;
234
235       /*
236        * Set up the child's environment list.  It consists of the PAM
237        * environment, plus a few hand-picked PAM items.
238        */
239       envlist = pam_getenvlist(pamh);
240       for (envlen = 0; envlist[envlen] != NULL; ++envlen)
241         /* nothing */ ;
242       nitems = sizeof(env_items) / sizeof(*env_items);
243       tmp = realloc(envlist, (envlen + nitems + 1) * sizeof(*envlist));
244       if (tmp == NULL)
245       {
246         free(envlist);
247         pam_syslog (pamh, LOG_ERR, "realloc environment failed : %m");
248         exit (ENOMEM); 
249       }
250       envlist = tmp;
251       for (i = 0; i < nitems; ++i)
252       {
253         const void *item;
254         char *envstr;
255
256         if (pam_get_item(pamh, env_items[i].item, &item) != PAM_SUCCESS || item == NULL)
257           continue;
258         asprintf(&envstr, "%s=%s", env_items[i].name, (const char *)item);
259         if (envstr == NULL)
260         {
261           free(envlist);
262           pam_syslog (pamh, LOG_ERR, "prepare environment failed : %m");
263           exit (ENOMEM);
264         }
265         envlist[envlen++] = envstr;
266         envlist[envlen] = NULL;
267       }
268
269       if (debug)
270         pam_syslog (pamh, LOG_DEBUG, "Calling %s ...", arggv[0]);
271
272       if (execve (arggv[0], arggv, envlist) == -1)
273         {
274           int err = errno;
275           pam_syslog (pamh, LOG_ERR, "execve(%s,...) failed: %m",
276                       arggv[0]);
277           free(envlist);
278           exit (err);
279         }
280       free(envlist);
281       exit (1); /* should never be reached. */
282     }
283   return PAM_SYSTEM_ERR; /* will never be reached. */
284 }
285
286 PAM_EXTERN int
287 pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
288                      int argc, const char **argv)
289 {
290   return call_exec (pamh, argc, argv);
291 }
292
293 PAM_EXTERN int
294 pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED,
295                 int argc UNUSED, const char **argv UNUSED)
296 {
297   return PAM_IGNORE;
298 }
299
300 /* password updating functions */
301
302 PAM_EXTERN int
303 pam_sm_chauthtok(pam_handle_t *pamh, int flags,
304                  int argc, const char **argv)
305 {
306   if (flags & PAM_PRELIM_CHECK)
307     return PAM_SUCCESS;
308   return call_exec (pamh, argc, argv);
309 }
310
311 PAM_EXTERN int
312 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
313                  int argc, const char **argv)
314 {
315   return call_exec (pamh, argc, argv);
316 }
317
318 PAM_EXTERN int
319 pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
320                     int argc, const char **argv)
321 {
322   return call_exec (pamh, argc, argv);
323 }
324
325 PAM_EXTERN int
326 pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
327                      int argc, const char **argv)
328 {
329   return call_exec (pamh, argc, argv);
330 }
331
332 #ifdef PAM_STATIC
333 struct pam_module _pam_exec_modstruct = {
334   "pam_exec",
335   pam_sm_authenticate,
336   pam_sm_setcred,
337   pam_sm_acct_mgmt,
338   pam_sm_open_session,
339   pam_sm_close_session,
340   pam_sm_chauthtok,
341 };
342 #endif