Pull fix from branch_1_1, so proxied EAP replies work
[freeradius.git] / src / modules / rlm_acct_unique / rlm_acct_unique.c
1 /*
2  * rlm_acct_unique.c
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License as published by
6  *   the Free Software Foundation; either version 2 of the License, or
7  *   (at your option) any later version.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU General Public License for more details.
13  *
14  *   You should have received a copy of the GNU General Public License
15  *   along with this program; if not, write to the Free Software
16  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  * Copyright 2000,2006  The FreeRADIUS server project
19  */
20
21 #include <freeradius-devel/ident.h>
22 RCSID("$Id$")
23
24 #include <freeradius-devel/autoconf.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30
31 #include <freeradius-devel/radiusd.h>
32 #include <freeradius-devel/modules.h>
33
34 /*
35  *  Room for at least 16 attributes.
36  */
37 #define  BUFFERLEN  4096
38
39 typedef struct rlm_acct_unique_list_t {
40         DICT_ATTR                     *dattr;
41         struct rlm_acct_unique_list_t *next;
42 } rlm_acct_unique_list_t;
43
44 typedef struct rlm_acct_unique_t {
45         char                    *key;
46         rlm_acct_unique_list_t *head;
47 } rlm_acct_unique_t;
48
49 static const CONF_PARSER module_config[] = {
50   { "key",  PW_TYPE_STRING_PTR, offsetof(rlm_acct_unique_t,key), NULL,  NULL },
51   { NULL, -1, 0, NULL, NULL }    /* end the list */
52 };
53
54 /*
55  *      Add an attribute to the list.
56  */
57 static void unique_add_attr(rlm_acct_unique_t *inst, DICT_ATTR *dattr)
58 {
59         rlm_acct_unique_list_t  *new;
60
61         new = rad_malloc(sizeof(*new));
62         memset(new, 0, sizeof(*new));
63
64         /* Assign the attr to our new structure */
65         new->dattr = dattr;
66
67         new->next = inst->head;
68         inst->head = new;
69 }
70
71 /*
72  *      Parse a key.
73  */
74 static int unique_parse_key(rlm_acct_unique_t *inst, char *key)
75 {
76         char *ptr, *prev, *keyptr;
77         DICT_ATTR *a;
78
79         prev = key;
80         keyptr = ptr = key;
81
82         /* Let's remove spaces in the string */
83         while (*keyptr) {
84                 if (isspace((int) *keyptr)) {
85                         keyptr++;
86                 } else {
87                         *(ptr++) = *(keyptr++);
88                 }
89         }
90         *ptr = '\0';
91
92
93         keyptr = ptr = key;
94         while(ptr) {
95                 switch(*ptr) {
96                 case ',':
97                         *ptr = '\0';
98                         if((a = dict_attrbyname(prev)) == NULL) {
99                                 radlog(L_ERR, "rlm_acct_unique: Cannot find attribute '%s' in dictionary", prev);
100                                 return -1;
101                         }
102                         *ptr = ',';
103                         prev = ptr+1;
104                         unique_add_attr(inst, a);
105                         break;
106                 case '\0':
107                         if((a = dict_attrbyname(prev)) == NULL) {
108                                 radlog(L_ERR, "rlm_acct_unique: Cannot find attribute '%s' in dictionary", prev);
109                                 return -1;
110                         }
111                         unique_add_attr(inst, a);
112                         return 0;
113                         break;
114                 case ' ':
115                         continue;
116                         break;
117                 }
118                 ptr++;
119         }
120
121         return 0;
122 }
123
124 /*
125  *      Needed before instantiate for cleanup.
126  */
127 static int unique_detach(void *instance)
128 {
129         rlm_acct_unique_t *inst = instance;
130         rlm_acct_unique_list_t *this, *next;
131
132         free(inst->key);
133         for (this = inst->head; this != NULL; this = next) {
134                 next = this->next;
135                 free(this);
136         }
137         free(inst);
138
139         return 0;
140 }
141
142 static int unique_instantiate(CONF_SECTION *conf, void **instance)
143 {
144         rlm_acct_unique_t *inst;
145
146         /*
147          *  Set up a storage area for instance data
148          */
149         inst = rad_malloc(sizeof(*inst));
150         memset(inst, 0, sizeof(*inst));
151
152         if (cf_section_parse(conf, inst, module_config) < 0) {
153                 free(inst);
154                 return -1;
155         }
156
157         /*
158          *      Check to see if 'key' has something in it
159          */
160         if (!inst->key) {
161                 radlog(L_ERR,"rlm_acct_unique: Cannot find value for 'key' in configuration.");
162                 free(inst);
163                 return -1;
164         }
165
166         /*
167          * Go thru the list of keys and build attr_list;
168          */
169         if (unique_parse_key(inst, inst->key) < 0) {
170                 unique_detach(inst); /* clean up memory */
171                 return -1;
172         };
173
174         *instance = inst;
175
176         return 0;
177 }
178
179 /*
180  *  Create a (hopefully) unique Acct-Unique-Session-Id from
181  *  attributes listed in 'key' from radiusd.conf
182  */
183 static int add_unique_id(void *instance, REQUEST *request)
184 {
185         char buffer[BUFFERLEN];
186         u_char md5_buf[16];
187
188         VALUE_PAIR *vp;
189         char *p;
190         int length, left;
191         rlm_acct_unique_t *inst = instance;
192         rlm_acct_unique_list_t *cur;
193
194         /* initialize variables */
195         p = buffer;
196         left = BUFFERLEN;
197         length = 0;
198         cur = inst->head;
199
200         /*
201          *  A unique ID already exists: don't do anything.
202          */
203         vp = pairfind(request->packet->vps, PW_ACCT_UNIQUE_SESSION_ID);
204         if (vp) {
205                 return RLM_MODULE_NOOP;
206         }
207
208         /* loop over items to create unique identifiers */
209         while (cur) {
210                 vp = pairfind(request->packet->vps, cur->dattr->attr);
211                 if (!vp) {
212                         DEBUG2("rlm_acct_unique: WARNING: Attribute %s was not found in request, unique ID MAY be inconsistent", cur->dattr->name);
213                 }
214                 length = vp_prints(p, left, vp);
215                 left -= length + 1;     /* account for ',' in between elements */
216                 p += length;
217                 *(p++) = ',';           /* ensure seperation of elements */
218                 cur = cur->next;
219         }
220         buffer[BUFFERLEN-left-1] = '\0';
221
222         DEBUG2("rlm_acct_unique: Hashing '%s'", buffer);
223         /* calculate a 'unique' string based on the above information */
224         librad_md5_calc(md5_buf, (u_char *)buffer, (p - buffer));
225         sprintf(buffer, "%02x%02x%02x%02x%02x%02x%02x%02x",
226                 md5_buf[0], md5_buf[1], md5_buf[2], md5_buf[3],
227                 md5_buf[4], md5_buf[5], md5_buf[6], md5_buf[7]);
228
229         DEBUG2("rlm_acct_unique: Acct-Unique-Session-ID = \"%s\".", buffer);
230
231         vp = pairmake("Acct-Unique-Session-Id", buffer, 0);
232         if (!vp) {
233                 radlog(L_ERR, "%s", librad_errstr);
234                 return RLM_MODULE_FAIL;
235         }
236
237         /* add the (hopefully) unique session ID to the packet */
238         pairadd(&request->packet->vps, vp);
239
240         return RLM_MODULE_OK;
241 }
242
243 /* globally exported name */
244 module_t rlm_acct_unique = {
245         RLM_MODULE_INIT,
246         "Acct-Unique-Session-Id",
247         0,                              /* type: reserved */
248         unique_instantiate,             /* instantiation */
249         unique_detach,          /* detach */
250         {
251                 NULL,                   /* authentication */
252                 add_unique_id,  /* authorization */
253                 add_unique_id,  /* preaccounting */
254                 add_unique_id,  /* accounting */
255                 NULL,                   /* checksimul */
256                 NULL,                   /* pre-proxy */
257                 NULL,                   /* post-proxy */
258                 NULL                    /* post-auth */
259         },
260 };