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