]> git.stg.codes - stg.git/blob - projects/rlm_stg/rlm_stg.cpp
Синхронізовано з CVS
[stg.git] / projects / rlm_stg / rlm_stg.cpp
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 <cstdio>
30 #include <cstdlib>
31 #include <cstring>
32
33 extern "C" {
34 //#include <freeradius/libradius.h>
35 #include "radiusd.h"
36 #include "modules.h"
37 //#include <freeradius/conffile.h>
38 }
39
40 #include "stg_client.h"
41 #include "common.h"
42
43 STG_CLIENT * cli;
44 volatile time_t stgTime;
45
46 /*
47  *      Define a structure for our module configuration.
48  *
49  *      These variables do not need to be in a structure, but it's
50  *      a lot cleaner to do so, and a pointer to the structure can
51  *      be used as the instance handle.
52  */
53 typedef struct rlm_stg_t {
54     char * server;
55     char * password;
56     uint32_t port;
57     uint32_t localPort;
58 } rlm_stg_t;
59
60 /*
61  *      A mapping of configuration file names to internal variables.
62  *
63  *      Note that the string is dynamically allocated, so it MUST
64  *      be freed.  When the configuration file parse re-reads the string,
65  *      it free's the old one, and strdup's the new one, placing the pointer
66  *      to the strdup'd string into 'config.string'.  This gets around
67  *      buffer over-flows.
68  */
69 static CONF_PARSER module_config[] = {
70   { "password",  PW_TYPE_STRING_PTR, offsetof(rlm_stg_t,password), NULL,  NULL},
71   { "server",  PW_TYPE_STRING_PTR, offsetof(rlm_stg_t,server), NULL,  NULL},
72   { "port",  PW_TYPE_INTEGER,     offsetof(rlm_stg_t,port), NULL,  "5555" },
73   { "local_port", PW_TYPE_INTEGER,    offsetof(rlm_stg_t,localPort), NULL,   "0" },
74
75   { NULL, -1, 0, NULL, NULL }           /* end the list */
76 };
77
78 /*
79  *      Do any per-module initialization that is separate to each
80  *      configured instance of the module.  e.g. set up connections
81  *      to external databases, read configuration files, set up
82  *      dictionary entries, etc.
83  *
84  *      If configuration information is given in the config section
85  *      that must be referenced in later calls, store a handle to it
86  *      in *instance otherwise put a null pointer there.
87  */
88 static int stg_instantiate(CONF_SECTION *conf, void **instance)
89 {
90         rlm_stg_t *data;
91
92         /*
93          *      Set up a storage area for instance data
94          */
95         DEBUG("rlm_stg: stg_instantiate()");
96         data = (rlm_stg_t *)rad_malloc(sizeof(rlm_stg_t));
97         if (!data) {
98                 return -1;
99         }
100         memset(data, 0, sizeof(rlm_stg_t));
101
102         /*
103          *      If the configuration parameters can't be parsed, then
104          *      fail.
105          */
106         if (cf_section_parse(conf, data, module_config) < 0) {
107                 free(data);
108                 return -1;
109         }
110
111         cli = new STG_CLIENT();
112         cli->SetServer(data->server);
113         cli->SetPort(data->port);
114         cli->SetLocalPort(data->localPort);
115         cli->SetPassword(data->password);
116         if (cli->Start()) {
117             DEBUG("rlm_stg: stg_instantiate() error: '%s'", cli->GetError().c_str());
118             return -1;
119         }
120
121         *instance = data;
122
123         return 0;
124 }
125
126 /*
127  *      Find the named user in this modules database.  Create the set
128  *      of attribute-value pairs to check and reply with for this user
129  *      from the database. The authentication code only needs to check
130  *      the password, the rest is done here.
131  */
132 static int stg_authorize(void *instance, REQUEST *request)
133 {
134         VALUE_PAIR *uname;
135         VALUE_PAIR *pwd;
136         VALUE_PAIR *svc;
137         DEBUG("rlm_stg: stg_authorize()");
138
139         /* quiet the compiler */
140         instance = instance;
141         request = request;
142
143         uname = pairfind(request->packet->vps, PW_USER_NAME);
144         if (uname) {
145             DEBUG("rlm_stg: stg_authorize() user name defined as '%s'", uname->vp_strvalue);
146         } else {
147             DEBUG("rlm_stg: stg_authorize() user name undefined");
148             return RLM_MODULE_FAIL;
149         }
150         if (request->username) {
151             DEBUG("rlm_stg: stg_authorize() request username field: '%s'", request->username->vp_strvalue);
152         }
153         if (request->password) {
154             DEBUG("rlm_stg: stg_authorize() request password field: '%s'", request->password->vp_strvalue);
155         }
156         // Here we need to define Framed-Protocol
157         svc = pairfind(request->packet->vps, PW_SERVICE_TYPE);
158         if (svc) {
159             DEBUG("rlm_stg: stg_authorize() Service-Type defined as '%s'", svc->vp_strvalue);
160             if (cli->Authorize((const char *)request->username->vp_strvalue, (const char *)svc->vp_strvalue)) {
161                 DEBUG("rlm_stg: stg_authorize() stg status: '%s'", cli->GetError().c_str());
162                 return RLM_MODULE_REJECT;
163             }
164         } else {
165             DEBUG("rlm_stg: stg_authorize() Service-Type undefined");
166             if (cli->Authorize((const char *)request->username->vp_strvalue, "")) {
167                 DEBUG("rlm_stg: stg_authorize() stg status: '%s'", cli->GetError().c_str());
168                 return RLM_MODULE_REJECT;
169             }
170         }
171         pwd = pairmake("Cleartext-Password", cli->GetUserPassword().c_str(), T_OP_SET);
172         pairadd(&request->config_items, pwd);
173         //pairadd(&request->reply->vps, uname);
174
175         return RLM_MODULE_UPDATED;
176 }
177
178 /*
179  *      Authenticate the user with the given password.
180  */
181 static int stg_authenticate(void *instance, REQUEST *request)
182 {
183         /* quiet the compiler */
184         VALUE_PAIR *svc;
185         instance = instance;
186         request = request;
187         DEBUG("rlm_stg: stg_authenticate()");
188         svc = pairfind(request->packet->vps, PW_SERVICE_TYPE);
189         if (svc) {
190             DEBUG("rlm_stg: stg_authenticate() Service-Type defined as '%s'", svc->vp_strvalue);
191             if (cli->Authenticate((char *)request->username->vp_strvalue, (const char *)svc->vp_strvalue)) {
192                 DEBUG("rlm_stg: stg_authenticate() stg status: '%s'", cli->GetError().c_str());
193                 return RLM_MODULE_REJECT;
194             }
195         } else {
196             DEBUG("rlm_stg: stg_authenticate() Service-Type undefined");
197             if (cli->Authenticate((char *)request->username->vp_strvalue, "")) {
198                 DEBUG("rlm_stg: stg_authenticate() stg status: '%s'", cli->GetError().c_str());
199                 return RLM_MODULE_REJECT;
200             }
201         }
202
203         return RLM_MODULE_NOOP;
204 }
205
206 /*
207  *      Massage the request before recording it or proxying it
208  */
209 static int stg_preacct(void *instance, REQUEST *request)
210 {
211         /* quiet the compiler */
212         instance = instance;
213         request = request;
214         DEBUG("rlm_stg: stg_preacct()");
215
216         return RLM_MODULE_OK;
217 }
218
219 /*
220  *      Write accounting information to this modules database.
221  */
222 static int stg_accounting(void *instance, REQUEST *request)
223 {
224         /* quiet the compiler */
225         VALUE_PAIR * sttype;
226         VALUE_PAIR * svc;
227         VALUE_PAIR * sessid;
228         svc = pairfind(request->packet->vps, PW_SERVICE_TYPE);
229         instance = instance;
230         request = request;
231         DEBUG("rlm_stg: stg_accounting()");
232
233         sessid = pairfind(request->packet->vps, PW_ACCT_SESSION_ID);
234         if (!sessid) {
235             DEBUG("rlm_stg: stg_accounting() Acct-Session-ID undefined");
236             return RLM_MODULE_FAIL;
237         }
238         sttype = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE);
239         if (sttype) {
240             DEBUG("Acct-Status-Type := %s", sttype->vp_strvalue);
241             if (svc) {
242                 DEBUG("rlm_stg: stg_accounting() Service-Type defined as '%s'", svc->vp_strvalue);
243                 if (cli->Account((const char *)sttype->vp_strvalue, (const char *)request->username->vp_strvalue, (const char *)svc->vp_strvalue, (const char *)sessid->vp_strvalue)) {
244                     DEBUG("rlm_stg: stg_accounting error: '%s'", cli->GetError().c_str());
245                     return RLM_MODULE_FAIL;
246                 }
247             } else {
248                 DEBUG("rlm_stg: stg_accounting() Service-Type undefined");
249                 if (cli->Account((const char *)sttype->vp_strvalue, (const char *)request->username->vp_strvalue, "", (const char *)sessid->vp_strvalue)) {
250                     DEBUG("rlm_stg: stg_accounting error: '%s'", cli->GetError().c_str());
251                     return RLM_MODULE_FAIL;
252                 }
253             }
254         } else {
255             DEBUG("Acct-Status-Type := NULL");
256         }
257
258         return RLM_MODULE_OK;
259 }
260
261 /*
262  *      See if a user is already logged in. Sets request->simul_count to the
263  *      current session count for this user and sets request->simul_mpp to 2
264  *      if it looks like a multilink attempt based on the requested IP
265  *      address, otherwise leaves request->simul_mpp alone.
266  *
267  *      Check twice. If on the first pass the user exceeds his
268  *      max. number of logins, do a second pass and validate all
269  *      logins by querying the terminal server (using eg. SNMP).
270  */
271 static int stg_checksimul(void *instance, REQUEST *request)
272 {
273         instance = instance;
274         DEBUG("rlm_stg: stg_checksimul()");
275
276         request->simul_count=0;
277
278         return RLM_MODULE_OK;
279 }
280
281 static int stg_postauth(void *instance, REQUEST *request)
282 {
283         instance = instance;
284         VALUE_PAIR *fia;
285         VALUE_PAIR *svc;
286         struct in_addr fip;
287         DEBUG("rlm_stg: stg_postauth()");
288         svc = pairfind(request->packet->vps, PW_SERVICE_TYPE);
289         if (svc) {
290             DEBUG("rlm_stg: stg_postauth() Service-Type defined as '%s'", svc->vp_strvalue);
291             if (cli->PostAuthenticate((const char *)request->username->vp_strvalue, (const char *)svc->vp_strvalue)) {
292                 DEBUG("rlm_stg: stg_postauth() error: '%s'", cli->GetError().c_str());
293                 return RLM_MODULE_FAIL;
294             }
295         } else {
296             DEBUG("rlm_stg: stg_postauth() Service-Type undefined");
297             if (cli->PostAuthenticate((const char *)request->username->vp_strvalue, "")) {
298                 DEBUG("rlm_stg: stg_postauth() error: '%s'", cli->GetError().c_str());
299                 return RLM_MODULE_FAIL;
300             }
301         }
302         if (strncmp((const char *)svc->vp_strvalue, "Framed-User", 11) == 0) {
303             fip.s_addr = cli->GetFramedIP();
304             DEBUG("rlm_stg: stg_postauth() ip = '%s'", inet_ntostring(fip.s_addr).c_str());
305             fia = pairmake("Framed-IP-Address", inet_ntostring(fip.s_addr).c_str(), T_OP_SET);
306             pairadd(&request->reply->vps, fia);
307         }
308
309         return RLM_MODULE_UPDATED;
310 }
311
312 static int stg_detach(void *instance)
313 {
314         DEBUG("rlm_stg: stg_detach()");
315         cli->Stop();
316         delete cli;
317         free(((struct rlm_stg_t *)instance)->server);
318         free(((struct rlm_stg_t *)instance)->password);
319         free(instance);
320         return 0;
321 }
322
323 /*
324  *      The module name should be the only globally exported symbol.
325  *      That is, everything else should be 'static'.
326  *
327  *      If the module needs to temporarily modify it's instantiation
328  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
329  *      The server will then take care of ensuring that the module
330  *      is single-threaded.
331  */
332 module_t rlm_stg = {
333         RLM_MODULE_INIT,
334         "stg",
335         RLM_TYPE_THREAD_SAFE,           /* type */
336         stg_instantiate,                /* instantiation */
337         stg_detach,                     /* detach */
338         {
339                 stg_authenticate,       /* authentication */
340                 stg_authorize,  /* authorization */
341                 stg_preacct,    /* preaccounting */
342                 stg_accounting, /* accounting */
343                 stg_checksimul, /* checksimul */
344                 NULL,                   /* pre-proxy */
345                 NULL,                   /* post-proxy */
346                 stg_postauth                    /* post-auth */
347         },
348 };