]>
Commit | Line | Data |
---|---|---|
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 | */ | |
26 | ||
27 | #include "include/conf.h" | |
28 | #include "include/defs.h" | |
29 | ||
965c4a6f FM |
30 | #ifdef HAVE_LDAP_H |
31 | #define LDAP_DEPRECATED 1 | |
32 | ||
7179962a PO |
33 | #include <ldap.h> |
34 | #include <ldap_cdefs.h> | |
35 | #include <ldap_features.h> | |
fd75cd90 FM |
36 | |
37 | #if defined(HAVE_ICONV_H) | |
38 | #include <iconv.h> | |
39 | #define USE_ICONV 1 | |
40 | #endif //HAVE_ICONV_H | |
41 | ||
965c4a6f | 42 | #endif //HAVE_LDAP_H |
7179962a | 43 | |
965c4a6f FM |
44 | enum UserTabEnum |
45 | { | |
9bd92830 FM |
46 | //! Users matched against the ::UserTabFile file. |
47 | UTT_File, | |
48 | //! Users matched agains a LDAP. | |
49 | UTT_Ldap, | |
50 | //! No user matching performed. | |
51 | UTT_None | |
965c4a6f FM |
52 | }; |
53 | ||
54 | enum UserTabEnum which_usertab=UTT_None; | |
7179962a | 55 | |
965c4a6f | 56 | static char *userfile=NULL; |
7179962a | 57 | |
965c4a6f FM |
58 | #ifdef HAVE_LDAP_H |
59 | static LDAP *ldap_handle=NULL; | |
60 | #endif //HAVE_LDAP_H | |
61 | ||
fd75cd90 FM |
62 | #ifdef USE_ICONV |
63 | //! iconv conversion descriptor to convert the string returned by LDAP. | |
64 | static iconv_t ldapiconv=(iconv_t)-1; | |
65 | //! Buffer to store the converted string. | |
66 | static char *ldapconvbuffer=NULL; | |
67 | //! Size of the converted string buffer. | |
68 | static int ldapconvbuffersize=0; | |
69 | #endif | |
70 | ||
965c4a6f FM |
71 | static void init_file_usertab(const char *UserTabFile) |
72 | { | |
9bd92830 FM |
73 | FILE *fp_usr; |
74 | long int nreg; | |
75 | char buf[MAXLEN]; | |
76 | int z1, z2; | |
965c4a6f | 77 | |
9bd92830 | 78 | if((fp_usr=fopen(UserTabFile,"r"))==NULL) { |
af961877 | 79 | debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),UserTabFile,strerror(errno)); |
9bd92830 FM |
80 | exit(EXIT_FAILURE); |
81 | } | |
82 | if (fseek(fp_usr, 0, SEEK_END)==-1) { | |
af961877 | 83 | debuga(__FILE__,__LINE__,_("Failed to move till the end of file \"%s\": %s\n"),UserTabFile,strerror(errno)); |
9bd92830 FM |
84 | exit(EXIT_FAILURE); |
85 | } | |
86 | nreg = ftell(fp_usr); | |
87 | if (nreg<0) { | |
af961877 | 88 | debuga(__FILE__,__LINE__,_("Cannot get the size of file \"%s\"\n"),UserTabFile); |
9bd92830 FM |
89 | exit(EXIT_FAILURE); |
90 | } | |
91 | nreg += 100; | |
92 | if (fseek(fp_usr, 0, SEEK_SET)==-1) { | |
af961877 | 93 | debuga(__FILE__,__LINE__,_("Failed to rewind file \"%s\": %s\n"),UserTabFile,strerror(errno)); |
9bd92830 FM |
94 | exit(EXIT_FAILURE); |
95 | } | |
96 | if((userfile=(char *) malloc(nreg))==NULL){ | |
af961877 | 97 | debuga(__FILE__,__LINE__,_("ERROR: Cannot load. Memory fault\n")); |
9bd92830 FM |
98 | exit(EXIT_FAILURE); |
99 | } | |
100 | userfile[0]='\t'; | |
101 | z2=1; | |
102 | while(fgets(buf,sizeof(buf),fp_usr)!=NULL) { | |
103 | if (buf[0]=='#') continue; | |
104 | fixendofline(buf); | |
105 | z1=0; | |
106 | while(buf[z1] && (unsigned char)buf[z1]>' ') { | |
107 | if (z2+3>=nreg) { //need at least 3 additional bytes for the minimum string "\n\t\0" | |
af961877 | 108 | debuga(__FILE__,__LINE__,_("The list of users is too long in file \"%s\"\n"),UserTabFile); |
9bd92830 FM |
109 | exit(EXIT_FAILURE); |
110 | } | |
111 | userfile[z2++]=buf[z1++]; | |
112 | } | |
113 | while(buf[z1] && (unsigned char)buf[z1]<=' ') z1++; | |
114 | userfile[z2++]='\n'; | |
115 | while(buf[z1] && (unsigned char)buf[z1]>=' ') { | |
116 | if (z2+2>=nreg) { //need at least 2 additional bytes for "\t\0" | |
af961877 | 117 | debuga(__FILE__,__LINE__,_("The list of users is too long in file \"%s\"\n"),UserTabFile); |
9bd92830 FM |
118 | exit(EXIT_FAILURE); |
119 | } | |
120 | userfile[z2++]=buf[z1++]; | |
121 | } | |
122 | while(userfile[z2-1]==' ') z2--; | |
123 | userfile[z2++]='\t'; | |
124 | } | |
125 | userfile[z2]='\0'; | |
204781f4 | 126 | if (fclose(fp_usr)==EOF) { |
af961877 | 127 | debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),UserTabFile,strerror(errno)); |
204781f4 FM |
128 | exit(EXIT_FAILURE); |
129 | } | |
965c4a6f FM |
130 | } |
131 | ||
132 | static void get_usertab_name(const char *user,char *name,int namelen) | |
133 | { | |
9bd92830 FM |
134 | char warea[MAXLEN]; |
135 | char *str; | |
965c4a6f | 136 | |
9bd92830 FM |
137 | sprintf(warea,"\t%s\n",user); |
138 | if((str=(char *) strstr(userfile,warea)) == (char *) NULL ) { | |
a87d4d11 | 139 | safe_strcpy(name,user,namelen); |
9bd92830 FM |
140 | } else { |
141 | str=strchr(str+1,'\n'); | |
142 | str++; | |
a87d4d11 | 143 | namelen--; |
9bd92830 FM |
144 | for(z1=0; *str != '\t' && z1<namelen ; z1++) { |
145 | name[z1]=*str++; | |
146 | } | |
a87d4d11 | 147 | name[z1]='\0'; |
9bd92830 | 148 | } |
965c4a6f FM |
149 | } |
150 | ||
151 | #ifdef HAVE_LDAP_H | |
152 | static void init_ldap_usertab(void) { | |
0ee7618e FM |
153 | char *ldapuri; |
154 | LDAPURLDesc url; | |
155 | int rc; | |
156 | ||
9bd92830 | 157 | ldap_handle = NULL; |
0ee7618e FM |
158 | |
159 | /* Setting LDAP connection and initializing cache */ | |
160 | memset(&url,0,sizeof(url)); | |
161 | url.lud_scheme = "ldap"; | |
162 | url.lud_host = LDAPHost; | |
163 | url.lud_port = LDAPPort; | |
164 | url.lud_scope = LDAP_SCOPE_DEFAULT; | |
165 | ldapuri = ldap_url_desc2str(&url); | |
166 | if (ldapuri==NULL) { | |
af961877 | 167 | debuga(__FILE__,__LINE__,_("Cannot prepare ldap URI for server %s on port %d\n"),LDAPHost,LDAPPort); |
9bd92830 FM |
168 | exit(EXIT_FAILURE); |
169 | } | |
7179962a | 170 | |
0ee7618e FM |
171 | rc = ldap_initialize(&ldap_handle, ldapuri); |
172 | if (rc != LDAP_SUCCESS) { | |
af961877 | 173 | debuga(__FILE__,__LINE__,_("Unable to connect to LDAP server %s on port %d: %d (%s)\n"), LDAPHost, LDAPPort, rc, ldap_err2string(rc)); |
0ee7618e FM |
174 | exit(EXIT_FAILURE); |
175 | } | |
176 | ldap_memfree(ldapuri); | |
177 | ||
52fe76a6 | 178 | if (ldap_set_option(ldap_handle, LDAP_OPT_REFERRALS, LDAP_OPT_OFF) != LDAP_OPT_SUCCESS) { |
af961877 | 179 | debuga(__FILE__,__LINE__,_("Could not disable LDAP_OPT_REFERRALS\n")); |
52fe76a6 FM |
180 | exit(EXIT_FAILURE); |
181 | } | |
9bd92830 FM |
182 | int ldap_protocol_version = LDAPProtocolVersion; |
183 | if (ldap_set_option(ldap_handle, LDAP_OPT_PROTOCOL_VERSION, &ldap_protocol_version) != LDAP_SUCCESS) { | |
af961877 | 184 | debuga(__FILE__,__LINE__,_("Could not set LDAP protocol version %d\n"), ldap_protocol_version); |
9bd92830 FM |
185 | exit(EXIT_FAILURE); |
186 | } | |
7179962a | 187 | |
9bd92830 | 188 | /* Bind to the LDAP server. */ |
9bd92830 FM |
189 | rc = ldap_simple_bind_s( ldap_handle, LDAPBindDN, LDAPBindPW ); |
190 | if ( rc != LDAP_SUCCESS ) { | |
af961877 | 191 | debuga(__FILE__,__LINE__,_("Cannot bind to LDAP server: %s\n"), ldap_err2string(rc)); |
9bd92830 FM |
192 | exit(EXIT_FAILURE); |
193 | } | |
965c4a6f | 194 | |
fd75cd90 FM |
195 | #ifdef USE_ICONV |
196 | // prepare for the string conversion | |
197 | if (LDAPNativeCharset[0]!='\0') { | |
198 | ldapiconv = iconv_open( LDAPNativeCharset, "UTF-8" ); | |
199 | if (ldapiconv==(iconv_t)-1) { | |
af961877 | 200 | debuga(__FILE__,__LINE__,_("iconv cannot convert from UTF-8 to %s: %s\n"),LDAPNativeCharset,strerror(errno)); |
fd75cd90 FM |
201 | exit(EXIT_FAILURE); |
202 | } | |
203 | } | |
204 | ldapconvbuffer=NULL; | |
205 | ldapconvbuffersize=0; | |
206 | #endif | |
207 | ||
9bd92830 | 208 | /* Initializing cache */ |
7179962a | 209 | |
9bd92830 | 210 | init_cache(); |
7179962a PO |
211 | } |
212 | ||
fd75cd90 FM |
213 | const char * charset_convert( const char * str_in, const char * charset_to ) |
214 | { | |
215 | #ifdef USE_ICONV | |
216 | size_t return_value; | |
217 | const char * str_in_orig; | |
218 | char * str_out; | |
219 | size_t str_in_len; | |
220 | size_t str_out_len; | |
221 | ||
26ed86a9 | 222 | str_in_len = strlen( str_in ) + 1;//process the terminating NUL too |
fd75cd90 FM |
223 | str_out_len = ( 2 * str_in_len ); |
224 | if (ldapconvbuffer==NULL || ldapconvbuffersize<str_out_len) { | |
225 | ldapconvbuffersize=str_out_len; | |
26ed86a9 | 226 | str_out = realloc(ldapconvbuffer,ldapconvbuffersize); |
fd75cd90 | 227 | if (!str_out) { |
af961877 | 228 | debuga(__FILE__,__LINE__,_("Not enough memory to convert a LDAP returned string: %lu bytes required\n"),(unsigned long int)str_out_len); |
fd75cd90 FM |
229 | exit(EXIT_FAILURE); |
230 | } | |
231 | ldapconvbuffer = str_out; | |
232 | } else { | |
233 | str_out = ldapconvbuffer; | |
26ed86a9 | 234 | str_out_len = ldapconvbuffersize; |
fd75cd90 FM |
235 | } |
236 | str_in_orig = str_in; | |
237 | return_value = iconv(ldapiconv, (ICONV_CONST char **)&str_in, &str_in_len, &str_out, &str_out_len ); | |
238 | if ( return_value == ( size_t ) -1 ) { | |
2eb432c7 | 239 | /* TRANSLATORS: The message is followed by the reason for the failure. */ |
af961877 | 240 | debuga(__FILE__,__LINE__,_("iconv failed on string \"%s\":\n"),str_in_orig); |
fd75cd90 FM |
241 | switch ( errno ) { |
242 | /* See "man 3 iconv" for an explanation. */ | |
243 | case EILSEQ: | |
af961877 | 244 | debuga(__FILE__,__LINE__,_("Invalid multibyte sequence.\n")); |
fd75cd90 FM |
245 | break; |
246 | case EINVAL: | |
af961877 | 247 | debuga(__FILE__,__LINE__,_("Incomplete multibyte sequence.\n")); |
fd75cd90 FM |
248 | break; |
249 | case E2BIG: | |
af961877 | 250 | debuga(__FILE__,__LINE__,_("No more room.\n")); |
fd75cd90 FM |
251 | break; |
252 | default: | |
af961877 | 253 | debuga(__FILE__,__LINE__,_("Error: %s.\n"),strerror( errno )); |
fd75cd90 FM |
254 | } |
255 | exit(EXIT_FAILURE); | |
256 | } | |
257 | return(ldapconvbuffer); | |
258 | #else //USE_ICONV | |
259 | return(str_in); | |
260 | #endif //USE_ICONV | |
261 | } | |
262 | ||
965c4a6f FM |
263 | static void get_ldap_name(const char *userlogin,char *mappedname,int namelen) |
264 | { | |
9bd92830 FM |
265 | /* Start searching username in cache */ |
266 | // According to rfc2254 section 4, only *()\ and NUL must be escaped. This list is rather conservative ! | |
267 | const char strictchars[] = " ~!@^&(){}|<>?:;\"\'\\[]`,\r\n\0"; | |
268 | char filtersearch[256], *searched_in_cache; | |
269 | char searchloginname[3*MAX_USER_LEN]; | |
270 | char *attr, **vals; | |
fd75cd90 | 271 | const char *attr_out; |
0ee7618e | 272 | const char *ptr; |
9bd92830 FM |
273 | LDAPMessage *result, *e; |
274 | BerElement *ber; | |
275 | int i; | |
d1d8390c FM |
276 | int slen; |
277 | int rc; | |
0bfbafc0 | 278 | char *attrs[2]; |
0ee7618e FM |
279 | |
280 | searched_in_cache = search_in_cache(userlogin); | |
281 | if (searched_in_cache!=NULL) { | |
a87d4d11 | 282 | safe_strcpy(mappedname, searched_in_cache,namelen); |
0ee7618e FM |
283 | return; |
284 | } | |
1b048c43 | 285 | |
0ee7618e | 286 | // escape characters according to rfc2254 section 4 |
d1d8390c | 287 | for (slen=0 , ptr=userlogin ; slen<sizeof(searchloginname)-1 && *ptr ; ptr++) { |
0ee7618e | 288 | if (strchr(strictchars,*ptr)) { |
d1d8390c | 289 | if (slen+3>=sizeof(searchloginname)-1) break; |
63413116 | 290 | slen+=sprintf(searchloginname+slen,"\\%02X",*ptr); |
9bd92830 | 291 | } else { |
d1d8390c | 292 | searchloginname[slen++]=*ptr; |
9bd92830 FM |
293 | } |
294 | } | |
d1d8390c FM |
295 | searchloginname[slen]='\0'; |
296 | ||
297 | i=0; | |
298 | ptr=LDAPFilterSearch; | |
299 | while (i<sizeof(filtersearch)-1 && *ptr) { | |
300 | if (ptr[0]=='%' && ptr[1]=='s') { | |
301 | if (i+slen>=sizeof(filtersearch)) break; | |
302 | memcpy(filtersearch+i,searchloginname,slen); | |
303 | i+=slen; | |
304 | ptr+=2; | |
305 | } else { | |
306 | filtersearch[i++]=*ptr++; | |
307 | } | |
308 | } | |
309 | filtersearch[i]='\0'; | |
965c4a6f | 310 | |
0ee7618e FM |
311 | /* Search record(s) in LDAP base */ |
312 | attrs[0]=LDAPTargetAttr; | |
0bfbafc0 | 313 | attrs[1]=NULL; |
d1d8390c | 314 | rc= ldap_search_ext_s(ldap_handle, LDAPBaseSearch, LDAP_SCOPE_SUBTREE, filtersearch, attrs, 0, NULL, NULL, NULL, -1, &result); |
0ee7618e | 315 | if (rc != LDAP_SUCCESS) { |
af961877 | 316 | debuga(__FILE__,__LINE__,_("LDAP search failed: %s\nlooking for \"%s\" at or below \"%s\"\n"), ldap_err2string(rc),filtersearch,LDAPBaseSearch); |
a87d4d11 | 317 | safe_strcpy(mappedname,userlogin,namelen); |
0ee7618e FM |
318 | return; |
319 | } | |
965c4a6f | 320 | |
0ee7618e FM |
321 | if (!(e = ldap_first_entry(ldap_handle, result))) { |
322 | insert_to_cache(userlogin, userlogin); | |
a87d4d11 | 323 | safe_strcpy(mappedname, userlogin,namelen); |
0ee7618e FM |
324 | return; |
325 | } | |
965c4a6f | 326 | |
0ee7618e FM |
327 | for (attr = ldap_first_attribute(ldap_handle, e, &ber); attr != NULL; attr = ldap_next_attribute(ldap_handle, e, ber)) { |
328 | if (!strcasecmp(attr, LDAPTargetAttr)) { | |
329 | if ((vals = (char **)ldap_get_values(ldap_handle, e, attr))!=NULL) { | |
fd75cd90 FM |
330 | attr_out = charset_convert( vals[0], LDAPNativeCharset ); |
331 | insert_to_cache(userlogin, attr_out); | |
332 | safe_strcpy(mappedname, attr_out, namelen); | |
0ee7618e | 333 | ldap_memfree(vals); |
9bd92830 | 334 | } |
0ee7618e FM |
335 | ldap_memfree(attr); |
336 | break; | |
337 | } | |
338 | ldap_memfree(attr); | |
9bd92830 | 339 | } |
0ee7618e | 340 | ldap_msgfree(result); |
965c4a6f FM |
341 | } |
342 | #endif //HAVE_LDAP_H | |
343 | ||
344 | void init_usertab(const char *UserTabFile) | |
345 | { | |
9bd92830 | 346 | if (strcmp(UserTabFile, "ldap") == 0) { |
2eb432c7 FM |
347 | if(debug) { |
348 | /* TRANSLATORS: The %s may be the string "ldap" or a file name.*/ | |
af961877 | 349 | debuga(__FILE__,__LINE__,_("Loading User table from \"%s\"\n"),UserTabFile); |
2eb432c7 | 350 | } |
965c4a6f | 351 | #ifdef HAVE_LDAP_H |
9bd92830 FM |
352 | which_usertab=UTT_Ldap; |
353 | init_ldap_usertab(); | |
965c4a6f | 354 | #else |
af961877 | 355 | debuga(__FILE__,__LINE__,_("LDAP module not compiled in sarg\n")); |
9bd92830 | 356 | exit(EXIT_FAILURE); |
965c4a6f | 357 | #endif //HAVE_LDAP_H |
9bd92830 FM |
358 | } else if (UserTabFile[0] != '\0') { |
359 | if(debug) | |
af961877 | 360 | debuga(__FILE__,__LINE__,_("Loading User table from \"%s\"\n"),UserTabFile); |
9bd92830 FM |
361 | which_usertab=UTT_File; |
362 | init_file_usertab(UserTabFile); | |
363 | } else { | |
364 | which_usertab=UTT_None; | |
365 | } | |
965c4a6f FM |
366 | } |
367 | ||
368 | void user_find(char *mappedname, int namelen, const char *userlogin) | |
369 | { | |
9bd92830 FM |
370 | if (which_usertab==UTT_File) { |
371 | get_usertab_name(userlogin,mappedname,namelen); | |
372 | } | |
965c4a6f | 373 | #ifdef HAVE_LDAP_H |
9bd92830 FM |
374 | else if (which_usertab==UTT_Ldap) { |
375 | get_ldap_name(userlogin,mappedname,namelen); | |
376 | } | |
965c4a6f | 377 | #endif //HAVE_LDAP_H |
9bd92830 | 378 | else { |
a87d4d11 | 379 | safe_strcpy(mappedname,userlogin,namelen); |
9bd92830 | 380 | } |
7179962a PO |
381 | } |
382 | ||
965c4a6f FM |
383 | void close_usertab(void) |
384 | { | |
385 | #ifdef HAVE_LDAP_H | |
9bd92830 FM |
386 | if (ldap_handle) { |
387 | destroy_cache(); | |
388 | ldap_unbind(ldap_handle); | |
389 | ldap_handle=NULL; | |
390 | } | |
965c4a6f | 391 | #endif //HAVE_LDAP_H |
fd75cd90 FM |
392 | #ifdef USE_ICONV |
393 | if (ldapiconv!=(iconv_t)-1) { | |
394 | iconv_close (ldapiconv); | |
395 | ldapiconv=(iconv_t)-1; | |
396 | } | |
397 | if (ldapconvbuffer) { | |
398 | free(ldapconvbuffer); | |
399 | ldapconvbuffer=NULL; | |
400 | } | |
401 | #endif // USE_ICONV | |
9bd92830 FM |
402 | if(userfile) { |
403 | free(userfile); | |
404 | userfile=NULL; | |
405 | } | |
7179962a PO |
406 | } |
407 |