1 |
/* This file is part of GNU RADIUS. |
/* This file is part of GNU Radius. |
2 |
Copyright (C) 2000,2001, Sergey Poznyakoff |
Copyright (C) 2002,2003 Sergey Poznyakoff |
3 |
|
|
4 |
This program is free software; you can redistribute it and/or modify |
GNU Radius is free software; you can redistribute it and/or modify |
5 |
it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
6 |
the Free Software Foundation; either version 2 of the License, or |
the Free Software Foundation; either version 2 of the License, or |
7 |
(at your option) any later version. |
(at your option) any later version. |
8 |
|
|
9 |
This program is distributed in the hope that it will be useful, |
GNU Radius is distributed in the hope that it will be useful, |
10 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
GNU General Public License for more details. |
GNU General Public License for more details. |
13 |
|
|
14 |
You should have received a copy of the GNU General Public License |
You should have received a copy of the GNU General Public License |
15 |
along with this program; if not, write to the Free Software Foundation, |
along with GNU Radius; if not, write to the Free Software Foundation, |
16 |
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
17 |
|
|
18 |
#define RADIUS_MODULE_EXEC_C |
#define RADIUS_MODULE_EXEC_C |
|
#ifndef lint |
|
|
static char rcsid[] = |
|
|
"@(#) $Id$"; |
|
|
#endif |
|
19 |
|
|
20 |
#ifdef HAVE_CONFIG_H |
#ifdef HAVE_CONFIG_H |
21 |
# include <config.h> |
# include <config.h> |
44 |
#include <symtab.h> |
#include <symtab.h> |
45 |
#include <rewrite.h> |
#include <rewrite.h> |
46 |
|
|
|
struct cleanup_data { |
|
|
VALUE_PAIR **vp; |
|
|
FILE *fp; |
|
|
pid_t pid; |
|
|
}; |
|
|
|
|
|
void |
|
|
rad_exec_cleanup(arg) |
|
|
void *arg; |
|
|
{ |
|
|
struct cleanup_data *p = arg; |
|
|
|
|
|
fclose(p->fp); |
|
|
avl_free(*p->vp); |
|
|
if (p->pid > 0) |
|
|
kill(p->pid, SIGKILL); |
|
|
} |
|
47 |
|
|
48 |
int |
int |
49 |
radius_change_uid(pwd) |
radius_change_uid(struct passwd *pwd) |
|
struct passwd *pwd; |
|
50 |
{ |
{ |
51 |
if (pwd->pw_gid != 0 && setgid(pwd->pw_gid)) { |
if (pwd->pw_gid != 0 && setgid(pwd->pw_gid)) { |
52 |
radlog(L_ERR|L_PERROR, |
radlog(L_ERR|L_PERROR, |
69 |
} |
} |
70 |
} |
} |
71 |
|
|
|
static int |
|
|
exec_sigchld(sig, data, id, owner) |
|
|
int sig; |
|
|
void *data; |
|
|
rad_sigid_t id; |
|
|
const void *owner; |
|
|
{ |
|
|
int status; |
|
|
pid_t pid = *(pid_t*)data; |
|
|
if (waitpid(pid, &status, 0) == pid) { |
|
|
rad_signal_remove(sig, id, owner); |
|
|
return 0; |
|
|
} |
|
|
return 1; |
|
|
} |
|
|
|
|
72 |
/* Execute a program on successful authentication. |
/* Execute a program on successful authentication. |
73 |
Return 0 if exec_wait == 0. |
Return 0 if exec_wait == 0. |
74 |
Return the exit code of the called program if exec_wait != 0. |
Return the exit code of the called program if exec_wait != 0. */ |
|
NOTE: The routine relies upon SIGCHLD being set ti SIG_IGN and |
|
|
SIGPIPE being set to SIG_DFL. */ |
|
75 |
int |
int |
76 |
radius_exec_program(cmd, req, reply, exec_wait, user_msg) |
radius_exec_program(char *cmd, RADIUS_REQ *req, VALUE_PAIR **reply, |
77 |
char *cmd; |
int exec_wait, char **user_msg) |
|
RADIUS_REQ *req; |
|
|
VALUE_PAIR **reply; |
|
|
int exec_wait; |
|
|
char **user_msg; |
|
78 |
{ |
{ |
79 |
int p[2]; |
int p[2]; |
80 |
int n; |
int n; |
83 |
FILE *fp; |
FILE *fp; |
84 |
int line_num; |
int line_num; |
85 |
char buffer[RAD_BUFFER_SIZE]; |
char buffer[RAD_BUFFER_SIZE]; |
86 |
struct passwd pw, *pwd; |
struct passwd *pwd; |
|
struct cleanup_data cleanup_data; |
|
87 |
pid_t pid; |
pid_t pid; |
88 |
int status; |
int status; |
89 |
rad_sigid_t id = NULL; |
RETSIGTYPE (*oldsig)(); |
90 |
|
|
91 |
if (cmd[0] != '/') { |
if (cmd[0] != '/') { |
92 |
radlog(L_ERR, |
radlog(L_ERR, |
98 |
/* Check user/group |
/* Check user/group |
99 |
FIXME: This should be checked *once* after re-reading the |
FIXME: This should be checked *once* after re-reading the |
100 |
configuration */ |
configuration */ |
101 |
pwd = rad_getpwnam_r(exec_user, &pw, buffer, sizeof buffer); |
pwd = getpwnam(exec_user); |
102 |
if (!pwd) { |
if (!pwd) { |
103 |
radlog(L_ERR, |
radlog(L_ERR, |
104 |
_("radius_exec_program(): won't execute, no such user: %s"), |
_("radius_exec_program(): won't execute, no such user: %s"), |
111 |
radlog(L_ERR|L_PERROR, _("couldn't open pipe")); |
radlog(L_ERR|L_PERROR, _("couldn't open pipe")); |
112 |
return -1; |
return -1; |
113 |
} |
} |
114 |
} else |
if ((oldsig = signal(SIGCHLD, SIG_DFL)) == SIG_ERR) { |
115 |
id = rad_signal_install(SIGCHLD, SH_SYNC, exec_sigchld, &pid); |
radlog(L_ERR|L_PERROR, _("can't reset SIGCHLD")); |
116 |
|
return -1; |
117 |
|
} |
118 |
|
} |
119 |
|
|
120 |
if ((pid = fork()) == 0) { |
if ((pid = fork()) == 0) { |
121 |
int argc; |
int argc; |
159 |
/* Parent branch */ |
/* Parent branch */ |
160 |
if (pid < 0) { |
if (pid < 0) { |
161 |
radlog(L_ERR|L_PERROR, "fork"); |
radlog(L_ERR|L_PERROR, "fork"); |
|
if (id) |
|
|
rad_signal_remove (SIGCHLD, id, NULL); |
|
162 |
return -1; |
return -1; |
163 |
} |
} |
164 |
if (!exec_wait) |
if (!exec_wait) |
172 |
vp = NULL; |
vp = NULL; |
173 |
line_num = 0; |
line_num = 0; |
174 |
|
|
|
cleanup_data.vp = &vp; |
|
|
cleanup_data.fp = fp; |
|
|
cleanup_data.pid = pid; |
|
|
pthread_cleanup_push((void (*)(void*))rad_exec_cleanup, &cleanup_data); |
|
|
|
|
175 |
while (ptr = fgets(buffer, sizeof(buffer), fp)) { |
while (ptr = fgets(buffer, sizeof(buffer), fp)) { |
176 |
line_num++; |
line_num++; |
177 |
debug(1, ("got `%s'", buffer)); |
debug(1, ("got `%s'", buffer)); |
184 |
} |
} |
185 |
} |
} |
186 |
|
|
|
waitpid(pid, &status, 0); |
|
|
|
|
|
pthread_cleanup_pop(0); |
|
|
|
|
187 |
fclose(fp); |
fclose(fp); |
188 |
|
|
189 |
if (vp) { |
if (vp) { |
191 |
avl_free(vp); |
avl_free(vp); |
192 |
} |
} |
193 |
|
|
194 |
|
waitpid(pid, &status, 0); |
195 |
|
if (signal(SIGCHLD, oldsig) == SIG_ERR) |
196 |
|
radlog(L_CRIT|L_PERROR, |
197 |
|
_("can't restore SIGCHLD")); |
198 |
|
|
199 |
if (WIFEXITED(status)) { |
if (WIFEXITED(status)) { |
200 |
status = WEXITSTATUS(status); |
status = WEXITSTATUS(status); |
201 |
debug(1, ("returned: %d", status)); |
debug(1, ("returned: %d", status)); |
215 |
} |
} |
216 |
|
|
217 |
pid_t |
pid_t |
218 |
radius_run_filter(argc, argv, errfile, p) |
radius_run_filter(int argc, char **argv, char *errfile, int *p) |
|
int argc; |
|
|
char **argv; |
|
|
char *errfile; |
|
|
int *p; |
|
219 |
{ |
{ |
220 |
pid_t pid; |
pid_t pid; |
221 |
int rightp[2], leftp[2]; |
int rightp[2], leftp[2]; |
222 |
struct passwd pw, *pwd; |
struct passwd *pwd; |
|
char buffer[512]; |
|
223 |
int i; |
int i; |
224 |
|
|
225 |
pwd = rad_getpwnam_r(exec_user, &pw, buffer, sizeof buffer); |
pwd = getpwnam(exec_user); |
226 |
if (!pwd) { |
if (!pwd) { |
227 |
radlog(L_ERR, |
radlog(L_ERR, |
228 |
_("won't execute, no such user: %s"), |
_("won't execute, no such user: %s"), |
292 |
return pid; |
return pid; |
293 |
} |
} |
294 |
|
|
295 |
int |
typedef struct filter_symbol Filter; |
|
timed_readline(fd, buffer, buflen, abstime) |
|
|
int fd; |
|
|
char *buffer; |
|
|
size_t buflen; |
|
|
struct timeval *abstime; |
|
|
{ |
|
|
struct timeval tv; |
|
|
fd_set rset; |
|
|
int rc; |
|
|
int rbytes = 0; |
|
|
|
|
|
while (1) { |
|
|
rc = -1; |
|
|
if (rbytes >= buflen-1) { |
|
|
errno = ENOMEM; |
|
|
break; |
|
|
} |
|
|
gettimeofday(&tv, NULL); |
|
|
if (timercmp(&tv, abstime, >)) { |
|
|
errno = ETIMEDOUT; |
|
|
break; |
|
|
} |
|
|
tv.tv_sec = abstime->tv_sec - tv.tv_sec; |
|
|
if (abstime->tv_usec > tv.tv_usec) |
|
|
tv.tv_usec = abstime->tv_usec - tv.tv_usec; |
|
|
else { |
|
|
tv.tv_usec = 1000000 + abstime->tv_usec - tv.tv_usec; |
|
|
tv.tv_sec++; |
|
|
} |
|
|
FD_ZERO(&rset); |
|
|
FD_SET(fd, &rset); |
|
|
rc = select(fd + 1, &rset, NULL, NULL, &tv); |
|
|
if (rc < 0) { |
|
|
if (errno == EINTR) |
|
|
continue; |
|
|
else |
|
|
break; |
|
|
} |
|
|
if (FD_ISSET(fd, &rset)) { |
|
|
if (read(fd, buffer + rbytes, 1) != 1) |
|
|
break; |
|
|
rbytes++; |
|
|
if (buffer[rbytes-1] == '\n') { |
|
|
rc = 0; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
buffer[rbytes] = 0; |
|
|
return rc == 0 ? rbytes : rc; |
|
|
} |
|
|
|
|
|
typedef struct filter_symbol Filter_symbol; |
|
|
typedef struct filter_runtime_data Filter; |
|
|
|
|
|
struct filter_runtime_data { |
|
|
Filter *next; |
|
|
Filter_symbol *sym; |
|
|
|
|
|
pthread_mutex_t mutex; /* Mutex for sigid */ |
|
|
rad_sigid_t id; /* Signal handler */ |
|
|
pid_t oldpid; /* Pid of the exited filter process */ |
|
|
int status; /* Exit status of it */ |
|
|
int stopped; /* SIGKILL has been sent to the filter */ |
|
|
|
|
|
pthread_t tid; /* Thread ID of the thread this filter |
|
|
belongs to */ |
|
|
pid_t pid; /* Pid of the filter process */ |
|
|
size_t lines_input; /* Number of lines read from the filter */ |
|
|
size_t lines_output; /* Number of lines written to the filter */ |
|
|
int input; /* Input file descriptor */ |
|
|
int output; /* Output file descriptor */ |
|
|
}; |
|
296 |
|
|
297 |
/* NOTE,NOTE,NOTE: The code below works on the assumption that |
/* NOTE,NOTE,NOTE: The code below works on the assumption that |
298 |
R_AUTH=0, R_ACCT=1 */ |
R_AUTH=0, R_ACCT=1 */ |
300 |
#define FILTER_MAX 2 |
#define FILTER_MAX 2 |
301 |
|
|
302 |
struct filter_symbol { |
struct filter_symbol { |
303 |
struct user_symbol *next; |
struct filter_symbol *next; |
304 |
char *name; /* Name of the filter */ |
char *name; /* Name of the filter */ |
305 |
/* Configuration data */ |
/* Configuration data */ |
306 |
int line_num; /* Number of line in raddb/config where |
int line_num; /* Number of line in raddb/config where |
308 |
int argc; /* Number of entries in the argv */ |
int argc; /* Number of entries in the argv */ |
309 |
char **argv; /* Invocation vector */ |
char **argv; /* Invocation vector */ |
310 |
char *errfile; /* Filename for error output (fd 2) */ |
char *errfile; /* Filename for error output (fd 2) */ |
|
int common; /* Is the filter common for all threads */ |
|
|
int max_percent; /* For common filters, maximum percentage |
|
|
of threads allowed to wait on the mutex. |
|
|
When this number of threads is reached, |
|
|
the filter is deemed to have hung */ |
|
311 |
struct { |
struct { |
312 |
char *input_fmt; |
char *input_fmt; |
313 |
int wait_reply; |
int wait_reply; |
314 |
int on_fail; |
int on_fail; |
315 |
} descr[FILTER_MAX]; |
} descr[FILTER_MAX]; |
316 |
|
|
317 |
Filter *filter; /* Runtime data */ |
/* Runtime data */ |
318 |
int counter; /* Number of threads currently waiting |
pid_t pid; /* Pid of the filter process */ |
319 |
on the mutex */ |
size_t lines_input; /* Number of lines read from the filter */ |
320 |
pthread_mutex_t mutex; /* Prevent concurrent access */ |
size_t lines_output; /* Number of lines written to the filter */ |
321 |
|
int input; /* Input file descriptor */ |
322 |
|
int output; /* Output file descriptor */ |
323 |
}; |
}; |
324 |
|
|
325 |
static Symtab *filter_tab; |
static Symtab *filter_tab; |
|
static Filter_symbol filter_symbol; |
|
326 |
|
|
327 |
static void filter_lock(Filter *filter); |
struct cleanup_info { |
328 |
static void filter_unlock(Filter *filter); |
pid_t pid; |
329 |
static void filter_close(Filter *filter); |
int status; |
330 |
static Filter *filter_open(char *name, RADIUS_REQ *req, int type, int *errp); |
}; |
|
static char *filter_xlate(struct obstack *sp, char *fmt, RADIUS_REQ *radreq); |
|
|
static int filter_write(Filter *filter, char *fmt, RADIUS_REQ *radreq); |
|
|
static int filter_read(Filter *filter, int type, char *buffer, size_t buflen); |
|
|
static int filter_stmt_handler(int argc, cfg_value_t *argv, void *block_data, |
|
|
void *handler_data); |
|
|
static int filter_stmt_end(void *block_data, void *handler_data); |
|
|
static int exec_path_handler(int argc, cfg_value_t *argv, void *block_data, |
|
|
void *handler_data); |
|
|
static int error_log_handler(int argc, cfg_value_t *argv, void *block_data, |
|
|
void *handler_data); |
|
|
static int _store_format_ptr(int argc, cfg_value_t *argv, void *block_data, |
|
|
void *handler_data); |
|
|
|
|
|
|
|
|
static int |
|
|
filter_cleanup_proc(unused, sym) |
|
|
void *unused; |
|
|
Filter_symbol *sym; |
|
|
{ |
|
|
Filter *filter; |
|
|
pthread_t tid = pthread_self(); |
|
331 |
|
|
|
for (filter = sym->filter; filter; filter = filter->next) { |
|
|
if (filter->tid == tid) |
|
|
filter_close(filter); |
|
|
} |
|
|
} |
|
|
|
|
|
/* Called from the first thread cleanup handler */ |
|
|
void |
|
|
filter_cleanup() |
|
|
{ |
|
|
symtab_iterate(filter_tab, filter_cleanup_proc, NULL); |
|
|
} |
|
|
|
|
332 |
static int |
static int |
333 |
filter_symbol_lock(sym, type) |
filter_cleanup_proc(void *ptr, Filter *filter) |
|
Filter_symbol *sym; |
|
|
int type; |
|
334 |
{ |
{ |
335 |
int rc; |
struct cleanup_info *info = ptr; |
|
int counter = sym->counter; |
|
|
|
|
|
if (sym->common |
|
|
&& sym->max_percent |
|
|
&& num_threads > max_threads * sym->max_percent / 100 |
|
|
&& counter > num_threads * sym->max_percent / 100) { |
|
|
radlog(L_NOTICE, |
|
|
_("Too many (%d) threads waiting for filter %s."), |
|
|
counter, |
|
|
sym->name); |
|
|
return 1; |
|
|
} |
|
336 |
|
|
337 |
sym->counter++; |
if (filter->pid == info->pid) { |
338 |
rc = radiusd_mutex_lock(&sym->mutex, type); |
static char buffer[512]; |
339 |
sym->counter--; |
|
340 |
return 0; |
format_exit_status(buffer, sizeof buffer, info->status); |
341 |
} |
radlog(L_ERR, |
342 |
|
_("filter %s (pid %d) %s (in: %u, out: %u)"), |
343 |
static void |
filter->name, filter->pid, |
344 |
filter_symbol_unlock(sym) |
buffer, |
345 |
Filter_symbol *sym; |
filter->lines_input, filter->lines_output); |
346 |
{ |
filter->pid = 0; |
347 |
radiusd_mutex_unlock(&sym->mutex); |
} |
|
} |
|
|
|
|
|
static void |
|
|
filter_lock(filter) |
|
|
Filter *filter; |
|
|
{ |
|
|
if (filter->sym->common) |
|
|
Pthread_mutex_lock(&filter->sym->mutex); |
|
348 |
} |
} |
349 |
|
|
350 |
static void |
void |
351 |
filter_unlock(filter) |
filter_cleanup(pid_t pid, int status) |
352 |
Filter *filter; |
{ |
353 |
{ |
struct cleanup_info info; |
354 |
if (filter->sym->common) |
info.pid = pid; |
355 |
Pthread_mutex_unlock(&filter->sym->mutex); |
info.status = status; |
356 |
|
symtab_iterate(filter_tab, filter_cleanup_proc, &info); |
357 |
} |
} |
358 |
|
|
|
int |
|
|
filter_sigchld(sig, data, id, owner) |
|
|
int sig; |
|
|
void *data; |
|
|
rad_sigid_t id; |
|
|
void *owner; |
|
|
{ |
|
|
Filter *filter = data; |
|
|
int status; |
|
|
|
|
|
if (waitpid(filter->pid, &filter->status, WNOHANG) == filter->pid) { |
|
|
if (filter->input >= 0) { |
|
|
close(filter->input); |
|
|
filter->input = -1; |
|
|
} |
|
|
if (filter->output >= 0) { |
|
|
close(filter->output); |
|
|
filter->output = -1; |
|
|
} |
|
|
|
|
|
filter->oldpid = filter->pid; |
|
|
filter->pid = -1; |
|
|
rad_signal_remove(SIGCHLD, filter->id, owner); |
|
|
filter->id = 0; |
|
|
filter->stopped = 0; |
|
|
|
|
|
return 0; |
|
|
} |
|
|
return 1; |
|
|
} |
|
|
|
|
359 |
void |
void |
360 |
filter_close(filter) |
filter_close(Filter *filter) |
|
Filter *filter; |
|
361 |
{ |
{ |
362 |
if (filter->pid == -1) |
if (filter->pid == -1) |
363 |
return; |
return; |
370 |
close(filter->output); |
close(filter->output); |
371 |
filter->output = -1; |
filter->output = -1; |
372 |
} |
} |
373 |
if (filter->pid > 0) |
if (filter->pid > 0) { |
374 |
kill(filter->pid, SIGTERM); |
kill(filter->pid, SIGTERM); |
375 |
|
filter->pid = 0; |
376 |
filter->stopped = 1; |
} |
377 |
} |
} |
378 |
|
|
379 |
void |
void |
380 |
filter_kill(filter) |
filter_kill(Filter *filter) |
|
Filter *filter; |
|
381 |
{ |
{ |
382 |
if (filter->pid == -1 || !filter->stopped) |
if (filter->pid == 0) |
383 |
return; |
return; |
384 |
do { |
kill(filter->pid, SIGKILL); |
|
kill(filter->pid, SIGKILL); |
|
|
sleep(2); |
|
|
} while (filter->pid != -1); |
|
385 |
} |
} |
386 |
|
|
387 |
static Filter * |
static Filter * |
388 |
filter_open(name, req, type, errp) |
filter_open(char *name, RADIUS_REQ *req, int type, int *errp) |
|
char *name; |
|
|
RADIUS_REQ *req; |
|
|
int type; |
|
|
int *errp; |
|
389 |
{ |
{ |
390 |
Filter *filter; |
Filter *filter = sym_lookup(filter_tab, name); |
391 |
Filter_symbol *sym = sym_lookup(filter_tab, name); |
if (!filter) { |
|
if (!sym) { |
|
392 |
radlog_req(L_ERR, req, |
radlog_req(L_ERR, req, |
393 |
_("filter %s is not declared"), |
_("filter %s is not declared"), |
394 |
name); |
name); |
396 |
return NULL; |
return NULL; |
397 |
} |
} |
398 |
|
|
399 |
*errp = sym->descr[type].on_fail; |
*errp = filter->descr[type].on_fail; |
|
pthread_cleanup_push((void (*)(void*))filter_symbol_unlock, sym); |
|
|
if (filter_symbol_lock(sym, type)) |
|
|
return NULL; |
|
|
|
|
|
if (!sym->common) { |
|
|
pthread_t tid = pthread_self(); |
|
|
for (filter = sym->filter; filter; filter = filter->next) |
|
|
if (filter->tid == tid) |
|
|
break; |
|
|
} else |
|
|
filter = sym->filter; |
|
|
if (!filter) { |
|
|
filter = mem_alloc(sizeof(*filter)); |
|
|
filter->tid = pthread_self(); |
|
|
filter->sym = sym; |
|
|
filter->input = filter->output = -1; |
|
|
pthread_mutex_init(&filter->mutex, NULL); |
|
|
filter->next = sym->filter; |
|
|
sym->filter = filter; |
|
|
} else { |
|
|
if (filter->stopped) { |
|
|
radlog(L_ERR, |
|
|
_("Killing unresponsive filter %s (pid %d)"), |
|
|
filter->sym->name, filter->pid); |
|
|
filter_kill(filter); |
|
|
} |
|
|
|
|
|
if (filter->pid <= 0) { |
|
|
char buffer[80]; |
|
|
Filter_symbol *sym = filter->sym; |
|
|
|
|
|
format_exit_status(buffer, sizeof buffer, |
|
|
filter->status); |
|
|
radlog(L_ERR, |
|
|
_("filter %s (pid %d) %s (in: %u, out: %u)"), |
|
|
sym->name, filter->oldpid, |
|
|
buffer, |
|
|
filter->lines_input, filter->lines_output); |
|
|
} |
|
|
} |
|
|
|
|
400 |
if (filter && filter->pid <= 0) { |
if (filter && filter->pid <= 0) { |
401 |
int pipe[2]; |
int pipe[2]; |
402 |
|
|
403 |
pthread_mutex_lock(&filter->mutex); |
filter->pid = radius_run_filter(filter->argc, |
404 |
filter->id = rad_signal_install(SIGCHLD, SH_SYNC, |
filter->argv, |
405 |
filter_sigchld, |
filter->errfile, |
|
filter); |
|
|
filter->pid = radius_run_filter(sym->argc, sym->argv, |
|
|
sym->errfile, |
|
406 |
pipe); |
pipe); |
|
pthread_mutex_unlock(&filter->mutex); |
|
407 |
|
|
408 |
if (filter->pid < 0) { |
if (filter->pid <= 0) { |
409 |
radlog_req(L_ERR|L_PERROR, req, |
radlog_req(L_ERR|L_PERROR, req, |
410 |
_("cannot run filter %s"), |
_("cannot run filter %s"), |
411 |
name); |
name); |
|
rad_signal_remove(SIGCHLD, filter->id, NULL); |
|
|
filter->id = 0; |
|
412 |
filter = NULL; |
filter = NULL; |
413 |
} else { |
} else { |
414 |
if (!sym->descr[R_AUTH].wait_reply |
if (!filter->descr[R_AUTH].wait_reply |
415 |
&& !sym->descr[R_ACCT].wait_reply) { |
&& !filter->descr[R_ACCT].wait_reply) { |
416 |
close(pipe[0]); |
close(pipe[0]); |
417 |
filter->input = -1; |
filter->input = -1; |
418 |
} else |
} else |
428 |
filter_close(filter); |
filter_close(filter); |
429 |
filter = NULL; |
filter = NULL; |
430 |
} |
} |
|
pthread_cleanup_pop(1); |
|
431 |
|
|
432 |
return filter; |
return filter; |
433 |
} |
} |
434 |
|
|
|
static void |
|
|
cleanup_obstack(arg) |
|
|
void *arg; |
|
|
{ |
|
|
struct obstack *stk = arg; |
|
|
obstack_free(stk, NULL); |
|
|
} |
|
|
|
|
435 |
static char * |
static char * |
436 |
filter_xlate(sp, fmt, radreq) |
filter_xlate(struct obstack *sp, char *fmt, RADIUS_REQ *radreq) |
|
struct obstack *sp; |
|
|
char *fmt; |
|
|
RADIUS_REQ *radreq; |
|
437 |
{ |
{ |
438 |
char *str; |
char *str; |
439 |
|
|
440 |
if (fmt[0] == '=') { |
if (fmt[0] == '=') { |
441 |
Datatype type; |
Datatype type; |
442 |
Datum datum; |
Datum datum; |
458 |
} |
} |
459 |
|
|
460 |
static int |
static int |
461 |
filter_write(filter, fmt, radreq) |
filter_write(Filter *filter, char *fmt, RADIUS_REQ *radreq) |
|
Filter *filter; |
|
|
char *fmt; |
|
|
RADIUS_REQ *radreq; |
|
462 |
{ |
{ |
463 |
int rc, length; |
int rc, length; |
464 |
struct obstack stack; |
struct obstack stack; |
468 |
return -1; |
return -1; |
469 |
|
|
470 |
obstack_init(&stack); |
obstack_init(&stack); |
|
pthread_cleanup_push((void (*)(void*))cleanup_obstack, &stack); |
|
471 |
str = filter_xlate(&stack, fmt, radreq); |
str = filter_xlate(&stack, fmt, radreq); |
472 |
if (!str) { |
if (!str) { |
473 |
rc = length = 0; |
rc = length = 0; |
474 |
} else { |
} else { |
475 |
char nl = '\n'; |
char nl = '\n'; |
476 |
length = strlen(str); |
length = strlen(str); |
477 |
debug(1,("%s < \"%s\"", filter->sym->name, str)); |
debug(1,("%s < \"%s\"", filter->name, str)); |
478 |
rc = write(filter->output, str, length); |
rc = write(filter->output, str, length); |
479 |
if (rc == length) { |
if (rc == length) { |
480 |
if (write(filter->output, &nl, 1) == 1) |
if (write(filter->output, &nl, 1) == 1) |
482 |
} |
} |
483 |
|
|
484 |
} |
} |
485 |
pthread_cleanup_pop(1); |
obstack_free(&stack, NULL); |
486 |
filter->lines_output++; |
filter->lines_output++; |
487 |
return rc != length + 1; |
return rc != length + 1; |
488 |
} |
} |
489 |
|
|
490 |
static int |
static int |
491 |
filter_read(filter, type, buffer, buflen) |
filter_read(Filter *filter, int type, char *buffer, size_t buflen) |
|
Filter *filter; |
|
|
int type; |
|
|
char *buffer; |
|
|
size_t buflen; |
|
492 |
{ |
{ |
493 |
struct timeval abstime; |
int rc; |
494 |
int status; |
int rbytes = 0; |
495 |
|
|
496 |
radiusd_get_timeout(type, &abstime); |
while (1) { |
497 |
status = timed_readline(filter->input, buffer, buflen, &abstime); |
rc = -1; |
498 |
filter->lines_input++; |
if (rbytes >= buflen-1) { |
499 |
return status; |
errno = ENOMEM; |
500 |
|
break; |
501 |
|
} |
502 |
|
if (read(filter->input, buffer + rbytes, 1) != 1) { |
503 |
|
if (errno == EINTR) |
504 |
|
continue; |
505 |
|
break; |
506 |
|
} |
507 |
|
rbytes++; |
508 |
|
if (buffer[rbytes-1] == '\n') { |
509 |
|
rc = 0; |
510 |
|
break; |
511 |
|
} |
512 |
|
} |
513 |
|
|
514 |
|
if (rc == 0) { |
515 |
|
buffer[rbytes] = 0; |
516 |
|
filter->lines_input++; |
517 |
|
return rbytes; |
518 |
|
} |
519 |
|
return rc; |
520 |
} |
} |
521 |
|
|
522 |
/* Interface triggered by Auth-External-Filter. |
/* Interface triggered by Auth-External-Filter. |
523 |
Returns: 0 -- Authentication succeeded |
Returns: 0 -- Authentication succeeded |
524 |
!0 -- Authentication failed */ |
!0 -- Authentication failed */ |
525 |
int |
int |
526 |
filter_auth(name, req, reply_pairs) |
filter_auth(char *name, RADIUS_REQ *req, VALUE_PAIR **reply_pairs) |
|
char *name; |
|
|
RADIUS_REQ *req; |
|
|
VALUE_PAIR **reply_pairs; |
|
527 |
{ |
{ |
528 |
Filter *filter; |
Filter *filter; |
529 |
int rc = -1; |
int rc = -1; |
532 |
filter = filter_open(name, req, R_AUTH, &err); |
filter = filter_open(name, req, R_AUTH, &err); |
533 |
if (!filter) |
if (!filter) |
534 |
return err; |
return err; |
|
pthread_cleanup_push((void (*)(void*)) filter_unlock, filter); |
|
|
filter_lock(filter); |
|
535 |
if (filter->pid == -1) |
if (filter->pid == -1) |
536 |
rc = err; |
rc = err; |
537 |
else if (filter_write(filter, |
else if (filter_write(filter, filter->descr[R_AUTH].input_fmt, req)) |
|
filter->sym->descr[R_AUTH].input_fmt, req)) |
|
538 |
rc = err; |
rc = err; |
539 |
else if (!filter->sym->descr[R_AUTH].wait_reply) |
else if (!filter->descr[R_AUTH].wait_reply) |
540 |
rc = 0; |
rc = 0; |
541 |
else { |
else { |
542 |
int status; |
int status; |
547 |
if (status <= 0) { |
if (status <= 0) { |
548 |
radlog(L_ERR|L_PERROR, |
radlog(L_ERR|L_PERROR, |
549 |
_("reading from filter %s"), |
_("reading from filter %s"), |
550 |
filter->sym->name); |
filter->name); |
551 |
filter_close(filter); |
filter_close(filter); |
552 |
rc = err; |
rc = err; |
553 |
} else if (isdigit(buffer[0])) { |
} else if (isdigit(buffer[0])) { |
555 |
VALUE_PAIR *vp = NULL; |
VALUE_PAIR *vp = NULL; |
556 |
char *errp; |
char *errp; |
557 |
|
|
558 |
debug(1, ("%s > \"%s\"", filter->sym->name, |
debug(1, ("%s > \"%s\"", filter->name, buffer)); |
|
buffer)); |
|
559 |
rc = strtoul(buffer, &ptr, 0); |
rc = strtoul(buffer, &ptr, 0); |
560 |
if (userparse(ptr, &vp, &errp)) { |
if (userparse(ptr, &vp, &errp)) { |
561 |
radlog(L_ERR, |
radlog(L_ERR, |
562 |
_("<stdout of %s>:%d: %s"), |
_("<stdout of %s>:%d: %s"), |
563 |
filter->sym->name, |
filter->name, |
564 |
filter->lines_output, |
filter->lines_output, |
565 |
errp); |
errp); |
566 |
avl_free(vp); |
avl_free(vp); |
569 |
} else { |
} else { |
570 |
radlog(L_ERR, |
radlog(L_ERR, |
571 |
_("filter %s (auth): bad output: %s"), |
_("filter %s (auth): bad output: %s"), |
572 |
filter->sym->name, buffer); |
filter->name, buffer); |
573 |
rc = err; |
rc = err; |
574 |
} |
} |
575 |
} |
} |
576 |
|
|
|
pthread_cleanup_pop(1); |
|
577 |
return rc; |
return rc; |
578 |
} |
} |
579 |
|
|
580 |
int |
int |
581 |
filter_acct(name, req) |
filter_acct(char *name, RADIUS_REQ *req) |
|
char *name; |
|
|
RADIUS_REQ *req; |
|
582 |
{ |
{ |
583 |
Filter *filter; |
Filter *filter; |
584 |
int rc = -1; |
int rc = -1; |
592 |
filter = filter_open(name, req, R_ACCT, &err); |
filter = filter_open(name, req, R_ACCT, &err); |
593 |
if (!filter) |
if (!filter) |
594 |
return err; |
return err; |
|
pthread_cleanup_push((void (*)(void*)) filter_unlock, filter); |
|
|
filter_lock(filter); |
|
595 |
if (filter->pid == -1) |
if (filter->pid == -1) |
596 |
rc = err; |
rc = err; |
597 |
else if (filter_write(filter, |
else if (filter_write(filter, filter->descr[R_ACCT].input_fmt, req)) |
|
filter->sym->descr[R_ACCT].input_fmt, req)) |
|
598 |
rc = err; |
rc = err; |
599 |
else if (!filter->sym->descr[R_ACCT].wait_reply) |
else if (!filter->descr[R_ACCT].wait_reply) |
600 |
rc = 0; |
rc = 0; |
601 |
else { |
else { |
602 |
int status; |
int status; |
607 |
if (status <= 0) { |
if (status <= 0) { |
608 |
radlog(L_ERR|L_PERROR, |
radlog(L_ERR|L_PERROR, |
609 |
_("reading from filter %s"), |
_("reading from filter %s"), |
610 |
filter->sym->name); |
filter->name); |
611 |
rc = err; |
rc = err; |
612 |
filter_close(filter); |
filter_close(filter); |
613 |
} else if (isdigit(buffer[0])) { |
} else if (isdigit(buffer[0])) { |
614 |
char *ptr; |
char *ptr; |
615 |
char *errp; |
char *errp; |
616 |
|
|
617 |
debug(1, ("%s > \"%s\"", |
debug(1, ("%s > \"%s\"", filter->name, buffer)); |
|
filter->sym->name, buffer)); |
|
618 |
rc = strtoul(buffer, &ptr, 0); |
rc = strtoul(buffer, &ptr, 0); |
619 |
} else { |
} else { |
620 |
radlog(L_ERR, |
radlog(L_ERR, |
621 |
_("filter %s (acct): bad output: %s"), |
_("filter %s (acct): bad output: %s"), |
622 |
filter->sym->name, buffer); |
filter->name, buffer); |
623 |
rc = err; |
rc = err; |
624 |
} |
} |
625 |
} |
} |
626 |
|
|
|
pthread_cleanup_pop(1); |
|
627 |
return rc; |
return rc; |
628 |
} |
} |
629 |
|
|
630 |
|
|
631 |
|
/* ***************************** Configuration ***************************** */ |
632 |
|
|
633 |
|
static struct filter_symbol filter_symbol; |
634 |
|
|
635 |
static int |
static int |
636 |
free_symbol_entry(sym) |
free_symbol_entry(Filter *filter) |
|
Filter_symbol *sym; |
|
637 |
{ |
{ |
638 |
Filter *filter; |
efree(filter->descr[R_AUTH].input_fmt); |
639 |
efree(sym->descr[R_AUTH].input_fmt); |
efree(filter->descr[R_ACCT].input_fmt); |
640 |
efree(sym->descr[R_ACCT].input_fmt); |
argcv_free(filter->argc, filter->argv); |
641 |
argcv_free(sym->argc, sym->argv); |
efree(filter->errfile); |
642 |
efree(sym->errfile); |
if (filter->pid > 0) |
643 |
filter = sym->filter; |
filter_close(filter); |
|
while (filter) { |
|
|
Filter *next = filter->next; |
|
|
if (filter->pid > 0) |
|
|
filter_close(filter); |
|
|
pthread_mutex_destroy(&filter->mutex); |
|
|
mem_free(filter); |
|
|
filter = next; |
|
|
} |
|
|
pthread_mutex_destroy(&sym->mutex); |
|
644 |
return 0; |
return 0; |
645 |
} |
} |
646 |
|
|
647 |
int |
int |
648 |
filters_stmt_term(finish, block_data, handler_data) |
filters_stmt_term(int finish, void *block_data, void *handler_data) |
|
int finish; |
|
|
void *block_data; |
|
|
void *handler_data; |
|
649 |
{ |
{ |
650 |
if (!finish) { |
if (!finish) { |
651 |
if (filter_tab) |
if (filter_tab) |
652 |
symtab_clear(filter_tab); |
symtab_clear(filter_tab); |
653 |
else |
else |
654 |
filter_tab = symtab_create(sizeof(Filter_symbol), |
filter_tab = symtab_create(sizeof(Filter), |
655 |
free_symbol_entry); |
free_symbol_entry); |
656 |
} |
} |
657 |
return 0; |
return 0; |
658 |
} |
} |
659 |
|
|
660 |
static int |
static int |
661 |
filter_stmt_handler(argc, argv, block_data, handler_data) |
filter_stmt_handler(int argc, cfg_value_t *argv, void *block_data, |
662 |
int argc; |
void *handler_data) |
|
cfg_value_t *argv; |
|
|
void *block_data; |
|
|
void *handler_data; |
|
663 |
{ |
{ |
664 |
if (argc > 2) { |
if (argc > 2) { |
665 |
cfg_argc_error(0); |
cfg_argc_error(0); |
677 |
filter_symbol.errfile = NULL; |
filter_symbol.errfile = NULL; |
678 |
filter_symbol.descr[R_AUTH].wait_reply = 1; |
filter_symbol.descr[R_AUTH].wait_reply = 1; |
679 |
filter_symbol.descr[R_ACCT].wait_reply = 1; |
filter_symbol.descr[R_ACCT].wait_reply = 1; |
|
filter_symbol.common = 0; |
|
680 |
return 0; |
return 0; |
681 |
} |
} |
682 |
|
|
683 |
static int |
static int |
684 |
filter_stmt_end(block_data, handler_data) |
filter_stmt_end(void *block_data, void *handler_data) |
|
void *block_data; |
|
|
void *handler_data; |
|
685 |
{ |
{ |
686 |
if (filter_symbol.argc) { |
if (filter_symbol.argc) { |
687 |
Filter_symbol *sym = sym_lookup_or_install(filter_tab, |
Filter *sym = sym_lookup_or_install(filter_tab, |
688 |
filter_symbol.name, |
filter_symbol.name, |
689 |
1); |
1); |
690 |
if (sym->argc) { |
if (sym->argc) { |
691 |
radlog(L_ERR, |
radlog(L_ERR, |
692 |
_("%s:%d: filter already declared at %s:%d"), |
_("%s:%d: filter already declared at %s:%d"), |
711 |
sym->descr[R_ACCT].on_fail = |
sym->descr[R_ACCT].on_fail = |
712 |
!filter_symbol.descr[R_ACCT].on_fail; |
!filter_symbol.descr[R_ACCT].on_fail; |
713 |
sym->errfile = estrdup(filter_symbol.errfile); |
sym->errfile = estrdup(filter_symbol.errfile); |
714 |
sym->common = filter_symbol.common; |
sym->pid = 0; |
|
sym->max_percent = filter_symbol.max_percent; |
|
|
sym->filter = NULL; |
|
|
pthread_mutex_init(&sym->mutex, NULL); |
|
715 |
} |
} |
716 |
return 0; |
return 0; |
717 |
} |
} |
718 |
|
|
719 |
static int |
static int |
720 |
exec_path_handler(argc, argv, block_data, handler_data) |
exec_path_handler(int argc, cfg_value_t *argv, |
721 |
int argc; |
void *block_data, void *handler_data) |
|
cfg_value_t *argv; |
|
|
void *block_data; |
|
|
void *handler_data; |
|
722 |
{ |
{ |
723 |
if (argc > 2) { |
if (argc > 2) { |
724 |
cfg_argc_error(0); |
cfg_argc_error(0); |
739 |
} |
} |
740 |
|
|
741 |
static int |
static int |
742 |
common_handler(argc, argv, block_data, handler_data) |
error_log_handler(int argc, cfg_value_t *argv, |
743 |
int argc; |
void *block_data, void *handler_data) |
|
cfg_value_t *argv; |
|
|
void *block_data; |
|
|
void *handler_data; |
|
|
{ |
|
|
if (argc > 3) { |
|
|
cfg_argc_error(0); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
if (argv[1].type != CFG_BOOLEAN) { |
|
|
cfg_type_error(CFG_BOOLEAN); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
filter_symbol.common = argv[1].v.bool; |
|
|
|
|
|
if (argc > 2) { |
|
|
if (argv[2].type != CFG_INTEGER || !filter_symbol.common) |
|
|
return 1; |
|
|
filter_symbol.max_percent = argv[2].v.number; |
|
|
if (filter_symbol.max_percent < 0 |
|
|
|| filter_symbol.max_percent > 100) |
|
|
return 1; |
|
|
} |
|
|
return 0; |
|
|
} |
|
|
|
|
|
static int |
|
|
error_log_handler(argc, argv, block_data, handler_data) |
|
|
int argc; |
|
|
cfg_value_t *argv; |
|
|
void *block_data; |
|
|
void *handler_data; |
|
744 |
{ |
{ |
745 |
if (argc > 2) { |
if (argc > 2) { |
746 |
cfg_argc_error(0); |
cfg_argc_error(0); |
766 |
} |
} |
767 |
|
|
768 |
static int |
static int |
769 |
_store_format_ptr(argc, argv, block_data, handler_data) |
_store_format_ptr(int argc, cfg_value_t *argv, void *block_data, |
770 |
int argc; |
void *handler_data) |
|
cfg_value_t *argv; |
|
|
void *block_data; |
|
|
void *handler_data; |
|
771 |
{ |
{ |
772 |
if (argc > 2) { |
if (argc > 2) { |
773 |
cfg_argc_error(0); |
cfg_argc_error(0); |
813 |
static struct cfg_stmt filter_stmt[] = { |
static struct cfg_stmt filter_stmt[] = { |
814 |
{ "exec-path", CS_STMT, NULL, exec_path_handler, NULL, NULL, NULL }, |
{ "exec-path", CS_STMT, NULL, exec_path_handler, NULL, NULL, NULL }, |
815 |
{ "error-log", CS_STMT, NULL, error_log_handler, NULL, NULL, NULL }, |
{ "error-log", CS_STMT, NULL, error_log_handler, NULL, NULL, NULL }, |
|
{ "common", CS_STMT, NULL, common_handler, NULL, |
|
|
NULL, NULL }, |
|
816 |
{ "auth", CS_BLOCK, NULL, NULL, NULL, filter_auth_stmt, NULL }, |
{ "auth", CS_BLOCK, NULL, NULL, NULL, filter_auth_stmt, NULL }, |
817 |
{ "acct", CS_BLOCK, NULL, NULL, NULL, filter_acct_stmt, NULL }, |
{ "acct", CS_BLOCK, NULL, NULL, NULL, filter_acct_stmt, NULL }, |
818 |
{ NULL }, |
{ NULL }, |