]> git.stg.codes - stg.git/blob - stargazer/plugins/other/radius/config.cpp
Public interfaces: part 1
[stg.git] / stargazer / plugins / other / radius / config.cpp
1 /*
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.
6  *
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.
11  *
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
15  */
16
17 /*
18  *    Author : Maxim Mamontov <faust@stargazer.dp.ua>
19  */
20
21 #include "config.h"
22
23 #include "stg/user.h"
24 #include "stg/common.h"
25
26 #include <vector>
27 #include <stdexcept>
28
29 #include <strings.h> // strncasecmp
30
31 using STG::Config;
32
33 namespace
34 {
35
36 struct ParserError : public std::runtime_error
37 {
38     ParserError(const std::string& message)
39         : runtime_error("Config is not valid. " + message),
40           position(0),
41           error(message)
42     {}
43     ParserError(size_t pos, const std::string& message)
44         : runtime_error("Parsing error at position " + std::to_string(pos) + ". " + message),
45           position(pos),
46           error(message)
47     {}
48
49     size_t position;
50     std::string error;
51 };
52
53 size_t skipSpaces(const std::string& value, size_t start)
54 {
55     while (start < value.length() && std::isspace(value[start]))
56         ++start;
57     return start;
58 }
59
60 size_t checkChar(const std::string& value, size_t start, char ch)
61 {
62     if (start >= value.length())
63         throw ParserError(start, "Unexpected end of string. Expected '" + std::string(1, ch) + "'.");
64     if (value[start] != ch)
65         throw ParserError(start, "Expected '" + std::string(1, ch) + "', got '" + std::string(1, value[start]) + "'.");
66     return start + 1;
67 }
68
69 std::pair<size_t, std::string> readString(const std::string& value, size_t start)
70 {
71     std::string dest;
72     while (start < value.length() && !std::isspace(value[start]) &&
73            value[start] != ',' && value[start] != '(' && value[start] != ')')
74         dest.push_back(value[start++]);
75     if (dest.empty()) {
76         if (start == value.length())
77             throw ParserError(start, "Unexpected end of string. Expected string.");
78         else
79             throw ParserError(start, "Unexpected whitespace. Expected string.");
80     }
81     return std::make_pair(start, dest);
82 }
83
84 Config::Pairs toPairs(const std::vector<std::string>& values)
85 {
86     if (values.empty())
87         return Config::Pairs();
88     std::string value(values[0]);
89     Config::Pairs res;
90     size_t start = 0;
91     while (start < value.size()) {
92         Config::Pair pair;
93         start = skipSpaces(value, start);
94         if (!res.empty())
95         {
96             start = checkChar(value, start, ',');
97             start = skipSpaces(value, start);
98         }
99         size_t pairStart = start;
100         start = checkChar(value, start, '(');
101         const std::pair<size_t, std::string> key = readString(value, start);
102         start = key.first;
103         pair.first = key.second;
104         start = skipSpaces(value, start);
105         start = checkChar(value, start, ',');
106         start = skipSpaces(value, start);
107         const std::pair<size_t, std::string> val = readString(value, start);
108         start = val.first;
109         pair.second = val.second;
110         start = skipSpaces(value, start);
111         start = checkChar(value, start, ')');
112         if (res.find(pair.first) != res.end())
113             throw ParserError(pairStart, "Duplicate field.");
114         res.insert(pair);
115     }
116     return res;
117 }
118
119 bool toBool(const std::vector<std::string>& values)
120 {
121     if (values.empty())
122         return false;
123     std::string value(values[0]);
124     return strncasecmp(value.c_str(), "yes", 3) == 0;
125 }
126
127 std::string toString(const std::vector<std::string>& values)
128 {
129     if (values.empty())
130         return "";
131     return values[0];
132 }
133
134 uid_t toUID(const std::vector<std::string>& values)
135 {
136     if (values.empty())
137         return -1;
138     uid_t res = str2uid(values[0]);
139     if (res == static_cast<uid_t>(-1))
140         throw ParserError("Invalid user name: '" + values[0] + "'");
141     return res;
142 }
143
144 gid_t toGID(const std::vector<std::string>& values)
145 {
146     if (values.empty())
147         return -1;
148     gid_t res = str2gid(values[0]);
149     if (res == static_cast<gid_t>(-1))
150         throw ParserError("Invalid group name: '" + values[0] + "'");
151     return res;
152 }
153
154 mode_t toMode(const std::vector<std::string>& values)
155 {
156     if (values.empty())
157         return -1;
158     mode_t res = str2mode(values[0]);
159     if (res == static_cast<mode_t>(-1))
160         throw ParserError("Invalid mode: '" + values[0] + "'");
161     return res;
162 }
163
164 template <typename T>
165 T toInt(const std::vector<std::string>& values)
166 {
167     if (values.empty())
168         return 0;
169     T res = 0;
170     if (str2x(values[0], res) == 0)
171         return res;
172     return 0;
173 }
174
175 uint16_t toPort(const std::string& value)
176 {
177     if (value.empty())
178         return 0;
179     uint16_t res = 0;
180     if (str2x(value, res) == 0)
181         return res;
182     throw ParserError("'" + value + "' is not a valid port number.");
183 }
184
185 typedef std::map<std::string, Config::ReturnCode> Codes;
186
187 // One-time call to initialize the list of codes.
188 Codes getCodes()
189 {
190     Codes res;
191     res["reject"]   = Config::REJECT;
192     res["fail"]     = Config::FAIL;
193     res["ok"]       = Config::OK;
194     res["handled"]  = Config::HANDLED;
195     res["invalid"]  = Config::INVALID;
196     res["userlock"] = Config::USERLOCK;
197     res["notfound"] = Config::NOTFOUND;
198     res["noop"]     = Config::NOOP;
199     res["updated"]  = Config::UPDATED;
200     return res;
201 }
202
203 Config::ReturnCode toReturnCode(const std::vector<std::string>& values)
204 {
205     static Codes codes(getCodes());
206     if (values.empty())
207         return Config::REJECT;
208     std::string code = ToLower(values[0]);
209     const Codes::const_iterator it = codes.find(code);
210     if (it == codes.end())
211         return Config::REJECT;
212     return it->second;
213 }
214
215 Config::Pairs parseVector(const std::string& paramName, const std::vector<STG::ParamValue>& params)
216 {
217     for (size_t i = 0; i < params.size(); ++i)
218         if (params[i].param == paramName)
219             return toPairs(params[i].value);
220     return Config::Pairs();
221 }
222
223 Config::Authorize parseAuthorize(const std::string& paramName, const std::vector<STG::ParamValue>& params)
224 {
225     for (size_t i = 0; i < params.size(); ++i)
226         if (params[i].param == paramName)
227             return Config::Authorize(toPairs(params[i].value));
228     return Config::Authorize();
229 }
230
231 Config::ReturnCode parseReturnCode(const std::string& paramName, const std::vector<STG::ParamValue>& params)
232 {
233     for (size_t i = 0; i < params.size(); ++i)
234         if (params[i].param == paramName)
235             return toReturnCode(params[i].value);
236     return Config::REJECT;
237 }
238
239 bool parseBool(const std::string& paramName, const std::vector<STG::ParamValue>& params)
240 {
241     for (size_t i = 0; i < params.size(); ++i)
242         if (params[i].param == paramName)
243             return toBool(params[i].value);
244     return false;
245 }
246
247 std::string parseString(const std::string& paramName, const std::vector<STG::ParamValue>& params)
248 {
249     for (size_t i = 0; i < params.size(); ++i)
250         if (params[i].param == paramName)
251             return toString(params[i].value);
252     return "";
253 }
254
255 std::string parseAddress(Config::Type connectionType, const std::string& value)
256 {
257     size_t pos = value.find_first_of(':');
258     if (pos == std::string::npos)
259         throw ParserError("Connection type is not specified. Should be either 'unix' or 'tcp'.");
260     if (connectionType == Config::UNIX)
261         return value.substr(pos + 1);
262     std::string address(value.substr(pos + 1));
263     pos = address.find_first_of(':', pos + 1);
264     if (pos == std::string::npos)
265         throw ParserError("Port is not specified.");
266     return address.substr(0, pos - 1);
267 }
268
269 std::string parsePort(Config::Type connectionType, const std::string& value)
270 {
271     size_t pos = value.find_first_of(':');
272     if (pos == std::string::npos)
273         throw ParserError("Connection type is not specified. Should be either 'unix' or 'tcp'.");
274     if (connectionType == Config::UNIX)
275         return "";
276     std::string address(value.substr(pos + 1));
277     pos = address.find_first_of(':', pos + 1);
278     if (pos == std::string::npos)
279         throw ParserError("Port is not specified.");
280     return address.substr(pos + 1);
281 }
282
283 Config::Type parseConnectionType(const std::string& address)
284 {
285     size_t pos = address.find_first_of(':');
286     if (pos == std::string::npos)
287         throw ParserError("Connection type is not specified. Should be either 'unix' or 'tcp'.");
288     std::string type = ToLower(address.substr(0, pos));
289     if (type == "unix")
290         return Config::UNIX;
291     else if (type == "tcp")
292         return Config::TCP;
293     throw ParserError("Invalid connection type. Should be either 'unix' or 'tcp', got '" + type + "'");
294 }
295
296 Config::Section parseSection(const std::string& paramName, const std::vector<STG::ParamValue>& params)
297 {
298     for (size_t i = 0; i < params.size(); ++i)
299         if (params[i].param == paramName)
300             return Config::Section(parseVector("match", params[i].sections),
301                                    parseVector("modify", params[i].sections),
302                                    parseVector("reply", params[i].sections),
303                                    parseReturnCode("no_match", params[i].sections),
304                                    parseAuthorize("authorize", params[i].sections));
305     return Config::Section();
306 }
307
308 uid_t parseUID(const std::string& paramName, const std::vector<STG::ParamValue>& params)
309 {
310     for (size_t i = 0; i < params.size(); ++i)
311         if (params[i].param == paramName)
312             return toUID(params[i].value);
313     return -1;
314 }
315
316 gid_t parseGID(const std::string& paramName, const std::vector<STG::ParamValue>& params)
317 {
318     for (size_t i = 0; i < params.size(); ++i)
319         if (params[i].param == paramName)
320             return toGID(params[i].value);
321     return -1;
322 }
323
324 mode_t parseMode(const std::string& paramName, const std::vector<STG::ParamValue>& params)
325 {
326     for (size_t i = 0; i < params.size(); ++i)
327         if (params[i].param == paramName)
328             return toMode(params[i].value);
329     return -1;
330 }
331
332 } // namespace anonymous
333
334 bool Config::Authorize::check(const User& user, const Config::Pairs& radiusData) const
335 {
336     if (!m_auth)
337         return false; // No flag - no authorization.
338
339     if (m_cond.empty())
340         return true; // Empty parameter - always authorize.
341
342     Config::Pairs::const_iterator it = m_cond.begin();
343     for (; it != m_cond.end(); ++it)
344     {
345         const Config::Pairs::const_iterator pos = radiusData.find(it->first);
346         if (pos == radiusData.end())
347             return false; // No required Radius parameter.
348         if (user.GetParamValue(it->second) != pos->second)
349             return false; // No match with the user.
350     }
351
352     return true;
353 }
354
355 Config::Config(const ModuleSettings& settings)
356     : autz(parseSection("autz", settings.moduleParams)),
357       auth(parseSection("auth", settings.moduleParams)),
358       postauth(parseSection("postauth", settings.moduleParams)),
359       preacct(parseSection("preacct", settings.moduleParams)),
360       acct(parseSection("acct", settings.moduleParams)),
361       verbose(parseBool("verbose", settings.moduleParams)),
362       address(parseString("bind_address", settings.moduleParams)),
363       connectionType(parseConnectionType(address)),
364       bindAddress(parseAddress(connectionType, address)),
365       portStr(parsePort(connectionType, address)),
366       port(toPort(portStr)),
367       key(parseString("key", settings.moduleParams)),
368       sockUID(parseUID("sock_owner", settings.moduleParams)),
369       sockGID(parseGID("sock_group", settings.moduleParams)),
370       sockMode(parseMode("sock_mode", settings.moduleParams))
371 {
372     size_t count = 0;
373     if (autz.authorize.exists())
374         ++count;
375     if (auth.authorize.exists())
376         ++count;
377     if (postauth.authorize.exists())
378         ++count;
379     if (preacct.authorize.exists())
380         ++count;
381     if (acct.authorize.exists())
382         ++count;
383     if (count > 0)
384         throw ParserError("Authorization flag is specified in more than one section.");
385 }