Added -C command-line option, documentation, debug messages,
[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
88         keyptr = ptr = key;
89         while(ptr) {
90                 switch(*ptr) {
91                 case ',':
92                         *ptr = '\0';
93                         if((a = dict_attrbyname(prev)) == NULL) {
94                                 radlog(L_ERR, "rlm_acct_unique: Cannot find attribute '%s' in dictionary", prev);
95                                 return -1;
96                         }
97                         *ptr = ',';
98                         prev = ptr+1;
99                         unique_add_attr(inst, a);
100                         break;
101                 case '\0':
102                         if((a = dict_attrbyname(prev)) == NULL) {
103                                 radlog(L_ERR, "rlm_acct_unique: Cannot find attribute '%s' in dictionary", prev);
104                                 return -1;
105                         }
106                         unique_add_attr(inst, a);
107                         return 0;
108                         break;
109                 case ' ':
110                         continue;
111                         break;
112                 }
113                 ptr++;
114         }
115
116         return 0;
117 }
118
119 /*
120  *      Needed before instantiate for cleanup.
121  */
122 static int unique_detach(void *instance)
123 {
124         rlm_acct_unique_t *inst = instance;
125         rlm_acct_unique_list_t *this, *next;
126
127         for (this = inst->head; this != NULL; this = next) {
128                 next = this->next;
129                 free(this);
130         }
131         free(inst);
132
133         return 0;
134 }
135
136 static int unique_instantiate(CONF_SECTION *conf, void **instance)
137 {
138         rlm_acct_unique_t *inst;
139
140         /*
141          *  Set up a storage area for instance data
142          */
143         inst = rad_malloc(sizeof(*inst));
144         memset(inst, 0, sizeof(*inst));
145
146         if (cf_section_parse(conf, inst, module_config) < 0) {
147                 free(inst);
148                 return -1;
149         }
150
151         /*
152          *      Check to see if 'key' has something in it
153          */
154         if (!inst->key) {
155                 radlog(L_ERR,"rlm_acct_unique: Cannot find value for 'key' in configuration.");
156                 free(inst);
157                 return -1;
158         }
159
160         /*
161          * Go thru the list of keys and build attr_list;
162          */
163         if (unique_parse_key(inst, inst->key) < 0) {
164                 unique_detach(inst); /* clean up memory */
165                 return -1;
166         };
167
168         *instance = inst;
169
170         return 0;
171 }
172
173 /*
174  *  Create a (hopefully) unique Acct-Unique-Session-Id from
175  *  attributes listed in 'key' from radiusd.conf
176  */
177 static int add_unique_id(void *instance, REQUEST *request)
178 {
179         char buffer[BUFFERLEN];
180         u_char md5_buf[16];
181
182         VALUE_PAIR *vp;
183         char *p;
184         int length, left;
185         rlm_acct_unique_t *inst = instance;
186         rlm_acct_unique_list_t *cur;
187
188         /* initialize variables */
189         p = buffer;
190         left = BUFFERLEN;
191         length = 0;
192         cur = inst->head;
193
194         /*
195          *  A unique ID already exists: don't do anything.
196          */
197         vp = pairfind(request->packet->vps, PW_ACCT_UNIQUE_SESSION_ID);
198         if (vp) {
199                 return RLM_MODULE_NOOP;
200         }
201
202         /* loop over items to create unique identifiers */
203         while (cur) {
204                 vp = pairfind(request->packet->vps, cur->dattr->attr);
205                 if (!vp) {
206                         DEBUG2("rlm_acct_unique: WARNING: Attribute %s was not found in request, unique ID MAY be inconsistent", cur->dattr->name);
207                 }
208                 length = vp_prints(p, left, vp);
209                 left -= length + 1;     /* account for ',' in between elements */
210                 p += length;
211                 *(p++) = ',';           /* ensure seperation of elements */
212                 cur = cur->next;
213         }
214         buffer[BUFFERLEN-left-1] = '\0';
215
216         DEBUG2("rlm_acct_unique: Hashing '%s'", buffer);
217         /* calculate a 'unique' string based on the above information */
218         librad_md5_calc(md5_buf, (u_char *)buffer, (p - buffer));
219         sprintf(buffer, "%02x%02x%02x%02x%02x%02x%02x%02x",
220                 md5_buf[0], md5_buf[1], md5_buf[2], md5_buf[3],
221                 md5_buf[4], md5_buf[5], md5_buf[6], md5_buf[7]);
222
223         DEBUG2("rlm_acct_unique: Acct-Unique-Session-ID = \"%s\".", buffer);
224
225         vp = pairmake("Acct-Unique-Session-Id", buffer, 0);
226         if (!vp) {
227                 radlog(L_ERR, "%s", librad_errstr);
228                 return RLM_MODULE_FAIL;
229         }
230
231         /* add the (hopefully) unique session ID to the packet */
232         pairadd(&request->packet->vps, vp);
233
234         return RLM_MODULE_OK;
235 }
236
237 /* globally exported name */
238 module_t rlm_acct_unique = {
239         RLM_MODULE_INIT,
240         "Acct-Unique-Session-Id",
241         RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
242         unique_instantiate,             /* instantiation */
243         unique_detach,          /* detach */
244         {
245                 NULL,                   /* authentication */
246                 add_unique_id,  /* authorization */
247                 add_unique_id,  /* preaccounting */
248                 add_unique_id,  /* accounting */
249                 NULL,                   /* checksimul */
250                 NULL,                   /* pre-proxy */
251                 NULL,                   /* post-proxy */
252                 NULL                    /* post-auth */
253         },
254 };