]> git.stg.codes - stg.git/blob - projects/stargazer/plugins/other/radius/config.cpp
Merge branch 'stg-2.409' into stg-2.409-radius
[stg.git] / projects / 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 " + x2str(pos) + ". " + message),
45           position(pos),
46           error(message)
47     {}
48     virtual ~ParserError() throw() {}
49
50     size_t position;
51     std::string error;
52 };
53
54 size_t skipSpaces(const std::string& value, size_t start)
55 {
56     while (start < value.length() && std::isspace(value[start]))
57         ++start;
58     return start;
59 }
60
61 size_t checkChar(const std::string& value, size_t start, char ch)
62 {
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]) + "'.");
67     return start + 1;
68 }
69
70 std::pair<size_t, std::string> readString(const std::string& value, size_t start)
71 {
72     std::string dest;
73     while (start < value.length() && !std::isspace(value[start]) &&
74            value[start] != ',' && value[start] != '(' && value[start] != ')')
75         dest.push_back(value[start++]);
76     if (dest.empty()) {
77         if (start == value.length())
78             throw ParserError(start, "Unexpected end of string. Expected string.");
79         else
80             throw ParserError(start, "Unexpected whitespace. Expected string.");
81     }
82     return std::make_pair(start, dest);
83 }
84
85 Config::Pairs toPairs(const std::vector<std::string>& values)
86 {
87     if (values.empty())
88         return Config::Pairs();
89     std::string value(values[0]);
90     Config::Pairs res;
91     size_t start = 0;
92     while (start < value.size()) {
93         Config::Pair pair;
94         start = skipSpaces(value, start);
95         if (!res.empty())
96         {
97             start = checkChar(value, start, ',');
98             start = skipSpaces(value, start);
99         }
100         size_t pairStart = start;
101         start = checkChar(value, start, '(');
102         const std::pair<size_t, std::string> key = readString(value, start);
103         start = key.first;
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);
109         start = val.first;
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.");
115         res.insert(pair);
116     }
117     return res;
118 }
119
120 bool toBool(const std::vector<std::string>& values)
121 {
122     if (values.empty())
123         return false;
124     std::string value(values[0]);
125     return strncasecmp(value.c_str(), "yes", 3) == 0;
126 }
127
128 std::string toString(const std::vector<std::string>& values)
129 {
130     if (values.empty())
131         return "";
132     return values[0];
133 }
134
135 uid_t toUID(const std::vector<std::string>& values)
136 {
137     if (values.empty())
138         return -1;
139     uid_t res = str2uid(values[0]);
140     if (res == static_cast<uid_t>(-1))
141         throw ParserError("Invalid user name: '" + values[0] + "'");
142     return res;
143 }
144
145 gid_t toGID(const std::vector<std::string>& values)
146 {
147     if (values.empty())
148         return -1;
149     gid_t res = str2gid(values[0]);
150     if (res == static_cast<gid_t>(-1))
151         throw ParserError("Invalid group name: '" + values[0] + "'");
152     return res;
153 }
154
155 mode_t toMode(const std::vector<std::string>& values)
156 {
157     if (values.empty())
158         return -1;
159     mode_t res = str2mode(values[0]);
160     if (res == static_cast<mode_t>(-1))
161         throw ParserError("Invalid mode: '" + values[0] + "'");
162     return res;
163 }
164
165 template <typename T>
166 T toInt(const std::vector<std::string>& values)
167 {
168     if (values.empty())
169         return 0;
170     T res = 0;
171     if (str2x(values[0], res) == 0)
172         return res;
173     return 0;
174 }
175
176 uint16_t toPort(const std::string& value)
177 {
178     if (value.empty())
179         return 0;
180     uint16_t res = 0;
181     if (str2x(value, res) == 0)
182         return res;
183     throw ParserError("'" + value + "' is not a valid port number.");
184 }
185
186 typedef std::map<std::string, Config::ReturnCode> Codes;
187
188 // One-time call to initialize the list of codes.
189 Codes getCodes()
190 {
191     Codes res;
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;
201     return res;
202 }
203
204 Config::ReturnCode toReturnCode(const std::vector<std::string>& values)
205 {
206     static Codes codes(getCodes());
207     if (values.empty())
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;
213     return it->second;
214 }
215
216 Config::Pairs parseVector(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
217 {
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();
222 }
223
224 Config::Authorize parseAuthorize(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
225 {
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();
230 }
231
232 Config::ReturnCode parseReturnCode(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
233 {
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;
238 }
239
240 bool parseBool(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
241 {
242     for (size_t i = 0; i < params.size(); ++i)
243         if (params[i].param == paramName)
244             return toBool(params[i].value);
245     return false;
246 }
247
248 std::string parseString(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
249 {
250     for (size_t i = 0; i < params.size(); ++i)
251         if (params[i].param == paramName)
252             return toString(params[i].value);
253     return "";
254 }
255
256 std::string parseAddress(Config::Type connectionType, const std::string& value)
257 {
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);
268 }
269
270 std::string parsePort(Config::Type connectionType, const std::string& value)
271 {
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)
276         return "";
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);
282 }
283
284 Config::Type parseConnectionType(const std::string& address)
285 {
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));
290     if (type == "unix")
291         return Config::UNIX;
292     else if (type == "tcp")
293         return Config::TCP;
294     throw ParserError("Invalid connection type. Should be either 'unix' or 'tcp', got '" + type + "'");
295 }
296
297 Config::Section parseSection(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
298 {
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();
307 }
308
309 uid_t parseUID(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
310 {
311     for (size_t i = 0; i < params.size(); ++i)
312         if (params[i].param == paramName)
313             return toUID(params[i].value);
314     return -1;
315 }
316
317 gid_t parseGID(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
318 {
319     for (size_t i = 0; i < params.size(); ++i)
320         if (params[i].param == paramName)
321             return toGID(params[i].value);
322     return -1;
323 }
324
325 mode_t parseMode(const std::string& paramName, const std::vector<PARAM_VALUE>& params)
326 {
327     for (size_t i = 0; i < params.size(); ++i)
328         if (params[i].param == paramName)
329             return toMode(params[i].value);
330     return -1;
331 }
332
333 } // namespace anonymous
334
335 bool Config::Authorize::check(const USER& user, const Config::Pairs& radiusData) const
336 {
337     if (!m_auth)
338         return false; // No flag - no authorization.
339
340     if (m_cond.empty())
341         return true; // Empty parameter - always authorize.
342
343     Config::Pairs::const_iterator it = m_cond.begin();
344     for (; it != m_cond.end(); ++it)
345     {
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.
351     }
352
353     return true;
354 }
355
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))
372 {
373     size_t count = 0;
374     if (autz.authorize.exists())
375         ++count;
376     if (auth.authorize.exists())
377         ++count;
378     if (postauth.authorize.exists())
379         ++count;
380     if (preacct.authorize.exists())
381         ++count;
382     if (acct.authorize.exists())
383         ++count;
384     if (count > 0)
385         throw ParserError("Authorization flag is specified in more than one section.");
386 }