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