PageRenderTime 48ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/dspam-3.10.2/src/external_lookup.c

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