e9242743470f332b6daae83d194baf3a7b8a111a
[freeradius.git] / src / modules / rlm_acct_unique / rlm_acct_unique.c
1 /*
2  * rlm_acct_unique.c
3  *
4  *  This program is is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License, version 2 if the
6  *  License as published by the Free Software Foundation.
7  * 
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *  
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16  *
17  * Copyright 2000  The FreeRADIUS server project
18  */
19
20 #include "autoconf.h"
21 #include "libradius.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include "radiusd.h"
27 #include "modules.h"
28
29 #define  BUFFERLEN  2048
30
31 static const char rcsid[] = "$Id$";
32
33 typedef struct unique_attr_list {
34         int             attr;
35         struct unique_attr_list *next;
36 } unique_attr_list;
37
38 typedef struct unique_config_t {
39         char                    *key;
40         struct unique_attr_list *head;          
41 } unique_config_t;
42
43 static unique_config_t config;
44
45 static CONF_PARSER module_config[] = {
46   { "key",  PW_TYPE_STRING_PTR, &config.key,  NULL },
47   { NULL, -1, NULL, NULL }    /* end the list */
48 };
49
50 /*
51  *      Add an attribute to the list.
52  */
53 static void unique_add_attr(int dictattr) {
54         struct unique_attr_list         *new;           
55         
56         if((new = malloc(sizeof(struct unique_attr_list))) == NULL) {
57                 radlog(L_ERR, "rlm_acct_unique:  out of memory");
58                 exit(1);
59         }
60         memset((struct unique_attr_list *)new, 0, sizeof(unique_attr_list));
61
62         /* Assign the attr to our new structure */
63         new->attr = dictattr;   
64
65         if (config.head) {
66                 new->next = config.head;
67                 config.head = new;
68         } else {
69                 config.head = new;
70         }
71 }
72
73 /*
74  *      Parse a key.
75  */
76 static int unique_parse_key(char *key) {
77         char *ptr, *prev, *keyptr;
78         DICT_ATTR *a;
79         
80         keyptr = key;
81         ptr = key;
82         prev = key;
83         
84         /* Let's remove spaces in the string */
85         rad_rmspace(key);
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(a->attr); 
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(a->attr);
106                         return 0;
107                         break;
108                 case ' ':
109                         continue;
110                         break;
111                 }
112                 ptr++;  
113         }       
114         
115         return 0;
116 }
117
118 static int unique_instantiate(CONF_SECTION *conf, void **instance) {
119
120         struct unique_config_t *inst;
121
122         /*
123          *  Set up a storage area for instance data
124          */
125         if ((inst = malloc(sizeof(*inst))) == NULL) {
126                 radlog(L_ERR, "rlm_acct_unique:  out of memory");
127                 return -1;
128         }
129         memset(inst, 0, sizeof(*inst));
130         
131         if (cf_section_parse(conf, module_config) < 0) {
132                 free(inst);
133                 return -1;
134         }
135
136         /*
137          *      Grab the key.
138          */
139         inst->key = config.key;
140         config.key = NULL;
141
142         /* 
143          *      Check to see if 'key' has something in it 
144          */     
145         if (!inst->key) {
146                 radlog(L_ERR,"rlm_acct_unique: Cannot find value for 'key' in configuration.");
147                 return -1;
148         }
149
150         /* 
151          * Go thru the list of keys and build attr_list;
152          */     
153         if (unique_parse_key(inst->key) < 0) {
154                 return -1;
155         };
156
157         inst->head = config.head;
158         *instance = inst;
159
160         return 0;
161 }
162
163 /*
164  *  Create a (hopefully) unique Acct-Unique-Session-Id from
165  *  attributes listed in 'key' from radiusd.conf
166  */
167 static int unique_accounting(void *instance, REQUEST *request)
168 {
169   char buffer[BUFFERLEN];
170   u_char md5_buf[16];
171
172   VALUE_PAIR *vp;
173   char *p;
174   int length, left;
175   struct unique_config_t *inst = instance;
176   struct unique_attr_list *cur;
177   
178   /* initialize variables */
179   p = buffer;
180   left = BUFFERLEN;
181   length = 0;
182   cur = inst->head;
183   
184   /* loop over items to create unique identifiers */
185   while (cur) {
186           vp = pairfind(request->packet->vps, cur->attr);
187           length = vp_prints(p, left, vp);
188           left -= length + 1;   /* account for ',' in between elements */
189           p += length;
190           *(p++) = ',';         /* ensure seperation of elements */
191           cur = cur->next;
192   }
193   buffer[BUFFERLEN-left-1] = '\0';
194
195   DEBUG2("rlm_acct_unique: Hashing '%s'", buffer);
196   /* calculate a 'unique' string based on the above information */
197   librad_md5_calc(md5_buf, (u_char *)buffer, (p - buffer));
198   sprintf(buffer, "%02x%02x%02x%02x%02x%02x%02x%02x",
199           md5_buf[0], md5_buf[1], md5_buf[2], md5_buf[3],
200           md5_buf[4], md5_buf[5], md5_buf[6], md5_buf[7]);
201
202   DEBUG2("rlm_acct_unique: Acct-Unique-Session-ID = \"%s\".", buffer);
203   
204   vp = pairmake("Acct-Unique-Session-Id", buffer, 0);
205   if (!vp) {
206           radlog(L_ERR, "%s", librad_errstr);
207           return RLM_MODULE_FAIL;
208   }
209
210   /* add the (hopefully) unique session ID to the packet */
211   pairadd(&request->packet->vps, vp);
212   
213   /* FIXME:  Uncomment here once we iron out module_accounting() */
214   /*return RLM_MODULE_UPDATED;*/
215   return RLM_MODULE_OK;
216 }
217
218 static int unique_detach(void *instance) {
219         struct unique_config_t *inst = instance;
220         struct unique_attr_list *next = inst->head;
221         
222         free(inst->key);
223         while(inst->head) {
224                 next = inst->head->next;
225                 DEBUG("HERE:  %d", inst->head->attr);
226                 free(inst->head);
227                 inst->head = next;
228         }
229         free(inst);
230
231         return 0;
232 }
233
234 /* FIXME: unique_accounting should probably be called from preacct */
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   NULL,                         /* authorization */
242   NULL,                         /* authentication */
243   NULL,                         /* preaccounting */
244   unique_accounting,            /* accounting */
245   NULL,                         /* checksimul */
246   unique_detach,                /* detach */
247   NULL,                         /* destroy */
248 };