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