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>
 
  24 #include "stg/common.h"
 
  29 #include <strings.h> // strncasecmp
 
  36 struct ParserError : public std::runtime_error
 
  38     ParserError(const std::string& message)
 
  39         : runtime_error("Config is not valid. " + message),
 
  43     ParserError(size_t pos, const std::string& message)
 
  44         : runtime_error("Parsing error at position " + x2str(pos) + ". " + message),
 
  48     virtual ~ParserError() throw() {}
 
  54 size_t skipSpaces(const std::string& value, size_t start)
 
  56     while (start < value.length() && std::isspace(value[start]))
 
  61 size_t checkChar(const std::string& value, size_t start, char ch)
 
  63     if (start >= value.length())
 
  64         throw ParserError(start, "Unexpected end of string. Expected '" + std::string(1, ch) + "'.");
 
  65     if (value[start] != ch)
 
  66         throw ParserError(start, "Expected '" + std::string(1, ch) + "', got '" + std::string(1, value[start]) + "'.");
 
  70 std::pair<size_t, std::string> readString(const std::string& value, size_t start)
 
  73     while (start < value.length() && !std::isspace(value[start]) &&
 
  74            value[start] != ',' && value[start] != '(' && value[start] != ')')
 
  75         dest.push_back(value[start++]);
 
  77         if (start == value.length())
 
  78             throw ParserError(start, "Unexpected end of string. Expected string.");
 
  80             throw ParserError(start, "Unexpected whitespace. Expected string.");
 
  82     return std::make_pair(start, dest);
 
  85 Config::Pairs toPairs(const std::vector<std::string>& values)
 
  88         return Config::Pairs();
 
  89     std::string value(values[0]);
 
  92     while (start < value.size()) {
 
  94         start = skipSpaces(value, start);
 
  97             start = checkChar(value, start, ',');
 
  98             start = skipSpaces(value, start);
 
 100         size_t pairStart = start;
 
 101         start = checkChar(value, start, '(');
 
 102         const std::pair<size_t, std::string> key = readString(value, start);
 
 104         pair.first = key.second;
 
 105         start = skipSpaces(value, start);
 
 106         start = checkChar(value, start, ',');
 
 107         start = skipSpaces(value, start);
 
 108         const std::pair<size_t, std::string> val = readString(value, start);
 
 110         pair.second = val.second;
 
 111         start = skipSpaces(value, start);
 
 112         start = checkChar(value, start, ')');
 
 113         if (res.find(pair.first) != res.end())
 
 114             throw ParserError(pairStart, "Duplicate field.");
 
 120 bool toBool(const std::vector<std::string>& values)
 
 124     std::string value(values[0]);
 
 125     return strncasecmp(value.c_str(), "yes", 3) == 0;
 
 128 std::string toString(const std::vector<std::string>& values)
 
 135 uid_t toUID(const std::vector<std::string>& values)
 
 139     uid_t res = str2uid(values[0]);
 
 140     if (res == static_cast<uid_t>(-1))
 
 141         throw ParserError("Invalid user name: '" + values[0] + "'");
 
 145 gid_t toGID(const std::vector<std::string>& values)
 
 149     gid_t res = str2gid(values[0]);
 
 150     if (res == static_cast<gid_t>(-1))
 
 151         throw ParserError("Invalid group name: '" + values[0] + "'");
 
 155 mode_t toMode(const std::vector<std::string>& values)
 
 159     mode_t res = str2mode(values[0]);
 
 160     if (res == static_cast<mode_t>(-1))
 
 161         throw ParserError("Invalid mode: '" + values[0] + "'");
 
 165 template <typename T>
 
 166 T toInt(const std::vector<std::string>& values)
 
 171     if (str2x(values[0], res) == 0)
 
 176 uint16_t toPort(const std::string& value)
 
 181     if (str2x(value, res) == 0)
 
 183     throw ParserError("'" + value + "' is not a valid port number.");
 
 186 typedef std::map<std::string, Config::ReturnCode> Codes;
 
 188 // One-time call to initialize the list of codes.
 
 192     res["reject"]   = Config::REJECT;
 
 193     res["fail"]     = Config::FAIL;
 
 194     res["ok"]       = Config::OK;
 
 195     res["handled"]  = Config::HANDLED;
 
 196     res["invalid"]  = Config::INVALID;
 
 197     res["userlock"] = Config::USERLOCK;
 
 198     res["notfound"] = Config::NOTFOUND;
 
 199     res["noop"]     = Config::NOOP;
 
 200     res["updated"]  = Config::UPDATED;
 
 204 Config::ReturnCode toReturnCode(const std::vector<std::string>& values)
 
 206     static Codes codes(getCodes());
 
 208         return Config::REJECT;
 
 209     std::string code = ToLower(values[0]);
 
 210     const Codes::const_iterator it = codes.find(code);
 
 211     if (it == codes.end())
 
 212         return Config::REJECT;
 
 216 Config::Pairs parseVector(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
 
 218     for (size_t i = 0; i < params.size(); ++i)
 
 219         if (params[i].param == paramName)
 
 220             return toPairs(params[i].value);
 
 221     return Config::Pairs();
 
 224 Config::Authorize parseAuthorize(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
 
 226     for (size_t i = 0; i < params.size(); ++i)
 
 227         if (params[i].param == paramName)
 
 228             return Config::Authorize(toPairs(params[i].value));
 
 229     return Config::Authorize();
 
 232 Config::ReturnCode parseReturnCode(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
 
 234     for (size_t i = 0; i < params.size(); ++i)
 
 235         if (params[i].param == paramName)
 
 236             return toReturnCode(params[i].value);
 
 237     return Config::REJECT;
 
 240 bool parseBool(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
 
 242     for (size_t i = 0; i < params.size(); ++i)
 
 243         if (params[i].param == paramName)
 
 244             return toBool(params[i].value);
 
 248 std::string parseString(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
 
 250     for (size_t i = 0; i < params.size(); ++i)
 
 251         if (params[i].param == paramName)
 
 252             return toString(params[i].value);
 
 256 std::string parseAddress(Config::Type connectionType, const std::string& value)
 
 258     size_t pos = value.find_first_of(':');
 
 259     if (pos == std::string::npos)
 
 260         throw ParserError("Connection type is not specified. Should be either 'unix' or 'tcp'.");
 
 261     if (connectionType == Config::UNIX)
 
 262         return value.substr(pos + 1);
 
 263     std::string address(value.substr(pos + 1));
 
 264     pos = address.find_first_of(':', pos + 1);
 
 265     if (pos == std::string::npos)
 
 266         throw ParserError("Port is not specified.");
 
 267     return address.substr(0, pos - 1);
 
 270 std::string parsePort(Config::Type connectionType, const std::string& value)
 
 272     size_t pos = value.find_first_of(':');
 
 273     if (pos == std::string::npos)
 
 274         throw ParserError("Connection type is not specified. Should be either 'unix' or 'tcp'.");
 
 275     if (connectionType == Config::UNIX)
 
 277     std::string address(value.substr(pos + 1));
 
 278     pos = address.find_first_of(':', pos + 1);
 
 279     if (pos == std::string::npos)
 
 280         throw ParserError("Port is not specified.");
 
 281     return address.substr(pos + 1);
 
 284 Config::Type parseConnectionType(const std::string& address)
 
 286     size_t pos = address.find_first_of(':');
 
 287     if (pos == std::string::npos)
 
 288         throw ParserError("Connection type is not specified. Should be either 'unix' or 'tcp'.");
 
 289     std::string type = ToLower(address.substr(0, pos));
 
 292     else if (type == "tcp")
 
 294     throw ParserError("Invalid connection type. Should be either 'unix' or 'tcp', got '" + type + "'");
 
 297 Config::Section parseSection(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
 
 299     for (size_t i = 0; i < params.size(); ++i)
 
 300         if (params[i].param == paramName)
 
 301             return Config::Section(parseVector("match", params[i].sections),
 
 302                                    parseVector("modify", params[i].sections),
 
 303                                    parseVector("reply", params[i].sections),
 
 304                                    parseReturnCode("no_match", params[i].sections),
 
 305                                    parseAuthorize("authorize", params[i].sections));
 
 306     return Config::Section();
 
 309 uid_t parseUID(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
 
 311     for (size_t i = 0; i < params.size(); ++i)
 
 312         if (params[i].param == paramName)
 
 313             return toUID(params[i].value);
 
 317 gid_t parseGID(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
 
 319     for (size_t i = 0; i < params.size(); ++i)
 
 320         if (params[i].param == paramName)
 
 321             return toGID(params[i].value);
 
 325 mode_t parseMode(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
 
 327     for (size_t i = 0; i < params.size(); ++i)
 
 328         if (params[i].param == paramName)
 
 329             return toMode(params[i].value);
 
 333 } // namespace anonymous
 
 335 bool Config::Authorize::check(const USER& user, const Config::Pairs& radiusData) const
 
 338         return false; // No flag - no authorization.
 
 341         return true; // Empty parameter - always authorize.
 
 343     Config::Pairs::const_iterator it = m_cond.begin();
 
 344     for (; it != m_cond.end(); ++it)
 
 346         const Config::Pairs::const_iterator pos = radiusData.find(it->first);
 
 347         if (pos == radiusData.end())
 
 348             return false; // No required Radius parameter.
 
 349         if (user.GetParamValue(it->second) != pos->second)
 
 350             return false; // No match with the user.
 
 356 Config::Config(const MODULE_SETTINGS& settings)
 
 357     : autz(parseSection("autz", settings.moduleParams)),
 
 358       auth(parseSection("auth", settings.moduleParams)),
 
 359       postauth(parseSection("postauth", settings.moduleParams)),
 
 360       preacct(parseSection("preacct", settings.moduleParams)),
 
 361       acct(parseSection("acct", settings.moduleParams)),
 
 362       verbose(parseBool("verbose", settings.moduleParams)),
 
 363       address(parseString("bind_address", settings.moduleParams)),
 
 364       connectionType(parseConnectionType(address)),
 
 365       bindAddress(parseAddress(connectionType, address)),
 
 366       portStr(parsePort(connectionType, address)),
 
 367       port(toPort(portStr)),
 
 368       key(parseString("key", settings.moduleParams)),
 
 369       sockUID(parseUID("sock_owner", settings.moduleParams)),
 
 370       sockGID(parseGID("sock_group", settings.moduleParams)),
 
 371       sockMode(parseMode("sock_mode", settings.moduleParams))
 
 374     if (autz.authorize.exists())
 
 376     if (auth.authorize.exists())
 
 378     if (postauth.authorize.exists())
 
 380     if (preacct.authorize.exists())
 
 382     if (acct.authorize.exists())
 
 385         throw ParserError("Authorization flag is specified in more than one section.");