import from HEAD:
[freeradius.git] / src / main / exec.c
1 /*
2  * exec.c       Execute external programs.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * Copyright 2000  Michael J. Hartwick <hartwick@hartwick.com>
22  */
23 static const char rcsid[] = "$Id$";
24
25 #include "autoconf.h"
26
27 #include <sys/file.h>
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <ctype.h>
33 #include <signal.h>
34
35 #ifdef HAVE_SYS_WAIT_H
36 #       include <sys/wait.h>
37 #endif
38 #ifndef WEXITSTATUS
39 #       define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
40 #endif
41 #ifndef WIFEXITED
42 #       define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
43 #endif
44
45 #include "radiusd.h"
46 #include "rad_assert.h"
47
48 /*
49  *      Execute a program on successful authentication.
50  *      Return 0 if exec_wait == 0.
51  *      Return the exit code of the called program if exec_wait != 0.
52  *      Return -1 on fork/other errors in the parent process.
53  */
54 int radius_exec_program(const char *cmd, REQUEST *request,
55                         int exec_wait,
56                         char *user_msg, int msg_len,
57                         VALUE_PAIR *input_pairs,
58                         VALUE_PAIR **output_pairs)
59 {
60         VALUE_PAIR *vp;
61         char answer[4096];
62         char *argv[256];
63         char *buf, *p;
64         int pd[2];
65         pid_t pid, child_pid;
66         int argc = -1;
67         int comma = 0;
68         int status;
69         int n, left, done;
70
71         if (user_msg) *user_msg = '\0';
72         if (output_pairs) *output_pairs = NULL;
73
74         /*
75          *      Open a pipe for child/parent communication, if
76          *      necessary.
77          */
78         if (exec_wait) {
79                 if (pipe(pd) != 0) {
80                         radlog(L_ERR|L_CONS, "Couldn't open pipe: %s",
81                                strerror(errno));
82                         return -1;
83                 }
84         } else {
85                 /*
86                  *      We're not waiting, so we don't look for a
87                  *      message, or VP's.
88                  */
89                 user_msg = NULL;
90                 output_pairs = NULL;
91         }
92
93         /*
94          *      Do the translation (as the parent) of the command to
95          *      execute.  This MAY involve calling other modules, so
96          *      we want to do it in the parent.
97          */
98         radius_xlat(answer, sizeof(answer), cmd, request, NULL);
99         buf = answer;
100
101         /*
102          *      Log the command if we are debugging something
103          */
104         DEBUG("Exec-Program: %s", buf);
105
106         /*
107          *      Build vector list of arguments and execute.
108          *
109          *      FIXME: This parsing gets excited over spaces in
110          *      the translated strings, e.g. User-Name = "aa bb"
111          *      is passed as two seperate arguments, instead of one.
112          *
113          *      What we SHOULD do instead is to split the exec program
114          *      buffer first, and then do the translation on every
115          *      subsequent string.
116          */
117         p = strtok(buf, " \t");
118         if (p) do {
119                 argv[++argc] = p;
120                 p = strtok(NULL, " \t");
121         } while(p != NULL);
122
123         argv[++argc] = p;
124         if (argc == 0) {
125                 radlog(L_ERR, "Exec-Program: empty command line.");
126                 return -1;
127         }
128
129         if ((pid = rad_fork(exec_wait)) == 0) {
130 #define MAX_ENVP 1024
131                 int i, devnull;
132                 char *envp[MAX_ENVP];
133                 int envlen;
134                 char buffer[1024];
135
136                 /*
137                  *      Child process.
138                  *
139                  *      We try to be fail-safe here.  So if ANYTHING
140                  *      goes wrong, we exit with status 1.
141                  */
142
143                 /*
144                  *      Open STDIN to /dev/null
145                  */
146                 devnull = open("/dev/null", O_RDWR);
147                 if (devnull < 0) {
148                         radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n",
149                                strerror(errno));
150                         exit(1);
151                 }
152                 dup2(devnull, STDIN_FILENO);
153
154                 /*
155                  *      Only massage the pipe handles if the parent
156                  *      has created them.
157                  */
158                 if (exec_wait) {
159                         /*
160                          *      pd[0] is the FD the child will read from,
161                          *      which we don't want.
162                          */
163                         if (close(pd[0]) != 0) {
164                                 radlog(L_ERR|L_CONS, "Can't close pipe: %s",
165                                        strerror(errno));
166                                 exit(1);
167                         }
168
169                         /*
170                          *      pd[1] is the FD that the child will write to,
171                          *      so we make it STDOUT.
172                          */
173                         if (dup2(pd[1], STDOUT_FILENO) != 1) {
174                                 radlog(L_ERR|L_CONS, "Can't dup stdout: %s",
175                                        strerror(errno));
176                                 exit(1);
177                         }
178
179                 } else {        /* no pipe, STDOUT should be /dev/null */
180                         dup2(devnull, STDOUT_FILENO);
181                 }
182
183                 /*
184                  *      If we're not debugging, then we can't do
185                  *      anything with the error messages, so we throw
186                  *      them away.
187                  *
188                  *      If we are debugging, then we want the error
189                  *      messages to go to the STDERR of the server.
190                  */
191                 if (debug_flag == 0) {
192                         dup2(devnull, STDERR_FILENO);
193                 }
194                 close(devnull);
195
196                 /*
197                  *      The server may have MANY FD's open.  We don't
198                  *      want to leave dangling FD's for the child process
199                  *      to play funky games with, so we close them.
200                  */
201                 for (i = 3; i < 256; i++) {
202                         close(i);
203                 }
204
205                 /*
206                  *      Set up the environment variables.
207                  *      We're in the child, and it will exit in 4 lines
208                  *      anyhow, so memory allocation isn't an issue.
209                  */
210                 envlen = 0;
211
212                 for (vp = input_pairs; vp != NULL; vp = vp->next) {
213                         /*
214                          *      Hmm... maybe we shouldn't pass the
215                          *      user's password in an environment
216                          *      variable...
217                          */
218                         snprintf(buffer, sizeof(buffer), "%s=", vp->name);
219                         for (p = buffer; *p != '='; p++) {
220                                 if (*p == '-') {
221                                         *p = '_';
222                                 } else if (isalpha((int) *p)) {
223                                         *p = toupper(*p);
224                                 }
225                         }
226
227                         n = strlen(buffer);
228                         vp_prints_value(buffer+n, sizeof(buffer) - n, vp, 1);
229
230                         envp[envlen++] = strdup(buffer);
231                 }
232                 envp[envlen] = NULL;
233                 execve(argv[0], argv, envp);
234                 radlog(L_ERR, "Exec-Program: FAILED to execute %s: %s",
235                        argv[0], strerror(errno));
236                 exit(1);
237         }
238
239         /*
240          *      Parent process.
241          */
242         if (pid < 0) {
243                 radlog(L_ERR|L_CONS, "Couldn't fork %s: %s",
244                        argv[0], strerror(errno));
245                 return -1;
246         }
247
248         /*
249          *      We're not waiting, exit, and ignore any child's
250          *      status.
251          */
252         if (!exec_wait) {
253                 return 0;
254         }
255
256         /*
257          *      Close the FD to which the child writes it's data.
258          *
259          *      If we can't close it, then we close pd[0], and return an
260          *      error.
261          */
262         if (close(pd[1]) != 0) {
263                 radlog(L_ERR|L_CONS, "Can't close pipe: %s", strerror(errno));
264                 close(pd[0]);
265                 return -1;
266         }
267
268         /*
269          *      Read from the pipe until we doesn't get any more or
270          *      until the message is full.
271          */
272         done = 0;
273         left = sizeof(answer) - 1;
274         while (1) {
275                 status = read(pd[0], answer + done, left);
276                 /*
277                  *      Nothing more to read: stop.
278                  */
279                 if (status == 0) {
280                         break;
281                 }
282
283                 /*
284                  *      Error: See if we have to continue.
285                  */
286                 if (status < 0) {
287                         /*
288                          *      We were interrupted: continue reading.
289                          */
290                         if (errno == EINTR) {
291                                 continue;
292                         }
293
294                         /*
295                          *      There was another error.  Most likely
296                          *      The child process has finished, and
297                          *      exited.
298                          */
299                         break;
300                 }
301
302                 done += status;
303                 left -= status;
304                 if (left <= 0) break;
305         }
306         answer[done] = 0;
307
308         /*
309          *      Make sure that the writer can't block while writing to
310          *      a pipe that no one is reading from anymore.
311          */
312         close(pd[0]);
313
314         DEBUG2("Exec-Program output: %s", answer);
315
316         /*
317          *      Parse the output, if any.
318          */
319         if (done) {
320                 n = T_INVALID;
321                 if (output_pairs) {
322                         /*
323                          *      For backwards compatibility, first check
324                          *      for plain text (user_msg).
325                          */
326                         vp = NULL;
327                         n = userparse(answer, &vp);
328                         if (vp) {
329                                 pairfree(&vp);
330                         }
331                 }
332
333                 if (n == T_INVALID) {
334                         radlog(L_DBG, "Exec-Program-Wait: plaintext: %s", answer);
335                         if (user_msg) {
336                                 strNcpy(user_msg, answer, msg_len);
337                         }
338                 } else {
339                         /*
340                          *      HACK: Replace '\n' with ',' so that
341                          *      userparse() can parse the buffer in
342                          *      one go (the proper way would be to
343                          *      fix userparse(), but oh well).
344                          */
345                         for (p = answer; *p; p++) {
346                                 if (*p == '\n') {
347                                         *p = comma ? ' ' : ',';
348                                         p++;
349                                         comma = 0;
350                                 }
351                                 if (*p == ',') comma++;
352                         }
353
354                         /*
355                          *  Replace any trailing comma by a NUL.
356                          */
357                         if (answer[strlen(answer) - 1] == ',') {
358                                 answer[strlen(answer) - 1] = '\0';
359                         }
360
361                         radlog(L_DBG,"Exec-Program-Wait: value-pairs: %s", answer);
362                         if (userparse(answer, &vp) == T_INVALID) {
363                                 radlog(L_ERR, "Exec-Program-Wait: %s: unparsable reply", cmd);
364
365                         } else {
366                                 /*
367                                  *      Tell the caller about the value
368                                  *      pairs.
369                                  */
370                                 *output_pairs = vp;
371                         }
372                 } /* else the answer was a set of VP's, not a text message */
373         } /* else we didn't read anything from the child. */
374
375         /*
376          *      Call rad_waitpid (should map to waitpid on non-threaded
377          *      or single-server systems).
378          */
379         child_pid = rad_waitpid(pid, &status, 0);
380         if (child_pid == pid) {
381                 if (WIFEXITED(status)) {
382                         status = WEXITSTATUS(status);
383                         radlog(L_DBG, "Exec-Program: returned: %d", status);
384                         return status;
385                 }
386         }
387
388         radlog(L_ERR|L_CONS, "Exec-Program: Abnormal child exit: %s",
389                strerror(errno));
390         return 1;
391 }