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