2 Copyright (c) 1995-1998 by Cisco systems, Inc.
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.
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.
20 /* Routines to fork children and communicate with them via pipes */
35 #ifdef HAVE_SYS_SYSLOG_H
36 #include <sys/syslog.h>
42 #include "do_author.h" /* for "struct author_data" */
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:
53 address -- NAC address (remote user location)
54 priv -- privilege level (0 to 15)
58 status -- (pass, fail, error, unknown) */
60 static char *lookup TAC_ARGS((char *sym, struct author_data *data));
65 struct author_data *data;
69 if (STREQ(sym, "user")) {
70 return (tac_strdup(data->id->username));
72 if (STREQ(sym, "name")) {
73 return (tac_strdup(data->id->NAS_name));
75 if (STREQ(sym, "port")) {
76 return (tac_strdup(data->id->NAS_port));
78 if (STREQ(sym, "port")) {
79 return (tac_strdup(data->id->NAS_port));
81 if (STREQ(sym, "address")) {
82 return (tac_strdup(data->id->NAC_address));
84 if (STREQ(sym, "priv")) {
85 sprintf(buf, "%d", data->id->priv_lvl);
86 return (tac_strdup(buf));
88 if (STREQ(sym, "method")) {
89 sprintf(buf, "%d", data->authen_method);
90 return (tac_strdup(buf));
92 if (STREQ(sym, "type")) {
93 sprintf(buf, "%d", data->authen_type);
94 return (tac_strdup(buf));
96 if (STREQ(sym, "service")) {
97 sprintf(buf, "%d", data->service);
98 return (tac_strdup(buf));
100 if (STREQ(sym, "status")) {
101 switch (data->status) {
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"));
113 return (tac_strdup("unknown"));
116 /* Interpolate values of dollar variables into a string. Determine
117 values for the various $ variables by looking in the authorization
120 static char *substitute TAC_ARGS((const char *string, struct author_data *data));
123 substitute(string, data)
125 struct author_data *data;
128 char out[MAX_INPUT_LINE_LEN], *outp;
129 char sym[MAX_INPUT_LINE_LEN], *symp;
130 char *value, *valuep;
132 if (debug & DEBUG_AUTHOR_FLAG)
133 report(LOG_DEBUG, "substitute: %s", string);
139 if (*cp != DOLLARSIGN) {
143 cp++; /* skip dollar sign */
146 /* does it have curly braces e.g. ${foo} ? */
149 while (*cp && *cp != '}')
154 /* copy symbol into sym */
155 while (*cp && isalpha((int) *cp))
162 if (debug & DEBUG_SUBST_FLAG)
163 report(LOG_DEBUG, "Lookup %s", sym);
165 valuep = value = lookup(sym, data);
167 if (debug & DEBUG_SUBST_FLAG)
168 report(LOG_DEBUG, "Expands to: %s", value);
170 /* copy value into output */
171 while (valuep && *valuep)
177 if (debug & DEBUG_AUTHOR_FLAG)
178 report(LOG_DEBUG, "Dollar substitution: %s", out);
180 return (tac_strdup(out));
183 /* Wait for a (child) pid to terminate. Return its status. Probably
184 horribly implementation dependent. */
186 static int waitfor TAC_ARGS((int pid));
198 #endif /* UNIONWAIT */
200 ret = waitpid(pid, &status, 0);
203 report(LOG_ERR, "%s: pid %d no child exists", session.peer, pid);
206 if (!WIFEXITED(status)) {
207 report(LOG_ERR, "%s: pid %d child in illegal state", session.peer, pid);
210 if (debug & DEBUG_AUTHOR_FLAG)
211 report(LOG_DEBUG, "pid %d child exited status %d",
212 pid, WEXITSTATUS(status));
214 return (WEXITSTATUS(status));
217 static int write_args TAC_ARGS((int fd, char **args, int arg_cnt));
219 /* Write an argv array of strings to fd, adding a newline to each one */
221 write_args(fd, args, arg_cnt)
227 for (i = 0; i < arg_cnt; i++) {
228 int n = strlen(args[i]);
230 m = write(fd, args[i], n);
231 m += write(fd, "\n", 1);
234 report(LOG_ERR, "%s: Process write failure", session.peer);
241 static void close_fds TAC_ARGS((int fd1, int fd2, int fd3));
243 /* Close the three given file-descruptors */
245 close_fds(fd1, fd2, fd3)
259 /* Fork a command. Return read and write file descriptors in readfdp
260 and writefdp. Return the pid or -1 if unsuccessful */
262 static int my_popen TAC_ARGS((char *cmd, int *readfdp, int *writefdp, int *errorfdp));
265 my_popen(cmd, readfdp, writefdp, errorfdp)
267 int *readfdp, *writefdp, *errorfdp;
269 int fd1[2], fd2[2], fd3[2];
272 fd1[0] = fd1[1] = fd2[0] = fd2[1] = fd3[0] = fd3[1] = -1;
273 *readfdp = *writefdp = *errorfdp = -1;
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]);
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 */
286 signal(SIGCHLD, SIG_DFL);
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]);
298 close_fds(fd1[0], fd2[1], fd3[1]);
309 close_fds(fd1[1], fd2[0], fd3[0]);
311 if (fd1[0] != STDIN_FILENO) {
312 if (dup2(fd1[0], STDIN_FILENO) < 0)
316 if (fd2[1] != STDOUT_FILENO) {
317 if (dup2(fd2[1], STDOUT_FILENO) < 0)
321 if (fd3[1] != STDERR_FILENO) {
322 if (dup2(fd3[1], STDERR_FILENO) < 0)
326 (void) execl("/bin/sh", "sh", "-c", cmd, (char *) NULL);
328 return(0); /* keep Codecenter quiet */
331 static int read_string TAC_ARGS((int fd, char *string, int len));
333 /* read the file descriptor and stuff the data into the given array for
334 * the number of bytes given. Throw the rest away.
337 read_string (fd, string, len)
346 ret = read(fd, &c, 1);
347 if ( (ret > 0) && ((i+1)<len) ) {
351 } while ((i<len) && (ret>0));
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 */
360 static char **read_args TAC_ARGS((int n, int fd));
366 char buf[255], *bufp, c, **out;
370 while (read(fd, &c, 1) > 0) {
376 out = read_args(n + 1, fd);
377 out[n] = (char *) tac_malloc(strlen(buf) + 1);
382 out = (char **) tac_malloc(sizeof(char *) * (n + 1));
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 */
394 int call_pre_process TAC_ARGS((const char *string, struct author_data *data, char ***outargsp, int *outargs_cntp, char *error, int err_len));
397 call_pre_process(string, data, outargsp, outargs_cntp, error, err_len)
399 struct author_data *data;
406 int readfd, writefd, errorfd;
408 char *cmd = substitute(string, data);
409 int pid = my_popen(cmd, &readfd, &writefd, &errorfd);
411 memset(error, '\0', err_len);
416 close_fds(readfd, writefd, errorfd);
417 return (1); /* deny */
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]);
425 if (write_args(writefd, data->input_args, data->num_in_args)) {
426 close_fds(readfd, writefd, errorfd);
427 return (1); /* deny */
433 new_args = read_args(0, readfd);
434 *outargsp = new_args;
436 if (debug & DEBUG_AUTHOR_FLAG) {
437 for (i = 0; new_args[i]; i++) {
438 report(LOG_DEBUG, "output %s", new_args[i]);
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);
449 for (i = 0; new_args[i]; i++)
454 status = waitfor(pid);
455 close_fds(readfd, writefd, errorfd);
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 */
464 int call_post_process TAC_ARGS((const char *string, struct author_data *data, char ***outargsp, int *outargs_cntp));
467 call_post_process(string, data, outargsp, outargs_cntp)
469 struct author_data *data;
475 int readfd, writefd, errorfd;
477 char *cmd = substitute(string, data);
478 int pid = my_popen(cmd, &readfd, &writefd, &errorfd);
483 close_fds(readfd, writefd, errorfd);
484 return (1); /* deny */
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 */
490 if (data->status == AUTHOR_STATUS_PASS_ADD) {
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]);
497 if (write_args(writefd, data->input_args, data->num_in_args)) {
498 close_fds(readfd, writefd, errorfd);
499 return (1); /* deny */
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]);
507 if (write_args(writefd, data->output_args, data->num_out_args)) {
508 close_fds(readfd, writefd, errorfd);
509 return (1); /* deny */
515 new_args = read_args(0, readfd);
516 *outargsp = new_args;
518 if (debug & DEBUG_AUTHOR_FLAG) {
519 for (i = 0; new_args[i]; i++) {
520 report(LOG_DEBUG, "output %s", new_args[i]);
523 /* count the output args */
524 for (i = 0; new_args[i]; i++)
529 status = waitfor(pid);
530 close_fds(readfd, writefd, errorfd);