Added a second mode of operation to cf_section_parse, where it takes a base
[freeradius.git] / src / modules / rlm_example / rlm_example.c
1 /*
2  * rlm_example.c        
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * Copyright 2000  your name <your address>
22  */
23
24 #include "autoconf.h"
25 #include "libradius.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 #include "radiusd.h"
31 #include "modules.h"
32 #include "conffile.h"
33
34 static const char rcsid[] = "$Id$";
35
36 /*
37  *      Define a structure for our module configuration.
38  *
39  *      These variables do not need to be in a structure, but it's
40  *      a lot cleaner to do so, and a pointer to the structure can
41  *      be used as the instance handle.
42  */
43 typedef struct rlm_example_t {
44         int             boolean;
45         int             value;
46         char            *string;
47         uint32_t        ipaddr;
48 } rlm_example_t;
49
50 /*
51  *      A mapping of configuration file names to internal variables.
52  *
53  *      Note that the string is dynamically allocated, so it MUST
54  *      be freed.  When the configuration file parse re-reads the string,
55  *      it free's the old one, and strdup's the new one, placing the pointer
56  *      to the strdup'd string into 'config.string'.  This gets around
57  *      buffer over-flows.
58  */
59 static CONF_PARSER module_config[] = {
60   { "integer", PW_TYPE_INTEGER,    offsetof(rlm_example_t,value), NULL,   "1" },
61   { "boolean", PW_TYPE_BOOLEAN,    offsetof(rlm_example_t,boolean), NULL, "no"},
62   { "string",  PW_TYPE_STRING_PTR, offsetof(rlm_example_t,string), NULL,  NULL},
63   { "ipaddr",  PW_TYPE_IPADDR,     offsetof(rlm_example_t,ipaddr), NULL,  "*" },
64
65   { NULL, -1, 0, NULL, NULL }           /* end the list */
66 };
67
68 /*
69  *      Do any per-module initialization.  e.g. set up connections
70  *      to external databases, read configuration files, set up
71  *      dictionary entries, etc.
72  *
73  *      Try to avoid putting too much stuff in here - it's better to
74  *      do it in instantiate() where it is not global.
75  */
76 static int example_init(void)
77 {
78         /*
79          *      Everything's OK, return without an error.
80          */
81         return 0;
82 }
83
84 /*
85  *      Do any per-module initialization that is separate to each
86  *      configured instance of the module.  e.g. set up connections
87  *      to external databases, read configuration files, set up
88  *      dictionary entries, etc.
89  *
90  *      If configuration information is given in the config section
91  *      that must be referenced in later calls, store a handle to it
92  *      in *instance otherwise put a null pointer there.
93  */
94 static int example_instantiate(CONF_SECTION *conf, void **instance)
95 {
96         rlm_example_t *data;
97         
98         /*
99          *      Set up a storage area for instance data
100          */
101         data = rad_malloc(sizeof(*data));
102
103         /*
104          *      If the configuration parameters can't be parsed, then
105          *      fail.
106          */
107         if (cf_section_parse(conf, data, module_config) < 0) {
108                 free(data);
109                 return -1;
110         }
111         
112         *instance = data;
113         
114         return 0;
115 }
116
117 /*
118  *      Find the named user in this modules database.  Create the set
119  *      of attribute-value pairs to check and reply with for this user
120  *      from the database. The authentication code only needs to check
121  *      the password, the rest is done here.
122  */
123 static int example_authorize(void *instance, REQUEST *request)
124 {
125         VALUE_PAIR *state;
126         VALUE_PAIR *reply;
127
128         /* quiet the compiler */
129         instance = instance;
130         request = request;
131
132         /*
133          *  Look for the 'state' attribute.
134          */
135         state =  pairfind(request->packet->vps, PW_STATE);
136         if (state != NULL) {
137                 DEBUG("rlm_example: Found reply to access challenge");
138                 return RLM_MODULE_OK;
139         }
140
141         /*
142          *  Create the challenge, and add it to the reply.
143          */
144         reply = pairmake("Reply-Message", "This is a challenge", T_OP_EQ);
145         pairadd(&request->reply->vps, reply);
146         state = pairmake("State", "0", T_OP_EQ);
147         pairadd(&request->reply->vps, state);
148
149         /*
150          *  Mark the packet as an Access-Challenge packet.
151          *
152          *  The server will take care of sending it to the user.
153          */
154         request->reply->code = PW_ACCESS_CHALLENGE;
155         DEBUG("rlm_example: Sending Access-Challenge.");
156
157         return RLM_MODULE_HANDLED;
158 }
159
160 /*
161  *      Authenticate the user with the given password.
162  */
163 static int example_authenticate(void *instance, REQUEST *request)
164 {
165         /* quiet the compiler */
166         instance = instance;
167         request = request;
168         
169         return RLM_MODULE_OK;
170 }
171
172 /*
173  *      Massage the request before recording it or proxying it
174  */
175 static int example_preacct(void *instance, REQUEST *request)
176 {
177         /* quiet the compiler */
178         instance = instance;
179         request = request;
180         
181         return RLM_MODULE_OK;
182 }
183
184 /*
185  *      Write accounting information to this modules database.
186  */
187 static int example_accounting(void *instance, REQUEST *request)
188 {
189         /* quiet the compiler */
190         instance = instance;
191         request = request;
192         
193         return RLM_MODULE_OK;
194 }
195
196 /*
197  *      See if a user is already logged in. Sets request->simul_count to the
198  *      current session count for this user and sets request->simul_mpp to 2
199  *      if it looks like a multilink attempt based on the requested IP
200  *      address, otherwise leaves request->simul_mpp alone.
201  *
202  *      Check twice. If on the first pass the user exceeds his
203  *      max. number of logins, do a second pass and validate all
204  *      logins by querying the terminal server (using eg. SNMP).
205  */
206 static int example_checksimul(void *instance, REQUEST *request)
207 {
208   instance = instance;
209
210   request->simul_count=0;
211
212   return RLM_MODULE_OK;
213 }
214
215 static int example_detach(void *instance)
216 {
217         free(((struct rlm_example_t *)instance)->string);
218         free(instance);
219         return 0;
220 }
221
222 /*
223  *      The module name should be the only globally exported symbol.
224  *      That is, everything else should be 'static'.
225  *
226  *      If the module needs to temporarily modify it's instantiation
227  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
228  *      The server will then take care of ensuring that the module
229  *      is single-threaded.
230  */
231 module_t rlm_example = {
232         "example",      
233         RLM_TYPE_THREAD_SAFE,           /* type */
234         example_init,                   /* initialization */
235         example_instantiate,            /* instantiation */
236         example_authorize,              /* authorization */
237         example_authenticate,           /* authentication */
238         example_preacct,                /* preaccounting */
239         example_accounting,             /* accounting */
240         example_checksimul,             /* checksimul */
241         example_detach,                 /* detach */
242         NULL,                           /* destroy */
243 };