Automatic search and replace for pairfind.
[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/radiusd.h>
25 #include <freeradius-devel/modules.h>
26
27 #include <ctype.h>
28
29 /*
30  *  Room for at least 16 attributes.
31  */
32 #define  BUFFERLEN  4096
33
34 typedef struct rlm_acct_unique_list_t {
35         DICT_ATTR                     *dattr;
36         struct rlm_acct_unique_list_t *next;
37 } rlm_acct_unique_list_t;
38
39 typedef struct rlm_acct_unique_t {
40         char                    *key;
41         rlm_acct_unique_list_t *head;
42 } rlm_acct_unique_t;
43
44 static const CONF_PARSER module_config[] = {
45   { "key",  PW_TYPE_STRING_PTR, offsetof(rlm_acct_unique_t,key), NULL,  NULL },
46   { NULL, -1, 0, NULL, NULL }    /* end the list */
47 };
48
49 /*
50  *      Add an attribute to the list.
51  */
52 static void unique_add_attr(rlm_acct_unique_t *inst, DICT_ATTR *dattr)
53 {
54         rlm_acct_unique_list_t  *new;
55
56         new = rad_malloc(sizeof(*new));
57         memset(new, 0, sizeof(*new));
58
59         /* Assign the attr to our new structure */
60         new->dattr = dattr;
61
62         new->next = inst->head;
63         inst->head = new;
64 }
65
66 /*
67  *      Parse a key.
68  */
69 static int unique_parse_key(rlm_acct_unique_t *inst, char *key)
70 {
71         char *ptr, *prev, *keyptr;
72         DICT_ATTR *a;
73
74         prev = key;
75         keyptr = ptr = key;
76
77         /* Let's remove spaces in the string */
78         while (*keyptr) {
79                 if (isspace((int) *keyptr)) {
80                         keyptr++;
81                 } else {
82                         *(ptr++) = *(keyptr++);
83                 }
84         }
85         *ptr = '\0';
86
87         ptr = key;
88         while(ptr) {
89                 switch(*ptr) {
90                 case ',':
91                         *ptr = '\0';
92                         if((a = dict_attrbyname(prev)) == NULL) {
93                                 radlog(L_ERR, "rlm_acct_unique: Cannot find attribute '%s' in dictionary", prev);
94                                 return -1;
95                         }
96                         *ptr = ',';
97                         prev = ptr+1;
98                         unique_add_attr(inst, a);
99                         break;
100                 case '\0':
101                         if((a = dict_attrbyname(prev)) == NULL) {
102                                 radlog(L_ERR, "rlm_acct_unique: Cannot find attribute '%s' in dictionary", prev);
103                                 return -1;
104                         }
105                         unique_add_attr(inst, a);
106                         return 0;
107                         break;
108                 case ' ':
109                         continue;
110                         break;
111                 }
112                 ptr++;
113         }
114
115         return 0;
116 }
117
118 /*
119  *      Needed before instantiate for cleanup.
120  */
121 static int unique_detach(void *instance)
122 {
123         rlm_acct_unique_t *inst = instance;
124         rlm_acct_unique_list_t *this, *next;
125
126         for (this = inst->head; this != NULL; this = next) {
127                 next = this->next;
128                 free(this);
129         }
130         free(inst);
131
132         return 0;
133 }
134
135 static int unique_instantiate(CONF_SECTION *conf, void **instance)
136 {
137         rlm_acct_unique_t *inst;
138
139         /*
140          *  Set up a storage area for instance data
141          */
142         inst = rad_malloc(sizeof(*inst));
143         memset(inst, 0, sizeof(*inst));
144
145         if (cf_section_parse(conf, inst, module_config) < 0) {
146                 free(inst);
147                 return -1;
148         }
149
150         /*
151          *      Check to see if 'key' has something in it
152          */
153         if (!inst->key) {
154                 radlog(L_ERR,"rlm_acct_unique: Cannot find value for 'key' in configuration.");
155                 free(inst);
156                 return -1;
157         }
158
159         /*
160          * Go thru the list of keys and build attr_list;
161          */
162         if (unique_parse_key(inst, inst->key) < 0) {
163                 unique_detach(inst); /* clean up memory */
164                 return -1;
165         };
166
167         *instance = inst;
168
169         return 0;
170 }
171
172 /*
173  *  Create a (hopefully) unique Acct-Unique-Session-Id from
174  *  attributes listed in 'key' from radiusd.conf
175  */
176 static int add_unique_id(void *instance, REQUEST *request)
177 {
178         char buffer[BUFFERLEN];
179         u_char md5_buf[16];
180
181         VALUE_PAIR *vp;
182         char *p;
183         int length, left;
184         rlm_acct_unique_t *inst = instance;
185         rlm_acct_unique_list_t *cur;
186
187         /* initialize variables */
188         p = buffer;
189         left = BUFFERLEN;
190         cur = inst->head;
191
192         /*
193          *  A unique ID already exists: don't do anything.
194          */
195         vp = pairfind(request->packet->vps, PW_ACCT_UNIQUE_SESSION_ID, 0);
196         if (vp) {
197                 return RLM_MODULE_NOOP;
198         }
199
200         /* loop over items to create unique identifiers */
201         while (cur) {
202                 VALUE_PAIR hack;
203
204                 vp = pairfind(request->packet->vps, cur->dattr->attr);
205                 if (!vp) {
206                         /*
207                          *      This was changed in 2.x, but it's still
208                          *      useful.
209                          */
210                         if ((cur->dattr->attr == PW_CLIENT_IP_ADDRESS) &&
211                             (request->packet->src_ipaddr.af == AF_INET)) {
212                                 memset(&hack, 0, sizeof(hack));
213                                 hack.name = cur->dattr->name;
214                                 hack.attribute = cur->dattr->attr;
215                                 hack.type = cur->dattr->type;
216                                 hack.operator = T_OP_EQ;
217                                 hack.length = 4;
218                                 hack.lvalue = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
219                                 vp = &hack;
220                         } else {
221                                 RDEBUG2("WARNING: Attribute %s was not found in request, unique ID MAY be inconsistent", cur->dattr->name);
222                         }
223                 }
224                 length = vp_prints(p, left, vp);
225                 left -= length + 1;     /* account for ',' in between elements */
226                 p += length;
227                 *(p++) = ',';           /* ensure seperation of elements */
228                 cur = cur->next;
229         }
230         buffer[BUFFERLEN-left-1] = '\0';
231
232         RDEBUG2("Hashing '%s'", buffer);
233         /* calculate a 'unique' string based on the above information */
234         fr_md5_calc(md5_buf, (u_char *)buffer, (p - buffer));
235         sprintf(buffer, "%02x%02x%02x%02x%02x%02x%02x%02x",
236                 md5_buf[0], md5_buf[1], md5_buf[2], md5_buf[3],
237                 md5_buf[4], md5_buf[5], md5_buf[6], md5_buf[7]);
238
239         RDEBUG2("Acct-Unique-Session-ID = \"%s\".", buffer);
240
241         vp = pairmake("Acct-Unique-Session-Id", buffer, 0);
242         if (!vp) {
243                 radlog(L_ERR, "%s", fr_strerror());
244                 return RLM_MODULE_FAIL;
245         }
246
247         /* add the (hopefully) unique session ID to the packet */
248         pairadd(&request->packet->vps, vp);
249
250         return RLM_MODULE_OK;
251 }
252
253 /* globally exported name */
254 module_t rlm_acct_unique = {
255         RLM_MODULE_INIT,
256         "Acct-Unique-Session-Id",
257         RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
258         unique_instantiate,             /* instantiation */
259         unique_detach,          /* detach */
260         {
261                 NULL,                   /* authentication */
262                 add_unique_id,  /* authorization */
263                 add_unique_id,  /* preaccounting */
264                 add_unique_id,  /* accounting */
265                 NULL,                   /* checksimul */
266                 NULL,                   /* pre-proxy */
267                 NULL,                   /* post-proxy */
268                 NULL                    /* post-auth */
269         },
270 };