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