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