~ubuntu-branches/ubuntu/precise/dspam/precise

« back to all changes in this revision

Viewing changes to src/external_lookup.c

  • Committer: Bazaar Package Importer
  • Author(s): Julien Valroff, Julien Valroff, Thomas Preud'homme
  • Date: 2011-05-08 13:43:52 UTC
  • mfrom: (1.2.1 upstream) (8.2.2 experimental)
  • mto: This revision was merged to the branch mainline in revision 23.
  • Revision ID: james.westby@ubuntu.com-20110508134352-dxjx9m6tx1cbzlhq
Tags: 3.9.1~rc1+git20110419.29261fb+dfsg-1
[ Julien Valroff ]
* New git snapshot
* Install all dspam_* tools setgid so that they can be used by standard
  users
* Add symbols file for libdspam7
* Upload to unstable

[ Thomas Preud'homme ]
* Fix permissions on dspam_stats and missing opt-{in,out} directories
  (Closes: #394443)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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
 
 
54
void 
 
55
sig_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
 
 
63
char*
 
64
external_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
 
 
89
char
 
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
 
153
char*
 
154
ldap_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
 
 
388
char*
 
389
program_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