source: npl/mailserver/dspam/dspam-3.10.2/src/external_lookup.c

Last change on this file was c5c522c, checked in by Edwin Eefting <edwin@datux.nl>, 8 years ago

initial commit, transferred from cleaned syn3 svn tree

  • Property mode set to 100644
File size: 13.4 KB
Line 
1/* $Id: external_lookup.c,v 0.6.1 2009/12/31 04:00:01 sbajic Exp $ */
2
3/*
4 COPYRIGHT (C) 2006 HUGO MONTEIRO
5
6 external lookup library for DSPAM v0.6
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; version 2
11 of the License.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21
22*/
23
24#ifdef HAVE_CONFIG_H
25#include <auto-config.h>
26#endif
27
28#ifdef EXT_LOOKUP
29#define LDAP_DEPRECATED 1
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <signal.h>
37#include <errno.h>
38#include <sys/types.h>
39#include <sys/wait.h>
40#include <sys/time.h>
41#include "libdspam.h"
42#include "external_lookup.h"
43#include "config.h"
44#include "language.h"
45#include "error.h"
46#include "config_shared.h"
47
48/* LDAP */
49#ifdef USE_LDAP
50#   include <ldap.h>
51#   define BIND_TIMEOUT 10
52#endif
53
54void
55sig_alrm(int signum)
56{
57   signum = signum;  /* Keep compiler happy */
58   LOG(LOG_ERR,"%s: Timed out.", ERR_EXT_LOOKUP_INIT_FAIL);
59   exit(200);
60}
61
62
63char*
64external_lookup(config_t agent_config, const char *username, char *external_uid)
65{
66
67        char *driver = _ds_read_attribute(agent_config, "ExtLookupDriver");
68
69        if (strcmp(driver, "ldap") == 0) {
70#ifdef USE_LDAP
71                return ldap_lookup(agent_config, username, external_uid);
72#else
73                LOG(LOG_ERR, "external_lookup: LDAP driver was not enabled at compile time.");
74                return NULL;
75#endif
76        } else if (strcmp(driver, "program") == 0) {
77                return program_lookup(agent_config, username, external_uid);
78        /* add here your 'else if' statements like the one above to extend */
79        } else if (driver == NULL) {
80                LOG(LOG_ERR, "external_lookup: lookup driver not defined");
81                return NULL;
82        } else {
83                LOG(LOG_ERR, "external_lookup: lookup driver %s not yet implemented.", driver);
84                return NULL;
85        }
86}
87
88
89char
90*transcode_query(const char *query, const char *username, char *transcoded_query)
91{       
92       
93        char *saveptr = NULL, *token;
94        int i, j, len, replacements;
95        int namelen = strlen(username);
96        int querylen = strlen(query);
97        char *str = malloc (querylen);
98
99        /* count aprox. the number of replacements.
100         * %% escaping is also accounted and shouldn't
101         * in the TODO */
102        for (replacements = 0, str=strdup(query); ; str = NULL) {
103                token = strtok_r(str, "%", &saveptr);
104                if (token == NULL)
105                        break;
106                if (token[0] == 'u') {
107                        replacements++;
108                }
109        }
110
111        free(str);
112       
113        len = querylen + namelen * replacements - 2 * replacements + 1;
114
115        transcoded_query = malloc (len);
116        memset(transcoded_query, 0, len);
117
118    for (i=j=0;j<len && query[i]; ){
119                if (query[i] == '%') {
120                    switch (query[i+1]) {
121                                case '%': /* escaped '%' character */
122                                    transcoded_query[j++] = '%';
123                                    break;
124                                case 'u': /* paste in the username */
125                                    if (j+namelen>=len) {
126                                                LOG(LOG_ERR, "%s: check ExtLookupQuery", ERR_EXT_LOOKUP_MISCONFIGURED);
127                                                return NULL;
128                                    }
129                                    memcpy (transcoded_query+j, username, namelen);
130                                    j += namelen;
131                                    break;
132                                default: /* unrecognised formatting, abort!  */
133                                        LOG(LOG_ERR, "%s: check ExtLookupQuery", ERR_EXT_LOOKUP_MISCONFIGURED);
134                                return NULL;
135                    }
136                    i += 2;
137                } else {
138                    transcoded_query[j++] = query[i++];
139                }
140        }
141
142    if (j>=len) {
143                LOG(LOG_ERR, "%s: check ExtLookupQuery", ERR_EXT_LOOKUP_MISCONFIGURED);
144                return NULL;
145    }
146    /* and finally zero terminate string */
147    transcoded_query[j] = 0;
148       
149        return transcoded_query;
150}
151
152#ifdef USE_LDAP
153char*
154ldap_lookup(config_t agent_config, const char *username, char *external_uid)
155{
156        LDAP            *ld;
157        LDAPMessage     *result = (LDAPMessage *) 0;
158        LDAPMessage     *e;
159        BerElement      *ber;
160        char            *a, *dn;
161        char            *sane_username = malloc(strlen(username)*2);
162        char            *p = sane_username;
163        char            **vals = NULL;
164        struct          timeval ldaptimeout = {.tv_sec = BIND_TIMEOUT, .tv_usec = 0};
165        int                     i, rc=0, num_entries=0;
166        char            *transcoded_query = NULL;
167        char            *ldap_uri = NULL;
168        char            *end_ptr;
169        char            *ldap_host = _ds_read_attribute(agent_config, "ExtLookupServer");
170        char            *port = _ds_read_attribute(agent_config, "ExtLookupPort");
171        long            lldap_port;
172        int                     ldap_port = 389;
173        char            *ldap_binddn = _ds_read_attribute(agent_config, "ExtLookupLogin");
174        char            *ldap_passwd = _ds_read_attribute(agent_config, "ExtLookupPassword");
175        char            *ldap_base = _ds_read_attribute(agent_config, "ExtLookupDB");
176        char            *ldap_attrs[] = {_ds_read_attribute(agent_config, "ExtLookupLDAPAttribute"),0};
177        char            *version = _ds_read_attribute(agent_config, "ExtLookupLDAPVersion");
178        long            lldap_version;
179        int                     ldap_version = 3;
180        char            *ldap_filter = _ds_read_attribute(agent_config, "ExtLookupQuery");
181        int                     ldap_scope;
182
183        if (port != NULL) {
184                errno=0;
185                lldap_port = strtol(port, &end_ptr, 0);
186                if ( (errno != 0) || (lldap_port < INT_MIN) || (lldap_port > INT_MAX) || (*end_ptr != '\0')) {
187                        LOG(LOG_ERR, "External Lookup: bad LDAP port number");
188                        return NULL;
189                } else
190                        ldap_port = (int)lldap_port;
191        }
192
193        /* set ldap protocol version */
194        if (version != NULL) {
195                errno=0;
196                lldap_version = strtol(version, &end_ptr, 0);
197                if ((errno != 0) || (lldap_version < 1) || (lldap_version > 3) || (*end_ptr != '\0')) {
198                        LOG(LOG_ERR, "External Lookup: bad LDAP protocol version");
199                        return NULL;
200                } else
201                        ldap_version = (int)lldap_version;
202        }
203               
204        if (_ds_match_attribute(agent_config, "ExtLookupLDAPScope", "one"))
205                ldap_scope = LDAP_SCOPE_ONELEVEL;
206        else /* defaults to sub */
207                ldap_scope = LDAP_SCOPE_SUBTREE;
208
209        /* set alarm handler */
210        signal(SIGALRM, sig_alrm);
211
212        /* sanitize username for filter integration */
213        for (; *username != '\0'; username++) {
214                switch(*username) {
215                        case 0x2a: /* '*' */
216                        case 0x28: /* '(' */
217                        case 0x29: /* ')' */
218                        case 0x5c: /* '\' */
219                        case 0x00: /* NUL */
220                                *p++ = 0x5c; /* '\' */
221                                *p++ = *username;
222                                break;
223
224                        default:
225                                *p++ = *username;
226                                break;
227                }
228        }
229
230        *p = '\0';
231
232        LOGDEBUG("External Lookup: sanitized username is %s\n", sane_username);
233
234        /* build proper LDAP filter*/
235        transcoded_query = strdup(transcode_query(ldap_filter, sane_username, transcoded_query));
236        free(sane_username);
237        if (transcoded_query == NULL) {
238                LOG(LOG_ERR, "External Lookup: %s", ERR_EXT_LOOKUP_MISCONFIGURED);
239                return NULL;
240        }
241
242        if( ldap_host != NULL || ldap_port ) {
243        /* construct URL */
244                LDAPURLDesc url;
245                memset( &url, 0, sizeof(url));
246
247                url.lud_scheme = "ldap";
248                url.lud_host = ldap_host;
249                url.lud_port = ldap_port;
250                url.lud_scope = LDAP_SCOPE_SUBTREE;
251
252                ldap_uri = ldap_url_desc2str( &url );
253        }
254
255        rc = ldap_initialize( &ld, ldap_uri );
256        if( rc != LDAP_SUCCESS ) {
257                LOG(LOG_ERR, "External Lookup: Could not create LDAP session handle for URI=%s (%d): %s\n", ldap_uri, rc, ldap_err2string(rc));
258                return NULL;
259        }
260
261        if( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &ldap_version ) != LDAP_OPT_SUCCESS ) {
262                LOG(LOG_ERR, "External Lookup: Could not set LDAP_OPT_PROTOCOL_VERSION %d\n", ldap_version );
263                return NULL;
264        }
265
266        /* use TLS if configured */
267        if ( _ds_match_attribute(agent_config, "ExtLookupCrypto", "tls" )) {
268                if (ldap_version != 3) {
269                        LOG(LOG_ERR, "External Lookup: TLS only supported with LDAP protocol version 3");
270                        return NULL;
271                }
272                if ( ldap_start_tls_s( ld, NULL, NULL ) != LDAP_SUCCESS ) {
273                        LOG(LOG_ERR, "External Lookup: %s: %s (%d)", ERR_EXT_LOOKUP_INIT_FAIL, strerror(errno), errno);
274                        return NULL;
275                }
276        }
277
278        /* schedules alarm */
279        alarm(BIND_TIMEOUT);
280       
281        /* authenticate to the directory */
282        if ( (rc = ldap_simple_bind_s( ld, ldap_binddn, ldap_passwd )) != LDAP_SUCCESS ) {
283                /* cancel alarms */
284                alarm(0);
285               
286                LOG(LOG_ERR, "External Lookup: %s: %s", ERR_EXT_LOOKUP_INIT_FAIL, ldap_err2string(rc) );
287                ldap_unbind(ld);
288                return NULL;
289        }
290        /* cancel alarms */
291        alarm(0);
292       
293        /* search for all entries matching the filter */
294
295        if ( (rc = ldap_search_st( ld,
296                                   ldap_base,
297                                   ldap_scope,
298                                   transcoded_query,
299                                   ldap_attrs,
300                                   0,
301                                   &ldaptimeout,
302                                   &result )) != LDAP_SUCCESS ) {
303
304        free(transcoded_query);
305
306        switch(rc) {
307                case LDAP_TIMEOUT:
308                case LDAP_BUSY:
309                case LDAP_UNAVAILABLE:
310                case LDAP_UNWILLING_TO_PERFORM:
311                case LDAP_SERVER_DOWN:
312                case LDAP_TIMELIMIT_EXCEEDED:
313                        LOG(LOG_ERR, "External Lookup: %s: %s", ERR_EXT_LOOKUP_SEARCH_FAIL, ldap_err2string(ldap_result2error(ld, result, 1)) );
314                        ldap_unbind( ld );
315                        return NULL;
316                        break;
317                case LDAP_FILTER_ERROR:
318                        LOG(LOG_ERR, "External Lookup: %s: %s", ERR_EXT_LOOKUP_SEARCH_FAIL, ldap_err2string(ldap_result2error(ld, result, 1)) );
319                        ldap_unbind( ld );
320                        return NULL;
321                        break;
322                case LDAP_SIZELIMIT_EXCEEDED:
323                        if ( result == NULL ) {
324                                LOG(LOG_ERR, "External Lookup: %s: %s", ERR_EXT_LOOKUP_SEARCH_FAIL, ldap_err2string(ldap_result2error(ld, result, 1)) );
325                                ldap_unbind( ld );
326                                return NULL;
327                        }
328                        break;
329                default:                       
330                        LOG(LOG_ERR, "External Lookup: %s: code=%d, %s", ERR_EXT_LOOKUP_SEARCH_FAIL, rc, ldap_err2string(ldap_result2error(ld, result, 1)) );
331                        ldap_unbind( ld );
332                        return NULL;
333                }
334        }
335
336        num_entries=ldap_count_entries(ld,result);
337
338        LOGDEBUG("External Lookup: found %d LDAP entries", num_entries);
339
340    switch (num_entries) {
341                                case 1: /* only one entry, let's proceed */
342                                        break;
343                                       
344                                case -1: /* an error occured */
345                                    LOG(LOG_ERR, "External Lookup: %s: %s", ERR_EXT_LOOKUP_SEARCH_FAIL, ldap_err2string(ldap_result2error(ld, result, 1)));
346                                        ldap_unbind( ld );
347                                        return NULL ;
348                                       
349                                case 0: /* no entries found */
350                                        LOGDEBUG("External Lookup: %s: no entries found.", ERR_EXT_LOOKUP_SEARCH_FAIL);
351                                        ldap_msgfree( result );
352                                        ldap_unbind( ld );
353                                        return NULL ;
354
355                                default: /* more than one entry returned */
356                                        LOG(LOG_ERR, "External Lookup: %s: more than one entry returned.", ERR_EXT_LOOKUP_SEARCH_FAIL);
357                                        ldap_msgfree( result );
358                                        ldap_unbind( ld );
359                                return NULL;
360        }
361
362        /* for each entry print out name + all attrs and values */
363        for ( e = ldap_first_entry( ld, result ); e != NULL;
364            e = ldap_next_entry( ld, e ) ) {
365                if ( (dn = ldap_get_dn( ld, e )) != NULL ) {
366                    ldap_memfree( dn );
367                }
368                for ( a = ldap_first_attribute( ld, e, &ber );
369                    a != NULL; a = ldap_next_attribute( ld, e, ber ) ) {
370                        if ((vals = ldap_get_values( ld, e, a)) != NULL ) {
371                                for ( i = 0; vals[i] != NULL; i++ ) {
372                                        external_uid = strdup(vals[i]);
373                                }
374                                ldap_value_free( vals );
375                        }
376                        ldap_memfree( a );
377                }
378                if ( ber != NULL ) {
379                        ber_free( ber, 0 );
380                }
381        }
382        ldap_msgfree( result );
383        ldap_unbind( ld );
384        return external_uid;
385}
386#endif
387
388char*
389program_lookup(config_t agent_config, const char *username, char *external_uid)
390{
391        pid_t wstatus, pid;
392        int i, status;
393        int fd[2];
394        char *output = malloc (1024);
395        char **args = malloc (1024);
396        char *token;
397        char *saveptr;
398        char *str;
399        char *command_line = 0;
400       
401    /* build proper command line*/
402        command_line = strdup(transcode_query(_ds_read_attribute(agent_config, "ExtLookupServer"), username, command_line));
403
404        if (command_line == NULL) {
405                LOG(LOG_ERR, ERR_EXT_LOOKUP_MISCONFIGURED);
406                free(output);
407                free(args);
408                return NULL;
409        }
410
411        LOGDEBUG("command line is %s", command_line);
412       
413        /* break the command line into arguments */
414        for (i = 0, str = command_line; ; i++, str = NULL) {
415                token = strtok_r(str, " ", &saveptr);
416                if (token == NULL)
417                        break;
418                args[i] = token;
419                LOGDEBUG("args[%d] = %s",i,token);
420        }
421        args[i] = (char *) 0;
422
423        if (pipe(fd) == -1) {
424                LOG(LOG_ERR, "%s: errno=%i (%s)", ERR_EXT_LOOKUP_INIT_FAIL, errno, strerror(errno));
425                free(output);
426                free(args);
427                return NULL;
428        }
429
430        switch(pid=fork()) {
431
432                case -1: /* couldn't fork - something went wrong */
433                        LOG(LOG_ERR, "%s: errno=%i (%s)", ERR_EXT_LOOKUP_INIT_FAIL, errno, strerror(errno));
434                        free(output);
435                        free(args);
436                        return NULL;
437
438                case 0: /* execute the command and write to fd */
439                        close(fd[0]);
440                        dup2(fd[1], fileno(stdout));
441                        execve(args[0], args, 0);
442                        exit(EXIT_FAILURE);
443                       
444                default: /* read from fd the first output line */
445                        do {
446                                wstatus = waitpid( pid, &status, WUNTRACED | WCONTINUED);
447                                if (wstatus == -1) {
448                                        LOGDEBUG("waitpid() exited with an error: %s: errno=%i", strerror(errno), errno);
449                                        free(output);
450                                        free(args);
451                                        return NULL;
452                                }
453                                if (WIFEXITED(status)) {
454                                        LOGDEBUG("exited, status=%d\n", WEXITSTATUS(status));
455                                        if (WEXITSTATUS(status)) {
456                                                LOGDEBUG("Error running %s. Check path and permissions.\n", args[0]);
457                                        }
458                                } else if (WIFSIGNALED(status)) {
459                                        LOGDEBUG("killed by signal %d\n", WTERMSIG(status));
460                                } else if (WIFSTOPPED(status)) {
461                                        LOGDEBUG("stopped by signal %d\n", WSTOPSIG(status));
462                                } else if (WIFCONTINUED(status)) {
463                                        LOGDEBUG("continued\n");
464                                }
465                        } while (!WIFEXITED(status) && !WIFSIGNALED(status));
466                        close(fd[1]);
467                        /* just in case there's no line break at the end of the return... */
468                        memset(output, 0, 1024);
469                        if (read(fd[0], output, 1024) == -1) {
470                                LOG(LOG_ERR, "%s: errno=%i (%s)", ERR_EXT_LOOKUP_INIT_FAIL, errno, strerror(errno));
471                                free(output);
472                                free(args);
473                                return NULL;
474                        }
475                        close(fd[0]);
476        }
477
478        if (strlen(output) == 0) {
479                free(output);
480                free(args);
481                return NULL;
482        }
483       
484        /* terminate the output string at the first \n */
485        token = strchr(output, '\n');
486        if (token != NULL)
487                *token = '\0';
488       
489        external_uid = strdup(output);
490        free(output);
491        free(command_line);
492        free(args);
493        return external_uid;
494}
495#endif
Note: See TracBrowser for help on using the repository browser.