Release bumped to "gts4".
[tac_plus.git] / programs.c
1 /*
2    Copyright (c) 1995-1998 by Cisco systems, Inc.
3
4    Permission to use, copy, modify, and distribute this software for
5    any purpose and without fee is hereby granted, provided that this
6    copyright and permission notice appear on all copies of the
7    software and supporting documentation, the name of Cisco Systems,
8    Inc. not be used in advertising or publicity pertaining to
9    distribution of the program without specific prior permission, and
10    notice be given in supporting documentation that modification,
11    copying and distribution is by permission of Cisco Systems, Inc.
12
13    Cisco Systems, Inc. makes no representations about the suitability
14    of this software for any purpose.  THIS SOFTWARE IS PROVIDED ``AS
15    IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
16    WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17    FITNESS FOR A PARTICULAR PURPOSE.
18 */
19
20 /* Routines to fork children and communicate with them via pipes */
21
22
23 #include "tac_plus.h"
24
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <sys/wait.h>
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #include <signal.h>
32 #ifdef HAVE_SYSLOG_H
33 #include <syslog.h>
34 #endif
35 #ifdef HAVE_SYS_SYSLOG_H
36 #include <sys/syslog.h>
37 #endif
38
39 #include "programs.h"
40 #include "utils.h"
41 #include "report.h"
42 #include "do_author.h"                  /* for "struct author_data" */
43 #include "main.h"
44
45
46 /* Support for dollar variables.  Look in the authorization data and
47 return strings representing values found there.  If not found, return
48 "unknown". Recognized strings and their interpolated value types are:
49
50 user    -- user name
51 name    -- NAS name
52 port    -- NAS port
53 address -- NAC address (remote user location)
54 priv    -- privilege level (0 to 15)
55 method  -- (1 to 4)
56 type    -- (1 to 4)
57 service -- (1 to 7)
58 status  -- (pass, fail, error, unknown) */
59
60 static char *lookup TAC_ARGS((char *sym, struct author_data *data));
61
62 static char *
63 lookup(sym, data)
64 char *sym;
65 struct author_data *data;
66 {
67     static char buf[5];
68
69     if (STREQ(sym, "user")) {
70         return (tac_strdup(data->id->username));
71     }
72     if (STREQ(sym, "name")) {
73         return (tac_strdup(data->id->NAS_name));
74     }
75     if (STREQ(sym, "port")) {
76         return (tac_strdup(data->id->NAS_port));
77     }
78     if (STREQ(sym, "port")) {
79         return (tac_strdup(data->id->NAS_port));
80     }
81     if (STREQ(sym, "address")) {
82         return (tac_strdup(data->id->NAC_address));
83     }
84     if (STREQ(sym, "priv")) {
85         sprintf(buf, "%d", data->id->priv_lvl);
86         return (tac_strdup(buf));
87     }
88     if (STREQ(sym, "method")) {
89         sprintf(buf, "%d", data->authen_method);
90         return (tac_strdup(buf));
91     }
92     if (STREQ(sym, "type")) {
93         sprintf(buf, "%d", data->authen_type);
94         return (tac_strdup(buf));
95     }
96     if (STREQ(sym, "service")) {
97         sprintf(buf, "%d", data->service);
98         return (tac_strdup(buf));
99     }
100     if (STREQ(sym, "status")) {
101         switch (data->status) {
102         default:
103             return (tac_strdup("unknown"));
104         case AUTHOR_STATUS_PASS_ADD:
105         case AUTHOR_STATUS_PASS_REPL:
106             return (tac_strdup("pass"));
107         case AUTHOR_STATUS_FAIL:
108             return (tac_strdup("fail"));
109         case AUTHOR_STATUS_ERROR:
110             return (tac_strdup("error"));
111         }
112     }
113     return (tac_strdup("unknown"));
114 }
115
116 /* Interpolate values of dollar variables into a string.  Determine
117    values for the various $ variables by looking in the authorization
118    data */
119
120 static char *substitute TAC_ARGS((const char *string, struct author_data *data));
121
122 static char *
123 substitute(string, data)
124 const char *string;
125 struct author_data *data;
126 {
127     const char *cp;
128     char out[MAX_INPUT_LINE_LEN], *outp;
129     char sym[MAX_INPUT_LINE_LEN], *symp;
130     char *value, *valuep;
131
132     if (debug & DEBUG_AUTHOR_FLAG)
133         report(LOG_DEBUG, "substitute: %s", string);
134
135     cp = string;
136     outp = out;
137
138     while (*cp) {
139         if (*cp != DOLLARSIGN) {
140             *outp++ = *cp++;
141             continue;
142         }
143         cp++;                   /* skip dollar sign */
144         symp = sym;
145
146         /* does it have curly braces e.g. ${foo} ? */
147         if (*cp == '{') {
148             cp++;               /* skip { */
149             while (*cp && *cp != '}')
150                 *symp++ = *cp++;
151             cp++;               /* skip } */
152
153         } else {
154             /* copy symbol into sym */
155             while (*cp && isalpha((int) *cp))
156                 *symp++ = *cp++;
157         }
158
159         *symp = '\0';
160         /* lookup value */
161
162         if (debug & DEBUG_SUBST_FLAG)
163             report(LOG_DEBUG, "Lookup %s", sym);
164
165         valuep = value = lookup(sym, data);
166
167         if (debug & DEBUG_SUBST_FLAG)
168             report(LOG_DEBUG, "Expands to: %s", value);
169
170         /* copy value into output */
171         while (valuep && *valuep)
172             *outp++ = *valuep++;
173         free(value);
174     }
175     *outp++ = '\0';
176
177     if (debug & DEBUG_AUTHOR_FLAG)
178         report(LOG_DEBUG, "Dollar substitution: %s", out);
179
180     return (tac_strdup(out));
181 }
182
183 /* Wait for a (child) pid to terminate. Return its status. Probably
184    horribly implementation dependent. */
185
186 static int waitfor TAC_ARGS((int pid));
187
188 static int
189 waitfor(pid)
190 int pid;
191 {
192     int ret;
193
194 #ifdef UNIONWAIT
195     union wait status;
196 #else
197     int status;
198 #endif /* UNIONWAIT */
199
200     ret = waitpid(pid, &status, 0);
201
202     if (ret < 0) {
203         report(LOG_ERR, "%s: pid %d no child exists", session.peer, pid);
204         return (-1);
205     }
206     if (!WIFEXITED(status)) {
207         report(LOG_ERR, "%s: pid %d child in illegal state", session.peer, pid);
208         return (-1);
209     }
210     if (debug & DEBUG_AUTHOR_FLAG)
211         report(LOG_DEBUG, "pid %d child exited status %d",
212                pid, WEXITSTATUS(status));
213
214     return (WEXITSTATUS(status));
215 }
216
217 static int write_args TAC_ARGS((int fd, char **args, int arg_cnt));
218
219 /* Write an argv array of strings to fd, adding a newline to each one */
220 static int
221 write_args(fd, args, arg_cnt)
222 int fd, arg_cnt;
223 char **args;
224 {
225     int i, m;
226
227     for (i = 0; i < arg_cnt; i++) {
228         int n = strlen(args[i]);
229
230         m = write(fd, args[i], n);
231         m += write(fd, "\n", 1);
232
233         if (m != (n + 1)) {
234             report(LOG_ERR, "%s: Process write failure", session.peer);
235             return (-1);
236         }
237     }
238     return (0);
239 }
240
241 static void close_fds TAC_ARGS((int fd1, int fd2, int fd3));
242
243 /* Close the three given file-descruptors */
244 static void
245 close_fds(fd1, fd2, fd3)
246  int fd1, fd2, fd3;
247 {
248     if (fd1 >= 0) {
249         close(fd1);
250     }
251     if (fd2 >= 0) {
252         close(fd2);
253     }
254     if (fd3 >= 0) {
255         close(fd3);
256     }
257 }
258
259 /* Fork a command. Return read and write file descriptors in readfdp
260    and writefdp. Return the pid or -1 if unsuccessful */
261
262 static int my_popen TAC_ARGS((char *cmd, int *readfdp, int *writefdp, int *errorfdp));
263
264 static int
265 my_popen(cmd, readfdp, writefdp, errorfdp)
266 char *cmd;
267 int *readfdp, *writefdp, *errorfdp;
268 {
269     int fd1[2], fd2[2], fd3[2];
270     int pid;
271
272     fd1[0] = fd1[1] = fd2[0] = fd2[1] = fd3[0] = fd3[1] = -1;
273     *readfdp = *writefdp = *errorfdp = -1;
274
275     if (pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(fd3) < 0) {
276         report(LOG_ERR, "%s: Cannot create pipes", session.peer);
277         close_fds(fd1[0], fd2[0], fd3[0]);
278         close_fds(fd1[1], fd2[1], fd3[1]);
279         return (-1);
280     }
281
282     /* The parent who forked us is set to reap all children
283        automatically. We disable this so we can explicitly reap our
284        children to read their status */
285
286     signal(SIGCHLD, SIG_DFL);
287
288     pid = fork();
289
290     if (pid < 0) {
291         report(LOG_ERR, "%s: fork failure", session.peer);
292         close_fds(fd1[0], fd2[0], fd3[0]);
293         close_fds(fd1[1], fd2[1], fd3[1]);
294         return (-1);
295     }
296     if (pid > 0) {
297         /* parent */
298         close_fds(fd1[0], fd2[1], fd3[1]);
299
300         *writefdp = fd1[1];
301         *readfdp = fd2[0];
302         *errorfdp = fd3[0];
303
304         return (pid);
305     }
306     /* child */
307     closelog();
308     close(session.sock);
309     close_fds(fd1[1], fd2[0], fd3[0]);
310
311     if (fd1[0] != STDIN_FILENO) {
312         if (dup2(fd1[0], STDIN_FILENO) < 0)
313             exit(-1);
314         close(fd1[0]);
315     }
316     if (fd2[1] != STDOUT_FILENO) {
317         if (dup2(fd2[1], STDOUT_FILENO) < 0)
318             exit(-1);
319         close(fd2[1]);
320     }
321     if (fd3[1] != STDERR_FILENO) {
322         if (dup2(fd3[1], STDERR_FILENO) < 0)
323             exit(-1);
324         close(fd3[1]);
325     }
326     (void) execl("/bin/sh", "sh", "-c", cmd, (char *) NULL);
327     _exit(-1);
328     return(0); /* keep Codecenter quiet */
329 }
330
331 static int read_string TAC_ARGS((int fd, char *string, int len));
332
333 /* read the file descriptor and stuff the data into the given array for
334  * the number of bytes given. Throw the rest away.
335  */
336 static int
337 read_string (fd, string, len)
338 int fd, len;
339 char *string;
340 {
341     int i, ret;
342     char c;
343
344     i=0;
345     do {
346         ret = read(fd, &c, 1);
347         if ( (ret > 0) && ((i+1)<len) ) {
348             string[i++] = c;
349             string[i] = '\0';
350         }
351     } while ((i<len) && (ret>0));
352     return(ret);
353 }
354
355 /* Read lines from fd and place them into an argv style array. Highly
356    recursive so we don't have to count lines in advance. Uses "n" as
357    the count of lines seen so far. When eof is read, the array is
358    allocated, and the recursion unravels */
359
360 static char **read_args TAC_ARGS((int n, int fd));
361
362 static char **
363 read_args(n, fd)
364 int n, fd;
365 {
366     char buf[255], *bufp, c, **out;
367
368     bufp = buf;
369
370     while (read(fd, &c, 1) > 0) {
371         if (c != '\n') {
372             *bufp++ = c;
373             continue;
374         }
375         *bufp = '\0';
376         out = read_args(n + 1, fd);
377         out[n] = (char *) tac_malloc(strlen(buf) + 1);
378         strcpy(out[n], buf);
379         return (out);
380     }
381     /* eof */
382     out = (char **) tac_malloc(sizeof(char *) * (n + 1));
383     out[n] = NULL;
384
385     return (out);
386 }
387
388
389 /* Do variable interpolation on a string, then invoke it as a shell
390    command. Write an appropriate set of AV pairs to the command's
391    standard input and read its standard output into outarray. Return
392    the commands final status when it terminates */
393
394 int call_pre_process TAC_ARGS((const char *string, struct author_data *data, char ***outargsp, int *outargs_cntp, char *error, int err_len));
395
396 int
397 call_pre_process(string, data, outargsp, outargs_cntp, error, err_len)
398 const char *string;
399 struct author_data *data;
400 char ***outargsp;
401 int *outargs_cntp;
402 char *error;
403 int err_len;
404 {
405     char **new_args;
406     int readfd, writefd, errorfd;
407     int status, i;
408     char *cmd = substitute(string, data);
409     int pid = my_popen(cmd, &readfd, &writefd, &errorfd);
410
411     memset(error, '\0', err_len);
412
413     free(cmd);
414
415     if (pid < 0) {
416         close_fds(readfd, writefd, errorfd);
417         return (1);             /* deny */
418     }
419
420     for (i = 0; i < data->num_in_args; i++) {
421         if (debug & DEBUG_AUTHOR_FLAG)
422             report(LOG_DEBUG, "input %s", data->input_args[i]);
423     }
424
425     if (write_args(writefd, data->input_args, data->num_in_args)) {
426         close_fds(readfd, writefd, errorfd);
427         return (1);             /* deny */
428     }
429
430     close(writefd);
431     writefd = -1;
432
433     new_args = read_args(0, readfd);
434     *outargsp = new_args;
435
436     if (debug & DEBUG_AUTHOR_FLAG) {
437         for (i = 0; new_args[i]; i++) {
438             report(LOG_DEBUG, "output %s", new_args[i]);
439         }
440     }
441
442     read_string(errorfd, error, err_len);
443     if (error[0] != '\0') {
444         report(LOG_ERR, "Error from program (%u): \"%s\" ",
445                (unsigned) strlen(error), error);
446     }
447
448     /* count the args */
449     for (i = 0; new_args[i]; i++)
450          /* NULL stmt */ ;
451
452     *outargs_cntp = i;
453
454     status = waitfor(pid);
455     close_fds(readfd, writefd, errorfd);
456     return (status);
457 }
458
459 /* Do variable interpolation on a string, then invoke it as a shell
460    command. Write an appropriate set of AV pairs to the command's
461    standard input and read its standard output into outarray. Return
462    the commands final status when it terminates */
463
464 int call_post_process TAC_ARGS((const char *string, struct author_data *data, char ***outargsp, int *outargs_cntp));
465
466 int
467 call_post_process(string, data, outargsp, outargs_cntp)
468 const char *string;
469 struct author_data *data;
470 char ***outargsp;
471 int *outargs_cntp;
472 {
473     char **new_args;
474     int status;
475     int readfd, writefd, errorfd;
476     int i;
477     char *cmd = substitute(string, data);
478     int pid = my_popen(cmd, &readfd, &writefd, &errorfd);
479
480     free(cmd);
481
482     if (pid < 0) {
483         close_fds(readfd, writefd, errorfd);
484         return (1);             /* deny */
485     }
486
487     /* If the status is AUTHOR_STATUS_PASS_ADD then the current output args
488      * represent *additions* to the input args, not the full set */
489
490     if (data->status == AUTHOR_STATUS_PASS_ADD) {
491
492         for (i = 0; i < data->num_in_args; i++) {
493             if (debug & DEBUG_AUTHOR_FLAG)
494                 report(LOG_DEBUG, "input %s", data->input_args[i]);
495         }
496
497         if (write_args(writefd, data->input_args, data->num_in_args)) {
498             close_fds(readfd, writefd, errorfd);
499             return (1);         /* deny */
500         }
501     }
502     for (i = 0; i < data->num_out_args; i++) {
503         if (debug & DEBUG_AUTHOR_FLAG)
504             report(LOG_DEBUG, "input %s", data->output_args[i]);
505     }
506
507     if (write_args(writefd, data->output_args, data->num_out_args)) {
508         close_fds(readfd, writefd, errorfd);
509         return (1);             /* deny */
510     }
511
512     close(writefd);
513     writefd = -1;
514
515     new_args = read_args(0, readfd);
516     *outargsp = new_args;
517
518     if (debug & DEBUG_AUTHOR_FLAG) {
519         for (i = 0; new_args[i]; i++) {
520             report(LOG_DEBUG, "output %s", new_args[i]);
521         }
522     }
523     /* count the output args */
524     for (i = 0; new_args[i]; i++)
525          /* NULL stmt */ ;
526
527     *outargs_cntp = i;
528
529     status = waitfor(pid);
530     close_fds(readfd, writefd, errorfd);
531     return (status);
532 }