]> git.ipfire.org Git - thirdparty/sarg.git/blame - usertab.c
Make b-tree cache functions static.
[thirdparty/sarg.git] / usertab.c
CommitLineData
7179962a 1/*
7179962a 2 * SARG Squid Analysis Report Generator http://sarg.sourceforge.net
110ce984 3 * 1998, 2015
7179962a
PO
4 *
5 * SARG donations:
6 * please look at http://sarg.sourceforge.net/donations.php
ac422f9b
FM
7 * Support:
8 * http://sourceforge.net/projects/sarg/forums/forum/363374
7179962a
PO
9 * ---------------------------------------------------------------------
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
24 *
25 */
9049db37
FM
26/*!\file
27\brief Provide a meanigfull name instead of the user ID or IP address shown in the
28reports.
29*/
7179962a
PO
30
31#include "include/conf.h"
32#include "include/defs.h"
33
965c4a6f
FM
34#ifdef HAVE_LDAP_H
35#define LDAP_DEPRECATED 1
36
7179962a
PO
37#include <ldap.h>
38#include <ldap_cdefs.h>
39#include <ldap_features.h>
fd75cd90
FM
40
41#if defined(HAVE_ICONV_H)
42#include <iconv.h>
43#define USE_ICONV 1
44#endif //HAVE_ICONV_H
45
965c4a6f 46#endif //HAVE_LDAP_H
7179962a 47
9049db37
FM
48/*!
49The possible sources to map the user ID or IP address to the name to display
50in the reports.
51*/
965c4a6f
FM
52enum UserTabEnum
53{
9bd92830
FM
54 //! Users matched against the ::UserTabFile file.
55 UTT_File,
56 //! Users matched agains a LDAP.
57 UTT_Ldap,
58 //! No user matching performed.
59 UTT_None
965c4a6f
FM
60};
61
9049db37
FM
62/*!
63Tell the database source to use to map the user ID or IP address to a meaningfull
64name.
65*/
965c4a6f 66enum UserTabEnum which_usertab=UTT_None;
7179962a 67
965c4a6f 68static char *userfile=NULL;
7179962a 69
965c4a6f
FM
70#ifdef HAVE_LDAP_H
71static LDAP *ldap_handle=NULL;
72#endif //HAVE_LDAP_H
73
fd75cd90
FM
74#ifdef USE_ICONV
75//! iconv conversion descriptor to convert the string returned by LDAP.
76static iconv_t ldapiconv=(iconv_t)-1;
77//! Buffer to store the converted string.
78static char *ldapconvbuffer=NULL;
79//! Size of the converted string buffer.
80static int ldapconvbuffersize=0;
81#endif
82
9049db37
FM
83/*!
84Read the \a UserTabFile database.
85
86The file contains the IP address or ID of the user then some spaces and
87the real name of the user to show in the report.
88
89Any trailing space or tabulation is removed from the real name. The user ID or IP cannot contain
90a space or a tabulation but it may contain any other character, including the colon that was
91forbidden in the past. That change was made to allow IPv6 addresses.
92
93The file may contain comments if the line starts with a #.
94
95\param UserTabFile The name of the file to read.
96*/
965c4a6f
FM
97static void init_file_usertab(const char *UserTabFile)
98{
9bd92830
FM
99 FILE *fp_usr;
100 long int nreg;
101 char buf[MAXLEN];
102 int z1, z2;
965c4a6f 103
9bd92830 104 if((fp_usr=fopen(UserTabFile,"r"))==NULL) {
af961877 105 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),UserTabFile,strerror(errno));
9bd92830
FM
106 exit(EXIT_FAILURE);
107 }
108 if (fseek(fp_usr, 0, SEEK_END)==-1) {
af961877 109 debuga(__FILE__,__LINE__,_("Failed to move till the end of file \"%s\": %s\n"),UserTabFile,strerror(errno));
9bd92830
FM
110 exit(EXIT_FAILURE);
111 }
112 nreg = ftell(fp_usr);
113 if (nreg<0) {
af961877 114 debuga(__FILE__,__LINE__,_("Cannot get the size of file \"%s\"\n"),UserTabFile);
9bd92830
FM
115 exit(EXIT_FAILURE);
116 }
117 nreg += 100;
118 if (fseek(fp_usr, 0, SEEK_SET)==-1) {
af961877 119 debuga(__FILE__,__LINE__,_("Failed to rewind file \"%s\": %s\n"),UserTabFile,strerror(errno));
9bd92830
FM
120 exit(EXIT_FAILURE);
121 }
122 if((userfile=(char *) malloc(nreg))==NULL){
af961877 123 debuga(__FILE__,__LINE__,_("ERROR: Cannot load. Memory fault\n"));
9bd92830
FM
124 exit(EXIT_FAILURE);
125 }
126 userfile[0]='\t';
127 z2=1;
128 while(fgets(buf,sizeof(buf),fp_usr)!=NULL) {
129 if (buf[0]=='#') continue;
130 fixendofline(buf);
131 z1=0;
132 while(buf[z1] && (unsigned char)buf[z1]>' ') {
133 if (z2+3>=nreg) { //need at least 3 additional bytes for the minimum string "\n\t\0"
af961877 134 debuga(__FILE__,__LINE__,_("The list of users is too long in file \"%s\"\n"),UserTabFile);
9bd92830
FM
135 exit(EXIT_FAILURE);
136 }
137 userfile[z2++]=buf[z1++];
138 }
139 while(buf[z1] && (unsigned char)buf[z1]<=' ') z1++;
140 userfile[z2++]='\n';
141 while(buf[z1] && (unsigned char)buf[z1]>=' ') {
142 if (z2+2>=nreg) { //need at least 2 additional bytes for "\t\0"
af961877 143 debuga(__FILE__,__LINE__,_("The list of users is too long in file \"%s\"\n"),UserTabFile);
9bd92830
FM
144 exit(EXIT_FAILURE);
145 }
146 userfile[z2++]=buf[z1++];
147 }
148 while(userfile[z2-1]==' ') z2--;
149 userfile[z2++]='\t';
150 }
151 userfile[z2]='\0';
204781f4 152 if (fclose(fp_usr)==EOF) {
af961877 153 debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),UserTabFile,strerror(errno));
204781f4
FM
154 exit(EXIT_FAILURE);
155 }
965c4a6f
FM
156}
157
9049db37
FM
158/*!
159Get the real name of the user from the usertab file read by init_file_usertab().
160
161\param user The user ID or IP address to search.
162\param name The buffer to store the real name of the user.
163\param namelen The size of the \a name buffer.
164
165If the user ID or IP address isn't found, the output buffer \a name contains
166the unmatched input string.
167*/
965c4a6f
FM
168static void get_usertab_name(const char *user,char *name,int namelen)
169{
9bd92830
FM
170 char warea[MAXLEN];
171 char *str;
965c4a6f 172
9bd92830
FM
173 sprintf(warea,"\t%s\n",user);
174 if((str=(char *) strstr(userfile,warea)) == (char *) NULL ) {
a87d4d11 175 safe_strcpy(name,user,namelen);
9bd92830
FM
176 } else {
177 str=strchr(str+1,'\n');
178 str++;
a87d4d11 179 namelen--;
9bd92830
FM
180 for(z1=0; *str != '\t' && z1<namelen ; z1++) {
181 name[z1]=*str++;
182 }
a87d4d11 183 name[z1]='\0';
9bd92830 184 }
965c4a6f
FM
185}
186
187#ifdef HAVE_LDAP_H
c167ad37
FM
188/*!
189 * \brief Connect to the LDAP server
190 */
191static void connect_ldap(void)
192{
0ee7618e
FM
193 char *ldapuri;
194 LDAPURLDesc url;
195 int rc;
196
c167ad37
FM
197 if (ldap_handle)
198 ldap_unbind(ldap_handle);
0ee7618e
FM
199
200 /* Setting LDAP connection and initializing cache */
201 memset(&url,0,sizeof(url));
202 url.lud_scheme = "ldap";
203 url.lud_host = LDAPHost;
204 url.lud_port = LDAPPort;
205 url.lud_scope = LDAP_SCOPE_DEFAULT;
206 ldapuri = ldap_url_desc2str(&url);
207 if (ldapuri==NULL) {
af961877 208 debuga(__FILE__,__LINE__,_("Cannot prepare ldap URI for server %s on port %d\n"),LDAPHost,LDAPPort);
9bd92830
FM
209 exit(EXIT_FAILURE);
210 }
7179962a 211
0ee7618e
FM
212 rc = ldap_initialize(&ldap_handle, ldapuri);
213 if (rc != LDAP_SUCCESS) {
af961877 214 debuga(__FILE__,__LINE__,_("Unable to connect to LDAP server %s on port %d: %d (%s)\n"), LDAPHost, LDAPPort, rc, ldap_err2string(rc));
0ee7618e
FM
215 exit(EXIT_FAILURE);
216 }
217 ldap_memfree(ldapuri);
218
52fe76a6 219 if (ldap_set_option(ldap_handle, LDAP_OPT_REFERRALS, LDAP_OPT_OFF) != LDAP_OPT_SUCCESS) {
af961877 220 debuga(__FILE__,__LINE__,_("Could not disable LDAP_OPT_REFERRALS\n"));
52fe76a6
FM
221 exit(EXIT_FAILURE);
222 }
9bd92830
FM
223 int ldap_protocol_version = LDAPProtocolVersion;
224 if (ldap_set_option(ldap_handle, LDAP_OPT_PROTOCOL_VERSION, &ldap_protocol_version) != LDAP_SUCCESS) {
af961877 225 debuga(__FILE__,__LINE__,_("Could not set LDAP protocol version %d\n"), ldap_protocol_version);
9bd92830
FM
226 exit(EXIT_FAILURE);
227 }
7179962a 228
9bd92830 229 /* Bind to the LDAP server. */
9bd92830
FM
230 rc = ldap_simple_bind_s( ldap_handle, LDAPBindDN, LDAPBindPW );
231 if ( rc != LDAP_SUCCESS ) {
af961877 232 debuga(__FILE__,__LINE__,_("Cannot bind to LDAP server: %s\n"), ldap_err2string(rc));
9bd92830
FM
233 exit(EXIT_FAILURE);
234 }
c167ad37
FM
235}
236
237/*!
238Initialize the communication with the LDAP server whose name is in
239::LDAPHost and connect to port ::LDAPPort.
240*/
241static void init_ldap_usertab(void)
242{
243 ldap_handle = NULL;
244 connect_ldap();
965c4a6f 245
fd75cd90
FM
246#ifdef USE_ICONV
247 // prepare for the string conversion
248 if (LDAPNativeCharset[0]!='\0') {
249 ldapiconv = iconv_open( LDAPNativeCharset, "UTF-8" );
250 if (ldapiconv==(iconv_t)-1) {
af961877 251 debuga(__FILE__,__LINE__,_("iconv cannot convert from UTF-8 to %s: %s\n"),LDAPNativeCharset,strerror(errno));
fd75cd90
FM
252 exit(EXIT_FAILURE);
253 }
254 }
255 ldapconvbuffer=NULL;
256 ldapconvbuffersize=0;
257#endif
258
9bd92830 259 /* Initializing cache */
9bd92830 260 init_cache();
7179962a
PO
261}
262
fd75cd90
FM
263const char * charset_convert( const char * str_in, const char * charset_to )
264{
265#ifdef USE_ICONV
266 size_t return_value;
267 const char * str_in_orig;
268 char * str_out;
269 size_t str_in_len;
270 size_t str_out_len;
271
26ed86a9 272 str_in_len = strlen( str_in ) + 1;//process the terminating NUL too
fd75cd90
FM
273 str_out_len = ( 2 * str_in_len );
274 if (ldapconvbuffer==NULL || ldapconvbuffersize<str_out_len) {
275 ldapconvbuffersize=str_out_len;
26ed86a9 276 str_out = realloc(ldapconvbuffer,ldapconvbuffersize);
fd75cd90 277 if (!str_out) {
af961877 278 debuga(__FILE__,__LINE__,_("Not enough memory to convert a LDAP returned string: %lu bytes required\n"),(unsigned long int)str_out_len);
fd75cd90
FM
279 exit(EXIT_FAILURE);
280 }
281 ldapconvbuffer = str_out;
282 } else {
283 str_out = ldapconvbuffer;
26ed86a9 284 str_out_len = ldapconvbuffersize;
fd75cd90
FM
285 }
286 str_in_orig = str_in;
287 return_value = iconv(ldapiconv, (ICONV_CONST char **)&str_in, &str_in_len, &str_out, &str_out_len );
288 if ( return_value == ( size_t ) -1 ) {
2eb432c7 289 /* TRANSLATORS: The message is followed by the reason for the failure. */
af961877 290 debuga(__FILE__,__LINE__,_("iconv failed on string \"%s\":\n"),str_in_orig);
fd75cd90
FM
291 switch ( errno ) {
292 /* See "man 3 iconv" for an explanation. */
293 case EILSEQ:
af961877 294 debuga(__FILE__,__LINE__,_("Invalid multibyte sequence.\n"));
fd75cd90
FM
295 break;
296 case EINVAL:
af961877 297 debuga(__FILE__,__LINE__,_("Incomplete multibyte sequence.\n"));
fd75cd90
FM
298 break;
299 case E2BIG:
af961877 300 debuga(__FILE__,__LINE__,_("No more room.\n"));
fd75cd90
FM
301 break;
302 default:
af961877 303 debuga(__FILE__,__LINE__,_("Error: %s.\n"),strerror( errno ));
fd75cd90
FM
304 }
305 exit(EXIT_FAILURE);
306 }
307 return(ldapconvbuffer);
308#else //USE_ICONV
309 return(str_in);
310#endif //USE_ICONV
311}
312
9049db37
FM
313/*!
314Get the real name of a user by searching the userlogin (user ID) in a LDAP.
315
316\param userlogin The user ID to search.
317\param name The buffer to store the real name of the user.
318\param namelen The size of the \a name buffer.
319
320If the user ID isn't found in the LDAP, the output buffer \a name contains
321the unmatched input string.
322*/
965c4a6f
FM
323static void get_ldap_name(const char *userlogin,char *mappedname,int namelen)
324{
9bd92830
FM
325 /* Start searching username in cache */
326 // According to rfc2254 section 4, only *()\ and NUL must be escaped. This list is rather conservative !
327 const char strictchars[] = " ~!@^&(){}|<>?:;\"\'\\[]`,\r\n\0";
328 char filtersearch[256], *searched_in_cache;
329 char searchloginname[3*MAX_USER_LEN];
330 char *attr, **vals;
fd75cd90 331 const char *attr_out;
0ee7618e 332 const char *ptr;
9bd92830
FM
333 LDAPMessage *result, *e;
334 BerElement *ber;
335 int i;
d1d8390c
FM
336 int slen;
337 int rc;
0bfbafc0 338 char *attrs[2];
0ee7618e
FM
339
340 searched_in_cache = search_in_cache(userlogin);
341 if (searched_in_cache!=NULL) {
a87d4d11 342 safe_strcpy(mappedname, searched_in_cache,namelen);
0ee7618e
FM
343 return;
344 }
1b048c43 345
0ee7618e 346 // escape characters according to rfc2254 section 4
d1d8390c 347 for (slen=0 , ptr=userlogin ; slen<sizeof(searchloginname)-1 && *ptr ; ptr++) {
0ee7618e 348 if (strchr(strictchars,*ptr)) {
d1d8390c 349 if (slen+3>=sizeof(searchloginname)-1) break;
63413116 350 slen+=sprintf(searchloginname+slen,"\\%02X",*ptr);
9bd92830 351 } else {
d1d8390c 352 searchloginname[slen++]=*ptr;
9bd92830
FM
353 }
354 }
d1d8390c
FM
355 searchloginname[slen]='\0';
356
357 i=0;
358 ptr=LDAPFilterSearch;
359 while (i<sizeof(filtersearch)-1 && *ptr) {
360 if (ptr[0]=='%' && ptr[1]=='s') {
361 if (i+slen>=sizeof(filtersearch)) break;
362 memcpy(filtersearch+i,searchloginname,slen);
363 i+=slen;
364 ptr+=2;
365 } else {
366 filtersearch[i++]=*ptr++;
367 }
368 }
369 filtersearch[i]='\0';
965c4a6f 370
0ee7618e
FM
371 /* Search record(s) in LDAP base */
372 attrs[0]=LDAPTargetAttr;
0bfbafc0 373 attrs[1]=NULL;
c167ad37 374 rc=ldap_search_ext_s(ldap_handle, LDAPBaseSearch, LDAP_SCOPE_SUBTREE, filtersearch, attrs, 0, NULL, NULL, NULL, -1, &result);
0ee7618e 375 if (rc != LDAP_SUCCESS) {
c167ad37
FM
376 /*
377 * We know the connection was successfully established once. If it fails now,
378 * it may be because the server timed out between two requests or because
379 * there is an error in the request.
380 *
381 * Just in case the failure is due to a timeout, we try to connect and send
382 * the query again.
383 */
384 connect_ldap();
385 rc=ldap_search_ext_s(ldap_handle, LDAPBaseSearch, LDAP_SCOPE_SUBTREE, filtersearch, attrs, 0, NULL, NULL, NULL, -1, &result);
386 if (rc != LDAP_SUCCESS) {
387 debuga(__FILE__,__LINE__,_("LDAP search failed: %s\nlooking for \"%s\" at or below \"%s\"\n"), ldap_err2string(rc),filtersearch,LDAPBaseSearch);
388 safe_strcpy(mappedname,userlogin,namelen);
389 return;
390 }
0ee7618e 391 }
965c4a6f 392
0ee7618e
FM
393 if (!(e = ldap_first_entry(ldap_handle, result))) {
394 insert_to_cache(userlogin, userlogin);
a87d4d11 395 safe_strcpy(mappedname, userlogin,namelen);
0ee7618e
FM
396 return;
397 }
965c4a6f 398
0ee7618e
FM
399 for (attr = ldap_first_attribute(ldap_handle, e, &ber); attr != NULL; attr = ldap_next_attribute(ldap_handle, e, ber)) {
400 if (!strcasecmp(attr, LDAPTargetAttr)) {
401 if ((vals = (char **)ldap_get_values(ldap_handle, e, attr))!=NULL) {
fd75cd90
FM
402 attr_out = charset_convert( vals[0], LDAPNativeCharset );
403 insert_to_cache(userlogin, attr_out);
404 safe_strcpy(mappedname, attr_out, namelen);
0ee7618e 405 ldap_memfree(vals);
9bd92830 406 }
0ee7618e
FM
407 ldap_memfree(attr);
408 break;
409 }
410 ldap_memfree(attr);
9bd92830 411 }
0ee7618e 412 ldap_msgfree(result);
965c4a6f
FM
413}
414#endif //HAVE_LDAP_H
415
9049db37
FM
416/*!
417Initialize the data used by user_find().
418
419If \a UserTabFile is ldap, the user ID is fetched from a LDAP server.
420
421\param UserTabFile The name of the file to read or ldap. If it is empty, the function does nothing.
422
423\note The memory and resources allocated by this function must be released by
424a call to close_usertab().
425*/
965c4a6f
FM
426void init_usertab(const char *UserTabFile)
427{
9bd92830 428 if (strcmp(UserTabFile, "ldap") == 0) {
2eb432c7
FM
429 if(debug) {
430 /* TRANSLATORS: The %s may be the string "ldap" or a file name.*/
af961877 431 debuga(__FILE__,__LINE__,_("Loading User table from \"%s\"\n"),UserTabFile);
2eb432c7 432 }
965c4a6f 433#ifdef HAVE_LDAP_H
9bd92830
FM
434 which_usertab=UTT_Ldap;
435 init_ldap_usertab();
965c4a6f 436#else
af961877 437 debuga(__FILE__,__LINE__,_("LDAP module not compiled in sarg\n"));
9bd92830 438 exit(EXIT_FAILURE);
965c4a6f 439#endif //HAVE_LDAP_H
9bd92830
FM
440 } else if (UserTabFile[0] != '\0') {
441 if(debug)
af961877 442 debuga(__FILE__,__LINE__,_("Loading User table from \"%s\"\n"),UserTabFile);
9bd92830
FM
443 which_usertab=UTT_File;
444 init_file_usertab(UserTabFile);
445 } else {
446 which_usertab=UTT_None;
447 }
965c4a6f
FM
448}
449
9049db37
FM
450/*!
451Find the real name of the user with the ID or IP address in \a userlogin. The name is fetched
452from the source initialized by init_usertab().
453
454The usertab data must have been initialized by init_usertab().
455
456\param mappedname A buffer to write the real name of the user.
457\param namelen The size of the buffer.
458\param userlogin The ID or IP address of the user.
459*/
965c4a6f
FM
460void user_find(char *mappedname, int namelen, const char *userlogin)
461{
9bd92830
FM
462 if (which_usertab==UTT_File) {
463 get_usertab_name(userlogin,mappedname,namelen);
464 }
965c4a6f 465#ifdef HAVE_LDAP_H
9bd92830
FM
466 else if (which_usertab==UTT_Ldap) {
467 get_ldap_name(userlogin,mappedname,namelen);
468 }
965c4a6f 469#endif //HAVE_LDAP_H
9bd92830 470 else {
a87d4d11 471 safe_strcpy(mappedname,userlogin,namelen);
9bd92830 472 }
7179962a
PO
473}
474
9049db37
FM
475/*!
476Free the memory and resources allocated by init_usertab().
477*/
965c4a6f
FM
478void close_usertab(void)
479{
480#ifdef HAVE_LDAP_H
9bd92830
FM
481 if (ldap_handle) {
482 destroy_cache();
483 ldap_unbind(ldap_handle);
484 ldap_handle=NULL;
485 }
965c4a6f 486#endif //HAVE_LDAP_H
fd75cd90
FM
487#ifdef USE_ICONV
488 if (ldapiconv!=(iconv_t)-1) {
489 iconv_close (ldapiconv);
490 ldapiconv=(iconv_t)-1;
491 }
492 if (ldapconvbuffer) {
493 free(ldapconvbuffer);
494 ldapconvbuffer=NULL;
495 }
496#endif // USE_ICONV
9bd92830
FM
497 if(userfile) {
498 free(userfile);
499 userfile=NULL;
500 }
7179962a
PO
501}
502