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