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