2  *    This program is free software; you can redistribute it and/or modify
 
   3  *    it under the terms of the GNU General Public License as published by
 
   4  *    the Free Software Foundation; either version 2 of the License, or
 
   5  *    (at your option) any later version.
 
   7  *    This program is distributed in the hope that it will be useful,
 
   8  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
   9  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  10  *    GNU General Public License for more details.
 
  12  *    You should have received a copy of the GNU General Public License
 
  13  *    along with this program; if not, write to the Free Software
 
  14  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
  18  *    Author : Maxim Mamontov <faust@stargazer.dp.ua>
 
  23 #include "stg/store.h"
 
  24 #include "stg/users.h"
 
  25 #include "stg/plugin_creator.h"
 
  26 #include "stg/common.h"
 
  34 #include <sys/types.h>
 
  35 #include <sys/socket.h>
 
  36 #include <sys/un.h> // UNIX
 
  37 #include <netinet/in.h> // IP
 
  38 #include <netinet/tcp.h> // TCP
 
  47 PLUGIN_CREATOR<RADIUS> creator;
 
  51 extern "C" PLUGIN * GetPlugin()
 
  53     return creator.GetPlugin();
 
  63       m_logger(GetPluginLogger(GetStgLogger(), "radius"))
 
  67 int RADIUS::ParseSettings()
 
  70         m_config = STG::Config(m_settings);
 
  71         return reconnect() ? 0 : -1;
 
  72     } catch (const std::runtime_error& ex) {
 
  73         m_logger("Failed to parse settings. %s", ex.what());
 
  83     int res = pthread_create(&m_thread, NULL, run, this);
 
  87     m_error = strerror(res);
 
  88     m_logger("Failed to create thread: '" + m_error + "'.");
 
  94     std::set<std::string>::const_iterator it = m_logins.begin();
 
  95     for (; it != m_logins.end(); ++it)
 
  96         m_users->Unauthorize(*it, this, "Stopping RADIUS plugin.");
 
 104     for (size_t i = 0; i < 25 && !m_stopped; i++) {
 
 105         struct timespec ts = {0, 200000000};
 
 106         nanosleep(&ts, NULL);
 
 110         pthread_join(m_thread, NULL);
 
 114     if (m_config.connectionType == Config::UNIX)
 
 115         unlink(m_config.bindAddress.c_str());
 
 117     m_error = "Failed to stop thread.";
 
 121 //-----------------------------------------------------------------------------
 
 122 void* RADIUS::run(void* d)
 
 125     sigfillset(&signalSet);
 
 126     pthread_sigmask(SIG_BLOCK, &signalSet, NULL);
 
 128     static_cast<RADIUS *>(d)->runImpl();
 
 133 bool RADIUS::reconnect()
 
 135     if (!m_conns.empty())
 
 137         std::deque<STG::Conn *>::const_iterator it;
 
 138         for (it = m_conns.begin(); it != m_conns.end(); ++it)
 
 142     if (m_listenSocket != 0)
 
 144         shutdown(m_listenSocket, SHUT_RDWR);
 
 145         close(m_listenSocket);
 
 147     if (m_config.connectionType == Config::UNIX)
 
 148         m_listenSocket = createUNIX();
 
 150         m_listenSocket = createTCP();
 
 151     if (m_listenSocket == 0)
 
 153     if (listen(m_listenSocket, 100) == -1)
 
 155         m_error = std::string("Error starting to listen socket: ") + strerror(errno);
 
 162 int RADIUS::createUNIX() const
 
 164     int fd = socket(AF_UNIX, SOCK_STREAM, 0);
 
 167         m_error = std::string("Error creating UNIX socket: ") + strerror(errno);
 
 171     struct sockaddr_un addr;
 
 172     memset(&addr, 0, sizeof(addr));
 
 173     addr.sun_family = AF_UNIX;
 
 174     strncpy(addr.sun_path, m_config.bindAddress.c_str(), m_config.bindAddress.length());
 
 175     unlink(m_config.bindAddress.c_str());
 
 176     if (bind(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == -1)
 
 178         shutdown(fd, SHUT_RDWR);
 
 180         m_error = std::string("Error binding UNIX socket: ") + strerror(errno);
 
 184     chown(m_config.bindAddress.c_str(), m_config.sockUID, m_config.sockGID);
 
 185     if (m_config.sockMode != static_cast<mode_t>(-1))
 
 186         chmod(m_config.bindAddress.c_str(), m_config.sockMode);
 
 190 int RADIUS::createTCP() const
 
 193     memset(&hints, 0, sizeof(addrinfo));
 
 195     hints.ai_family = AF_INET;       /* Allow IPv4 */
 
 196     hints.ai_socktype = SOCK_STREAM; /* Stream socket */
 
 197     hints.ai_flags = AI_PASSIVE;     /* For wildcard IP address */
 
 198     hints.ai_protocol = 0;           /* Any protocol */
 
 199     hints.ai_canonname = NULL;
 
 200     hints.ai_addr = NULL;
 
 201     hints.ai_next = NULL;
 
 203     addrinfo* ais = NULL;
 
 204     int res = getaddrinfo(m_config.bindAddress.c_str(), m_config.portStr.c_str(), &hints, &ais);
 
 207         m_error = "Error resolving address '" + m_config.bindAddress + "': " + gai_strerror(res);
 
 212     for (addrinfo* ai = ais; ai != NULL; ai = ai->ai_next)
 
 214         int fd = socket(AF_INET, SOCK_STREAM, 0);
 
 217             m_error = std::string("Error creating TCP socket: ") + strerror(errno);
 
 222         if (bind(fd, ai->ai_addr, ai->ai_addrlen) == -1)
 
 224             shutdown(fd, SHUT_RDWR);
 
 226             m_error = std::string("Error binding TCP socket: ") + strerror(errno);
 
 234     m_error = "Failed to resolve '" + m_config.bindAddress;
 
 241 void RADIUS::runImpl()
 
 255         int res = select(maxFD() + 1, &fds, NULL, NULL, &tv);
 
 260             m_error = std::string("'select' is failed: '") + strerror(errno) + "'.";
 
 272             for (std::deque<Conn*>::iterator it = m_conns.begin(); it != m_conns.end(); ++it)
 
 282 int RADIUS::maxFD() const
 
 284     int maxFD = m_listenSocket;
 
 285     std::deque<STG::Conn *>::const_iterator it;
 
 286     for (it = m_conns.begin(); it != m_conns.end(); ++it)
 
 287         if (maxFD < (*it)->sock())
 
 288             maxFD = (*it)->sock();
 
 292 void RADIUS::buildFDSet(fd_set & fds) const
 
 295     FD_SET(m_listenSocket, &fds);
 
 296     std::deque<STG::Conn *>::const_iterator it;
 
 297     for (it = m_conns.begin(); it != m_conns.end(); ++it)
 
 298         FD_SET((*it)->sock(), &fds);
 
 301 void RADIUS::cleanupConns()
 
 303     std::deque<STG::Conn *>::iterator pos;
 
 304     for (pos = m_conns.begin(); pos != m_conns.end(); ++pos)
 
 305         if (!(*pos)->isOk()) {
 
 310     pos = std::remove(m_conns.begin(), m_conns.end(), static_cast<STG::Conn *>(NULL));
 
 311     m_conns.erase(pos, m_conns.end());
 
 314 void RADIUS::handleEvents(const fd_set & fds)
 
 316     if (FD_ISSET(m_listenSocket, &fds))
 
 320         std::deque<STG::Conn *>::iterator it;
 
 321         for (it = m_conns.begin(); it != m_conns.end(); ++it)
 
 322             if (FD_ISSET((*it)->sock(), &fds))
 
 329 void RADIUS::acceptConnection()
 
 331     if (m_config.connectionType == Config::UNIX)
 
 337 void RADIUS::acceptUNIX()
 
 339     struct sockaddr_un addr;
 
 340     memset(&addr, 0, sizeof(addr));
 
 341     socklen_t size = sizeof(addr);
 
 342     int res = accept(m_listenSocket, reinterpret_cast<sockaddr*>(&addr), &size);
 
 345         m_error = std::string("Failed to accept UNIX connection: ") + strerror(errno);
 
 349     printfd(__FILE__, "New UNIX connection: '%s'\n", addr.sun_path);
 
 350     m_conns.push_back(new Conn(*m_users, m_logger, *this, m_config, res, addr.sun_path));
 
 353 void RADIUS::acceptTCP()
 
 355     struct sockaddr_in addr;
 
 356     memset(&addr, 0, sizeof(addr));
 
 357     socklen_t size = sizeof(addr);
 
 358     int res = accept(m_listenSocket, reinterpret_cast<sockaddr*>(&addr), &size);
 
 361         m_error = std::string("Failed to accept TCP connection: ") + strerror(errno);
 
 365     std::string remote = inet_ntostring(addr.sin_addr.s_addr) + ":" + x2str(ntohs(addr.sin_port));
 
 366     printfd(__FILE__, "New TCP connection: '%s'\n", remote.c_str());
 
 367     m_conns.push_back(new Conn(*m_users, m_logger, *this, m_config, res, remote));
 
 370 void RADIUS::authorize(const USER& user)
 
 373     const std::string& login(user.GetLogin());
 
 374     if (!m_users->Authorize(login, ip, 0xffFFffFF, this))
 
 376         m_error = "Unable to authorize user '" + login + "' with ip " + inet_ntostring(ip) + ".";
 
 380         m_logins.insert(login);
 
 383 void RADIUS::unauthorize(const std::string& login, const std::string& reason)
 
 385     const std::set<std::string>::const_iterator it = m_logins.find(login);
 
 386     if (it == m_logins.end())
 
 389     m_users->Unauthorize(login, this, reason);