]> git.stg.codes - stg.git/blob - projects/rlm_stg/rlm_stg.c
Minor fixes in JSON parser.
[stg.git] / projects / rlm_stg / rlm_stg.c
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 /*
22  *  FreeRADIUS module for data access via Stargazer
23  *
24  *  $Revision: 1.8 $
25  *  $Date: 2010/08/14 04:15:08 $
26  *
27  */
28
29 #include "iface.h"
30 #include "stgpair.h"
31
32 #ifndef NDEBUG
33 #define NDEBUG
34 #include <freeradius/ident.h>
35 #include <freeradius/radiusd.h>
36 #include <freeradius/modules.h>
37 #undef NDEBUG
38 #endif
39
40 #include <stddef.h> // size_t
41
42 typedef struct rlm_stg_t {
43     char* address;
44 } rlm_stg_t;
45
46 static const CONF_PARSER module_config[] = {
47   { "address",  PW_TYPE_STRING_PTR, offsetof(rlm_stg_t, address), NULL,  "unix:/var/run/stg.sock"},
48
49   { NULL, -1, 0, NULL, NULL }        /* end the list */
50 };
51
52 static void deletePairs(STG_PAIR* pairs)
53 {
54     free(pairs);
55 }
56
57 static size_t toVPS(const STG_PAIR* pairs, VALUE_PAIR* vps)
58 {
59     const STG_PAIR* pair = pairs;
60     size_t count = 0;
61
62     while (!emptyPair(pair)) {
63         VALUE_PAIR* vp = pairmake(pair->key, pair->value, T_OP_SET);
64         pairadd(&vps, vp);
65         DEBUG("Adding pair '%s': '%s'", pair->key, pair->value);
66         ++pair;
67         ++count;
68     }
69
70     return count;
71 }
72
73 static size_t toReply(STG_RESULT result, REQUEST* request)
74 {
75     size_t count = 0;
76
77     count += toVPS(result.modify, request->config_items);
78     count += toVPS(result.reply, request->reply->vps);
79
80     deletePairs(result.modify);
81     deletePairs(result.reply);
82
83     return count;
84 }
85
86 static int countVPS(const VALUE_PAIR* pairs)
87 {
88     unsigned count = 0;
89     while (pairs != NULL) {
90         ++count;
91         pairs = pairs->next;
92     }
93     return count;
94 }
95
96 static STG_PAIR* fromVPS(const VALUE_PAIR* pairs)
97 {
98     unsigned size = countVPS(pairs);
99     STG_PAIR* res = (STG_PAIR*)malloc(sizeof(STG_PAIR) * (size + 1));
100     size_t pos = 0;
101     while (pairs != NULL) {
102         bzero(res[pos].key, sizeof(res[0].key));
103         bzero(res[pos].value, sizeof(res[0].value));
104         strncpy(res[pos].key, pairs->name, sizeof(res[0].key));
105         strncpy(res[pos].value, pairs->data.strvalue, sizeof(res[0].value));
106         ++pos;
107         pairs = pairs->next;
108     }
109     bzero(res[pos].key, sizeof(res[0].key));
110     bzero(res[pos].value, sizeof(res[0].value));
111     return res;
112 }
113
114 /*
115  *    Do any per-module initialization that is separate to each
116  *    configured instance of the module.  e.g. set up connections
117  *    to external databases, read configuration files, set up
118  *    dictionary entries, etc.
119  *
120  *    If configuration information is given in the config section
121  *    that must be referenced in later calls, store a handle to it
122  *    in *instance otherwise put a null pointer there.
123  */
124 static int stg_instantiate(CONF_SECTION* conf, void** instance)
125 {
126     rlm_stg_t* data;
127
128     /*
129      *    Set up a storage area for instance data
130      */
131     data = rad_malloc(sizeof(*data));
132     if (!data)
133         return -1;
134
135     memset(data, 0, sizeof(*data));
136
137     /*
138      *    If the configuration parameters can't be parsed, then
139      *    fail.
140      */
141     if (cf_section_parse(conf, data, module_config) < 0) {
142         free(data);
143         return -1;
144     }
145
146     if (!stgInstantiateImpl(data->address)) {
147         free(data);
148         return -1;
149     }
150
151     *instance = data;
152
153     return 0;
154 }
155
156 /*
157  *    Find the named user in this modules database.  Create the set
158  *    of attribute-value pairs to check and reply with for this user
159  *    from the database. The authentication code only needs to check
160  *    the password, the rest is done here.
161  */
162 static int stg_authorize(void* instance, REQUEST* request)
163 {
164     STG_RESULT result;
165     STG_PAIR* pairs = fromVPS(request->packet->vps);
166     size_t count = 0;
167     const char* username = NULL;
168     const char* password = NULL;
169
170     instance = instance;
171
172     DEBUG("rlm_stg: stg_authorize()");
173
174     if (request->username) {
175         username = request->username->data.strvalue;
176         DEBUG("rlm_stg: stg_authorize() request username field: '%s'", username);
177     }
178
179     if (request->password) {
180         password = request->password->data.strvalue;
181         DEBUG("rlm_stg: stg_authorize() request password field: '%s'", password);
182     }
183
184     result = stgAuthorizeImpl(username, password, pairs);
185     deletePairs(pairs);
186
187     if (!result.modify && !result.reply) {
188         DEBUG("rlm_stg: stg_authorize() failed.");
189         return RLM_MODULE_REJECT;
190     }
191
192     count = toReply(result, request);
193
194     if (count)
195         return RLM_MODULE_UPDATED;
196
197     return RLM_MODULE_NOOP;
198 }
199
200 /*
201  *    Authenticate the user with the given password.
202  */
203 static int stg_authenticate(void* instance, REQUEST* request)
204 {
205     STG_RESULT result;
206     STG_PAIR* pairs = fromVPS(request->packet->vps);
207     size_t count = 0;
208     const char* username = NULL;
209     const char* password = NULL;
210
211     instance = instance;
212
213     DEBUG("rlm_stg: stg_authenticate()");
214
215     if (request->username) {
216         username = request->username->data.strvalue;
217         DEBUG("rlm_stg: stg_authenticate() request username field: '%s'", username);
218     }
219
220     if (request->password) {
221         password = request->password->data.strvalue;
222         DEBUG("rlm_stg: stg_authenticate() request password field: '%s'", password);
223     }
224
225     result = stgAuthenticateImpl(username, password, pairs);
226     deletePairs(pairs);
227
228     if (!result.modify && !result.reply) {
229         DEBUG("rlm_stg: stg_authenticate() failed.");
230         return RLM_MODULE_REJECT;
231     }
232
233     count = toReply(result, request);
234
235     if (count)
236         return RLM_MODULE_UPDATED;
237
238     return RLM_MODULE_NOOP;
239 }
240
241 /*
242  *    Massage the request before recording it or proxying it
243  */
244 static int stg_preacct(void* instance, REQUEST* request)
245 {
246     STG_RESULT result;
247     STG_PAIR* pairs = fromVPS(request->packet->vps);
248     size_t count = 0;
249     const char* username = NULL;
250     const char* password = NULL;
251
252     DEBUG("rlm_stg: stg_preacct()");
253
254     instance = instance;
255
256     if (request->username) {
257         username = request->username->data.strvalue;
258         DEBUG("rlm_stg: stg_preacct() request username field: '%s'", username);
259     }
260
261     if (request->password) {
262         password = request->password->data.strvalue;
263         DEBUG("rlm_stg: stg_preacct() request password field: '%s'", password);
264     }
265
266     result = stgPreAcctImpl(username, password, pairs);
267     deletePairs(pairs);
268
269     if (!result.modify && !result.reply) {
270         DEBUG("rlm_stg: stg_preacct() failed.");
271         return RLM_MODULE_REJECT;
272     }
273
274     count = toReply(result, request);
275
276     if (count)
277         return RLM_MODULE_UPDATED;
278
279     return RLM_MODULE_NOOP;
280 }
281
282 /*
283  *    Write accounting information to this modules database.
284  */
285 static int stg_accounting(void* instance, REQUEST* request)
286 {
287     STG_RESULT result;
288     STG_PAIR* pairs = fromVPS(request->packet->vps);
289     size_t count = 0;
290     const char* username = NULL;
291     const char* password = NULL;
292
293     DEBUG("rlm_stg: stg_accounting()");
294
295     instance = instance;
296
297     if (request->username) {
298         username = request->username->data.strvalue;
299         DEBUG("rlm_stg: stg_accounting() request username field: '%s'", username);
300     }
301
302     if (request->password) {
303         password = request->password->data.strvalue;
304         DEBUG("rlm_stg: stg_accounting() request password field: '%s'", password);
305     }
306
307     result = stgAccountingImpl(username, password, pairs);
308     deletePairs(pairs);
309
310     if (!result.modify && !result.reply) {
311         DEBUG("rlm_stg: stg_accounting() failed.");
312         return RLM_MODULE_REJECT;
313     }
314
315     count = toReply(result, request);
316
317     if (count)
318         return RLM_MODULE_UPDATED;
319
320     return RLM_MODULE_OK;
321 }
322
323 /*
324  *    See if a user is already logged in. Sets request->simul_count to the
325  *    current session count for this user and sets request->simul_mpp to 2
326  *    if it looks like a multilink attempt based on the requested IP
327  *    address, otherwise leaves request->simul_mpp alone.
328  *
329  *    Check twice. If on the first pass the user exceeds his
330  *    max. number of logins, do a second pass and validate all
331  *    logins by querying the terminal server (using eg. SNMP).
332  */
333 static int stg_checksimul(void* instance, REQUEST* request)
334 {
335     DEBUG("rlm_stg: stg_checksimul()");
336
337     instance = instance;
338
339     request->simul_count = 0;
340
341     return RLM_MODULE_OK;
342 }
343
344 static int stg_postauth(void* instance, REQUEST* request)
345 {
346     STG_RESULT result;
347     STG_PAIR* pairs = fromVPS(request->packet->vps);
348     size_t count = 0;
349     const char* username = NULL;
350     const char* password = NULL;
351
352     DEBUG("rlm_stg: stg_postauth()");
353
354     instance = instance;
355
356     if (request->username) {
357         username = request->username->data.strvalue;
358         DEBUG("rlm_stg: stg_postauth() request username field: '%s'", username);
359     }
360
361     if (request->password) {
362         password = request->password->data.strvalue;
363         DEBUG("rlm_stg: stg_postauth() request password field: '%s'", password);
364     }
365
366     result = stgPostAuthImpl(username, password, pairs);
367     deletePairs(pairs);
368
369     if (!result.modify && !result.reply) {
370         DEBUG("rlm_stg: stg_postauth() failed.");
371         return RLM_MODULE_REJECT;
372     }
373
374     count = toReply(result, request);
375
376     if (count)
377         return RLM_MODULE_UPDATED;
378
379     return RLM_MODULE_NOOP;
380 }
381
382 static int stg_detach(void* instance)
383 {
384     free(((struct rlm_stg_t*)instance)->address);
385     free(instance);
386     return 0;
387 }
388
389 /*
390  *    The module name should be the only globally exported symbol.
391  *    That is, everything else should be 'static'.
392  *
393  *    If the module needs to temporarily modify it's instantiation
394  *    data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
395  *    The server will then take care of ensuring that the module
396  *    is single-threaded.
397  */
398 module_t rlm_stg = {
399     RLM_MODULE_INIT,
400     "stg",
401     RLM_TYPE_THREAD_SAFE, /* type */
402     stg_instantiate,      /* instantiation */
403     stg_detach,           /* detach */
404     {
405         stg_authenticate, /* authentication */
406         stg_authorize,    /* authorization */
407         stg_preacct,      /* preaccounting */
408         stg_accounting,   /* accounting */
409         stg_checksimul,   /* checksimul */
410         NULL,    /* pre-proxy */
411         NULL,   /* post-proxy */
412         stg_postauth      /* post-auth */
413     },
414 };