From: Alan T. DeKok Date: Tue, 24 Oct 2017 15:58:46 +0000 (-0400) Subject: remove more unused files. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=900e5b98845da05264cdf9fa75d6d7a67d882e37;p=thirdparty%2Ffreeradius-server.git remove more unused files. If we need the code, it's in the v3 branch --- diff --git a/src/main/command.c b/src/main/command.c deleted file mode 100644 index fae85a2137d..00000000000 --- a/src/main/command.c +++ /dev/null @@ -1,3507 +0,0 @@ -/* - * command.c Command socket processing. - * - * Version: $Id$ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - * - * Copyright 2008 The FreeRADIUS server project - * Copyright 2008 Alan DeKok - */ - -#ifdef WITH_COMMAND_SOCKET - -#include -#include -#include -#include - -#include -#ifdef HAVE_INTTYPES_H -#include -#endif - -#ifdef HAVE_SYS_UN_H -#include -#ifndef SUN_LEN -#define SUN_LEN(su) (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) -#endif -#endif - -#ifdef HAVE_SYS_STAT_H -#include -#endif - -#include -#include - -#ifdef HAVE_GPERFTOOLS_PROFILER_H -# include -#endif - -typedef struct fr_command_table_t fr_command_table_t; - -typedef int (*fr_command_func_t)(rad_listen_t *, int, char *argv[]); - -#define FR_READ (1) -#define FR_WRITE (2) - -#define CMD_FAIL FR_CONDUIT_FAIL -#define CMD_OK FR_CONDUIT_SUCCESS - -struct fr_command_table_t { - char const *command; - int mode; /* read/write */ - char const *help; - fr_command_func_t func; - fr_command_table_t *table; -}; - -#define COMMAND_SOCKET_MAGIC (0xffdeadee) -typedef struct fr_command_socket_t { - uint32_t magic; - char const *path; - char *copy; /* */ - gid_t gid; //!< Additional group authorized to connect to socket. - char const *gid_name; //!< Name of additional group (resolved to gid later). - char const *mode_name; - bool peercred; - bool blocking; - char user[256]; - - /* - * The next few entries handle fake packets injected by - * the control socket. - */ - fr_ipaddr_t src_ipaddr; /* src_port is always 0 */ - fr_ipaddr_t dst_ipaddr; - uint16_t dst_port; - rad_listen_t *inject_listener; - RADCLIENT *inject_client; - - fr_cs_buffer_t co; -} fr_command_socket_t; - -static const CONF_PARSER command_config[] = { - { FR_CONF_OFFSET("socket", FR_TYPE_STRING, fr_command_socket_t, path), .dflt = "${run_dir}/radiusd.sock" }, - { FR_CONF_DEPRECATED("uid", FR_TYPE_STRING, fr_command_socket_t, NULL) }, - { FR_CONF_OFFSET("gid", FR_TYPE_STRING, fr_command_socket_t, gid_name) }, - { FR_CONF_OFFSET("mode", FR_TYPE_STRING, fr_command_socket_t, mode_name) }, - { FR_CONF_OFFSET("peercred", FR_TYPE_BOOL, fr_command_socket_t, peercred), .dflt = "yes" }, - { FR_CONF_OFFSET("blocking", FR_TYPE_BOOL, fr_command_socket_t, blocking), }, - CONF_PARSER_TERMINATOR -}; - -static FR_NAME_NUMBER mode_names[] = { - { "ro", FR_READ }, - { "read-only", FR_READ }, - { "read-write", FR_READ | FR_WRITE }, - { "rw", FR_READ | FR_WRITE }, - { NULL, 0 } -}; - -static char debug_log_file_buffer[1024]; -extern fr_cond_t *debug_condition; -extern fr_log_t debug_log; - -#if !defined(HAVE_GETPEEREID) && defined(SO_PEERCRED) -static int getpeereid(int s, uid_t *euid, gid_t *egid) -{ - struct ucred cr; - socklen_t cl = sizeof(cr); - - if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cl) < 0) { - return -1; - } - - *euid = cr.uid; - *egid = cr.gid; - return 0; -} - -/* we now have getpeereid() in this file */ -#define HAVE_GETPEEREID (1) - -#endif /* HAVE_GETPEEREID */ - -#if 0 -/* - * Enable this function if you're running on OSX and want to use - * valgrind. Valgrind doesn't implement openat() etc., so this - * old / shorter function works. - */ -static int fr_server_domain_socket(char const *path, UNUSED gid_t gid) -{ - int sockfd; - size_t len; - socklen_t socklen; - struct sockaddr_un salocal; - struct stat buf; - - len = strlen(path); - if (len >= sizeof(salocal.sun_path)) { - fr_strerror_printf("Path length (%zu) exceeds system limit for unix socket paths (%zu)", - len, sizeof(salocal.sun_path)); - return -1; - } - - if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { - fr_strerror_printf("Failed creating socket: %s", - fr_syserror(errno)); - return -1; - } - - memset(&salocal, 0, sizeof(salocal)); - salocal.sun_family = AF_UNIX; - memcpy(salocal.sun_path, path, len + 1); /* SUN_LEN does strlen */ - - socklen = SUN_LEN(&salocal); - - /* - * Check the path. - */ - if (stat(path, &buf) < 0) { - if (errno != ENOENT) { - fr_strerror_printf("Failed to stat %s: %s", - path, fr_syserror(errno)); - close(sockfd); - return -1; - } - - /* - * FIXME: Check the enclosing directory? - */ - } else { /* it exists */ - if (!S_ISREG(buf.st_mode) -#ifdef S_ISSOCK - && !S_ISSOCK(buf.st_mode) -#endif - ) { - fr_strerror_printf("Cannot turn %s into socket", path); - close(sockfd); - return -1; - } - - /* - * Refuse to open sockets not owned by us. - */ - if (buf.st_uid != geteuid()) { - fr_strerror_printf("We do not own %s", path); - close(sockfd); - return -1; - } - - if (unlink(path) < 0) { - fr_strerror_printf("Failed to delete %s: %s", - path, fr_syserror(errno)); - close(sockfd); - return -1; - } - } - - if (bind(sockfd, (struct sockaddr *)&salocal, socklen) < 0) { - fr_strerror_printf("Failed binding to %s: %s", - path, fr_syserror(errno)); - close(sockfd); - return -1; - } - - /* - * FIXME: There's a race condition here. But Linux - * doesn't seem to permit fchmod on domain sockets. - */ - if (chmod(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0) { - fr_strerror_printf("Failed setting permissions on %s: %s", - path, fr_syserror(errno)); - close(sockfd); - return -1; - } - - if (listen(sockfd, 8) < 0) { - fr_strerror_printf("Failed listening to %s: %s", - path, fr_syserror(errno)); - close(sockfd); - return -1; - } - -#ifdef O_NONBLOCK - { - int flags; - - if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0) { - fr_strerror_printf("Failure getting socket flags: %s", - fr_syserror(errno)); - close(sockfd); - return -1; - } - - flags |= O_NONBLOCK; - if( fcntl(sockfd, F_SETFL, flags) < 0) { - fr_strerror_printf("Failure setting socket flags: %s", - fr_syserror(errno)); - close(sockfd); - return -1; - } - } -#endif - - return sockfd; -} -#else /* OSX VALGRIND */ - -#if !defined(HAVE_OPENAT) || !defined(HAVE_MKDIRAT) || !defined(HAVE_UNLINKAT) || !defined(HAVE_FCHMODAT) || !defined(HAVE_FCHOWNAT) -static int fr_server_domain_socket(UNUSED char const *path, UNUSED gid_t gid) -{ - fr_strerror_printf("Unable to initialise control socket. Upgrade to POSIX-2008 compliant libc"); - return -1; -} -#else -/** Create a unix socket, and enforce permissions using the file system - * - * The way it does this depends on the operating system. On Linux systems permissions - * can be set on the socket directly and the system will enforce them. - * - * On most other systems fchown and fchmod fail when called with socket descriptors, - * and although permissions can be changed in other ways, they're not enforced. - * - * For these systems we use the permissions on the parent directory to enforce - * permissions on the socket. It's not safe to modify these permissions ourselves - * due to TOCTOU attacks, so if they don't match what we require, we error out and - * get the user to change them (which arguably isn't any safer, but releases us of - * the responsibility). - * - * @note must be called without effective root permissions (#fr_suid_down). - * - * @param path where domain socket should be created. - * @param gid Alternative group to grant read/write access to the socket. - * @return - * - A file descriptor for the bound socket on success. - * - -1 on failure. - */ -static int fr_server_domain_socket(char const *path, gid_t gid, bool blocking) -{ - int dir_fd = -1, sock_fd = -1, parent_fd = -1; - char const *name; - char *buff = NULL, *dir = NULL, *p; - - uid_t euid, suid; - gid_t egid; - - mode_t perm = 0; - mode_t dir_perm; - struct stat st; - - size_t len; - - socklen_t socklen; - struct sockaddr_un salocal; - - rad_assert(path); - - euid = geteuid(); - egid = getegid(); - - /* - * In the Linux implementation, sockets which are visible in the - * filesystem honor the permissions of the directory they are in. Their - * owner, group, and permissions can be changed. Creation of a new - * socket will fail if the process does not have write and search - * (execute) permission on the directory the socket is created in. - * Connecting to the socket object requires read/write permission. - */ - perm = (S_IREAD | S_IWRITE); - dir_perm = (S_IREAD | S_IWRITE | S_IEXEC); - if (gid != (gid_t) -1) { - perm |= (S_IRGRP | S_IWGRP); - dir_perm |= (S_IRGRP | S_IXGRP); - } - - buff = talloc_strdup(NULL, path); - if (!buff) { - fr_strerror_printf("Out of memory"); - return -1; - } - - /* - * Some implementations modify it in place others use internal - * storage *sigh*. dirname also formats the path else we wouldn't - * be using it. - */ - dir = dirname(buff); - if (dir != buff) { - dir = talloc_strdup(NULL, dir); - MEM(dir); - talloc_free(buff); - } - - p = strrchr(dir, FR_DIR_SEP); - if (!p) { - fr_strerror_printf("Failed determining parent directory"); - error: - talloc_free(dir); - if (dir_fd >= 0) close(dir_fd); - if (sock_fd >= 0) close(sock_fd); - if (parent_fd >= 0) close(parent_fd); - return -1; - } - - *p = '\0'; - - /* - * Ensure the parent of the control socket directory exists, - * and the euid we're running under has access to it. - * - * This must be done suid_down, so we can't be tricked into - * accessing a directory owned by root. - */ - parent_fd = open(dir, O_DIRECTORY); - if (parent_fd < 0) { - struct passwd *user; - struct group *group; - - if (errno != ENOENT) { - fr_strerror_printf("Can't open directory \"%s\": %s", dir, fr_syserror(errno)); - goto error; - } - - if (rad_getpwuid(NULL, &user, euid) < 0) { - fr_strerror_printf("Failed resolving euid to user: %s", fr_strerror()); - goto error; - } - if (rad_getgrgid(NULL, &group, egid) < 0) { - fr_strerror_printf("Failed resolving egid to group: %s", fr_strerror()); - talloc_free(user); - goto error; - } - - fr_strerror_printf("Can't open directory \"%s\": Create it and allow writing by " - "user %s or group %s", dir, user->pw_name, group->gr_name); - - talloc_free(user); - talloc_free(group); - goto error; - } - - *p = FR_DIR_SEP; - - dir_fd = openat(parent_fd, p + 1, O_NOFOLLOW | O_DIRECTORY); - if (dir_fd < 0) { - int ret = 0; - - if (errno != ENOENT) { - rad_file_error(errno); - fr_strerror_printf("Failed opening control socket directory \"%s\": %s", dir, fr_strerror()); - goto error; - } - - /* - * This fails if the radius user can't write - * to the parent directory. - */ - if (mkdirat(parent_fd, p + 1, dir_perm) < 0) { - rad_file_error(errno); - fr_strerror_printf("Failed creating control socket directory \"%s\": %s", dir, fr_strerror()); - goto error; - } - - dir_fd = openat(parent_fd, p + 1, O_NOFOLLOW | O_DIRECTORY); - if (dir_fd < 0) { - fr_strerror_printf("Failed opening the control socket directory we created: %s", - fr_syserror(errno)); - goto error; - } - - /* - * Can't set groups other than ones we belong - * to unless we suid_up. - */ - rad_suid_up(); - if (gid != (gid_t)-1) ret = fchown(dir_fd, euid, gid); - rad_suid_down(); - if (ret < 0) { - fr_strerror_printf("Failed changing group of control socket directory: %s", - fr_syserror(errno)); - goto error; - } - /* - * Control socket dir already exists, but we still need to - * check the permissions are what we expect. - */ - } else { - int ret; - int client_fd; - - ret = fstat(dir_fd, &st); - if (ret < 0) { - fr_strerror_printf("Failed checking permissions of control socket directory: %s", - fr_syserror(errno)); - goto error; - } - - if (st.st_uid != euid) { - struct passwd *need_user, *have_user; - - if (rad_getpwuid(NULL, &need_user, euid) < 0) { - fr_strerror_printf("Failed resolving socket dir uid to user: %s", fr_strerror()); - goto error; - } - if (rad_getpwuid(NULL, &have_user, st.st_uid) < 0) { - fr_strerror_printf("Failed resolving socket dir gid to group: %s", fr_strerror()); - talloc_free(need_user); - goto error; - } - fr_strerror_printf("Socket directory \"%s\" must be owned by user %s, currently owned " - "by user %s", dir, need_user->pw_name, have_user->pw_name); - talloc_free(need_user); - talloc_free(have_user); - goto error; - } - - if ((gid != (gid_t)-1) && (st.st_gid != gid)) { - /* - * Can't set groups other than ones we belong - * to unless we suid_up. - */ - rad_suid_up(); - if (gid != (gid_t)-1) ret = fchown(dir_fd, euid, gid); - rad_suid_down(); - if (ret < 0) { - struct group *need_group, *have_group; - - if (rad_getgrgid(NULL, &need_group, gid) < 0) { - fr_strerror_printf("Failed resolving socket directory uid to user: %s", - fr_strerror()); - goto error; - } - if (rad_getgrgid(NULL, &have_group, st.st_gid) < 0) { - fr_strerror_printf("Failed resolving socket directory gid to group: %s", - fr_strerror()); - talloc_free(need_group); - goto error; - } - fr_strerror_printf("Failed changing ownership of socket directory \"%s\" from " - "group %s, to group %s", dir, - need_group->gr_name, have_group->gr_name); - talloc_free(need_group); - talloc_free(have_group); - - goto error; - } - } - - if ((dir_perm & 0777) != (st.st_mode & 0777) && - (fchmod(dir_fd, (st.st_mode & 7000) | dir_perm)) < 0) { - char str_need[10], oct_need[5]; - char str_have[10], oct_have[5]; - - rad_mode_to_str(str_need, dir_perm); - rad_mode_to_oct(oct_need, dir_perm); - rad_mode_to_str(str_have, st.st_mode); - rad_mode_to_oct(oct_have, st.st_mode); - fr_strerror_printf("Failed changing permissions on socket directory \"%s\" from %s " - "(%s) to %s (%s): %s", dir, str_have, oct_have, - str_need, oct_need, fr_syserror(errno)); - - goto error; - } - - /* - * Check if a server is already listening on the - * socket? - */ - client_fd = fr_socket_client_unix(path, false); - if (client_fd >= 0) { - fr_strerror_printf("Control socket '%s' is already in use", path); - close(client_fd); - goto error; - } - fr_strerror(); /* Clear any errors */ - } - - name = strrchr(path, FR_DIR_SEP); - if (!name) { - fr_strerror_printf("Can't determine socket name"); - goto error; - } - name++; - - /* - * We've checked the containing directory has the permissions - * we expect, and as we have the FD, and aren't following - * symlinks no one can trick us into changing or creating a - * file elsewhere. - * - * It's possible an attacker may still be able to create hard - * links, for the socket file. But they would need write - * access to the directory we just created or verified, so - * this attack vector is unlikely. - */ - rad_suid_up(); /* Need to be root to change euid and egid */ - suid = geteuid(); - - /* - * Group needs to be changed first, because if we change - * to a non root user, we can no longer set it. - */ - if ((gid != (gid_t)-1) && (rad_segid(gid) < 0)) { - fr_strerror_printf("Failed setting egid: %s", fr_strerror()); - rad_suid_down(); - goto error; - } - - /* - * Reset euid back to FreeRADIUS user - */ - if (rad_seuid(euid) < 0) { - fr_strerror_printf("Failed restoring euid: %s", fr_strerror()); - rad_segid(egid); - rad_suid_down(); - goto error; - } - - /* - * The original code, did openat, used fstat to figure out - * what type the file was and then used unlinkat to unlink - * it. Except on OSX (at least) openat refuses to open - * socket files. So we now rely on the fact that unlinkat - * has sane and consistent behaviour, and will not unlink - * directories. unlinkat should also fail if the socket user - * hasn't got permission to modify the socket. - */ - if ((unlinkat(dir_fd, name, 0) < 0) && (errno != ENOENT)) { - fr_strerror_printf("Failed removing stale socket: %s", fr_syserror(errno)); - sock_error: - /* - * Restore suid to ensure rad_suid_up continues - * to work correctly. - */ - rad_seuid(suid); - if (gid != (gid_t)-1) rad_segid(egid); - /* - * Then SUID down, to ensure rad_suid_up/down continues - * to work correctly. - */ - rad_suid_down(); - goto error; - } - - /* - * At this point we should have established a secure directory - * to house our socket, and cleared out any stale sockets. - */ - sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock_fd < 0) { - fr_strerror_printf("Failed opening socket: %s", fr_syserror(errno)); - goto sock_error; - } - -#ifdef HAVE_BINDAT - len = strlen(name); -#else - len = strlen(path); -#endif - if (len >= sizeof(salocal.sun_path)) { - fr_strerror_printf("Path length (%zu) exceeds system limit for unix socket paths (%zu)", - len, sizeof(salocal.sun_path)); - goto sock_error; - } - - memset(&salocal, 0, sizeof(salocal)); - salocal.sun_family = AF_UNIX; - -#ifdef HAVE_BINDAT - memcpy(salocal.sun_path, name, len + 1); /* SUN_LEN does strlen */ -#else - memcpy(salocal.sun_path, path, len + 1); /* SUN_LEN does strlen */ -#endif - socklen = SUN_LEN(&salocal); - - /* - * The correct function to use here is bindat(), but only - * quite recent versions of FreeBSD actually have it, and - * it's definitely not POSIX. - */ -#ifdef HAVE_BINDAT - if (bindat(dir_fd, sock_fd, (struct sockaddr *)&salocal, socklen) < 0) { -#else - if (bind(sock_fd, (struct sockaddr *)&salocal, socklen) < 0) { -#endif - fr_strerror_printf("Failed binding socket: %s", fr_syserror(errno)); - goto sock_error; - } - - /* - * Previous code used fchown to set ownership before the - * socket was bound. Unfortunately this only seemed to - * work on Linux, on OSX and FreeBSD this operation would - * throw an EINVAL error. - */ - if (fchownat(dir_fd, name, euid, gid, AT_SYMLINK_NOFOLLOW) < 0) { - struct passwd *user; - struct group *group; - int fchown_err = errno; - - - if (rad_getpwuid(NULL, &user, euid) < 0) { - fr_strerror_printf("Failed resolving socket uid to user: %s", fr_strerror()); - goto sock_error; - } - if (rad_getgrgid(NULL, &group, gid) < 0) { - fr_strerror_printf("Failed resolving socket gid to group: %s", fr_strerror()); - talloc_free(user); - goto sock_error; - } - - fr_strerror_printf("Failed changing socket ownership to %s:%s: %s", user->pw_name, group->gr_name, - fr_syserror(fchown_err)); - talloc_free(user); - talloc_free(group); - goto sock_error; - } - - /* - * Direct socket permissions are only useful on Linux which - * actually enforces them. BSDs may not... or they may... - * OSX 10.11.x (EL-Capitan) seems to. - * - * Previous code used fchmod on sock_fd before the bind, - * but this didn't always set the correct permissions. - * - * fchmodat seems to work more reliably, and has the same - * resistance against TOCTOU attacks. - * - * AT_SYMLINK_NOFOLLOW causes this to fail on Linux. - */ - if (fchmodat(dir_fd, name, perm, 0) < 0) { - char str_need[10], oct_need[5]; - - rad_mode_to_str(str_need, perm); - rad_mode_to_oct(oct_need, perm); - fr_strerror_printf("Failed changing socket permissions to %s (%s): %s", str_need, oct_need, - fr_syserror(errno)); - goto sock_error; - } - - if (listen(sock_fd, 8) < 0) { - fr_strerror_printf("Failed listening on socket: %s", fr_syserror(errno)); - goto sock_error; - } - - if (!blocking && (fr_nonblock(sock_fd) < 0)) { - fr_strerror_printf("Failed setting nonblock on socket: %s", fr_strerror()); - goto sock_error; - } - - /* - * Restore suid to ensure rad_suid_up continues - * to work correctly. - */ - rad_seuid(suid); - if (gid != (gid_t)-1) rad_segid(egid); - rad_suid_down(); - - close(dir_fd); - close(parent_fd); - - return sock_fd; -} -#endif /* HAVE_OPENAT, etc. */ -#endif /* OSX VALGRIND */ - -/* - * Turn off all debugging. But don't touch the debug condition. - */ -static void command_debug_off(void) -{ - debug_log.dst = L_DST_NULL; - debug_log.file = NULL; - debug_log.cookie = NULL; - debug_log.cookie_write = NULL; -} - - -static void command_close_socket(rad_listen_t *this) -{ - this->status = RAD_LISTEN_STATUS_EOL; - - if (debug_log.cookie == this) { - command_debug_off(); - } - - /* - * This removes the socket from the event fd, so no one - * will be calling us any more. - */ - radius_update_listener(this); -} - - -#if defined(HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN) -static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER; - -/* - * Callback from log.c, so that we can write debug output to the - * radmin socket. - * - * We only have one debug condition, so we only need one mutex. - */ -#ifdef HAVE_FOPENCOOKIE -static ssize_t command_socket_write(void *cookie, char const *buffer, size_t len) -#else -static int command_socket_write(void *cookie, char const *buffer, int len) -#endif -{ - ssize_t r; - rad_listen_t *listener = cookie; - - if (listener->status == RAD_LISTEN_STATUS_EOL) return 0; - - pthread_mutex_lock(&debug_mutex); - - r = fr_conduit_write(listener->fd, FR_CONDUIT_STDOUT, buffer, len); - - pthread_mutex_unlock(&debug_mutex); - - if (r <= 0) { - command_close_socket(listener); - } - - return r; -} -#endif - -static ssize_t CC_HINT(format (printf, 2, 3)) cprintf(rad_listen_t *listener, char const *fmt, ...) -{ - ssize_t r, len; - va_list ap; - char buffer[256]; - - va_start(ap, fmt); - len = vsnprintf(buffer, sizeof(buffer), fmt, ap); - va_end(ap); - - if (listener->status == RAD_LISTEN_STATUS_EOL) return 0; - - r = fr_conduit_write(listener->fd, FR_CONDUIT_STDOUT, buffer, len); - if (r <= 0) command_close_socket(listener); - - /* - * FIXME: Keep writing until done? - */ - return r; -} - -static ssize_t CC_HINT(format (printf, 2, 3)) cprintf_error(rad_listen_t *listener, char const *fmt, ...) -{ - ssize_t r, len; - va_list ap; - char buffer[256]; - - va_start(ap, fmt); - len = vsnprintf(buffer, sizeof(buffer), fmt, ap); - va_end(ap); - - if (listener->status == RAD_LISTEN_STATUS_EOL) return 0; - - r = fr_conduit_write(listener->fd, FR_CONDUIT_STDERR, buffer, len); - if (r <= 0) command_close_socket(listener); - - /* - * FIXME: Keep writing until done? - */ - return r; -} - -static int command_hup(rad_listen_t *listener, int argc, char *argv[]) -{ - CONF_SECTION *cs; - module_instance_t *instance; - - if (argc == 0) { - radius_signal_self(RADIUS_SIGNAL_SELF_HUP); - return CMD_OK; - } - - /* - * Hack a "main" HUP thingy - */ - if (strcmp(argv[0], "main.log") == 0) { - hup_logfile(); - return CMD_OK; - } - - cs = cf_section_find(main_config.config, "modules", NULL); - if (!cs) return CMD_FAIL; - - instance = module_find(cs, argv[0]); - if (!instance) { - cprintf_error(listener, "No such module \"%s\"\n", argv[0]); - return CMD_FAIL; - } - - cprintf_error(listener, "HUP - NYI in version 4"); - - return CMD_FAIL; -} - -static int command_terminate(UNUSED rad_listen_t *listener, - UNUSED int argc, UNUSED char *argv[]) -{ - radius_signal_self(RADIUS_SIGNAL_SELF_TERM); - - return CMD_OK; -} - -static int command_uptime(rad_listen_t *listener, - UNUSED int argc, UNUSED char *argv[]) -{ - char buffer[128]; - - CTIME_R(&fr_start_time, buffer, sizeof(buffer)); - cprintf(listener, "Up since %s", buffer); /* no \r\n */ - - return CMD_OK; -} - -static int command_show_config(rad_listen_t *listener, int argc, char *argv[]) -{ - CONF_ITEM *ci; - CONF_PAIR *cp; - char const *value; - - if (argc != 1) { - cprintf_error(listener, "No path was given\n"); - return CMD_FAIL; - } - - ci = cf_reference_item(main_config.config, main_config.config, argv[0]); - if (!ci) return CMD_FAIL; - - if (!cf_item_is_pair(ci)) return CMD_FAIL; - - cp = cf_item_to_pair(ci); - value = cf_pair_value(cp); - if (!value) return CMD_FAIL; - - cprintf(listener, "%s\n", value); - - return CMD_OK; -} - -static char const tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; - -/* - * FIXME: Recurse && indent? - */ -static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION *cs, - void const *base) - -{ - int i; - char const *name1 = cf_section_name1(cs); - char const *name2 = cf_section_name2(cs); - CONF_PARSER const *variables = cf_section_parse_table(cs); - - if (name2) { - cprintf(listener, "%.*s%s %s {\n", indent, tabs, name1, name2); - } else { - cprintf(listener, "%.*s%s {\n", indent, tabs, name1); - } - - indent++; - - /* - * Print - */ - if (variables) for (i = 0; variables[i].name != NULL; i++) { - void const *data; - char buffer[INET6_ADDRSTRLEN]; - - /* - * No base struct offset, data must be the pointer. - * If data doesn't exist, ignore the entry, there - * must be something wrong. - */ - if (!base) { - if (!variables[i].data) { - continue; - } - - data = variables[i].data; - - } else if (variables[i].data) { - data = variables[i].data; - - } else { - data = (((char const *)base) + variables[i].offset); - } - - /* - * Ignore the various flags - */ - switch (variables[i].type & 0xff) { - default: - cprintf(listener, "%.*s%s = ?\n", indent, tabs, - variables[i].name); - break; - - case FR_TYPE_UINT32: - cprintf(listener, "%.*s%s = %u\n", indent, tabs, - variables[i].name, *(int const *) data); - break; - - case FR_TYPE_IPV4_ADDR: - inet_ntop(AF_INET, data, buffer, sizeof(buffer)); - break; - - case FR_TYPE_IPV6_ADDR: - inet_ntop(AF_INET6, data, buffer, sizeof(buffer)); - break; - - case FR_TYPE_BOOL: - cprintf(listener, "%.*s%s = %s\n", indent, tabs, - variables[i].name, - ((*(bool const *) data) == false) ? "no" : "yes"); - break; - - case FR_TYPE_STRING: - case FR_TYPE_FILE_INPUT: - case FR_TYPE_FILE_OUTPUT: - /* - * FIXME: Escape things in the string! - */ - if (*(char const * const *) data) { - cprintf(listener, "%.*s%s = \"%s\"\n", indent, tabs, - variables[i].name, *(char const * const *) data); - } else { - cprintf(listener, "%.*s%s = \n", indent, tabs, - variables[i].name); - } - - break; - } - } - - indent--; - - cprintf(listener, "%.*s}\n", indent, tabs); -} - -static int command_show_module_config(rad_listen_t *listener, int argc, char *argv[]) -{ - CONF_SECTION *cs; - module_instance_t *instance; - - if (argc != 1) { - cprintf_error(listener, "No module name was given\n"); - return CMD_FAIL; - } - - cs = cf_section_find(main_config.config, "modules", NULL); - if (!cs) return CMD_FAIL; - - instance = module_find(cs, argv[0]); - if (!instance) { - cprintf_error(listener, "No such module \"%s\"\n", argv[0]); - return CMD_FAIL; - } - - cprint_conf_parser(listener, 0, instance->dl_inst->conf, instance->dl_inst->data); - - return CMD_OK; -} - -static char const *method_names[MOD_COUNT] = { - "authenticate", - "authorize", - "preacct", - "accounting", - "pre-proxy", - "post-proxy", - "post-auth" -}; - - -static int command_show_module_methods(rad_listen_t *listener, int argc, char *argv[]) -{ - int i; - CONF_SECTION *cs; - module_instance_t const *instance; - - if (argc != 1) { - cprintf_error(listener, "No module name was given\n"); - return CMD_FAIL; - } - - cs = cf_section_find(main_config.config, "modules", NULL); - if (!cs) return CMD_FAIL; - - instance = module_find(cs, argv[0]); - if (!instance) { - cprintf_error(listener, "No such module \"%s\"\n", argv[0]); - return CMD_FAIL; - } - - for (i = 0; i < MOD_COUNT; i++) { - if (instance->module->methods[i]) cprintf(listener, "%s\n", method_names[i]); - } - - return CMD_OK; -} - - -static int command_show_module_flags(rad_listen_t *listener, int argc, char *argv[]) -{ - CONF_SECTION *cs; - module_instance_t const *instance; - - if (argc != 1) { - cprintf_error(listener, "No module name was given\n"); - return CMD_FAIL; - } - - cs = cf_section_find(main_config.config, "modules", NULL); - if (!cs) return CMD_FAIL; - - instance = module_find(cs, argv[0]); - if (!instance) { - cprintf_error(listener, "No such module \"%s\"\n", argv[0]); - return CMD_FAIL; - } - - if ((instance->module->type & RLM_TYPE_THREAD_UNSAFE) != 0) cprintf(listener, "thread-unsafe\n"); - - return CMD_OK; -} - -static int command_show_module_status(rad_listen_t *listener, int argc, char *argv[]) -{ - CONF_SECTION *cs; - const module_instance_t *instance; - - if (argc != 1) { - cprintf_error(listener, "No module name was given\n"); - return CMD_FAIL; - } - - cs = cf_section_find(main_config.config, "modules", NULL); - if (!cs) return CMD_FAIL; - - instance = module_find(cs, argv[0]); - if (!instance) { - cprintf_error(listener, "No such module \"%s\"\n", argv[0]); - return CMD_FAIL; - } - - if (!instance->force) { - cprintf(listener, "alive\n"); - } else { - cprintf(listener, "%s\n", fr_int2str(mod_rcode_table, instance->code, "")); - } - - - return CMD_OK; -} - - -/* - * Show all loaded modules - */ -static int command_show_modules(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[]) -{ - CONF_SECTION *cs, *subcs; - - cs = cf_section_find(main_config.config, "modules", NULL); - if (!cs) return CMD_FAIL; - - subcs = NULL; - while ((subcs = cf_section_next(cs, subcs)) != NULL) { - char const *name1 = cf_section_name1(subcs); - char const *name2 = cf_section_name2(subcs); - - module_instance_t *instance; - - if (name2) { - instance = module_find(cs, name2); - if (!instance) continue; - - cprintf(listener, "%s (%s)\n", name2, name1); - } else { - instance = module_find(cs, name1); - if (!instance) continue; - - cprintf(listener, "%s\n", name1); - } - } - - return CMD_OK; -} - -static int command_show_clients(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[]) -{ - int i; - RADCLIENT *client; - char buffer[256]; - char ipaddr[256]; - - for (i = 0; i < 256; i++) { - client = client_findbynumber(NULL, i); - if (!client) break; - - fr_inet_ntoh(&client->ipaddr, buffer, sizeof(buffer)); - - if (((client->ipaddr.af == AF_INET) && - (client->ipaddr.prefix != 32)) || - ((client->ipaddr.af == AF_INET6) && - (client->ipaddr.prefix != 128))) { - snprintf(ipaddr, sizeof(ipaddr), "%s/%d", buffer, client->ipaddr.prefix); - } else { - snprintf(ipaddr, sizeof(ipaddr), "%s", buffer); - } - - cprintf(listener, "%s\t%s\t%s\t%s\n", ipaddr, - client->shortname ? client->shortname : "\t", - client->nas_type ? client->nas_type : "\t", - client->server ? client->server : "\t"); - } - - return CMD_OK; -} - - -static int command_show_version(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[]) -{ - cprintf(listener, "%s\n", radiusd_version); - return CMD_OK; -} - -static int command_debug_level_global(rad_listen_t *listener, int argc, char *argv[]) -{ - int number; - - if (argc == 0) { - cprintf_error(listener, "Must specify \n"); - return -1; - } - - number = atoi(argv[0]); - if ((number < 0) || (number > 4)) { - cprintf_error(listener, " must be between 0 and 4\n"); - return -1; - } - - INFO("Global debug level set to %i, was %i", number, fr_debug_lvl); - fr_debug_lvl = rad_debug_lvl = number; - - return CMD_OK; -} - -static int command_debug_level_request(rad_listen_t *listener, int argc, char *argv[]) -{ - int number; - - if (argc == 0) { - cprintf_error(listener, "Must specify \n"); - return -1; - } - - number = atoi(argv[0]); - if ((number < 0) || (number > 4)) { - cprintf_error(listener, " must be between 0 and 4\n"); - return -1; - } - - INFO("Request debug level set to %i, was %i", number, req_debug_lvl); - req_debug_lvl = number; - - return CMD_OK; -} - -#ifndef NDEBUG -static void _command_talloc_report(const void *ptr, int depth, int max_depth, int is_ref, void *_f) -{ - const char *name = talloc_get_name(ptr); - rad_listen_t *listener = talloc_get_type_abort(_f, rad_listen_t); - - if (is_ref) { - cprintf(listener, "%*sreference to: %s\n", depth * 4, "", name); - return; - } - - if (depth == 0) { - cprintf(listener, - "%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", - (max_depth < 0 ? "full " :""), name, - (unsigned long)talloc_total_size(ptr), - (unsigned long)talloc_total_blocks(ptr)); - return; - } - - cprintf(listener, - "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", - depth * 4, "", - name, - (unsigned long)talloc_total_size(ptr), - (unsigned long)talloc_total_blocks(ptr), - (int)talloc_reference_count(ptr), ptr); -} - -static int command_show_memory_report(rad_listen_t *listener, int argc, UNUSED char *argv[]) -{ - if (argc != 0) { - cprintf_error(listener, "Command takes no arguments"); - } - - if (!main_config.talloc_memory_report) { - cprintf(listener, "Memory debugging not enabled. To enable, pass -Ms when starting the server\n"); - return CMD_OK; - } - - INFO("Writing talloc memory report to command socket"); - - talloc_report_depth_cb(talloc_null_ctx(), 0, -1, _command_talloc_report, listener); - - return CMD_OK; -} -#endif - -#if defined(HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN) -static int command_debug_socket(rad_listen_t *listener, int argc, char *argv[]) -{ - uint32_t notify; - - if (rad_debug_lvl && default_log.dst == L_DST_STDOUT) { - cprintf_error(listener, "Cannot redirect debug logs to a socket when already in debugging mode.\n"); - return -1; - } - - if ((argc == 0) || (strcmp(argv[0], "off") == 0)) { - notify = htonl(FR_NOTIFY_BUFFERED); - - /* - * Tell radmin to go into buffered mode. - */ - (void) fr_conduit_write(listener->fd, FR_CONDUIT_NOTIFY, ¬ify, sizeof(notify)); - - command_debug_off(); - return CMD_OK; - } - - if (strcmp(argv[0], "on") != 0) { - cprintf_error(listener, "Syntax error: got '%s', expected [on|off]", argv[0]); - return -1; - } - - /* - * Don't allow people to stomp on each other. - */ - if ((debug_log.cookie != NULL) && - (debug_log.cookie != listener)) { - cprintf_error(listener, "ERROR: Someone else is already using the debug socket"); - return -1; - } - - /* - * Disable logging while we're mucking with the buffer. - */ - command_debug_off(); - - debug_log.cookie = listener; - debug_log.cookie_write = command_socket_write; - debug_log.dst = L_DST_EXTRA; - - notify = htonl(FR_NOTIFY_UNBUFFERED); - - /* - * Tell radmin to go into unbuffered mode. - */ - (void) fr_conduit_write(listener->fd, FR_CONDUIT_NOTIFY, ¬ify, sizeof(notify)); - - return CMD_OK; -} -#endif - -static int command_debug_file(rad_listen_t *listener, int argc, char *argv[]) -{ - if (rad_debug_lvl && default_log.dst == L_DST_STDOUT) { - cprintf_error(listener, "Cannot redirect debug logs to a file when already in debugging mode.\n"); - return -1; - } - - if ((argc > 0) && (strchr(argv[0], FR_DIR_SEP) != NULL)) { - cprintf_error(listener, "Cannot direct debug logs to absolute path.\n"); - return -1; - } - - if (argc == 0) { - command_debug_off(); - return CMD_OK; - } - - /* - * Disable logging while we're mucking with the buffer. - */ - command_debug_off(); - - /* - * This looks weird, but it's here to avoid locking - * a mutex for every log message. - */ - memset(debug_log_file_buffer, 0, sizeof(debug_log_file_buffer)); - - /* - * Debug files always go to the logging directory. - */ - snprintf(debug_log_file_buffer, sizeof(debug_log_file_buffer), - "%s/%s", radlog_dir, argv[0]); - - debug_log.file = &debug_log_file_buffer[0]; - debug_log.dst = L_DST_FILES; - - INFO("Global debug log set to \"%s\"", debug_log.file); - - return CMD_OK; -} - -static int command_debug_condition(rad_listen_t *listener, int argc, char *argv[]) -{ - int i; - char const *error; - ssize_t slen = 0; - fr_cond_t *new_condition = NULL; - char *p, buffer[1024]; - - /* - * Disable it. - */ - if (argc == 0) { - TALLOC_FREE(debug_condition); - debug_condition = NULL; - return CMD_OK; - } - - if (!((argc == 1) && - ((argv[0][0] == '"') || (argv[0][0] == '\'')))) { - p = buffer; - *p = '\0'; - for (i = 0; i < argc; i++) { - size_t len; - - len = strlcpy(p, argv[i], buffer + sizeof(buffer) - p); - p += len; - *(p++) = ' '; - *p = '\0'; - } - - } else { - /* - * Backwards compatibility. De-escape the string. - */ - char quote; - char *q; - - p = argv[0]; - q = buffer; - - quote = *(p++); - - while (true) { - if (!*p) { - error = "Unexpected end of string"; - slen = -strlen(argv[0]); - p = argv[0]; - - goto parse_error; - } - - if (*p == quote) { - if (p[1]) { - error = "Unexpected text after end of string"; - slen = -(p - argv[0]); - p = argv[0]; - - goto parse_error; - } - *q = '\0'; - break; - } - - if (*p == '\\') { - *(q++) = p[1]; - p += 2; - continue; - } - - *(q++) = *(p++); - } - } - - p = buffer; - - slen = fr_cond_tokenize(NULL, NULL, p, &new_condition, &error, FR_COND_ONE_PASS); - if (slen <= 0) { - char *spaces, *text; - - parse_error: - fr_canonicalize_error(NULL, &spaces, &text, slen, p); - - ERROR("Parse error in condition"); - ERROR("%s", p); - ERROR("%s^ %s", spaces, error); - - cprintf_error(listener, "Parse error in condition \"%s\": %s\n", p, error); - - talloc_free(spaces); - talloc_free(text); - return CMD_FAIL; - } - - /* - * Delete old condition. - * - * This is thread-safe because the condition is evaluated - * in the main server thread, along with this code. - */ - TALLOC_FREE(debug_condition); - debug_condition = new_condition; - - return CMD_OK; -} - -#ifdef HAVE_GPERFTOOLS_PROFILER_H -static char profiler_log_buffer[1024]; -/** Start the gperftools profiler - * - */ -static int command_profiler_cpu_start(rad_listen_t *listener, int argc, char *argv[]) -{ - struct ProfilerState state; - - ProfilerGetCurrentState(&state); - - if (argc == 0) { - cprintf_error(listener, "Need filename for profiler to write to.\n"); - return -1; - } - - if ((argc > 0) && (strchr(argv[0], FR_DIR_SEP) != NULL)) { - cprintf_error(listener, "Profiler file must be a relative path.\n"); - return -1; - } - - /* - * We get an error if we don't stop the current - * profiler first. - */ - if (state.enabled) { - ProfilerFlush(); - ProfilerStop(); - } - - /* - * Profiler files always go to the logging directory. - */ - snprintf(profiler_log_buffer, sizeof(profiler_log_buffer), - "%s/%s", radlog_dir, argv[0]); - - errno = 0; - if (ProfilerStart(profiler_log_buffer) == 0) { - cprintf_error(listener, "Failed enabling profiler: %s\n", - errno ? fr_syserror(errno) : "unknown error"); - return -1; - } - - return CMD_OK; -} - -/** Stop the gperftools cpu profiler - * - */ -static int command_profiler_cpu_stop(UNUSED rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[]) -{ - ProfilerFlush(); - ProfilerStop(); - - return CMD_OK; -} - -/** Show gperftools cpu profiler output file - * - */ -static int command_profiler_cpu_show_file(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[]) -{ - struct ProfilerState state; - - ProfilerGetCurrentState(&state); - - if (!state.enabled) { - cprintf_error(listener, "Profiler not enabled.\n"); - return -1; - } - - cprintf(listener, "%s\n", state.profile_name); - - return CMD_OK; -} - -/** Show gperftools cpu profiler samples collected - * - */ -static int command_profiler_cpu_show_samples(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[]) -{ - struct ProfilerState state; - - ProfilerGetCurrentState(&state); - - if (!state.enabled) { - cprintf_error(listener, "Profiler not enabled.\n"); - return -1; - } - - cprintf(listener, "%i\n", state.samples_gathered); - - return CMD_OK; -} - -/** Show gperftools cpu profiler start_time - * - */ -static int command_profiler_cpu_show_start_time(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[]) -{ - char buffer[128]; - struct ProfilerState state; - - ProfilerGetCurrentState(&state); - - if (!state.enabled) { - cprintf_error(listener, "Profiler not enabled.\n"); - return -1; - } - - CTIME_R(&state.start_time, buffer, sizeof(buffer)); - cprintf(listener, "%s", buffer); - - return CMD_OK; -} - -/** Show gperftools cpu profiler status - * - */ -static int command_profiler_cpu_show_status(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[]) -{ - struct ProfilerState state; - - ProfilerGetCurrentState(&state); - - if (state.enabled) { - cprintf(listener, "running\n"); - } else { - cprintf(listener, "stopped\n"); - } - - return CMD_OK; -} -#endif - -static int command_show_debug_condition(rad_listen_t *listener, - UNUSED int argc, UNUSED char *argv[]) -{ - char buffer[1024]; - - if (!debug_condition) { - cprintf(listener, "\n"); - return CMD_OK; - } - - cond_snprint(buffer, sizeof(buffer), debug_condition); - - cprintf(listener, "%s\n", buffer); - return CMD_OK; -} - - -static int command_show_debug_file(rad_listen_t *listener, - UNUSED int argc, UNUSED char *argv[]) -{ - if (!debug_log.file) return CMD_FAIL; - - cprintf(listener, "%s\n", debug_log.file); - return CMD_OK; -} - - -static int command_show_debug_level_global(rad_listen_t *listener, - UNUSED int argc, UNUSED char *argv[]) -{ - cprintf(listener, "%d\n", rad_debug_lvl); - return CMD_OK; -} - -static int command_show_debug_level_request(rad_listen_t *listener, - UNUSED int argc, UNUSED char *argv[]) -{ - cprintf(listener, "%d\n", req_debug_lvl); - return CMD_OK; -} - -static RADCLIENT *get_client(rad_listen_t *listener, int argc, char *argv[]) -{ - RADCLIENT *client; - fr_ipaddr_t ipaddr; - int myarg; - int proto = IPPROTO_UDP; - RADCLIENT_LIST *list = NULL; - - if (argc < 1) { - cprintf_error(listener, "Must specify \n"); - return NULL; - } - - /* - * First arg is IP address. - */ - if (fr_inet_hton(&ipaddr, AF_UNSPEC, argv[0], false) < 0) { - cprintf_error(listener, "Failed parsing IP address; %s\n", - fr_strerror()); - return NULL; - } - myarg = 1; - - while (myarg < argc) { - if (strcmp(argv[myarg], "udp") == 0) { - proto = IPPROTO_UDP; - myarg++; - continue; - } - -#ifdef WITH_TCP - if (strcmp(argv[myarg], "tcp") == 0) { - proto = IPPROTO_TCP; - myarg++; - continue; - } -#endif - - if (strcmp(argv[myarg], "listen") == 0) { - uint16_t server_port; - fr_ipaddr_t server_ipaddr; - - if ((argc - myarg) < 2) { - cprintf_error(listener, "Must specify listen \n"); - return NULL; - } - - if (fr_inet_hton(&server_ipaddr, ipaddr.af, argv[myarg + 1], false) < 0) { - cprintf_error(listener, "Failed parsing IP address; %s\n", - fr_strerror()); - return NULL; - } - - server_port = atoi(argv[myarg + 2]); - - list = listener_find_client_list(&server_ipaddr, server_port, proto); - if (!list) { - cprintf_error(listener, "No such listener %s %s\n", argv[myarg + 1], argv[myarg + 2]); - return NULL; - } - myarg += 3; - continue; - } - - cprintf_error(listener, "Unknown argument %s.\n", argv[myarg]); - return NULL; - } - - client = client_find(list, &ipaddr, proto); - if (!client) { - cprintf_error(listener, "No such client\n"); - return NULL; - } - - return client; -} - -static int command_show_listener_enabled(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[]) -{ - if (main_config.drop_requests) { - cprintf(listener, "no\n"); - } else { - cprintf(listener, "yes\n"); - } - - return CMD_OK; -} - -/* - * For encode/decode stuff - */ -static int null_socket_dencode(UNUSED rad_listen_t *listener, UNUSED REQUEST *request) -{ - return 0; -} - -static int null_socket_send(UNUSED rad_listen_t *listener, REQUEST *request) -{ - fr_cursor_t cursor; - char *output_file; - FILE *fp; - - output_file = request_data_reference(request, (void *)null_socket_send, 0); - if (!output_file) { - ERROR("No output file for injected packet %" PRIu64 "", request->number); - return 0; - } - - fp = fopen(output_file, "w"); - if (!fp) { - ERROR("Failed to send injected file to %s: %s", output_file, fr_syserror(errno)); - return 0; - } - - if (request->reply->code != 0) { - char const *what = "reply"; - VALUE_PAIR *vp; - char buffer[1024]; - - if (request->reply->code < FR_MAX_PACKET_CODE) { - what = fr_packet_codes[request->reply->code]; - } - - fprintf(fp, "%s\n", what); - - if (rad_debug_lvl) { - RDEBUG("Injected %s packet to host %s port 0 code=%d, id=%d", what, - inet_ntop(request->reply->src_ipaddr.af, - &request->reply->src_ipaddr.addr, - buffer, sizeof(buffer)), - request->reply->code, request->reply->id); - } - - RINDENT(); - for (vp = fr_cursor_init(&cursor, &request->reply->vps); - vp; - vp = fr_cursor_next(&cursor)) { - fr_pair_snprint(buffer, sizeof(buffer), vp); - fprintf(fp, "%s\n", buffer); - RDEBUG("%s", buffer); - } - REXDENT(); - } - fclose(fp); - - return 0; -} - -static rad_listen_t *get_socket(rad_listen_t *listener, int argc, - char *argv[], int *last) -{ - rad_listen_t *sock; - uint16_t port; - int proto = IPPROTO_UDP; - fr_ipaddr_t ipaddr; - - if (argc < 2) { - cprintf_error(listener, "Must specify [udp|tcp]\n"); - return NULL; - } - - if (fr_inet_hton(&ipaddr, AF_UNSPEC, argv[0], false) < 0) { - cprintf_error(listener, "Failed parsing IP address; %s\n", - fr_strerror()); - return NULL; - } - - port = atoi(argv[1]); - - if (last) *last = 2; - if (argc > 2) { - if (strcmp(argv[2], "udp") == 0) { - proto = IPPROTO_UDP; - if (last) *last = 3; - } -#ifdef WITH_TCP - if (strcmp(argv[2], "tcp") == 0) { - proto = IPPROTO_TCP; - if (last) *last = 3; - } -#endif - } - - sock = listener_find_byipaddr(&ipaddr, port, proto); - if (!sock) { - cprintf_error(listener, "No such listen section\n"); - return NULL; - } - - return sock; -} - - -static int command_inject_to(rad_listen_t *listener, int argc, char *argv[]) -{ - fr_command_socket_t *sock = listener->data; - listen_socket_t *data; - rad_listen_t *found; - - found = get_socket(listener, argc, argv, NULL); - if (!found) { - return 0; - } - - data = found->data; - sock->inject_listener = found; - sock->dst_ipaddr = data->my_ipaddr; - sock->dst_port = data->my_port; - - return CMD_OK; -} - -static int command_inject_from(rad_listen_t *listener, int argc, char *argv[]) -{ - RADCLIENT *client; - fr_command_socket_t *sock = listener->data; - - if (argc < 1) { - cprintf_error(listener, "No was given\n"); - return 0; - } - - if (!sock->inject_listener) { - cprintf_error(listener, "You must specify \"inject to\" before using \"inject from\"\n"); - return 0; - } - - sock->src_ipaddr.af = AF_UNSPEC; - if (fr_inet_hton(&sock->src_ipaddr, AF_UNSPEC, argv[0], false) < 0) { - cprintf_error(listener, "Failed parsing IP address; %s\n", - fr_strerror()); - return 0; - } - - client = client_listener_find(sock->inject_listener, &sock->src_ipaddr, - 0); - if (!client) { - cprintf_error(listener, "No such client %s\n", argv[0]); - return 0; - } - sock->inject_client = client; - - return CMD_OK; -} - -static int command_inject_file(rad_listen_t *listener, int argc, char *argv[]) -{ - static int inject_id = 0; - int ret; - bool filedone; - fr_command_socket_t *sock = listener->data; - rad_listen_t *fake; - RADIUS_PACKET *packet; - vp_cursor_t cursor; - VALUE_PAIR *vp; - FILE *fp; - RAD_REQUEST_FUNP fun = NULL; - char buffer[2048]; - - if (argc < 2) { - cprintf_error(listener, "You must specify \n"); - return 0; - } - - if (!sock->inject_listener) { - cprintf_error(listener, "You must specify \"inject to\" before using \"inject file\"\n"); - return 0; - } - - if (!sock->inject_client) { - cprintf_error(listener, "You must specify \"inject from\" before using \"inject file\"\n"); - return 0; - } - - /* - * Output files always go to the logging directory. - */ - snprintf(buffer, sizeof(buffer), "%s/%s", radlog_dir, argv[1]); - - fp = fopen(argv[0], "r"); - if (!fp ) { - cprintf_error(listener, "Failed opening %s: %s\n", - argv[0], fr_syserror(errno)); - return 0; - } - - ret = fr_pair_list_afrom_file(NULL, &vp, fp, &filedone); - fclose(fp); - if (ret < 0) { - cprintf_error(listener, "Failed reading attributes from %s: %s\n", - argv[0], fr_strerror()); - return 0; - } - - fake = talloc(NULL, rad_listen_t); - memcpy(fake, sock->inject_listener, sizeof(*fake)); - - /* - * Re-write the IO for the listener. - */ - fake->encode = null_socket_dencode; - fake->decode = null_socket_dencode; - fake->send = null_socket_send; - - packet = fr_radius_alloc(NULL, false); - packet->src_ipaddr = sock->src_ipaddr; - packet->src_port = 0; - - packet->dst_ipaddr = sock->dst_ipaddr; - packet->dst_port = sock->dst_port; - packet->vps = vp; - packet->id = inject_id++; - - if (fake->type == RAD_LISTEN_AUTH) { - packet->code = FR_CODE_ACCESS_REQUEST; - fun = rad_authenticate; - - } else { -#ifdef WITH_ACCOUNTING - packet->code = FR_CODE_ACCOUNTING_REQUEST; - fun = rad_accounting; -#else - cprintf_error(listener, "This server was built without accounting support.\n"); - fr_radius_free(&packet); - talloc_free(fake); - return 0; -#endif - } - - if (rad_debug_lvl) { - DEBUG("Injecting %s packet from host %s port 0 code=%d, id=%d", - fr_packet_codes[packet->code], - inet_ntop(packet->src_ipaddr.af, - &packet->src_ipaddr.addr, - buffer, sizeof(buffer)), - packet->code, packet->id); - - for (vp = fr_cursor_init(&cursor, &packet->vps); - vp; - vp = fr_cursor_next(&cursor)) { - fr_pair_snprint(buffer, sizeof(buffer), vp); - DEBUG("\t%s", buffer); - } - - WARN("INJECTION IS LEAKING MEMORY!"); - } - - if (!request_receive(NULL, fake, packet, sock->inject_client, fun)) { - cprintf_error(listener, "Failed to inject request. See log file for details\n"); - fr_radius_free(&packet); - talloc_free(fake); - return 0; - } - -#if 0 - /* - * Remember what the output file is, and remember to - * delete the fake listener when done. - */ - request_data_add(request, null_socket_send, 0, talloc_typed_strdup(NULL, buffer), true, false, false); - request_data_add(request, null_socket_send, 1, fake, true, false, false); - -#endif - - return CMD_OK; -} - - -static fr_command_table_t command_table_inject[] = { - { "to", FR_WRITE, - "inject to - Inject packets to the destination IP and port.", - command_inject_to, NULL }, - - { "from", FR_WRITE, - "inject from - Inject packets as if they came from ", - command_inject_from, NULL }, - - { "file", FR_WRITE, - "inject file - Inject packet from , with results sent to ", - command_inject_file, NULL }, - - { NULL, 0, NULL, NULL, NULL } -}; - -static fr_command_table_t command_table_debug_level[] = { - { "global", FR_WRITE, - "debug level global - Set debug level for global server events and requests written to the main server log.", - command_debug_level_global, NULL }, - - { "request", FR_WRITE, - "debug level request - Set debug level for requests written to debug file or debug socket.", - command_debug_level_request, NULL }, - - { NULL, 0, NULL, NULL, NULL } -}; - -static fr_command_table_t command_table_debug[] = { - { "condition", FR_WRITE, - "debug condition [condition] - Enable debugging for requests matching [condition]", - command_debug_condition, NULL }, - - { "file", FR_WRITE, - "debug file [filename] - Send all request debug output to [filename]", - command_debug_file, NULL }, - -#if defined(HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN) - { "socket", FR_WRITE, - "debug socket [on|off] - Send all request debug output to radmin socket.", - command_debug_socket, NULL }, -#endif - - { "level", FR_READ, - "debug level - Set debug levels", - NULL, command_table_debug_level }, - - { NULL, 0, NULL, NULL, NULL } -}; - -#ifdef HAVE_GPERFTOOLS_PROFILER_H -/** Commands to control the gperftools profiler - * - */ -static fr_command_table_t command_table_profiler_cpu[] = { - { "start", FR_WRITE, - "profiler cpu start - Start gperftools cpu profiler, writing output to filename", - command_profiler_cpu_start, NULL }, - - { "stop", FR_WRITE, - "profiler cpu stop - Stop gperftools cpu profiler, and flush results to disk", - command_profiler_cpu_stop, NULL }, - - { NULL, 0, NULL, NULL, NULL } -}; - -static fr_command_table_t command_table_profiler[] = { - { "cpu", FR_WRITE, - "profiler cpu do sub-command of cpu profiler", - NULL, command_table_profiler_cpu }, - - { NULL, 0, NULL, NULL, NULL } -}; -#endif - -static fr_command_table_t command_table_show_debug_level[] = { - { "global", FR_WRITE, - "show debug level global - Show debug level for global server events and requests written to the main server log. Higher is more debugging.", - command_show_debug_level_global, NULL }, - - { "request", FR_WRITE, - "show debug level request - Show debug level for requests written to debug file or debug socket. Higher is more debugging", - command_show_debug_level_request, NULL }, - - { NULL, 0, NULL, NULL, NULL } -}; - - -static fr_command_table_t command_table_show_debug[] = { - { "condition", FR_READ, - "show debug condition - Shows current debugging condition.", - command_show_debug_condition, NULL }, - - { "file", FR_READ, - "show debug file - Shows current debugging file.", - command_show_debug_file, NULL }, - - { "level", FR_READ, - "show debug level - Shows current global or request debug level.", - NULL, command_table_show_debug_level }, - - { NULL, 0, NULL, NULL, NULL } -}; - -static fr_command_table_t command_table_show_module[] = { - { "config", FR_READ, - "show module config - show configuration for given module", - command_show_module_config, NULL }, - { "flags", FR_READ, - "show module flags - show other module properties", - command_show_module_flags, NULL }, - { "list", FR_READ, - "show module list - shows list of loaded modules", - command_show_modules, NULL }, - { "methods", FR_READ, - "show module methods - show sections where may be used", - command_show_module_methods, NULL }, - { "status", FR_READ, - "show module status - show the module status", - command_show_module_status, NULL }, - - { NULL, 0, NULL, NULL, NULL } -}; - -static fr_command_table_t command_table_show_client[] = { - { "list", FR_READ, - "show client list - shows list of global clients", - command_show_clients, NULL }, - - { NULL, 0, NULL, NULL, NULL } -}; - -static fr_command_table_t command_table_show_listener[] = { - { "enabled", FR_READ, - "show listener all enabled - shows whether the server is configured to accept packets", - command_show_listener_enabled, NULL}, - - { NULL, 0, NULL, NULL, NULL } -}; - -static fr_command_table_t command_table_show_listeners[] = { - { "all", FR_READ, - "show listener all - shows global listener state", - NULL, command_table_show_listener }, - - { NULL, 0, NULL, NULL, NULL } -}; - -#ifdef HAVE_GPERFTOOLS_PROFILER_H -static fr_command_table_t command_table_show_profiler_cpu[] = { - { "file", FR_WRITE, - "show profiler cpu file - show where profile data is being written", - command_profiler_cpu_show_file, NULL }, - - { "samples", FR_WRITE, - "show profiler cpu samples - show how many profiler samples have been collected", - command_profiler_cpu_show_samples, NULL }, - - { "start_time", FR_WRITE, - "show profiler cpu start_time - show when profiling last started", - command_profiler_cpu_show_start_time, NULL }, - - { "status", FR_WRITE, - "show profiler cpu status - show the current profiler state (running or stopped)", - command_profiler_cpu_show_status, NULL }, - - { NULL, 0, NULL, NULL, NULL } -}; - -static fr_command_table_t command_table_show_profiler[] = { - { "cpu", FR_WRITE, - "show profiler cpu do sub-command of cpu profiler", - NULL, command_table_show_profiler_cpu }, - - { NULL, 0, NULL, NULL, NULL } -}; -#endif - -static fr_command_table_t command_table_show[] = { - { "client", FR_READ, - "show client - do sub-command of client", - NULL, command_table_show_client }, - { "config", FR_READ, - "show config - shows the value of configuration option ", - command_show_config, NULL }, - { "debug", FR_READ, - "show debug - show debug properties", - NULL, command_table_show_debug }, - { "listener", FR_READ, - "show listener - do sub-command of listener", - NULL, command_table_show_listeners }, - -#ifndef NDEBUG - { "memory-report", FR_READ, - "show memory-report - show currently talloced memory", - command_show_memory_report, NULL }, -#endif - - { "module", FR_READ, - "show module - do sub-command of module", - NULL, command_table_show_module }, - -#ifdef HAVE_GPERFTOOLS_PROFILER_H - { "profiler", FR_READ, - "show profiler - do sub-command of profiler", - NULL, command_table_show_profiler }, -#endif - - { "uptime", FR_READ, - "show uptime - shows time at which server started", - command_uptime, NULL }, - { "version", FR_READ, - "show version - Prints version of the running server", - command_show_version, NULL }, - { NULL, 0, NULL, NULL, NULL } -}; - -static int command_set_module_config(rad_listen_t *listener, int argc, char *argv[]) -{ - int i, rcode; - CONF_PAIR *cp; - CONF_SECTION *cs; - module_instance_t *instance; - CONF_PARSER const *variables; - void *data; - - if (argc < 3) { - cprintf_error(listener, "No module name or variable was given\n"); - return 0; - } - - cs = cf_section_find(main_config.config, "modules", NULL); - if (!cs) return 0; - - instance = module_find(cs, argv[0]); - if (!instance) { - cprintf_error(listener, "No such module \"%s\"\n", argv[0]); - return 0; - } - - variables = cf_section_parse_table(instance->dl_inst->conf); - if (!variables) { - cprintf_error(listener, "Cannot find configuration for module\n"); - return 0; - } - - rcode = -1; - for (i = 0; variables[i].name != NULL; i++) { - /* - * FIXME: Recurse into sub-types somehow... - */ - if (FR_BASE_TYPE(variables[i].type) == FR_TYPE_SUBSECTION) continue; - - if (strcmp(variables[i].name, argv[1]) == 0) { - rcode = i; - break; - } - } - - if (rcode < 0) { - cprintf_error(listener, "No such variable \"%s\"\n", argv[1]); - return 0; - } - - i = rcode; /* just to be safe */ - - /* - * It's not part of the dynamic configuration. The module - * needs to re-parse && validate things. - */ - if (variables[i].data) { - cprintf_error(listener, "Variable cannot be dynamically updated\n"); - return 0; - } - - data = ((char *) instance->dl_inst->data) + variables[i].offset; - - cp = cf_pair_find(instance->dl_inst->conf, argv[1]); - if (!cp) return 0; - - /* - * Replace the OLD value in the configuration file with - * the NEW value. - * - * FIXME: Parse argv[2] depending on it's data type! - * If it's a string, look for leading single/double quotes, - * end then call tokenize functions??? - */ - cf_pair_replace(instance->dl_inst->conf, cp, argv[2]); - - rcode = cf_pair_parse(NULL, instance->dl_inst->conf, argv[1], variables[i].type, data, argv[2], T_DOUBLE_QUOTED_STRING); - if (rcode < 0) { - cprintf_error(listener, "Failed to parse value\n"); - return 0; - } - - return CMD_OK; -} - -static int command_set_module_status(rad_listen_t *listener, int argc, char *argv[]) -{ - CONF_SECTION *cs; - module_instance_t *instance; - - if (argc < 2) { - cprintf_error(listener, "No module name or status was given\n"); - return 0; - } - - cs = cf_section_find(main_config.config, "modules", NULL); - if (!cs) return 0; - - instance = module_find(cs, argv[0]); - if (!instance) { - cprintf_error(listener, "No such module \"%s\"\n", argv[0]); - return 0; - } - - - if (strcmp(argv[1], "alive") == 0) { - instance->force = false; - - } else if (strcmp(argv[1], "dead") == 0) { - instance->code = RLM_MODULE_FAIL; - instance->force = true; - - } else { - int rcode; - - rcode = fr_str2int(mod_rcode_table, argv[1], -1); - if (rcode < 0) { - cprintf_error(listener, "Unknown status \"%s\"\n", argv[1]); - return 0; - } - - instance->code = rcode; - instance->force = true; - } - - return CMD_OK; -} - -static int command_set_listener_enabled(rad_listen_t *listener, int argc, char *argv[]) -{ - if (argc < 1) { - cprintf_error(listener, "No value was provided\n"); - return 0; - } - - if ((strcmp(argv[0], "yes") == 0) || (strcmp(argv[0], "true") == 0)) { - INFO("Server has resumed accepting requests"); - main_config.drop_requests = false; - } else if ((strcmp(argv[0], "no") == 0) || (strcmp(argv[0], "false") == 0)) { - INFO("Server is no longer accepting new requests"); - main_config.drop_requests = true; - } - - return CMD_OK; -} - -#ifdef WITH_STATS -static char const *elapsed_names[8] = { - "1us", "10us", "100us", "1ms", "10ms", "100ms", "1s", "10s" -}; - -#undef PU -#ifdef WITH_STATS_64BIT -#ifdef PRIu64 -#define PU "%" PRIu64 -#else -#define PU "%lu" -#endif -#else -#ifdef PRIu32 -#define PU "%" PRIu32 -#else -#define PU "%u" -#endif -#endif - -static int command_print_stats(rad_listen_t *listener, fr_stats_t *stats, - int auth, int server) -{ - int i; - - cprintf(listener, "requests\t" PU "\n", stats->total_requests); - cprintf(listener, "responses\t" PU "\n", stats->total_responses); - - if (auth) { - cprintf(listener, "accepts\t\t" PU "\n", - stats->total_access_accepts); - cprintf(listener, "rejects\t\t" PU "\n", - stats->total_access_rejects); - cprintf(listener, "challenges\t" PU "\n", - stats->total_access_challenges); - } - - cprintf(listener, "dup\t\t" PU "\n", stats->total_dup_requests); - cprintf(listener, "invalid\t\t" PU "\n", stats->total_invalid_requests); - cprintf(listener, "malformed\t" PU "\n", stats->total_malformed_requests); - cprintf(listener, "bad_authenticator\t" PU "\n", stats->total_bad_authenticators); - cprintf(listener, "dropped\t\t" PU "\n", stats->total_packets_dropped); - cprintf(listener, "unknown_types\t" PU "\n", stats->total_unknown_types); - - if (server) { - cprintf(listener, "timeouts\t" PU "\n", stats->total_timeouts); - } - - cprintf(listener, "last_packet\t%" PRId64 "\n", (int64_t) stats->last_packet); - for (i = 0; i < 8; i++) { - cprintf(listener, "elapsed.%s\t%u\n", - elapsed_names[i], stats->elapsed[i]); - } - - return CMD_OK; -} - -static int command_stats_state(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[]) -{ - cprintf(listener, "states_created\t\t%" PRIu64 "\n", fr_state_entries_created(global_state)); - cprintf(listener, "states_timeout\t\t%" PRIu64 "\n", fr_state_entries_timeout(global_state)); - cprintf(listener, "states_tracked\t\t%" PRIu32 "\n", fr_state_entries_tracked(global_state)); - - return CMD_OK; -} - -#ifndef NDEBUG -static int command_stats_memory(rad_listen_t *listener, int argc, char *argv[]) -{ - if (!main_config.talloc_memory_report) { - cprintf(listener, "Memory debugging not enabled. To enable, pass -Ms when starting the server\n"); - return CMD_OK; - } - - if (argc == 0) goto fail; - - if (strcmp(argv[0], "total") == 0) { - cprintf(listener, "%zd\n", talloc_total_size(NULL)); - return CMD_OK; - } - - if (strcmp(argv[0], "blocks") == 0) { - cprintf(listener, "%zd\n", talloc_total_blocks(NULL)); - return CMD_OK; - } - -fail: - cprintf_error(listener, "Must use 'stats memory [blocks|total]'\n"); - return CMD_FAIL; -} -#endif - -#ifdef WITH_DETAIL -static FR_NAME_NUMBER state_names[] = { - { "unopened", STATE_UNOPENED }, - { "unlocked", STATE_UNLOCKED }, - { "processing", STATE_PROCESSING }, - - { "header", STATE_HEADER }, - { "vps", STATE_VPS }, - { "queued", STATE_QUEUED }, - { "running", STATE_RUNNING }, - { "no-reply", STATE_NO_REPLY }, - { "replied", STATE_REPLIED }, - - { NULL, 0 } -}; - -static int command_stats_detail(rad_listen_t *listener, int argc, char *argv[]) -{ - rad_listen_t *this; - listen_detail_t *data, *needle; - struct stat buf; - - if (argc == 0) { - cprintf_error(listener, "Must specify \n"); - return 0; - } - - data = NULL; - for (this = main_config.listen; this != NULL; this = this->next) { - if (this->type != RAD_LISTEN_DETAIL) continue; - - needle = this->data; - if (!strcmp(argv[0], needle->filename)) { - data = needle; - break; - } - } - - if (!data) { - cprintf_error(listener, "No detail file listener\n"); - return 0; - } - - cprintf(listener, "state\t%s\n", - fr_int2str(state_names, data->file_state, "?")); - - if ((data->file_state == STATE_UNOPENED) || - (data->file_state == STATE_UNLOCKED)) { - return CMD_OK; - } - - /* - * Race conditions: file might not exist. - */ - if (stat(data->filename_work, &buf) < 0) { - cprintf(listener, "packets\t0\n"); - cprintf(listener, "tries\t0\n"); - cprintf(listener, "offset\t0\n"); - cprintf(listener, "size\t0\n"); - return CMD_OK; - } - - cprintf(listener, "packets\t%d\n", data->packets); - cprintf(listener, "tries\t%d\n", data->tries); - cprintf(listener, "offset\t%u\n", (unsigned int) data->offset); - cprintf(listener, "size\t%u\n", (unsigned int) buf.st_size); - - return CMD_OK; -} -#endif - -static int command_stats_client(rad_listen_t *listener, int argc, char *argv[]) -{ - bool auth = true; - fr_stats_t *stats; - RADCLIENT *client, fake; - - if (argc < 1) { - cprintf_error(listener, "Must specify [auth/acct]\n"); - return 0; - } - - if (argc == 1) { - /* - * Global statistics. - */ - fake.auth = radius_auth_stats; -#ifdef WITH_ACCOUNTING - fake.acct = radius_acct_stats; -#endif -#ifdef WITH_COA - fake.coa = radius_coa_stats; - fake.dsc = radius_dsc_stats; -#endif - client = &fake; - - } else { - /* - * Per-client statistics. - */ - client = get_client(listener, argc - 1, argv + 1); - if (!client) return 0; - } - - if (strcmp(argv[0], "auth") == 0) { - auth = true; - stats = &client->auth; - - } else if (strcmp(argv[0], "acct") == 0) { -#ifdef WITH_ACCOUNTING - auth = false; - stats = &client->acct; -#else - cprintf_error(listener, "This server was built without accounting support.\n"); - return 0; -#endif - - } else if (strcmp(argv[0], "coa") == 0) { -#ifdef WITH_COA - auth = false; - stats = &client->coa; -#else - cprintf_error(listener, "This server was built without CoA support.\n"); - return 0; -#endif - - } else if (strcmp(argv[0], "disconnect") == 0) { -#ifdef WITH_COA - auth = false; - stats = &client->dsc; -#else - cprintf_error(listener, "This server was built without CoA support.\n"); - return 0; -#endif - - } else { - cprintf_error(listener, "Unknown statistics type\n"); - return 0; - } - - /* - * Global results for all client. - */ - if (argc == 1) { -#ifdef WITH_ACCOUNTING - if (!auth) { - return command_print_stats(listener, - &radius_acct_stats, auth, 0); - } -#endif - return command_print_stats(listener, &radius_auth_stats, auth, 0); - } - - return command_print_stats(listener, stats, auth, 0); -} - - -static int command_stats_socket(rad_listen_t *listener, int argc, char *argv[]) -{ - bool auth = true; - rad_listen_t *sock; - - sock = get_socket(listener, argc, argv, NULL); - if (!sock) return 0; - - if (sock->type != RAD_LISTEN_AUTH) auth = false; - - return command_print_stats(listener, &sock->stats, auth, 0); -} -#endif /* WITH_STATS */ - - -#ifdef WITH_DYNAMIC_CLIENTS -static int command_add_client_file(rad_listen_t *listener, int argc, char *argv[]) -{ - RADCLIENT *c; - - if (argc < 1) { - cprintf_error(listener, " is required\n"); - return 0; - } - - /* - * Read the file and generate the client. - */ - c = client_read(argv[0], NULL, false); - if (!c) { - cprintf_error(listener, "Unknown error reading client file.\n"); - return 0; - } - - if (!client_add(NULL, c)) { - cprintf_error(listener, "Unknown error inserting new client.\n"); - client_free(c); - return 0; - } - - return CMD_OK; -} - - -static int command_del_client(rad_listen_t *listener, int argc, char *argv[]) -{ - RADCLIENT *client; - - client = get_client(listener, argc, argv); - if (!client) return 0; - - if (!client->dynamic) { - cprintf_error(listener, "Client %s was not dynamically defined.\n", argv[0]); - return 0; - } - - /* - * DON'T delete it. Instead, mark it as "dead now". The - * next time we receive a packet for the client, it will - * be deleted. - * - * If we don't receive a packet from it, the client - * structure will stick around for a while. Oh well... - */ - client->lifetime = 1; - - return CMD_OK; -} - - -static fr_command_table_t command_table_del_client[] = { - { "ipaddr", FR_WRITE, - "del client ipaddr [udp|tcp] [listen ] - Delete a dynamically created client", - command_del_client, NULL }, - - { NULL, 0, NULL, NULL, NULL } -}; - - -static fr_command_table_t command_table_del[] = { - { "client", FR_WRITE, - "del client - Delete client configuration commands", - NULL, command_table_del_client }, - - { NULL, 0, NULL, NULL, NULL } -}; - - -static fr_command_table_t command_table_add_client[] = { - { "file", FR_WRITE, - "add client file - Add new client definition from ", - command_add_client_file, NULL }, - - { NULL, 0, NULL, NULL, NULL } -}; - - -static fr_command_table_t command_table_add[] = { - { "client", FR_WRITE, - "add client - Add client configuration commands", - NULL, command_table_add_client }, - - { NULL, 0, NULL, NULL, NULL } -}; -#endif - -static fr_command_table_t command_table_set_module[] = { - { "config", FR_WRITE, - "set module config variable value - set configuration for ", - command_set_module_config, NULL }, - - { "status", FR_WRITE, - "set module status [alive|...] - set the module status to be alive (operating normally), or force a particular code (ok,fail, etc.)", - command_set_module_status, NULL }, - - { NULL, 0, NULL, NULL, NULL } -}; - -static fr_command_table_t command_table_set_listener[] = { - { "enabled", FR_WRITE, - "set listener all enabled - enable or disable all listeners", - command_set_listener_enabled, NULL }, - - { NULL, 0, NULL, NULL, NULL } -}; - -static fr_command_table_t command_table_set_listeners[] = { - { "all", FR_WRITE, - "set listener all - change state of all listeners", - NULL, command_table_set_listener }, - - { NULL, 0, NULL, NULL, NULL } -}; - -static fr_command_table_t command_table_set[] = { - { "module", FR_WRITE, - "set module - set module commands", - NULL, command_table_set_module }, - { "listener", FR_WRITE, - "set listener - set listener commands", - NULL, command_table_set_listeners }, - - { NULL, 0, NULL, NULL, NULL } -}; - - -#ifdef WITH_STATS -static fr_command_table_t command_table_stats[] = { - { "client", FR_READ, - "stats client [auth/acct] [udp|tcp] [listen ] " - "- show statistics for given client, or for all clients (auth or acct)", - command_stats_client, NULL }, - -#ifdef WITH_DETAIL - { "detail", FR_READ, - "stats detail - show statistics for the given detail file", - command_stats_detail, NULL }, -#endif - - { "state", FR_READ, - "stats state - show statistics for states", - command_stats_state, NULL }, - - { "socket", FR_READ, - "stats socket [udp|tcp] " - "- show statistics for given socket", - command_stats_socket, NULL }, - -#ifndef NDEBUG - { "memory", FR_READ, - "stats memory [blocks|total] - show statistics on used memory", - command_stats_memory, NULL }, -#endif - - { NULL, 0, NULL, NULL, NULL } -}; -#endif - -static fr_command_table_t command_table[] = { -#ifdef WITH_DYNAMIC_CLIENTS - { "add", FR_WRITE, NULL, NULL, command_table_add }, -#endif - { "debug", FR_WRITE, - "debug - debugging commands", - NULL, command_table_debug }, -#ifdef WITH_DYNAMIC_CLIENTS - { "del", FR_WRITE, NULL, NULL, command_table_del }, -#endif - { "hup", FR_WRITE, - "hup [module] - sends a HUP signal to the server, or optionally to one module", - command_hup, NULL }, - { "inject", FR_WRITE, - "inject - commands to inject packets into a running server", - NULL, command_table_inject }, - -#ifdef HAVE_GPERFTOOLS_PROFILER_H - { "profiler", FR_WRITE, - "profiler - commands to alter the state of the gperftools profiler", - NULL, command_table_profiler }, -#endif - { "reconnect", FR_READ, - "reconnect - reconnect to a running server", - NULL, NULL }, /* just here for "help" */ - { "terminate", FR_WRITE, - "terminate - terminates the server, and cause it to exit", - command_terminate, NULL }, - { "set", FR_WRITE, NULL, NULL, command_table_set }, - { "show", FR_READ, NULL, NULL, command_table_show }, -#ifdef WITH_STATS - { "stats", FR_READ, NULL, NULL, command_table_stats }, -#endif - - { NULL, 0, NULL, NULL, NULL } -}; - - -static int _command_socket_free(fr_command_socket_t *cmd) -{ - /* - * If it's a TCP socket, don't do anything. - */ - if (cmd->magic != COMMAND_SOCKET_MAGIC) return 0; - - if (!cmd->copy) return 0; - unlink(cmd->copy); - - return 0; -} - - -/* - * Parse the unix domain sockets. - * - * FIXME: TCP + SSL, after RadSec is in. - */ -static int command_socket_parse_unix(CONF_SECTION *cs, rad_listen_t *this) -{ - fr_command_socket_t *sock; - - sock = this->data; - talloc_set_destructor(sock, _command_socket_free); - - if (cf_section_rules_push(cs, command_config) < 0) return -1; - if (cf_section_parse(sock, sock, cs) < 0) return -1; - - /* - * Can't get uid or gid of connecting user, so can't do - * peercred authentication. - */ -#ifndef HAVE_GETPEEREID - if (sock->peercred && (sock->uid_name || sock->gid_name)) { - ERROR("System does not support uid or gid authentication for sockets"); - return -1; - } -#endif - - sock->magic = COMMAND_SOCKET_MAGIC; - sock->copy = NULL; - if (sock->path) sock->copy = talloc_typed_strdup(sock, sock->path); - - if (sock->gid_name) { - if (rad_getgid(cs, &sock->gid, sock->gid_name) < 0) { - ERROR("Failed resolving gid of group %s: %s", sock->gid_name, fr_strerror()); - return -1; - } - } else { - sock->gid = -1; - } - - if (!sock->mode_name) { - sock->co.mode = FR_READ; - } else { - sock->co.mode = fr_str2int(mode_names, sock->mode_name, 0); - if (!sock->co.mode) { - ERROR("Invalid mode name \"%s\"", sock->mode_name); - return -1; - } - } - - return 0; -} - -static int command_socket_open_unix(UNUSED CONF_SECTION *cs, rad_listen_t *this) -{ - fr_command_socket_t *sock; - - sock = this->data; - - this->fd = fr_server_domain_socket(sock->path, sock->gid, sock->blocking); - if (this->fd < 0) { - ERROR("%s", fr_strerror()); - if (sock->copy) TALLOC_FREE(sock->copy); - return -1; - } - - return 0; -} - -static int command_socket_parse(CONF_SECTION *cs, rad_listen_t *this) -{ - int rcode; - CONF_PAIR const *cp; - listen_socket_t *sock; - - cp = cf_pair_find(cs, "socket"); - if (cp) return command_socket_parse_unix(cs, this); - - rcode = common_socket_parse(cs, this); - if (rcode < 0) return -1; - -#ifdef WITH_TLS - if (this->tls) { - cf_log_err(cs, - "TLS is not supported for control sockets"); - return -1; - } -#endif - - sock = this->data; - if (sock->proto != IPPROTO_TCP) { - cf_log_err(cs, - "UDP is not supported for control sockets"); - return -1; - } - - return 0; -} - -static int command_socket_open(CONF_SECTION *cs, rad_listen_t *this) -{ - CONF_PAIR const *cp; - - cp = cf_pair_find(cs, "socket"); - if (cp) return command_socket_open_unix(cs, this); - - return common_socket_open(cs, this); -} - -static int command_socket_print(rad_listen_t const *this, char *buffer, size_t bufsize) -{ - fr_command_socket_t *sock = this->data; - - if (sock->magic != COMMAND_SOCKET_MAGIC) { - return common_socket_print(this, buffer, bufsize); - } - - snprintf(buffer, bufsize, "command file %s", sock->path); - return 1; -} - - -/* - * String split routine. Splits an input string IN PLACE - * into pieces, based on spaces. - */ -static int dict_str_to_argvX(char *str, char **argv, int max_argc) -{ - int argc = 0; - - while (*str) { - if (argc >= max_argc) return argc; - - /* - * Chop out comments early. - */ - if (*str == '#') { - *str = '\0'; - break; - } - - while ((*str == ' ') || - (*str == '\t') || - (*str == '\r') || - (*str == '\n')) *(str++) = '\0'; - - if (!*str) return argc; - - argv[argc++] = str; - - if ((*str == '\'') || (*str == '"')) { - char quote = *str; - char *p = str + 1; - - while (true) { - if (!*p) return -1; - - if (*p == quote) { - str = p + 1; - break; - } - - /* - * Handle \" and nothing else. - */ - if (*p == '\\') { - p += 2; - continue; - } - - p++; - } - } - - while (*str && - (*str != ' ') && - (*str != '\t') && - (*str != '\r') && - (*str != '\n')) str++; - } - - return argc; -} - -static void print_help(rad_listen_t *listener, int argc, char *argv[], - fr_command_table_t *table, int recursive) -{ - int i; - - /* this should never happen, but if it does then just return gracefully */ - if (!table) return; - - for (i = 0; table[i].command != NULL; i++) { - if (argc > 0) { - if (strcmp(table[i].command, argv[0]) == 0) { - if (table[i].table) { - print_help(listener, argc - 1, argv + 1, table[i].table, recursive); - } else { - if (table[i].help) { - cprintf(listener, "%s\n", table[i].help); - } - } - return; - } - - continue; - } - - if (table[i].help) { - cprintf(listener, "%s\n", - table[i].help); - } else { - cprintf(listener, "%s - do sub-command of %s\n", - table[i].command, table[i].command); - } - - if (recursive && table[i].table) { - print_help(listener, 0, NULL, table[i].table, recursive); - } - } -} - -#define MAX_ARGV (16) - -/* - * Check if an incoming request is "ok" - * - * It takes packets, not requests. It sees if the packet looks - * OK. If so, it does a number of sanity checks on it. - */ -static int command_domain_recv_co(rad_listen_t *listener, fr_cs_buffer_t *co) -{ - int i; - uint32_t status; - ssize_t r, len; - int argc; - fr_conduit_type_t conduit; - char *my_argv[MAX_ARGV], **argv; - fr_command_table_t *table; - uint8_t *command; - - r = fr_conduit_drain(listener->fd, &conduit, co->buffer, sizeof(co->buffer) - 1, &command, &co->offset); - if ((r < 0) && ((errno == EINTR) || (errno == EAGAIN))) return 0; - - if (r <= 0) { - do_close: - command_close_socket(listener); - return 0; - } - - /* - * We need more data. Go read it. - */ - if (conduit == FR_CONDUIT_WANT_MORE) { - return 0; - } - - status = 0; - command[r] = '\0'; - DEBUG("radmin> %s", command); - - argc = dict_str_to_argvX((char *) command, my_argv, MAX_ARGV); - if (argc == 0) goto do_next; /* empty strings are OK */ - - if (argc < 0) { - cprintf_error(listener, "Failed parsing command '%s'.\n", - command); - goto do_next; - } - - argv = my_argv; - - for (len = 0; len <= co->offset; len++) { - if (command[len] < 0x20) { - command[len] = '\0'; - break; - } - } - - /* - * Hard-code exit && quit. - */ - if ((strcmp(argv[0], "exit") == 0) || - (strcmp(argv[0], "quit") == 0)) goto do_close; - - table = command_table; - retry: - len = 0; - for (i = 0; table[i].command != NULL; i++) { - if (strcmp(table[i].command, argv[0]) == 0) { - /* - * Check permissions. - */ - if (((co->mode & FR_WRITE) == 0) && - ((table[i].mode & FR_WRITE) != 0)) { - cprintf_error(listener, "You do not have write permission. See \"mode = rw\" in the \"listen\" section for this socket.\n"); - goto do_next; - } - - if (table[i].table) { - /* - * This is the last argument, but - * there's a sub-table. Print help. - * - */ - if (argc == 1) { - table = table[i].table; - goto do_help; - } - - argc--; - argv++; - table = table[i].table; - goto retry; - } - - if ((argc == 2) && (strcmp(argv[1], "?") == 0)) goto do_help; - - if (!table[i].func) { - cprintf_error(listener, "Invalid command\n"); - goto do_next; - } - - status = table[i].func(listener, argc - 1, argv + 1); - goto do_next; - } - } - - /* - * No such command - */ - if (!len) { - if ((strcmp(argv[0], "help") == 0) || - (strcmp(argv[0], "?") == 0)) { - int recursive; - - do_help: - if ((argc > 1) && (strcmp(argv[1], "-r") == 0)) { - recursive = true; - argc--; - argv++; - } else { - recursive = false; - } - - print_help(listener, argc - 1, argv + 1, table, recursive); - goto do_next; - } - - cprintf_error(listener, "Unknown command \"%s\"\n", - argv[0]); - } - - do_next: - /* - * Reset offset now that command has been fully read - */ - co->offset = 0; - - r = fr_conduit_write(listener->fd, FR_CONDUIT_CMD_STATUS, &status, sizeof(status)); - if (r <= 0) goto do_close; - - return 0; -} - - -static int command_tcp_recv(rad_listen_t *this) -{ - ssize_t r; - listen_socket_t *sock = this->data; - fr_cs_buffer_t *co = (void *) sock->packet; - fr_conduit_type_t conduit; - - if (!co) { - do_close: - command_close_socket(this); - return 0; - } - - if (!co->auth) { - uint8_t *data; - uint8_t expected[16]; - - r = fr_conduit_drain(this->fd, &conduit, co->buffer, sizeof(co->buffer) - 1, &data, &co->offset); - if ((r < 0) && ((errno == EINTR) || (errno == EAGAIN))) return 0; - - if (r <= 0) goto do_close; - - /* - * We need more data. Go read it. - */ - if (conduit == FR_CONDUIT_WANT_MORE) { - return 0; - } - - if ((r != sizeof(expected)) || (conduit != FR_CONDUIT_AUTH_RESPONSE)) goto do_close; - - fr_hmac_md5(expected, (void const *) sock->client->secret, - strlen(sock->client->secret), - data, sizeof(expected)); - - if (fr_digest_cmp(expected, data + sizeof(expected), sizeof(expected)) != 0) { - ERROR("radmin failed challenge: Closing socket"); - goto do_close; - } - - co->auth = true; - co->offset = 0; - } - - return command_domain_recv_co(this, co); -} - -/* - * Should never be called. The functions should just call write(). - */ -static int command_tcp_send(UNUSED rad_listen_t *listener, UNUSED REQUEST *request) -{ - return 0; -} - -static int command_domain_recv(rad_listen_t *listener) -{ - fr_command_socket_t *sock = listener->data; - - return command_domain_recv_co(listener, &sock->co); -} - -/* - * Write 32-bit magic number && version information. - */ -static int command_magic_recv(rad_listen_t *this, fr_cs_buffer_t *co, bool challenge) -{ - int i; - ssize_t r; - uint32_t magic; - fr_conduit_type_t conduit; - uint8_t *data; - - /* - * Start off by reading 4 bytes of magic, followed by 4 bytes of zero. - */ - r = fr_conduit_drain(this->fd, &conduit, co->buffer, sizeof(co->buffer) - 1, &data, &co->offset); - if ((r < 0) && ((errno == EINTR) || (errno == EAGAIN))) return 0; - - if (r <= 0) { - ERROR("Failed reading magic: %s", fr_syserror(errno)); - - do_close: - command_close_socket(this); - return 0; - } - - /* - * We need more data. Go read it. - */ - if (conduit == FR_CONDUIT_WANT_MORE) { - return 0; - } - - if ((r != 8) || (conduit != FR_CONDUIT_INIT_ACK)) goto do_close; - - magic = htonl(0xf7eead16); - if (memcmp(&magic, data, sizeof(magic)) != 0) { - ERROR("Incompatible versions"); - goto do_close; - } - - /* - * Ack the magic + 4 bytes of zero back. - */ - r = fr_conduit_write(this->fd, FR_CONDUIT_INIT_ACK, data, 8); - if (r <= 0) { - ERROR("Failed writing magic: %s", fr_syserror(errno)); - goto do_close; - } - - if (challenge) { - for (i = 0; i < 16; i++) { - co->buffer[i] = fr_rand(); - } - - r = fr_conduit_write(this->fd, FR_CONDUIT_AUTH_CHALLENGE, co->buffer, 16); - if (r <= 0) { - ERROR("Failed writing auth challenge: %s", fr_syserror(errno)); - goto do_close; - } - - } - - /* - * Reset offset now that version has been fully read - */ - co->offset = 0; - - return 1; -} - -static int command_init_recv(rad_listen_t *this) -{ - int rcode; - fr_command_socket_t *sock = this->data; - - if (sock->magic == COMMAND_SOCKET_MAGIC) { - rcode = command_magic_recv(this, &sock->co, false); - if (rcode <= 0) return rcode; - - this->recv = command_domain_recv; - } else { - listen_socket_t *sock2 = this->data; - - rcode = command_magic_recv(this, (fr_cs_buffer_t *) sock2->packet, true); - if (rcode <= 0) return rcode; - - this->recv = command_tcp_recv; - } - - return 0; -} - - -static int command_domain_accept(rad_listen_t *listener) -{ - int newfd; - rad_listen_t *this; - socklen_t salen; - struct sockaddr_storage src; - fr_command_socket_t *sock = listener->data; - - salen = sizeof(src); - - DEBUG2(" ... new connection request on command socket"); - - newfd = accept(listener->fd, (struct sockaddr *) &src, &salen); - if (newfd < 0) { - /* - * Non-blocking sockets must handle this. - */ - if (errno == EWOULDBLOCK) { - return 0; - } - - DEBUG2(" ... failed to accept connection"); - return 0; - } - - /* - * Is likely redundant as newfd should inherit blocking - * from listener->fd. But better to be safe. - */ - if (!sock->blocking) fr_nonblock(newfd); - -#ifdef HAVE_GETPEEREID - /* - * Perform user authentication. - */ - if (sock->peercred) { - uid_t uid; - gid_t gid; - - if (getpeereid(newfd, &uid, &gid) < 0) { - ERROR("Failed getting peer credentials for %s: %s", - sock->path, fr_syserror(errno)); - close(newfd); - return 0; - } - - /* - * Only do UID checking if the caller is - * non-root. The superuser can do anything, so - * we might as well let them. - */ - if ((uid != 0) && (uid != geteuid()) && (sock->gid_name && (sock->gid != gid))) { - ERROR("Unauthorized connection to %s from uid %ld, gid %ld", - sock->path, (long int) uid, (long int) gid); - close(newfd); - return 0; - } - } -#endif - - /* - * Add the new listener. - */ - this = listen_alloc(listener, listener->type, listener->proto); - if (!this) return 0; - - /* - * Copy everything, including the pointer to the socket - * information. - */ - sock = this->data; - memcpy(this, listener, sizeof(*this)); - this->status = RAD_LISTEN_STATUS_INIT; - this->next = NULL; - this->data = sock; /* fix it back */ - - sock->magic = COMMAND_SOCKET_MAGIC; - sock->user[0] = '\0'; - sock->path = ((fr_command_socket_t *) listener->data)->path; - sock->co.offset = 0; - sock->co.mode = ((fr_command_socket_t *) listener->data)->co.mode; - - this->fd = newfd; - - /* - * Start off by sending the magic handshake. - */ - this->recv = command_init_recv; - - /* - * Tell the event loop that we have a new FD - */ - radius_update_listener(this); - - return 0; -} - - -/* - * Send an authentication response packet - */ -static int command_domain_send(UNUSED rad_listen_t *listener, - UNUSED REQUEST *request) -{ - return 0; -} - - -static int command_socket_encode(UNUSED rad_listen_t *listener, - UNUSED REQUEST *request) -{ - return 0; -} - - -static int command_socket_decode(UNUSED rad_listen_t *listener, - UNUSED REQUEST *request) -{ - return 0; -} - -#endif /* WITH_COMMAND_SOCKET */ diff --git a/src/main/tls_listen.c b/src/main/tls_listen.c deleted file mode 100644 index 0fa6a8173ce..00000000000 --- a/src/main/tls_listen.c +++ /dev/null @@ -1,680 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * $Id$ - * - * @file tls_listen.c - * @brief TLS server/client socket functions. - * - * @copyright 2001 hereUare Communications, Inc. - * @copyright 2003 Alan DeKok - * @copyright 2006-2016 The FreeRADIUS server project - */ -RCSID("$Id$") -USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ - -#ifdef WITH_TLS -#define LOG_PREFIX "tls - " - -#include -#include -#include - -static void tls_socket_close(rad_listen_t *listener) -{ - listen_socket_t *sock = listener->data; - - SSL_shutdown(sock->tls_session->ssl); - - listener->status = RAD_LISTEN_STATUS_EOL; - listener->tls = NULL; /* parent owns this! */ - - /* - * Tell the event handler that an FD has disappeared. - */ - DEBUG("Client has closed connection"); - radius_update_listener(listener); - - /* - * Do NOT free the listener here. It's in use by - * a request, and will need to hang around until - * all of the requests are done. - * - * It is instead free'd in remove_from_request_hash() - */ -} - -static int CC_HINT(nonnull) tls_socket_write(rad_listen_t *listener, REQUEST *request) -{ - uint8_t *p; - ssize_t rcode; - listen_socket_t *sock = listener->data; - - p = sock->tls_session->dirty_out.data; - - while (p < (sock->tls_session->dirty_out.data + sock->tls_session->dirty_out.used)) { - RDEBUG3("Writing to socket %d", request->packet->sockfd); - rcode = write(request->packet->sockfd, p, - (sock->tls_session->dirty_out.data + sock->tls_session->dirty_out.used) - p); - if (rcode <= 0) { - RDEBUG("Error writing to TLS socket: %s", fr_syserror(errno)); - - tls_socket_close(listener); - return 0; - } - p += rcode; - } - - sock->tls_session->dirty_out.used = 0; - - return 1; -} - -static int tls_socket_recv(rad_listen_t *listener) -{ - bool doing_init = false; - ssize_t rcode; - RADIUS_PACKET *packet; - REQUEST *request; - listen_socket_t *sock = listener->data; - int ret; - RADCLIENT *client = sock->client; - - if (!sock->packet) { - sock->packet = fr_radius_alloc(sock, false); - if (!sock->packet) return 0; - - sock->packet->sockfd = listener->fd; - sock->packet->src_ipaddr = sock->other_ipaddr; - sock->packet->src_port = sock->other_port; - sock->packet->dst_ipaddr = sock->my_ipaddr; - sock->packet->dst_port = sock->my_port; - - if (sock->request) sock->request->packet = talloc_steal(sock->request, sock->packet); - } - - /* - * Allocate a REQUEST for debugging, and initialize the TLS session. - */ - if (!sock->request) { - sock->request = request = request_alloc(sock); - if (!sock->request) { - ERROR("Out of memory"); - return 0; - } - - rad_assert(request->packet == NULL); - rad_assert(sock->packet != NULL); - request->packet = talloc_steal(request, sock->packet); - - request->component = ""; - - request->reply = fr_radius_alloc(request, false); - if (!request->reply) return 0; - - rad_assert(sock->tls_session == NULL); - - sock->tls_session = tls_session_init_server(sock, listener->tls, sock->request, - listener->tls->require_client_cert); - if (!sock->tls_session) { - TALLOC_FREE(sock->request); - sock->packet = NULL; - return 0; - } - - SSL_set_ex_data(sock->tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, (void *)request); - - doing_init = true; - } - - rad_assert(sock->request != NULL); - rad_assert(sock->request->packet != NULL); - rad_assert(sock->packet != NULL); - rad_assert(sock->tls_session != NULL); - - request = sock->request; - - RDEBUG3("Reading from socket %d", request->packet->sockfd); - pthread_mutex_lock(&sock->mutex); - rcode = read(request->packet->sockfd, - sock->tls_session->dirty_in.data, - sizeof(sock->tls_session->dirty_in.data)); - if ((rcode < 0) && (errno == ECONNRESET)) { - do_close: - pthread_mutex_unlock(&sock->mutex); - DEBUG("Closing TLS socket from client port %u", sock->other_port); - tls_socket_close(listener); - pthread_mutex_unlock(&sock->mutex); - return 0; - } - - if (rcode < 0) { - RDEBUG("Error reading TLS socket: %s", fr_syserror(errno)); - goto do_close; - } - - /* - * Normal socket close. - */ - if (rcode == 0) goto do_close; - - sock->tls_session->dirty_in.used = rcode; - - RDEBUG2("Encrypted TLS data in (%zu bytes)", sock->tls_session->dirty_in.used); - radlog_request_hex(L_DBG, L_DBG_LVL_3, - request, sock->tls_session->dirty_in.data, sock->tls_session->dirty_in.used); - - /* - * Catch attempts to use non-SSL. - */ - if (doing_init && (sock->tls_session->dirty_in.data[0] != handshake)) { - RDEBUG("Non-TLS data sent to TLS socket: closing"); - goto do_close; - } - - /* - * If we need to do more initialization, do that here. - */ - if (!SSL_is_init_finished(sock->tls_session->ssl)) { - if (!tls_session_handshake(request, sock->tls_session)) { - RDEBUG("FAILED in TLS handshake receive"); - goto do_close; - } - - /* - * More ACK data to send. Do so. - */ - if (sock->tls_session->dirty_out.used > 0) { - tls_socket_write(listener, request); - pthread_mutex_unlock(&sock->mutex); - return 0; - } - - /* - * FIXME: Run the request through a virtual - * server in order to see if we like the - * certificate presented by the client. - */ - } - - /* - * Try to get application data. - */ - ret = tls_session_recv(request, sock->tls_session); - switch (ret) { - case 0: /* Record decrypted */ - break; - - case 1: /* Need more record data */ - pthread_mutex_unlock(&sock->mutex); - return 0; - - default: /* error */ - goto do_close; - } - - if (sock->tls_session->clean_out.used == 0) { - pthread_mutex_unlock(&sock->mutex); - return 0; - } - - /* - * If the packet is a complete RADIUS packet, return it to - * the caller. Otherwise... - */ - if ((sock->tls_session->clean_out.used < 20) || - (((sock->tls_session->clean_out.data[2] << 8) | sock->tls_session->clean_out.data[3]) != (int) sock->tls_session->clean_out.used)) { - RDEBUG("Received bad packet: Length %zd contents %d", - sock->tls_session->clean_out.used, - (sock->tls_session->clean_out.data[2] << 8) | sock->tls_session->clean_out.data[3]); - goto do_close; - } - - packet = sock->packet; - packet->data = talloc_array(packet, uint8_t, sock->tls_session->clean_out.used); - packet->data_len = sock->tls_session->clean_out.used; - sock->tls_session->record_to_buff(&sock->tls_session->clean_out, packet->data, packet->data_len); - packet->vps = NULL; - pthread_mutex_unlock(&sock->mutex); - - if (!fr_radius_packet_ok(packet, 0, NULL)) { - if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror()); - DEBUG("Closing TLS socket from client"); - pthread_mutex_lock(&sock->mutex); - tls_socket_close(listener); - pthread_mutex_unlock(&sock->mutex); - return 0; /* do_close unlocks the mutex */ - } - - /* - * Copied from src/lib/radius.c, fr_radius_packet_recv(); - */ - if (fr_debug_lvl) { - char host_ipaddr[INET6_ADDRSTRLEN]; - - if (is_radius_code(packet->code)) { - RDEBUG("tls_recv: %s packet from host %s port %d, id=%d, length=%d", - fr_packet_codes[packet->code], - inet_ntop(packet->src_ipaddr.af, - &packet->src_ipaddr.addr, - host_ipaddr, sizeof(host_ipaddr)), - packet->src_port, - packet->id, (int) packet->data_len); - } else { - RDEBUG("tls_recv: Packet from host %s port %d code=%d, id=%d, length=%d", - inet_ntop(packet->src_ipaddr.af, - &packet->src_ipaddr.addr, - host_ipaddr, sizeof(host_ipaddr)), - packet->src_port, - packet->code, - packet->id, (int) packet->data_len); - } - } - - FR_STATS_INC(auth, total_requests); - - return 1; -} - -int dual_tls_recv(rad_listen_t *listener) -{ - RADIUS_PACKET *packet; - RAD_REQUEST_FUNP fun = NULL; - listen_socket_t *sock = listener->data; - RADCLIENT *client = sock->client; - - if (!rad_cond_assert(client != NULL)) return 0; - if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0; - if (!tls_socket_recv(listener)) return 0; - - rad_assert(sock->packet != NULL); - rad_assert(sock->tls_session != NULL); - rad_assert(client != NULL); - - packet = talloc_steal(NULL, sock->packet); - sock->packet = NULL; - - gettimeofday(&packet->timestamp, NULL); - - /* - * Some sanity checks, based on the packet code. - * - * "auth+acct" are marked as "auth", with the "dual" flag - * set. - */ - switch (packet->code) { - case FR_CODE_ACCESS_REQUEST: - if (listener->type != RAD_LISTEN_AUTH) goto bad_packet; - FR_STATS_INC(auth, total_requests); - fun = rad_authenticate; - break; - -#ifdef WITH_ACCOUNTING - case FR_CODE_ACCOUNTING_REQUEST: - if (listener->type != RAD_LISTEN_ACCT) { - /* - * Allow auth + dual. Disallow - * everything else. - */ - if (!((listener->type == RAD_LISTEN_AUTH) && - (listener->dual))) { - goto bad_packet; - } - } - FR_STATS_INC(acct, total_requests); - fun = rad_accounting; - break; -#endif - - case FR_CODE_STATUS_SERVER: - if (!main_config.status_server) { - FR_STATS_INC(auth, total_unknown_types); - WARN("Ignoring Status-Server request due to security configuration"); - fr_radius_free(&packet); - return 0; - } - fun = rad_status_server; - break; - - default: - bad_packet: - FR_STATS_INC(auth, total_unknown_types); - - DEBUG("Invalid packet code %d sent from client %s port %d : IGNORED", - packet->code, client->shortname, packet->src_port); - fr_radius_free(&packet); - return 0; - } /* switch over packet types */ - - if (!request_receive(NULL, listener, packet, client, fun)) { - FR_STATS_INC(auth, total_packets_dropped); - fr_radius_free(&packet); - return 0; - } - - return 1; -} - -/* - * Send a response packet - */ -int dual_tls_send(rad_listen_t *listener, REQUEST *request) -{ - listen_socket_t *sock = listener->data; - - REQUEST_VERIFY(request); - - rad_assert(request->listener == listener); - rad_assert(listener->send == dual_tls_send); - - if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0; - - /* - * Accounting reject's are silently dropped. - * - * We do it here to avoid polluting the rest of the - * code with this knowledge - */ - if (request->reply->code == 0) return 0; - - /* - * Pack the VPs - */ - if (fr_radius_packet_encode(request->reply, request->packet, - request->client->secret) < 0) { - RPERROR("Failed encoding packet"); - return 0; - } - - if (request->reply->data_len > (MAX_PACKET_LEN - 100)) { - RWARN("Packet is large, and possibly truncated - %zd vs max %d", - request->reply->data_len, MAX_PACKET_LEN); - } - - /* - * Sign the packet. - */ - if (fr_radius_packet_sign(request->reply, request->packet, - request->client->secret) < 0) { - RPERROR("Failed signing packet"); - return 0; - } - - pthread_mutex_lock(&sock->mutex); - - /* - * Write the packet to the SSL buffers. - */ - sock->tls_session->record_from_buff(&sock->tls_session->clean_in, - request->reply->data, request->reply->data_len); - - /* - * Do SSL magic to get encrypted data. - */ - tls_session_send(request, sock->tls_session); - - /* - * And finally write the data to the socket. - */ - if (sock->tls_session->dirty_out.used > 0) { - RDEBUG2("Encrypted TLS data out (%zu bytes)", sock->tls_session->dirty_out.used); - radlog_request_hex(L_DBG, L_DBG_LVL_3, - request, sock->tls_session->dirty_out.data, sock->tls_session->dirty_out.used); - - tls_socket_write(listener, request); - } - pthread_mutex_unlock(&sock->mutex); - - return 0; -} - -#ifdef WITH_PROXY -/* - * Read from the SSL socket. Safe with either blocking or - * non-blocking IO. This level of complexity is probably not - * necessary, as each packet gets put into one SSL application - * record. When SSL has a full record, we should be able to read - * the entire packet via one SSL_read(). - * - * When SSL has a partial record, SSL_read() will return - * WANT_READ or WANT_WRITE, and zero application data. - * - * Called with the mutex held. - */ -static ssize_t proxy_tls_read(rad_listen_t *listener) -{ - int rcode; - size_t length; - uint8_t *data; - listen_socket_t *sock = listener->data; - - /* - * Get the maximum size of data to receive. - */ - if (!sock->data) sock->data = talloc_array(sock, uint8_t, - sock->tls_session->mtu); - - data = sock->data; - - if (sock->partial < 4) { - rcode = SSL_read(sock->tls_session->ssl, data + sock->partial, - 4 - sock->partial); - if (rcode <= 0) { - int err = SSL_get_error(sock->tls_session->ssl, rcode); - switch (err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - return 0; /* do some more work later */ - - case SSL_ERROR_ZERO_RETURN: - /* remote end sent close_notify, send one back */ - SSL_shutdown(sock->tls_session->ssl); - - case SSL_ERROR_SYSCALL: - do_close: - return -1; - - default: - tls_log_error(NULL, "Failed in proxy receive"); - - goto do_close; - } - } - - sock->partial = rcode; - } /* try reading the packet header */ - - if (sock->partial < 4) return 0; /* read more data */ - - length = (data[2] << 8) | data[3]; - - /* - * Do these checks only once, when we read the header. - */ - if (sock->partial == 4) { - DEBUG3("Proxy received header saying we have a packet of %u bytes", - (unsigned int) length); - - /* - * FIXME: allocate a RADIUS_PACKET, and set - * "data" to be as large as necessary. - */ - if (length > sock->tls_session->mtu) { - INFO("Received packet will be too large! Set \"fragment_size = %u\"", - (data[2] << 8) | data[3]); - goto do_close; - } - } - - /* - * Try to read some more. - */ - if (sock->partial < length) { - rcode = SSL_read(sock->tls_session->ssl, data + sock->partial, - length - sock->partial); - if (rcode <= 0) { - switch (SSL_get_error(sock->tls_session->ssl, rcode)) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - return 0; - - case SSL_ERROR_ZERO_RETURN: - /* remote end sent close_notify, send one back */ - SSL_shutdown(sock->tls_session->ssl); - goto do_close; - default: - goto do_close; - } - } - - sock->partial += rcode; - } - - /* - * If we're not done, say so. - * - * Otherwise, reset the partially read data flag, and say - * we have a packet. - */ - if (sock->partial < length) { - return 0; - } - - sock->partial = 0; /* we've now read the packet */ - return length; -} - -int proxy_tls_recv(rad_listen_t *listener) -{ - listen_socket_t *sock = listener->data; - char buffer[256]; - RADIUS_PACKET *packet; - uint8_t *data; - ssize_t data_len; - - if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0; - - DEBUG3("Proxy SSL socket has data to read"); - pthread_mutex_lock(&sock->mutex); - data_len = proxy_tls_read(listener); - pthread_mutex_unlock(&sock->mutex); - - if (data_len < 0) { - DEBUG("Closing TLS socket to home server"); - pthread_mutex_lock(&sock->mutex); - tls_socket_close(listener); - pthread_mutex_unlock(&sock->mutex); - return 0; - } - - if (data_len == 0) return 0; /* not done yet */ - - data = sock->data; - - packet = fr_radius_alloc(sock, false); - packet->sockfd = listener->fd; - packet->src_ipaddr = sock->other_ipaddr; - packet->src_port = sock->other_port; - packet->dst_ipaddr = sock->my_ipaddr; - packet->dst_port = sock->my_port; - packet->code = data[0]; - packet->id = data[1]; - packet->data_len = data_len; - packet->data = talloc_array(packet, uint8_t, packet->data_len); - memcpy(packet->data, data, packet->data_len); - memcpy(packet->vector, packet->data + 4, 16); - - /* - * FIXME: Client MIB updates? - */ - switch (packet->code) { - case FR_CODE_ACCESS_ACCEPT: - case FR_CODE_ACCESS_CHALLENGE: - case FR_CODE_ACCESS_REJECT: - break; - -#ifdef WITH_ACCOUNTING - case FR_CODE_ACCOUNTING_RESPONSE: - break; -#endif - - default: - /* - * FIXME: Update MIB for packet types? - */ - ERROR("Invalid packet code %d sent to a proxy port " - "from home server %s port %d - ID %d : IGNORED", - packet->code, - fr_inet_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)), - packet->src_port, packet->id); - fr_radius_free(&packet); - return 0; - } - - fr_radius_free(&packet); - return 0; -} - -int proxy_tls_send(rad_listen_t *listener, REQUEST *request) -{ - int rcode; - listen_socket_t *sock = listener->data; - - REQUEST_VERIFY(request); - - if ((listener->status != RAD_LISTEN_STATUS_INIT) && - (listener->status != RAD_LISTEN_STATUS_KNOWN)) return 0; - - /* - * Normal proxying calls us with the data already - * encoded. The "ping home server" code does not. So, - * if there's no packet, encode it here. - */ - if (!request->proxy->packet->data) { - request->proxy->listener->encode(request->proxy->listener, - request); - } - - DEBUG3("Proxy is writing %u bytes to SSL", - (unsigned int) request->proxy->packet->data_len); - pthread_mutex_lock(&sock->mutex); - rcode = SSL_write(sock->tls_session->ssl, request->proxy->packet->data, - request->proxy->packet->data_len); - if (rcode < 0) { - int err; - - err = ERR_get_error(); - switch (err) { - case SSL_ERROR_NONE: - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - break; /* let someone else retry */ - - default: - tls_log_error(NULL, "Failed in proxy send"); - DEBUG("Closing TLS socket to home server"); - tls_socket_close(listener); - pthread_mutex_unlock(&sock->mutex); - return 0; - } - } - pthread_mutex_unlock(&sock->mutex); - - return 1; -} -#endif /* WITH_PROXY */ -#endif /* WITH_TLS */