Imported Upstream version 1.1.6
[platform/upstream/pam.git] / modules / pam_exec / pam_exec.c
1 /*
2  * Copyright (c) 2006, 2008 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 #include <security/_pam_macros.h>
62
63 #define ENV_ITEM(n) { (n), #n }
64 static struct {
65   int item;
66   const char *name;
67 } env_items[] = {
68   ENV_ITEM(PAM_SERVICE),
69   ENV_ITEM(PAM_USER),
70   ENV_ITEM(PAM_TTY),
71   ENV_ITEM(PAM_RHOST),
72   ENV_ITEM(PAM_RUSER),
73 };
74
75
76 static int
77 call_exec (const char *pam_type, pam_handle_t *pamh,
78            int argc, const char **argv)
79 {
80   int debug = 0;
81   int call_setuid = 0;
82   int quiet = 0;
83   int expose_authtok = 0;
84   int optargc;
85   const char *logfile = NULL;
86   const char *authtok = NULL;
87   pid_t pid;
88   int fds[2];
89
90   if (argc < 1) {
91     pam_syslog (pamh, LOG_ERR,
92                 "This module needs at least one argument");
93     return PAM_SERVICE_ERR;
94   }
95
96   for (optargc = 0; optargc < argc; optargc++)
97     {
98       if (argv[optargc][0] == '/') /* paths starts with / */
99         break;
100
101       if (strcasecmp (argv[optargc], "debug") == 0)
102         debug = 1;
103       else if (strncasecmp (argv[optargc], "log=", 4) == 0)
104         logfile = &argv[optargc][4];
105       else if (strcasecmp (argv[optargc], "seteuid") == 0)
106         call_setuid = 1;
107       else if (strcasecmp (argv[optargc], "quiet") == 0)
108         quiet = 1;
109       else if (strcasecmp (argv[optargc], "expose_authtok") == 0)
110         expose_authtok = 1;
111       else
112         break; /* Unknown option, assume program to execute. */
113     }
114
115   if (expose_authtok == 1)
116     {
117       if (strcmp (pam_type, "auth") != 0)
118         {
119           pam_syslog (pamh, LOG_ERR,
120                       "expose_authtok not supported for type %s", pam_type);
121           expose_authtok = 0;
122         }
123       else
124         {
125           const void *void_pass;
126           int retval;
127
128           retval = pam_get_item (pamh, PAM_AUTHTOK, &void_pass);
129           if (retval != PAM_SUCCESS)
130             {
131               if (debug)
132                 pam_syslog (pamh, LOG_DEBUG,
133                             "pam_get_item (PAM_AUTHTOK) failed, return %d",
134                             retval);
135               return retval;
136             }
137           else if (void_pass == NULL)
138             {
139               char *resp = NULL;
140
141               retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF,
142                                    &resp, _("Password: "));
143
144               if (retval != PAM_SUCCESS)
145                 {
146                   _pam_drop (resp);
147                   if (retval == PAM_CONV_AGAIN)
148                     retval = PAM_INCOMPLETE;
149                   return retval;
150                 }
151
152               pam_set_item (pamh, PAM_AUTHTOK, resp);
153               authtok = strdupa (resp);
154               _pam_drop (resp);
155             }
156           else
157             authtok = void_pass;
158
159           if (pipe(fds) != 0)
160             {
161               pam_syslog (pamh, LOG_ERR, "Could not create pipe: %m");
162               return PAM_SYSTEM_ERR;
163             }
164         }
165     }
166
167   if (optargc >= argc) {
168     pam_syslog (pamh, LOG_ERR, "No path given as argument");
169     return PAM_SERVICE_ERR;
170   }
171
172   pid = fork();
173   if (pid == -1)
174     return PAM_SYSTEM_ERR;
175   if (pid > 0) /* parent */
176     {
177       int status = 0;
178       pid_t retval;
179
180       if (expose_authtok) /* send the password to the child */
181         {
182           if (authtok != NULL)
183             {            /* send the password to the child */
184               if (debug)
185                 pam_syslog (pamh, LOG_DEBUG, "send password to child");
186               if (write(fds[1], authtok, strlen(authtok)+1) == -1)
187                 pam_syslog (pamh, LOG_ERR,
188                             "sending password to child failed: %m");
189               authtok = NULL;
190             }
191           else
192             {
193               if (write(fds[1], "", 1) == -1)   /* blank password */
194                 pam_syslog (pamh, LOG_ERR,
195                             "sending password to child failed: %m");
196             }
197         close(fds[0]);       /* close here to avoid possible SIGPIPE above */
198         close(fds[1]);
199         }
200
201       while ((retval = waitpid (pid, &status, 0)) == -1 &&
202              errno == EINTR);
203       if (retval == (pid_t)-1)
204         {
205           pam_syslog (pamh, LOG_ERR, "waitpid returns with -1: %m");
206           return PAM_SYSTEM_ERR;
207         }
208       else if (status != 0)
209         {
210           if (WIFEXITED(status))
211             {
212               pam_syslog (pamh, LOG_ERR, "%s failed: exit code %d",
213                           argv[optargc], WEXITSTATUS(status));
214                 if (!quiet)
215               pam_error (pamh, _("%s failed: exit code %d"),
216                          argv[optargc], WEXITSTATUS(status));
217             }
218           else if (WIFSIGNALED(status))
219             {
220               pam_syslog (pamh, LOG_ERR, "%s failed: caught signal %d%s",
221                           argv[optargc], WTERMSIG(status),
222                           WCOREDUMP(status) ? " (core dumped)" : "");
223                 if (!quiet)
224               pam_error (pamh, _("%s failed: caught signal %d%s"),
225                          argv[optargc], WTERMSIG(status),
226                          WCOREDUMP(status) ? " (core dumped)" : "");
227             }
228           else
229             {
230               pam_syslog (pamh, LOG_ERR, "%s failed: unknown status 0x%x",
231                           argv[optargc], status);
232                 if (!quiet)
233               pam_error (pamh, _("%s failed: unknown status 0x%x"),
234                          argv[optargc], status);
235             }
236           return PAM_SYSTEM_ERR;
237         }
238       return PAM_SUCCESS;
239     }
240   else /* child */
241     {
242       char **arggv;
243       int i;
244       char **envlist, **tmp;
245       int envlen, nitems;
246       char *envstr;
247
248       if (expose_authtok)
249         {
250           /* reopen stdin as pipe */
251           if (dup2(fds[0], STDIN_FILENO) == -1)
252             {
253               int err = errno;
254               pam_syslog (pamh, LOG_ERR, "dup2 of STDIN failed: %m");
255               _exit (err);
256             }
257
258           for (i = 0; i < sysconf (_SC_OPEN_MAX); i++)
259             {
260               if (i != STDIN_FILENO)
261                 close (i);
262             }
263         }
264       else
265         {
266           for (i = 0; i < sysconf (_SC_OPEN_MAX); i++)
267             close (i);
268
269           /* New stdin.  */
270           if ((i = open ("/dev/null", O_RDWR)) < 0)
271             {
272               int err = errno;
273               pam_syslog (pamh, LOG_ERR, "open of /dev/null failed: %m");
274               _exit (err);
275             }
276         }
277
278       /* New stdout and stderr.  */
279       if (logfile)
280         {
281           time_t tm = time (NULL);
282           char *buffer = NULL;
283
284           if ((i = open (logfile, O_CREAT|O_APPEND|O_WRONLY,
285                          S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1)
286             {
287               int err = errno;
288               pam_syslog (pamh, LOG_ERR, "open of %s failed: %m",
289                           logfile);
290               _exit (err);
291             }
292           if (asprintf (&buffer, "*** %s", ctime (&tm)) > 0)
293             {
294               pam_modutil_write (i, buffer, strlen (buffer));
295               free (buffer);
296             }
297         }
298       else
299         {
300           /* New stdout/stderr.  */
301           if ((i = open ("/dev/null", O_RDWR)) < 0)
302             {
303               int err = errno;
304               pam_syslog (pamh, LOG_ERR, "open of /dev/null failed: %m");
305               _exit (err);
306             }
307         }
308
309       if (dup (i) == -1)
310         {
311           int err = errno;
312           pam_syslog (pamh, LOG_ERR, "dup failed: %m");
313           _exit (err);
314         }
315
316       if (call_setuid)
317         if (setuid (geteuid ()) == -1)
318           {
319             int err = errno;
320             pam_syslog (pamh, LOG_ERR, "setuid(%lu) failed: %m",
321                         (unsigned long) geteuid ());
322             _exit (err);
323           }
324
325       if (setsid () == -1)
326         {
327           int err = errno;
328           pam_syslog (pamh, LOG_ERR, "setsid failed: %m");
329           _exit (err);
330         }
331
332       arggv = calloc (argc + 4, sizeof (char *));
333       if (arggv == NULL)
334         _exit (ENOMEM);
335
336       for (i = 0; i < (argc - optargc); i++)
337         arggv[i] = strdup(argv[i+optargc]);
338       arggv[i] = NULL;
339
340       /*
341        * Set up the child's environment list.  It consists of the PAM
342        * environment, plus a few hand-picked PAM items.
343        */
344       envlist = pam_getenvlist(pamh);
345       for (envlen = 0; envlist[envlen] != NULL; ++envlen)
346         /* nothing */ ;
347       nitems = sizeof(env_items) / sizeof(*env_items);
348       /* + 2 because of PAM_TYPE and NULL entry */
349       tmp = realloc(envlist, (envlen + nitems + 2) * sizeof(*envlist));
350       if (tmp == NULL)
351       {
352         free(envlist);
353         pam_syslog (pamh, LOG_ERR, "realloc environment failed: %m");
354         _exit (ENOMEM);
355       }
356       envlist = tmp;
357       for (i = 0; i < nitems; ++i)
358       {
359         const void *item;
360
361         if (pam_get_item(pamh, env_items[i].item, &item) != PAM_SUCCESS || item == NULL)
362           continue;
363         if (asprintf(&envstr, "%s=%s", env_items[i].name, (const char *)item) < 0)
364         {
365           free(envlist);
366           pam_syslog (pamh, LOG_ERR, "prepare environment failed: %m");
367           _exit (ENOMEM);
368         }
369         envlist[envlen++] = envstr;
370         envlist[envlen] = NULL;
371       }
372
373       if (asprintf(&envstr, "PAM_TYPE=%s", pam_type) < 0)
374         {
375           free(envlist);
376           pam_syslog (pamh, LOG_ERR, "prepare environment failed: %m");
377           _exit (ENOMEM);
378         }
379       envlist[envlen++] = envstr;
380       envlist[envlen] = NULL;
381
382       if (debug)
383         pam_syslog (pamh, LOG_DEBUG, "Calling %s ...", arggv[0]);
384
385       execve (arggv[0], arggv, envlist);
386       i = errno;
387       pam_syslog (pamh, LOG_ERR, "execve(%s,...) failed: %m", arggv[0]);
388       free(envlist);
389       _exit (i);
390     }
391   return PAM_SYSTEM_ERR; /* will never be reached. */
392 }
393
394 PAM_EXTERN int
395 pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
396                      int argc, const char **argv)
397 {
398   return call_exec ("auth", pamh, argc, argv);
399 }
400
401 PAM_EXTERN int
402 pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED,
403                 int argc UNUSED, const char **argv UNUSED)
404 {
405   return PAM_IGNORE;
406 }
407
408 /* password updating functions */
409
410 PAM_EXTERN int
411 pam_sm_chauthtok(pam_handle_t *pamh, int flags,
412                  int argc, const char **argv)
413 {
414   if (flags & PAM_PRELIM_CHECK)
415     return PAM_SUCCESS;
416   return call_exec ("password", pamh, argc, argv);
417 }
418
419 PAM_EXTERN int
420 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
421                  int argc, const char **argv)
422 {
423   return call_exec ("account", pamh, argc, argv);
424 }
425
426 PAM_EXTERN int
427 pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
428                     int argc, const char **argv)
429 {
430   return call_exec ("open_session", pamh, argc, argv);
431 }
432
433 PAM_EXTERN int
434 pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
435                      int argc, const char **argv)
436 {
437   return call_exec ("close_session", pamh, argc, argv);
438 }
439
440 #ifdef PAM_STATIC
441 struct pam_module _pam_exec_modstruct = {
442   "pam_exec",
443   pam_sm_authenticate,
444   pam_sm_setcred,
445   pam_sm_acct_mgmt,
446   pam_sm_open_session,
447   pam_sm_close_session,
448   pam_sm_chauthtok,
449 };
450 #endif