1
/* $Id: external_lookup.c,v 0.6.1 2009/12/31 04:00:01 sbajic Exp $ */
4
COPYRIGHT (C) 2006 HUGO MONTEIRO
6
external lookup library for DSPAM v0.6
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
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.
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.
25
#include <auto-config.h>
29
#define LDAP_DEPRECATED 1
38
#include <sys/types.h>
42
#include "external_lookup.h"
46
#include "config_shared.h"
51
# define BIND_TIMEOUT 10
57
signum = signum; /* Keep compiler happy */
58
LOG(LOG_ERR,"%s: Timed out.", ERR_EXT_LOOKUP_INIT_FAIL);
64
external_lookup(config_t agent_config, const char *username, char *external_uid)
67
char *driver = _ds_read_attribute(agent_config, "ExtLookupDriver");
69
if (strcmp(driver, "ldap") == 0) {
71
return ldap_lookup(agent_config, username, external_uid);
73
LOG(LOG_ERR, "external_lookup: LDAP driver was not enabled at compile time.");
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");
83
LOG(LOG_ERR, "external_lookup: lookup driver %s not yet implemented.", driver);
90
*transcode_query(const char *query, const char *username, char *transcoded_query)
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);
99
/* count aprox. the number of replacements.
100
* %% escaping is also accounted and shouldn't
102
for (replacements = 0, str=strdup(query); ; str = NULL) {
103
token = strtok_r(str, "%", &saveptr);
106
if (token[0] == 'u') {
113
len = querylen + namelen * replacements - 2 * replacements + 1;
115
transcoded_query = malloc (len);
116
memset(transcoded_query, 0, len);
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++] = '%';
124
case 'u': /* paste in the username */
125
if (j+namelen>=len) {
126
LOG(LOG_ERR, "%s: check ExtLookupQuery", ERR_EXT_LOOKUP_MISCONFIGURED);
129
memcpy (transcoded_query+j, username, namelen);
132
default: /* unrecognised formatting, abort! */
133
LOG(LOG_ERR, "%s: check ExtLookupQuery", ERR_EXT_LOOKUP_MISCONFIGURED);
138
transcoded_query[j++] = query[i++];
143
LOG(LOG_ERR, "%s: check ExtLookupQuery", ERR_EXT_LOOKUP_MISCONFIGURED);
146
/* and finally zero terminate string */
147
transcoded_query[j] = 0;
149
return transcoded_query;
154
ldap_lookup(config_t agent_config, const char *username, char *external_uid)
157
LDAPMessage *result = (LDAPMessage *) 0;
161
char *sane_username = malloc(strlen(username)*2);
162
char *p = sane_username;
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;
169
char *ldap_host = _ds_read_attribute(agent_config, "ExtLookupServer");
170
char *port = _ds_read_attribute(agent_config, "ExtLookupPort");
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");
179
int ldap_version = 3;
180
char *ldap_filter = _ds_read_attribute(agent_config, "ExtLookupQuery");
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");
190
ldap_port = (int)lldap_port;
193
/* set ldap protocol version */
194
if (version != NULL) {
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");
201
ldap_version = (int)lldap_version;
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;
209
/* set alarm handler */
210
signal(SIGALRM, sig_alrm);
212
/* sanitize username for filter integration */
213
for (; *username != '\0'; username++) {
220
*p++ = 0x5c; /* '\' */
232
LOGDEBUG("External Lookup: sanitized username is %s\n", sane_username);
234
/* build proper LDAP filter*/
235
transcoded_query = strdup(transcode_query(ldap_filter, sane_username, transcoded_query));
237
if (transcoded_query == NULL) {
238
LOG(LOG_ERR, "External Lookup: %s", ERR_EXT_LOOKUP_MISCONFIGURED);
242
if( ldap_host != NULL || ldap_port ) {
245
memset( &url, 0, sizeof(url));
247
url.lud_scheme = "ldap";
248
url.lud_host = ldap_host;
249
url.lud_port = ldap_port;
250
url.lud_scope = LDAP_SCOPE_SUBTREE;
252
ldap_uri = ldap_url_desc2str( &url );
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));
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 );
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");
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);
278
/* schedules alarm */
281
/* authenticate to the directory */
282
if ( (rc = ldap_simple_bind_s( ld, ldap_binddn, ldap_passwd )) != LDAP_SUCCESS ) {
286
LOG(LOG_ERR, "External Lookup: %s: %s", ERR_EXT_LOOKUP_INIT_FAIL, ldap_err2string(rc) );
293
/* search for all entries matching the filter */
295
if ( (rc = ldap_search_st( ld,
302
&result )) != LDAP_SUCCESS ) {
304
free(transcoded_query);
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)) );
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)) );
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)) );
330
LOG(LOG_ERR, "External Lookup: %s: code=%d, %s", ERR_EXT_LOOKUP_SEARCH_FAIL, rc, ldap_err2string(ldap_result2error(ld, result, 1)) );
336
num_entries=ldap_count_entries(ld,result);
338
LOGDEBUG("External Lookup: found %d LDAP entries", num_entries);
340
switch (num_entries) {
341
case 1: /* only one entry, let's proceed */
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)));
349
case 0: /* no entries found */
350
LOGDEBUG("External Lookup: %s: no entries found.", ERR_EXT_LOOKUP_SEARCH_FAIL);
351
ldap_msgfree( result );
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 );
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 ) {
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]);
374
ldap_value_free( vals );
382
ldap_msgfree( result );
389
program_lookup(config_t agent_config, const char *username, char *external_uid)
394
char *output = malloc (1024);
395
char **args = malloc (1024);
399
char *command_line = 0;
401
/* build proper command line*/
402
command_line = strdup(transcode_query(_ds_read_attribute(agent_config, "ExtLookupServer"), username, command_line));
404
if (command_line == NULL) {
405
LOG(LOG_ERR, ERR_EXT_LOOKUP_MISCONFIGURED);
411
LOGDEBUG("command line is %s", command_line);
413
/* break the command line into arguments */
414
for (i = 0, str = command_line; ; i++, str = NULL) {
415
token = strtok_r(str, " ", &saveptr);
419
LOGDEBUG("args[%d] = %s",i,token);
421
args[i] = (char *) 0;
423
if (pipe(fd) == -1) {
424
LOG(LOG_ERR, "%s: errno=%i (%s)", ERR_EXT_LOOKUP_INIT_FAIL, errno, strerror(errno));
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));
438
case 0: /* execute the command and write to fd */
440
dup2(fd[1], fileno(stdout));
441
execve(args[0], args, 0);
444
default: /* read from fd the first output line */
446
wstatus = waitpid( pid, &status, WUNTRACED | WCONTINUED);
448
LOGDEBUG("waitpid() exited with an error: %s: errno=%i", strerror(errno), errno);
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]);
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");
465
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
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));
478
if (strlen(output) == 0) {
484
/* terminate the output string at the first \n */
485
token = strchr(output, '\n');
489
external_uid = strdup(output);