Mark packets with invalid signature as "done", otherwise they are
[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,2001,2002,2003,2004  The FreeRADIUS server project
21  */
22 static const char rcsid[] = "$Id$";
23
24 #include <freeradius-devel/autoconf.h>
25
26 #include <sys/file.h>
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <ctype.h>
32 #include <signal.h>
33
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #ifdef HAVE_SYS_WAIT_H
39 #       include <sys/wait.h>
40 #endif
41 #ifndef WEXITSTATUS
42 #       define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
43 #endif
44 #ifndef WIFEXITED
45 #       define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
46 #endif
47
48 #include <freeradius-devel/radiusd.h>
49 #include <freeradius-devel/rad_assert.h>
50
51 #define MAX_ARGV (256)
52 /*
53  *      Execute a program on successful authentication.
54  *      Return 0 if exec_wait == 0.
55  *      Return the exit code of the called program if exec_wait != 0.
56  *      Return -1 on fork/other errors in the parent process.
57  */
58 int radius_exec_program(const char *cmd, REQUEST *request,
59                         int exec_wait,
60                         char *user_msg, int msg_len,
61                         VALUE_PAIR *input_pairs,
62                         VALUE_PAIR **output_pairs,
63                         int shell_escape)
64 {
65         VALUE_PAIR *vp;
66         char mycmd[1024];
67         char answer[4096];
68         char argv_buf[4096];
69         char *argv[MAX_ARGV];
70         const char *from;
71         char *p, *to;
72         int pd[2];
73         pid_t pid, child_pid;
74         int argc = -1;
75         int comma = 0;
76         int status;
77         int i;
78         int n, left, done;
79
80         if (user_msg) *user_msg = '\0';
81         if (output_pairs) *output_pairs = NULL;
82
83         if (strlen(cmd) > (sizeof(mycmd) - 1)) {
84                 radlog(L_ERR|L_CONS, "Command line is too long");
85                 return -1;
86         }
87
88         /*
89          *      Check for bad escapes.
90          */
91         if (cmd[strlen(cmd) - 1] == '\\') {
92                 radlog(L_ERR|L_CONS, "Command line has final backslash, without a following character");
93                 return -1;
94         }
95
96         strNcpy(mycmd, cmd, sizeof(mycmd));
97
98         /*
99          *      Split the string into argv's BEFORE doing radius_xlat...
100          */
101         from = cmd;
102         to = mycmd;
103         argc = 0;
104         while (*from) {
105                 int length;
106
107                 /*
108                  *      Skip spaces.
109                  */
110                 if ((*from == ' ') || (*from == '\t')) {
111                         from++;
112                         continue;
113                 }
114
115                 argv[argc] = to;
116                 argc++;
117
118                 if (argc >= (MAX_ARGV - 1)) break;
119
120                 /*
121                  *      Copy the argv over to our buffer.
122                  */
123                 while (*from && (*from != ' ') && (*from != '\t')) {
124                         if (to >= mycmd + sizeof(mycmd) - 1) {
125                                 return -1; /* ran out of space */
126                         }
127
128                         switch (*from) {
129                         case '"':
130                         case '\'':
131                                 length = rad_copy_string(to, from);
132                                 if (length < 0) {
133                                         radlog(L_ERR|L_CONS, "Invalid string passed as argument for external program");
134                                         return -1;
135                                 }
136                                 from += length;
137                                 to += length;
138                                 break;
139
140                         case '%':
141                                 if (from[1] == '{') {
142                                         *(to++) = *(from++);
143
144                                         length = rad_copy_variable(to, from);
145                                         if (length < 0) {
146                                                 radlog(L_ERR|L_CONS, "Invalid variable expansion passed as argument for external program");
147                                                 return -1;
148                                         }
149                                         from += length;
150                                         to += length;
151                                 } else { /* FIXME: catch %%{ ? */
152                                         *(to++) = *(from++);
153                                 }
154                                 break;
155
156                         default:
157                                 *(to++) = *(from++);
158                         }
159                 } /* end of string, or found a space */
160
161                 *(to++) = '\0'; /* terminate the string */
162         }
163
164         /*
165          *      We have to have SOMETHING, at least.
166          */
167         if (argc <= 0) {
168                 radlog(L_ERR, "Exec-Program: empty command line.");
169                 return -1;
170         }
171
172         /*
173          *      Expand each string, as appropriate.
174          */
175         to = argv_buf;
176         left = sizeof(argv_buf);
177         for (i = 0; i < argc; i++) {
178                 int sublen;
179
180                 /*
181                  *      Don't touch argv's which won't be translated.
182                  */
183                 if (strchr(argv[i], '%') == NULL) continue;
184
185                 sublen = radius_xlat(to, left - 1, argv[i], request, NULL);
186                 if (sublen <= 0) {
187                         /*
188                          *      Fail to be backwards compatible.
189                          *
190                          *      It's yucky, but it won't break anything,
191                          *      and it won't cause security problems.
192                          */
193                         sublen = 0;
194                 }
195
196                 argv[i] = to;
197                 to += sublen;
198                 *(to++) = '\0';
199                 left -= sublen;
200                 left--;
201
202                 if (left <= 0) {
203                         radlog(L_ERR, "Exec-Program: Ran out of space while expanding arguments.");
204                         return -1;
205                 }
206         }
207         argv[argc] = NULL;
208
209         /*
210          *      Open a pipe for child/parent communication, if necessary.
211          */
212         if (exec_wait) {
213                 if (pipe(pd) != 0) {
214                         radlog(L_ERR|L_CONS, "Couldn't open pipe: %s",
215                                strerror(errno));
216                         return -1;
217                 }
218         } else {
219                 /*
220                  *      We're not waiting, so we don't look for a
221                  *      message, or VP's.
222                  */
223                 user_msg = NULL;
224                 output_pairs = NULL;
225         }
226
227         if ((pid = rad_fork(exec_wait)) == 0) {
228 #define MAX_ENVP 1024
229                 int devnull;
230                 char *envp[MAX_ENVP];
231                 int envlen;
232                 char buffer[1024];
233
234                 /*
235                  *      Child process.
236                  *
237                  *      We try to be fail-safe here. So if ANYTHING
238                  *      goes wrong, we exit with status 1.
239                  */
240
241                 /*
242                  *      Open STDIN to /dev/null
243                  */
244                 devnull = open("/dev/null", O_RDWR);
245                 if (devnull < 0) {
246                         radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n",
247                                strerror(errno));
248                         exit(1);
249                 }
250                 dup2(devnull, STDIN_FILENO);
251
252                 /*
253                  *      Only massage the pipe handles if the parent
254                  *      has created them.
255                  */
256                 if (exec_wait) {
257                         /*
258                          *      pd[0] is the FD the child will read from,
259                          *      which we don't want.
260                          */
261                         if (close(pd[0]) != 0) {
262                                 radlog(L_ERR|L_CONS, "Can't close pipe: %s",
263                                        strerror(errno));
264                                 exit(1);
265                         }
266
267                         /*
268                          *      pd[1] is the FD that the child will write to,
269                          *      so we make it STDOUT.
270                          */
271                         if (dup2(pd[1], STDOUT_FILENO) != 1) {
272                                 radlog(L_ERR|L_CONS, "Can't dup stdout: %s",
273                                        strerror(errno));
274                                 exit(1);
275                         }
276
277                 } else {        /* no pipe, STDOUT should be /dev/null */
278                         dup2(devnull, STDOUT_FILENO);
279                 }
280
281                 /*
282                  *      If we're not debugging, then we can't do
283                  *      anything with the error messages, so we throw
284                  *      them away.
285                  *
286                  *      If we are debugging, then we want the error
287                  *      messages to go to the STDERR of the server.
288                  */
289                 if (debug_flag == 0) {
290                         dup2(devnull, STDERR_FILENO);
291                 }
292                 close(devnull);
293
294                 /*
295                  *      The server may have MANY FD's open.  We don't
296                  *      want to leave dangling FD's for the child process
297                  *      to play funky games with, so we close them.
298                  */
299                 closefrom(3);
300
301                 /*
302                  *      Set up the environment variables.
303                  *      We're in the child, and it will exit in 4 lines
304                  *      anyhow, so memory allocation isn't an issue.
305                  */
306                 envlen = 0;
307
308                 for (vp = input_pairs; vp != NULL; vp = vp->next) {
309                         /*
310                          *      Hmm... maybe we shouldn't pass the
311                          *      user's password in an environment
312                          *      variable...
313                          */
314                         snprintf(buffer, sizeof(buffer), "%s=", vp->name);
315                         if (shell_escape) {
316                                 for (p = buffer; *p != '='; p++) {
317                                         if (*p == '-') {
318                                                 *p = '_';
319                                         } else if (isalpha((int) *p)) {
320                                                 *p = toupper(*p);
321                                         }
322                                 }
323                         }
324
325                         n = strlen(buffer);
326                         vp_prints_value(buffer+n, sizeof(buffer) - n, vp, shell_escape);
327
328                         envp[envlen++] = strdup(buffer);
329
330                         /*
331                          *      Don't add too many attributes.
332                          */
333                         if (envlen == (MAX_ENVP - 1)) break;
334                 }
335                 envp[envlen] = NULL;
336
337                 execve(argv[0], argv, envp);
338                 radlog(L_ERR, "Exec-Program: FAILED to execute %s: %s",
339                        argv[0], strerror(errno));
340                 exit(1);
341         }
342
343         /*
344          *      Parent process.
345          */
346         if (pid < 0) {
347                 radlog(L_ERR|L_CONS, "Couldn't fork %s: %s",
348                        argv[0], strerror(errno));
349                 return -1;
350         }
351
352         /*
353          *      We're not waiting, exit, and ignore any child's status.
354          */
355         if (!exec_wait) {
356                 return 0;
357         }
358
359         /*
360          *      Close the FD to which the child writes it's data.
361          *
362          *      If we can't close it, then we close pd[0], and return an
363          *      error.
364          */
365         if (close(pd[1]) != 0) {
366                 radlog(L_ERR|L_CONS, "Can't close pipe: %s", strerror(errno));
367                 close(pd[0]);
368                 return -1;
369         }
370
371         /*
372          *      Read from the pipe until we doesn't get any more or
373          *      until the message is full.
374          */
375         done = 0;
376         left = sizeof(answer) - 1;
377         while (1) {
378                 status = read(pd[0], answer + done, left);
379                 /*
380                  *      Nothing more to read: stop.
381                  */
382                 if (status == 0) {
383                         break;
384                 }
385
386                 /*
387                  *      Error: See if we have to continue.
388                  */
389                 if (status < 0) {
390                         /*
391                          *      We were interrupted: continue reading.
392                          */
393                         if (errno == EINTR) {
394                                 continue;
395                         }
396
397                         /*
398                          *      There was another error.  Most likely
399                          *      The child process has finished, and
400                          *      exited.
401                          */
402                         break;
403                 }
404
405                 done += status;
406                 left -= status;
407                 if (left <= 0) break;
408         }
409         answer[done] = 0;
410
411         /*
412          *      Make sure that the writer can't block while writing to
413          *      a pipe that no one is reading from anymore.
414          */
415         close(pd[0]);
416
417         DEBUG2("Exec-Program output: %s", answer);
418
419         /*
420          *      Parse the output, if any.
421          */
422         if (done) {
423                 n = T_OP_INVALID;
424                 if (output_pairs) {
425                         /*
426                          *      For backwards compatibility, first check
427                          *      for plain text (user_msg).
428                          */
429                         vp = NULL;
430                         n = userparse(answer, &vp);
431                         if (vp) {
432                                 pairfree(&vp);
433                         }
434                 }
435
436                 if (n == T_OP_INVALID) {
437                         DEBUG("Exec-Program-Wait: plaintext: %s", answer);
438                         if (user_msg) {
439                                 strNcpy(user_msg, answer, msg_len);
440                         }
441                 } else {
442                         /*
443                          *      HACK: Replace '\n' with ',' so that
444                          *      userparse() can parse the buffer in
445                          *      one go (the proper way would be to
446                          *      fix userparse(), but oh well).
447                          */
448                         for (p = answer; *p; p++) {
449                                 if (*p == '\n') {
450                                         *p = comma ? ' ' : ',';
451                                         p++;
452                                         comma = 0;
453                                 }
454                                 if (*p == ',') comma++;
455                         }
456
457                         /*
458                          *      Replace any trailing comma by a NUL.
459                          */
460                         if (answer[strlen(answer) - 1] == ',') {
461                                 answer[strlen(answer) - 1] = '\0';
462                         }
463
464                         radlog(L_DBG,"Exec-Program-Wait: value-pairs: %s", answer);
465                         if (userparse(answer, &vp) == T_OP_INVALID) {
466                                 radlog(L_ERR, "Exec-Program-Wait: %s: unparsable reply", cmd);
467
468                         } else {
469                                 /*
470                                  *      Tell the caller about the value
471                                  *      pairs.
472                                  */
473                                 *output_pairs = vp;
474                         }
475                 } /* else the answer was a set of VP's, not a text message */
476         } /* else we didn't read anything from the child */
477
478         /*
479          *      Call rad_waitpid (should map to waitpid on non-threaded
480          *      or single-server systems).
481          */
482         child_pid = rad_waitpid(pid, &status, 0);
483         if (child_pid == pid) {
484                 if (WIFEXITED(status)) {
485                         status = WEXITSTATUS(status);
486                         radlog(L_DBG, "Exec-Program: returned: %d", status);
487                         return status;
488                 }
489         }
490
491         radlog(L_ERR|L_CONS, "Exec-Program: Abnormal child exit: %s",
492                strerror(errno));
493         return 2;
494 }