int _ds_process_header_token (DSPAM_CTX * CTX, char *token, const char *previous_token, ds_diction_t diction, const char *heading) { char combined_token[256]; unsigned long long crc; char *tweaked_token; if (_ds_match_attribute(CTX->config->attributes, "IgnoreHeader", heading)) return 0; if (!strncmp(heading, "X-DSPAM-", 8)) return 0; /* This is where we used to ignore certain headings */ if (heading[0] != 0) snprintf (combined_token, sizeof (combined_token), "%s*%s", heading, token); else strlcpy (combined_token, token, sizeof (combined_token)); tweaked_token = _ds_truncate_token(token); if (tweaked_token == NULL) return EUNKNOWN; snprintf(combined_token, sizeof(combined_token), "%s*%s", heading, tweaked_token); crc = _ds_getcrc64 (combined_token); #ifdef VERBOSE LOGDEBUG ("Token Hit: '%s'", combined_token); #endif ds_diction_touch(diction, crc, combined_token, 0); if (CTX->tokenizer == DSZ_CHAIN && previous_token != NULL) { char *tweaked_previous; tweaked_previous = _ds_truncate_token(previous_token); if (tweaked_previous == NULL) { free(tweaked_token); return EUNKNOWN; } snprintf (combined_token, sizeof (combined_token), "%s*%s+%s", heading, tweaked_previous, tweaked_token); crc = _ds_getcrc64 (combined_token); ds_diction_touch(diction, crc, combined_token, DSD_CHAINED); free(tweaked_previous); } free(tweaked_token); return 0; }
agent_pref_t _ds_pref_aggregate(agent_pref_t STX, agent_pref_t UTX) { agent_pref_t PTX = static_cast<_ds_agent_attribute **>(calloc(1, PREF_MAX*sizeof(agent_attrib_t ))); int i, size = 0; if (STX) { for(i=0;STX[i];i++) { PTX[i] = _ds_pref_new(STX[i]->attribute, STX[i]->value); PTX[i+1] = NULL; size++; } } if (UTX) { for(i=0;UTX[i];i++) { if (_ds_match_attribute(agent_config, "AllowOverride", UTX[i]->attribute)) { int found = 0; int j; for(j=0;PTX[j];j++) { if (!strcasecmp(PTX[j]->attribute, UTX[i]->attribute)) { found = 1; free(PTX[j]->value); PTX[j]->value = strdup(UTX[i]->value); break; } } if (!found) { PTX[size] = _ds_pref_new(UTX[i]->attribute, UTX[i]->value); PTX[size+1] = NULL; size++; } } else { LOG(LOG_ERR, ERR_AGENT_IGNORE_PREF, UTX[i]->attribute); } } } return PTX; }
int main (int argc, char **argv) { #ifdef TRUSTED_USER_SECURITY struct passwd *p = getpwuid (getuid ()); #endif int i, valid = 0; /* Read dspam.conf */ agent_config = read_config(NULL); if (!agent_config) { LOG(LOG_ERR, ERR_AGENT_READ_CONFIG); exit(EXIT_FAILURE); } if (!_ds_read_attribute(agent_config, "Home")) { LOG(LOG_ERR, ERR_AGENT_DSPAM_HOME); _ds_destroy_config(agent_config); exit(EXIT_FAILURE); } libdspam_init(_ds_read_attribute(agent_config, "StorageDriver")); #ifndef _WIN32 #ifdef TRUSTED_USER_SECURITY if (!_ds_match_attribute(agent_config, "Trust", p->pw_name) && p->pw_uid) { fprintf(stderr, ERR_TRUSTED_MODE "\n"); _ds_destroy_config(agent_config); goto BAIL; } #endif #endif for(i=0;i<argc;i++) { if (!strncmp (argv[i], "--profile=", 10)) { if (!_ds_match_attribute(agent_config, "Profile", argv[i]+10)) { LOG(LOG_ERR, ERR_AGENT_NO_SUCH_PROFILE, argv[i]+10); _ds_destroy_config(agent_config); goto BAIL; } else { _ds_overwrite_attribute(agent_config, "DefaultProfile", argv[i]+10); } break; } } signal (SIGINT, dieout); signal (SIGPIPE, dieout); signal (SIGTERM, dieout); dspam_init_driver (NULL); if (argc < 3 || !strcmp(argv[1], "help")) { usage(); } /* PREFERENCE FUNCTIONS */ if (!strncmp(argv[2], "pref", 4)) { /* Delete */ if (!strncmp(argv[1], "d", 1)) { min_args(argc, 4); valid = 1; del_preference_attribute(argv[3], argv[4]); } /* Add, Change */ if (!strncmp(argv[1], "ch", 2) || !strncmp(argv[1], "ad", 2)) { min_args(argc, 5); valid = 1; set_preference_attribute(argv[3], argv[4], argv[5]); } /* List */ if (!strncmp(argv[1], "l", 1)) { min_args(argc, 3); valid = 1; list_preference_attributes(argv[3]); } /* Aggregate - Preference attr + AllowOverride attr + user prefs */ if (!strncmp(argv[1], "ag", 2)) { min_args(argc, 3); valid = 1; list_aggregate_preference_attributes(argv[3]); } } if (!valid) usage(); dspam_shutdown_driver (NULL); _ds_destroy_config(agent_config); libdspam_shutdown(); exit (EXIT_SUCCESS); BAIL: libdspam_shutdown(); exit(EXIT_FAILURE); }
int configure_algorithms(DSPAM_CTX *CTX) { if (_ds_read_attribute(agent_config, "Algorithm")) CTX->algorithms = 0; if (_ds_match_attribute(agent_config, "Algorithm", "graham")) CTX->algorithms |= DSA_GRAHAM; if (_ds_match_attribute(agent_config, "Algorithm", "burton")) CTX->algorithms |= DSA_BURTON; if (_ds_match_attribute(agent_config, "Algorithm", "robinson")) CTX->algorithms |= DSA_ROBINSON; if (_ds_match_attribute(agent_config, "Algorithm", "naive")) CTX->algorithms |= DSA_NAIVE; if (_ds_match_attribute(agent_config, "PValue", "robinson")) CTX->algorithms |= DSP_ROBINSON; else if (_ds_match_attribute(agent_config, "PValue", "markov")) CTX->algorithms |= DSP_MARKOV; else CTX->algorithms |= DSP_GRAHAM; if (_ds_match_attribute(agent_config, "Tokenizer", "word")) CTX->tokenizer = DSZ_WORD; else if (_ds_match_attribute(agent_config, "Tokenizer", "chain") || _ds_match_attribute(agent_config, "Tokenizer", "chained")) CTX->tokenizer = DSZ_CHAIN; else if (_ds_match_attribute(agent_config, "Tokenizer", "sbph")) CTX->tokenizer = DSZ_SBPH; else if (_ds_match_attribute(agent_config, "Tokenizer", "osb")) CTX->tokenizer = DSZ_OSB; if (_ds_match_attribute(agent_config, "Algorithm", "chi-square")) { if (CTX->algorithms != 0 && CTX->algorithms != DSP_ROBINSON) { LOG(LOG_WARNING, "Warning: Chi-Square algorithm enabled with other algorithms. False positives may ensue."); } CTX->algorithms |= DSA_CHI_SQUARE; } return 0; }
int main (int argc, char **argv) { #ifndef _WIN32 #ifdef TRUSTED_USER_SECURITY struct passwd *p = getpwuid (getuid ()); #endif #endif int i, ret; /* Read dspam.conf */ agent_config = read_config(NULL); if (!agent_config) { LOG(LOG_ERR, ERR_AGENT_READ_CONFIG); fprintf (stderr, ERR_AGENT_READ_CONFIG "\n"); exit(EXIT_FAILURE); } if (!_ds_read_attribute(agent_config, "Home")) { LOG(LOG_ERR, ERR_AGENT_DSPAM_HOME); fprintf (stderr, ERR_AGENT_DSPAM_HOME "\n"); _ds_destroy_config(agent_config); exit(EXIT_FAILURE); } if (libdspam_init(_ds_read_attribute(agent_config, "StorageDriver")) != 0) { LOG(LOG_ERR, ERR_DRV_INIT); fprintf (stderr, ERR_DRV_INIT "\n"); _ds_destroy_config(agent_config); exit(EXIT_FAILURE); } #ifndef _WIN32 #ifdef TRUSTED_USER_SECURITY if (!_ds_match_attribute(agent_config, "Trust", p->pw_name) && p->pw_uid) { fprintf(stderr, ERR_TRUSTED_MODE "\n"); _ds_destroy_config(agent_config); goto BAIL; } #endif #endif for(i=0;i<argc;i++) { if (!strncmp (argv[i], "--profile=", 10)) { if (!_ds_match_attribute(agent_config, "Profile", argv[i]+10)) { LOG(LOG_ERR, ERR_AGENT_NO_SUCH_PROFILE, argv[i]+10); fprintf (stderr, ERR_AGENT_NO_SUCH_PROFILE "\n", argv[i]+10); _ds_destroy_config(agent_config); goto BAIL; } else { _ds_overwrite_attribute(agent_config, "DefaultProfile", argv[i]+10); } break; } } open_ctx = open_mtx = NULL; signal (SIGINT, dieout); signal (SIGPIPE, dieout); signal (SIGTERM, dieout); dspam_init_driver (NULL); ret = process_all_users(); dspam_shutdown_driver (NULL); _ds_destroy_config(agent_config); libdspam_shutdown(); exit((ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE); BAIL: libdspam_shutdown(); exit(EXIT_FAILURE); }
int _ds_tokenize_ngram( DSPAM_CTX *CTX, char *headers, char *body, ds_diction_t diction) { char *token; /* current token */ char *previous_token = NULL; /* used for bigrams (chained tokens) */ char *line = NULL; /* header broken up into lines */ char *ptrptr; char heading[128]; /* current heading */ int l, tokenizer = CTX->tokenizer; struct nt *header = NULL; struct nt_node *node_nt; struct nt_c c_nt; /* Tokenize URLs in message */ if (_ds_match_attribute(CTX->config->attributes, "ProcessorURLContext", "on")) { _ds_url_tokenize(diction, body, "http://"); _ds_url_tokenize(diction, body, "www."); _ds_url_tokenize(diction, body, "href="); } /* * Header Tokenization */ header = nt_create (NT_CHAR); if (header == NULL) { LOG (LOG_CRIT, ERR_MEM_ALLOC); return EUNKNOWN; } line = strtok_r (headers, "\n", &ptrptr); while (line) { nt_add (header, line); line = strtok_r (NULL, "\n", &ptrptr); } node_nt = c_nt_first (header, &c_nt); heading[0] = 0; while (node_nt) { int multiline; #ifdef VERBOSE LOGDEBUG("processing line: %s", node_nt->ptr); #endif line = node_nt->ptr; token = strtok_r (line, ":", &ptrptr); if (token && token[0] != 32 && token[0] != 9 && !strstr (token, " ")) { multiline = 0; strlcpy (heading, token, 128); previous_token = NULL; } else { multiline = 1; } #ifdef VERBOSE LOGDEBUG ("Reading '%s' header from: '%s'", heading, line); #endif if (CTX->flags & DSF_WHITELIST) { /* Use the entire From: line for auto-whitelisting */ if (!strcmp(heading, "From")) { char wl[256]; char *fromline = line + 5; unsigned long long whitelist_token; if (fromline[0] == 32) fromline++; snprintf(wl, sizeof(wl), "%s*%s", heading, fromline); whitelist_token = _ds_getcrc64(wl); ds_diction_touch(diction, whitelist_token, wl, 0); diction->whitelist_token = whitelist_token; } } /* Received headers use a different set of delimiters to preserve things like ip addresses */ token = strtok_r ((multiline) ? line : NULL, DELIMITERS_HEADING, &ptrptr); while (token) { l = strlen(token); if (l >= 1 && l < 50) { #ifdef VERBOSE LOGDEBUG ("Processing '%s' token in '%s' header", token, heading); #endif /* Process "current" token */ if (!_ds_process_header_token (CTX, token, previous_token, diction, heading) && (tokenizer == DSZ_CHAIN)) { previous_token = token; } } token = strtok_r (NULL, DELIMITERS_HEADING, &ptrptr); } previous_token = NULL; node_nt = c_nt_next (header, &c_nt); } nt_destroy (header); /* * Body Tokenization */ #ifdef VERBOSE LOGDEBUG("parsing message body"); #endif token = strtok_r (body, DELIMITERS, &ptrptr); while (token != NULL) { l = strlen (token); if (l >= 1 && l < 50) { #ifdef VERBOSE LOGDEBUG ("Processing body token '%s'", token); #endif /* Process "current" token */ if ( !_ds_process_body_token(CTX, token, previous_token, diction) && tokenizer == DSZ_CHAIN) { previous_token = token; } } token = strtok_r (NULL, DELIMITERS, &ptrptr); } #ifdef VERBOSE LOGDEBUG("Finished tokenizing (ngram) message"); #endif /* Final token reassembly (anything left in the buffer) */ return 0; }
int _ds_map_header_token (DSPAM_CTX * CTX, char *token, char **previous_tokens, ds_diction_t diction, const char *heading, const char *bitpattern) { int i, t, keylen, breadth; u_int32_t mask; unsigned long long crc; char key[256]; int active = 0, top, tokenizer = CTX->tokenizer; if (_ds_match_attribute(CTX->config->attributes, "IgnoreHeader", heading)) return 0; if (!strncmp(heading, "X-DSPAM-", 8)) return 0; /* Shift all previous tokens up */ for(i=0;i<SPARSE_WINDOW_SIZE-1;i++) { previous_tokens[i] = previous_tokens[i+1]; if (previous_tokens[i]) active++; } previous_tokens[SPARSE_WINDOW_SIZE-1] = token; if (token) active++; breadth = _ds_pow2(active); /* Iterate and generate all keys necessary */ for (mask=0; mask < (u_int32_t)breadth; mask++) { int terms = 0; key[0] = 0; keylen = 0; t = 0; top = 1; /* Each Bit */ for(i=0;i<SPARSE_WINDOW_SIZE;i++) { if (t) { if ((size_t)keylen < (sizeof(key)-1)) { key[keylen] = '+'; key[++keylen] = 0; } } if (bitpattern[(mask*SPARSE_WINDOW_SIZE) + i] == 1) { if (previous_tokens[i] == NULL || previous_tokens[i][0] == 0) { if ((size_t)keylen < (sizeof(key)-1)) { key[keylen] = '#'; key[++keylen] = 0; } } else { int tl = strlen(previous_tokens[i]); if ((size_t)(keylen + tl) < (sizeof(key)-1)) { strcpy(key+keylen, previous_tokens[i]); keylen += tl; } terms++; } } else { if ((size_t)keylen < (sizeof(key)-1)) { key[keylen] = '#'; key[++keylen] = 0; } } t++; } /* If the bucket has at least 1 literal, hit it */ if ((tokenizer == DSZ_SBPH && terms != 0) || (tokenizer == DSZ_OSB && terms == 2)) { char hkey[256]; char *k = key; while(keylen>2 && !strcmp((key+keylen)-2, "+#")) { key[keylen-2] = 0; keylen -=2; } while(!strncmp(k, "#+", 2)) { top = 0; k+=2; keylen -= 2; } if (top) { snprintf(hkey, sizeof(hkey), "%s*%s", heading, k); crc = _ds_getcrc64(hkey); ds_diction_touch(diction, crc, hkey, DSD_CONTEXT); } } } return 0; }
buffer * read_sock(THREAD_CTX *TTX, AGENT_CTX *ATX) { buffer *message; int body = 0, line = 1; char *buf; int strip = _ds_match_attribute(agent_config, "Broken", "lineStripping"); int parseto = _ds_match_attribute(agent_config, "ParseToHeaders", "on"); message = buffer_create(NULL); if (message == NULL) { LOG(LOG_CRIT, ERR_MEM_ALLOC); return NULL; } if (_ds_match_attribute(agent_config, "DataSource", "document")) { buffer_cat(message, ".\n\n"); body = 1; } while ((buf = daemon_getline(TTX, 300))!=NULL) { chomp(buf); if (!strcmp(buf, ".")) { free(buf); return message; } if (strip) { size_t len = strlen(buf); while (len>1 && buf[len-2]==13) { buf[len-2] = buf[len-1]; buf[len-1] = 0; len--; } } if (line > 1 || strncmp (buf, "From QUARANTINE", 15)) { if (parseto) { if (buf[0] == 0) body = 1; if (!body && !strncasecmp(buf, "To: ", 4)) process_parseto(ATX, buf); } if (buffer_cat (message, buf) || buffer_cat(message, "\n")) { LOG (LOG_CRIT, ERR_MEM_ALLOC); goto bail; } } /* Use the original user id if we are reversing a false positive */ if (ATX->source == DSS_ERROR && ATX->classification == DSR_ISINNOCENT && ATX->operating_mode == DSM_PROCESS && !strncasecmp (buf, "X-DSPAM-User: ", 14)) { char user[MAX_USERNAME_LENGTH]; strlcpy (user, buf + 14, sizeof (user)); chomp (user); nt_destroy (ATX->users); ATX->users = nt_create (NT_CHAR); if (ATX->users == NULL) { LOG(LOG_CRIT, ERR_MEM_ALLOC); goto bail; } nt_add (ATX->users, user); } free(buf); line++; } return NULL; bail: buffer_destroy(message); return NULL; }
int _ds_tokenize_sparse( DSPAM_CTX *CTX, char *headers, char *body, ds_diction_t diction) { int i; char *token; /* current token */ char *previous_tokens[SPARSE_WINDOW_SIZE]; /* sparse chain */ char *line = NULL; /* header broken up into lines */ char *ptrptr; char *bitpattern; char heading[128]; /* current heading */ int l; struct nt *header = NULL; struct nt_node *node_nt; struct nt_c c_nt; for(i=0;i<SPARSE_WINDOW_SIZE;i++) previous_tokens[i] = NULL; bitpattern = _ds_generate_bitpattern(_ds_pow2(SPARSE_WINDOW_SIZE)); /* Tokenize URLs in message */ if (_ds_match_attribute(CTX->config->attributes, "ProcessorURLContext", "on")) { _ds_url_tokenize(diction, body, "http://"); _ds_url_tokenize(diction, body, "www."); _ds_url_tokenize(diction, body, "href="); } /* * Header Tokenization */ header = nt_create (NT_CHAR); if (header == NULL) { LOG (LOG_CRIT, ERR_MEM_ALLOC); free(bitpattern); return EUNKNOWN; } line = strtok_r (headers, "\n", &ptrptr); while (line) { nt_add (header, line); line = strtok_r (NULL, "\n", &ptrptr); } node_nt = c_nt_first (header, &c_nt); heading[0] = 0; while (node_nt) { int multiline; #ifdef VERBOSE LOGDEBUG("processing line: %s", node_nt->ptr); #endif _ds_sparse_clear(previous_tokens); line = node_nt->ptr; token = strtok_r (line, ":", &ptrptr); if (token && token[0] != 32 && token[0] != 9 && !strstr (token, " ")) { multiline = 0; strlcpy (heading, token, 128); _ds_sparse_clear(previous_tokens); } else { multiline = 1; } #ifdef VERBOSE LOGDEBUG ("Reading '%s' header from: '%s'", heading, line); #endif if (CTX->flags & DSF_WHITELIST) { /* Use the entire From: line for auto-whitelisting */ if (!strcmp(heading, "From")) { char wl[256]; char *fromline = line + 5; unsigned long long whitelist_token; if (fromline[0] == 32) fromline++; snprintf(wl, sizeof(wl), "%s*%s", heading, fromline); whitelist_token = _ds_getcrc64(wl); ds_diction_touch(diction, whitelist_token, wl, 0); diction->whitelist_token = whitelist_token; } } /* Received headers use a different set of delimiters to preserve things like ip addresses */ token = strtok_r ((multiline) ? line : NULL, SPARSE_DELIMITERS_HEADING, &ptrptr); while (token) { l = strlen(token); if (l > 0 && l < 50) { #ifdef VERBOSE LOGDEBUG ("Processing '%s' token in '%s' header", token, heading); #endif _ds_map_header_token (CTX, token, previous_tokens, diction, heading, bitpattern); } token = strtok_r (NULL, SPARSE_DELIMITERS_HEADING, &ptrptr); } for(i=0;i<SPARSE_WINDOW_SIZE;i++) { _ds_map_header_token(CTX, NULL, previous_tokens, diction, heading, bitpattern); } _ds_sparse_clear(previous_tokens); node_nt = c_nt_next (header, &c_nt); } nt_destroy (header); /* * Body Tokenization */ #ifdef VERBOSE LOGDEBUG("parsing message body"); #endif token = strtok_r (body, SPARSE_DELIMITERS, &ptrptr); while (token != NULL) { l = strlen (token); if (l > 0 && l < 50) { #ifdef VERBOSE LOGDEBUG ("Processing body token '%s'", token); #endif /* Process "current" token */ _ds_map_body_token (CTX, token, previous_tokens, diction, bitpattern); } token = strtok_r (NULL, SPARSE_DELIMITERS, &ptrptr); } for(i=0;i<SPARSE_WINDOW_SIZE;i++) { _ds_map_body_token(CTX, NULL, previous_tokens, diction, bitpattern); } _ds_sparse_clear(previous_tokens); free(bitpattern); #ifdef VERBOSE LOGDEBUG("Finished tokenizing (sparse) message"); #endif return 0; }
void *process_connection(void *ptr) { char *server_ident = _ds_read_attribute(agent_config, "ServerIdent"); THREAD_CTX *TTX = (THREAD_CTX *) ptr; AGENT_CTX *ATX = NULL; char *input, *cmdline = NULL, *token, *ptrptr; buffer *message = NULL; char *parms=NULL, *p=NULL; int i, locked = -1, invalid = 0; int server_mode = SSM_DSPAM; char *argv[64]; char buf[1024]; int tries = 0; int argc = 0; FILE *fd = 0; if (_ds_read_attribute(agent_config, "ServerMode") && !strcasecmp(_ds_read_attribute(agent_config, "ServerMode"), "standard")) { server_mode = SSM_STANDARD; } if (_ds_read_attribute(agent_config, "ServerMode") && !strcasecmp(_ds_read_attribute(agent_config, "ServerMode"), "auto")) { server_mode = SSM_AUTO; } /* Initialize a file descriptor hook for dspam to use as stdout */ fd = fdopen(TTX->sockfd, "w"); if (!fd) { close(TTX->sockfd); goto CLOSE; } setbuf(fd, NULL); TTX->packet_buffer = buffer_create(NULL); if (TTX->packet_buffer == NULL) goto CLOSE; /* * Send greeting banner * in auto mode, we want to look like a regular LMTP server so we don't * cause any compatibility problems. in dspam mode, we can change this. */ snprintf(buf, sizeof(buf), "%d DSPAM %sLMTP %s %s", LMTP_GREETING, (server_mode == SSM_DSPAM) ? "D" : "", VERSION, (server_mode == SSM_DSPAM) ? "Authentication Required" : "Ready"); if (send_socket(TTX, buf)<=0) goto CLOSE; TTX->authenticated = 0; /* LHLO */ input = daemon_expect(TTX, "LHLO"); if (input == NULL) goto CLOSE; if (server_mode == SSM_AUTO && input[4]) { char buff[128]; /* * Auto-detect the server mode based on whether or not the ident is * assigned a password in dspam.conf */ snprintf(buff, sizeof(buff), "ServerPass.%s", input + 5); chomp(buff); if (_ds_read_attribute(agent_config, buff)) server_mode = SSM_DSPAM; else server_mode = SSM_STANDARD; } free(input); /* Advertise extensions */ if (daemon_extension(TTX, (server_ident) ? server_ident : "localhost.localdomain")<=0) goto CLOSE; if (daemon_extension(TTX, "PIPELINING")<=0) goto CLOSE; if (daemon_extension(TTX, "ENHANCEDSTATUSCODES")<=0) goto CLOSE; if (server_mode == SSM_DSPAM) if (daemon_extension(TTX, "DSPAMPROCESSMODE")<=0) goto CLOSE; if (daemon_extension(TTX, "8BITMIME")<=0) goto CLOSE; if (daemon_reply(TTX, LMTP_OK, "", "SIZE")<=0) goto CLOSE; /* Main protocol loop */ while(1) { char processmode[256]; parms = NULL; /* Configure a new agent context for each pass */ ATX = calloc(1, sizeof(AGENT_CTX)); if (ATX == NULL) { LOG(LOG_CRIT, ERR_MEM_ALLOC); daemon_reply(TTX, LMTP_TEMP_FAIL, "4.3.0", ERR_MEM_ALLOC); goto CLOSE; } if (initialize_atx(ATX)) { LOG(LOG_ERR, ERR_AGENT_INIT_ATX); daemon_reply(TTX, LMTP_BAD_CMD, "5.3.0", ERR_AGENT_INIT_ATX); goto CLOSE; } /* MAIL FROM (and authentication, if SSM_DSPAM) */ processmode[0] = 0; while(!TTX->authenticated) { input = daemon_expect(TTX, "MAIL FROM"); if (RSET(input)) goto RSET; if (input == NULL) goto CLOSE; else { char *pass, *ident; chomp(input); if (server_mode == SSM_STANDARD) { TTX->authenticated = 1; ATX->mailfrom[0] = 0; _ds_extract_address(ATX->mailfrom, input, sizeof(ATX->mailfrom)); if (daemon_reply(TTX, LMTP_OK, "2.1.0", "OK")<=0) { free(input); goto CLOSE; } } else { char id[256]; pass = ident = NULL; id[0] = 0; if (!_ds_extract_address(id, input, sizeof(id))) { pass = strtok_r(id, "@", &ptrptr); ident = strtok_r(NULL, "@", &ptrptr); } if (pass && ident) { char *serverpass; char *ptr, *ptr2, *ptr3; snprintf(buf, sizeof(buf), "ServerPass.%s", ident); serverpass = _ds_read_attribute(agent_config, buf); snprintf(buf, sizeof(buf), "ServerPass.%s", ident); if (serverpass && !strcmp(pass, serverpass)) { TTX->authenticated = 1; /* Parse PROCESSMODE service tag */ ptr = strstr(input, "DSPAMPROCESSMODE=\""); if (ptr) { char *mode; int i; ptr2 = strchr(ptr, '"')+1; mode = ptr2; while((ptr3 = strstr(ptr2, "\\\""))) ptr2 = ptr3+2; ptr3 = strchr(ptr2+2, '"'); if (ptr3) ptr3[0] = 0; strlcpy(processmode, mode, sizeof(processmode)); ptr = processmode; for(i=0; ptr[i]; i++) { if (ptr[i] == '\\' && ptr[i+1] == '"') { strcpy(ptr+i, ptr+i+1); } } LOGDEBUG("process mode: '%s'", processmode); } if (daemon_reply(TTX, LMTP_OK, "2.1.0", "OK")<=0) { free(input); goto CLOSE; } } } } free(input); if (!TTX->authenticated) { LOGDEBUG("fd %d authentication failure.", TTX->sockfd); if (daemon_reply(TTX, LMTP_AUTH_ERROR, "5.1.0", "Authentication Required")<=0) { free(input); goto CLOSE; } tries++; if (tries>=3) { struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; select(0, NULL, NULL, NULL, &tv); goto CLOSE; } } } } /* MAIL FROM response */ snprintf(buf, sizeof(buf), "%d OK", LMTP_OK); argc = 1; argv[0] = "dspam"; argv[1] = 0; /* Load open-LMTP configuration parameters */ if (server_mode == SSM_STANDARD) { parms = _ds_read_attribute(agent_config, "ServerParameters"); if (parms) { p = strdup(parms); if (p) { token = strtok_r(p, " ", &ptrptr); while(token != NULL && argc<63) { argv[argc] = token; argc++; argv[argc] = 0; token = strtok_r(NULL, " ", &ptrptr); } } } } /* RCPT TO */ while(ATX->users->items == 0 || invalid) { free(cmdline); cmdline = daemon_getline(TTX, 300); while(cmdline && (!strncasecmp(cmdline, "RCPT TO:", 8) || !strncasecmp(cmdline, "RSET", 4))) { char username[256]; char *at = NULL; if (!strncasecmp(cmdline, "RSET", 4)) { snprintf(buf, sizeof(buf), "%d OK", LMTP_OK); if (send_socket(TTX, buf)<=0) goto CLOSE; goto RSET; } if (_ds_extract_address(username, cmdline, sizeof(username)) || username[0] == 0 || username[0] == '-' || username[0] == '@') { if ((server_mode == SSM_DSPAM) || (server_mode == SSM_STANDARD && _ds_validate_address(username) == 0)) { daemon_reply(TTX, LMTP_BAD_CMD, "5.1.2", ERR_LMTP_BAD_RCPT); goto GETCMD; } } if (_ds_match_attribute(agent_config, "Broken", "case")) lc(username, username); /* Chop of @.* from the recipient */ if (_ds_match_attribute(agent_config, "StripRcptDomain", "on")) { at = strchr(username, '@'); if (at != NULL) *at = '\0'; } if (server_mode == SSM_DSPAM) { nt_add(ATX->users, username); } else { if (!parms || !strstr(parms, "--user ")) nt_add(ATX->users, username); if (!ATX->recipients) { ATX->recipients = nt_create(NT_CHAR); if (ATX->recipients == NULL) { LOG(LOG_CRIT, ERR_MEM_ALLOC); goto CLOSE; } } if (at != NULL) *at = '@'; /* always add complete address (user@domain) to recipient list */ nt_add(ATX->recipients, username); } if (daemon_reply(TTX, LMTP_OK, "2.1.5", "OK")<=0) goto CLOSE; GETCMD: free(cmdline); cmdline = daemon_getline(TTX, 300); } if (cmdline == NULL) goto CLOSE; if (!strncasecmp(cmdline, "RSET", 4)) { snprintf(buf, sizeof(buf), "%d OK", LMTP_OK); if (send_socket(TTX, buf)<=0) goto CLOSE; goto RSET; } if (!strncasecmp(cmdline, "quit", 4)) { daemon_reply(TTX, LMTP_OK, "2.0.0", "OK"); goto CLOSE; } /* Parse DSPAMPROCESSMODE input and set up process arguments */ if (server_mode == SSM_DSPAM && processmode[0] != 0) { token = strtok_r(processmode, " ", &ptrptr); while(token != NULL && argc<63) { argv[argc] = token; argc++; argv[argc] = 0; token = strtok_r(NULL, " ", &ptrptr); } } invalid = 0; if (process_arguments(ATX, argc, argv) || apply_defaults(ATX)) { LOG(LOG_ERR, ERR_AGENT_INIT_ATX); daemon_reply(TTX, LMTP_NO_RCPT, "5.1.0", ERR_AGENT_INIT_ATX); invalid = 1; } else if (ATX->users->items == 0) { daemon_reply(TTX, LMTP_NO_RCPT, "5.1.1", ERR_AGENT_USER_UNDEFINED); } } ATX->sockfd = fd; ATX->sockfd_output = 0; /* Something's terribly misconfigured */ if (check_configuration(ATX)) { LOG(LOG_ERR, ERR_AGENT_MISCONFIGURED); daemon_reply(TTX, LMTP_BAD_CMD, "5.3.5", ERR_AGENT_MISCONFIGURED); goto CLOSE; } /* DATA */ if (cmdline != NULL) { if (strncasecmp(cmdline, "DATA", 4)) { if (daemon_reply(TTX, LMTP_BAD_CMD, "5.0.0", "Need DATA Here")<0) goto CLOSE; input = daemon_expect(TTX, "DATA"); if (input == NULL) goto CLOSE; if (RSET(input)) goto RSET; } } if (daemon_reply(TTX, LMTP_DATA, "", INFO_LMTP_DATA)<=0) goto CLOSE; /* * Read in the message from a DATA. I personally like to just hang up on * a client stupid enough to pass in a NULL message for DATA, but you're * welcome to do whatever you want. */ message = read_sock(TTX, ATX); if (message == NULL || message->data == NULL || message->used == 0) { daemon_reply(TTX, LMTP_FAILURE, "5.2.0", ERR_LMTP_MSG_NULL); goto CLOSE; } /* * Lock a database handle. We currently use the modulus of the socket * id against the number of database connections in the cache. This * seems to work rather well, as we would need to lock up the entire * cache to wrap back around. And if we do wrap back around, that means * we're busy enough to justify spinning on the current lock (vs. seeking * out a free handle, which there likely are none). */ i = (TTX->sockfd % TTX->DTX->connection_cache); LOGDEBUG("using database handle id %d", i); if (TTX->DTX->flags & DRF_RWLOCK) { if (ATX->operating_mode == DSM_CLASSIFY || ATX->training_mode == DST_NOTRAIN || (ATX->training_mode == DST_TOE && ATX->classification == DSR_NONE)) { pthread_rwlock_rdlock(&TTX->DTX->connections[i]->rwlock); } else { pthread_rwlock_wrlock(&TTX->DTX->connections[i]->rwlock); } } else { pthread_mutex_lock(&TTX->DTX->connections[i]->lock); } LOGDEBUG("handle locked"); ATX->dbh = TTX->DTX->connections[i]->dbh; locked = i; /* Process the message by tying back into the agent functions */ ATX->results = nt_create(NT_PTR); if (ATX->results == NULL) { LOG(LOG_CRIT, ERR_MEM_ALLOC); goto CLOSE; } process_users(ATX, message); /* * Unlock the database handle as soon as we're done. We also need to * refresh our handle index with a new handle if for some reason we * had to re-establish a dewedged connection. */ if (TTX->DTX->connections[locked]->dbh != ATX->dbh) TTX->DTX->connections[locked]->dbh = ATX->dbh; if (TTX->DTX->flags & DRF_RWLOCK) { pthread_rwlock_unlock(&TTX->DTX->connections[locked]->rwlock); } else { pthread_mutex_unlock(&TTX->DTX->connections[locked]->lock); } locked = -1; /* Send a terminating '.' if --stdout in 'dspam' mode */ if (ATX->sockfd_output) { if (send_socket(TTX, ".")<=0) goto CLOSE; /* Otherwise, produce standard delivery results */ } else { struct nt_node *node_nt, *node_res = NULL; struct nt_c c_nt; if (ATX->recipients) node_nt = c_nt_first(ATX->recipients, &c_nt); else node_nt = c_nt_first(ATX->users, &c_nt); if (ATX->results) node_res = ATX->results->first; while(node_res && node_nt != NULL) { agent_result_t result = (agent_result_t) node_res->ptr; if (result != NULL && result->exitcode == ERC_SUCCESS) { if (server_mode == SSM_DSPAM) { snprintf(buf, sizeof(buf), "%d 2.6.0 <%s> Message accepted for delivery: %s", LMTP_OK, (char *) node_nt->ptr, (result->classification == DSR_ISSPAM) ? "SPAM" : "INNOCENT"); } else { snprintf(buf, sizeof(buf), "%d 2.6.0 <%s> Message accepted for delivery", LMTP_OK, (char *) node_nt->ptr); } } else { if (result != NULL && result->exitcode == ERC_PERMANENT_DELIVERY) { snprintf(buf, sizeof(buf), "%d 5.3.0 <%s> %s", LMTP_FAILURE, (char *) node_nt->ptr, (result->text[0]) ? result->text : "Permanent error occured"); } else { if (result != NULL && result->text[0]) { snprintf(buf, sizeof(buf), "%d 4.3.0 <%s> %s", LMTP_TEMP_FAIL, (char *) node_nt->ptr, result->text); } else { snprintf(buf, sizeof(buf), "%d 4.3.0 <%s> Error occured during %s", LMTP_TEMP_FAIL, (char *) node_nt->ptr, (result != NULL && result->exitcode == ERC_DELIVERY) ? "delivery" : "processing"); } } } if (send_socket(TTX, buf)<=0) goto CLOSE; if (ATX->recipients) node_nt = c_nt_next(ATX->recipients, &c_nt); else node_nt = c_nt_next(ATX->users, &c_nt); if (node_res) node_res = node_res->next; } } /* Cleanup and get ready for another message */ RSET: fflush(fd); buffer_destroy(message); message = NULL; if (ATX != NULL) { nt_destroy(ATX->users); nt_destroy(ATX->recipients); nt_destroy(ATX->results); free(ATX); ATX = NULL; free(cmdline); cmdline = NULL; TTX->authenticated = 0; /* argc = 0; */ } free(p); p = NULL; } /* while(1) */ /* Close connection and return */ CLOSE: if (locked>=0) pthread_mutex_unlock(&TTX->DTX->connections[locked]->lock); if (fd) fclose(fd); buffer_destroy(TTX->packet_buffer); if (message) buffer_destroy(message); if (ATX != NULL) { nt_destroy(ATX->users); nt_destroy(ATX->recipients); nt_destroy(ATX->results); } free(ATX); free(cmdline); free(TTX); decrement_thread_count(); pthread_exit(0); return 0; }
char* ldap_lookup(config_t agent_config, const char *username, char *external_uid) { LDAP *ld; LDAPMessage *result = (LDAPMessage *) 0; LDAPMessage *e; BerElement *ber; char *a, *dn; char *sane_username = malloc(strlen(username)*2); char *p = sane_username; char **vals = NULL; struct timeval ldaptimeout = {.tv_sec = BIND_TIMEOUT, .tv_usec = 0}; int i, rc=0, num_entries=0; char *transcoded_query = NULL; char *ldap_uri = NULL; char *end_ptr; char *ldap_host = _ds_read_attribute(agent_config, "ExtLookupServer"); char *port = _ds_read_attribute(agent_config, "ExtLookupPort"); long lldap_port; int ldap_port = 389; char *ldap_binddn = _ds_read_attribute(agent_config, "ExtLookupLogin"); char *ldap_passwd = _ds_read_attribute(agent_config, "ExtLookupPassword"); char *ldap_base = _ds_read_attribute(agent_config, "ExtLookupDB"); char *ldap_attrs[] = {_ds_read_attribute(agent_config, "ExtLookupLDAPAttribute"),0}; char *version = _ds_read_attribute(agent_config, "ExtLookupLDAPVersion"); long lldap_version; int ldap_version = 3; char *ldap_filter = _ds_read_attribute(agent_config, "ExtLookupQuery"); int ldap_scope; if (port != NULL) { errno=0; lldap_port = strtol(port, &end_ptr, 0); if ( (errno != 0) || (lldap_port < INT_MIN) || (lldap_port > INT_MAX) || (*end_ptr != '\0')) { LOG(LOG_ERR, "External Lookup: bad LDAP port number"); return NULL; } else ldap_port = (int)lldap_port; } /* set ldap protocol version */ if (version != NULL) { errno=0; lldap_version = strtol(version, &end_ptr, 0); if ((errno != 0) || (lldap_version < 1) || (lldap_version > 3) || (*end_ptr != '\0')) { LOG(LOG_ERR, "External Lookup: bad LDAP protocol version"); return NULL; } else ldap_version = (int)lldap_version; } if (_ds_match_attribute(agent_config, "ExtLookupLDAPScope", "one")) ldap_scope = LDAP_SCOPE_ONELEVEL; else /* defaults to sub */ ldap_scope = LDAP_SCOPE_SUBTREE; /* set alarm handler */ signal(SIGALRM, sig_alrm); /* sanitize username for filter integration */ for (; *username != '\0'; username++) { switch(*username) { case 0x2a: /* '*' */ case 0x28: /* '(' */ case 0x29: /* ')' */ case 0x5c: /* '\' */ case 0x00: /* NUL */ *p++ = 0x5c; /* '\' */ *p++ = *username; break; default: *p++ = *username; break; } } *p = '\0'; LOGDEBUG("External Lookup: sanitized username is %s\n", sane_username); /* build proper LDAP filter*/ transcoded_query = strdup(transcode_query(ldap_filter, sane_username, transcoded_query)); free(sane_username); if (transcoded_query == NULL) { LOG(LOG_ERR, "External Lookup: %s", ERR_EXT_LOOKUP_MISCONFIGURED); return NULL; } if( ldap_host != NULL || ldap_port ) { /* construct URL */ LDAPURLDesc url; memset( &url, 0, sizeof(url)); url.lud_scheme = "ldap"; url.lud_host = ldap_host; url.lud_port = ldap_port; url.lud_scope = LDAP_SCOPE_SUBTREE; ldap_uri = ldap_url_desc2str( &url ); } rc = ldap_initialize( &ld, ldap_uri ); if( rc != LDAP_SUCCESS ) { LOG(LOG_ERR, "External Lookup: Could not create LDAP session handle for URI=%s (%d): %s\n", ldap_uri, rc, ldap_err2string(rc)); return NULL; } if( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &ldap_version ) != LDAP_OPT_SUCCESS ) { LOG(LOG_ERR, "External Lookup: Could not set LDAP_OPT_PROTOCOL_VERSION %d\n", ldap_version ); return NULL; } /* use TLS if configured */ if ( _ds_match_attribute(agent_config, "ExtLookupCrypto", "tls" )) { if (ldap_version != 3) { LOG(LOG_ERR, "External Lookup: TLS only supported with LDAP protocol version 3"); return NULL; } if ( ldap_start_tls_s( ld, NULL, NULL ) != LDAP_SUCCESS ) { LOG(LOG_ERR, "External Lookup: %s: %s (%d)", ERR_EXT_LOOKUP_INIT_FAIL, strerror(errno), errno); return NULL; } } /* schedules alarm */ alarm(BIND_TIMEOUT); /* authenticate to the directory */ if ( (rc = ldap_simple_bind_s( ld, ldap_binddn, ldap_passwd )) != LDAP_SUCCESS ) { /* cancel alarms */ alarm(0); LOG(LOG_ERR, "External Lookup: %s: %s", ERR_EXT_LOOKUP_INIT_FAIL, ldap_err2string(rc) ); ldap_unbind(ld); return NULL; } /* cancel alarms */ alarm(0); /* search for all entries matching the filter */ if ( (rc = ldap_search_st( ld, ldap_base, ldap_scope, transcoded_query, ldap_attrs, 0, &ldaptimeout, &result )) != LDAP_SUCCESS ) { free(transcoded_query); switch(rc) { case LDAP_TIMEOUT: case LDAP_BUSY: case LDAP_UNAVAILABLE: case LDAP_UNWILLING_TO_PERFORM: case LDAP_SERVER_DOWN: case LDAP_TIMELIMIT_EXCEEDED: LOG(LOG_ERR, "External Lookup: %s: %s", ERR_EXT_LOOKUP_SEARCH_FAIL, ldap_err2string(ldap_result2error(ld, result, 1)) ); ldap_unbind( ld ); return NULL; break; case LDAP_FILTER_ERROR: LOG(LOG_ERR, "External Lookup: %s: %s", ERR_EXT_LOOKUP_SEARCH_FAIL, ldap_err2string(ldap_result2error(ld, result, 1)) ); ldap_unbind( ld ); return NULL; break; case LDAP_SIZELIMIT_EXCEEDED: if ( result == NULL ) { LOG(LOG_ERR, "External Lookup: %s: %s", ERR_EXT_LOOKUP_SEARCH_FAIL, ldap_err2string(ldap_result2error(ld, result, 1)) ); ldap_unbind( ld ); return NULL; } break; default: LOG(LOG_ERR, "External Lookup: %s: code=%d, %s", ERR_EXT_LOOKUP_SEARCH_FAIL, rc, ldap_err2string(ldap_result2error(ld, result, 1)) ); ldap_unbind( ld ); return NULL; } } num_entries=ldap_count_entries(ld,result); LOGDEBUG("External Lookup: found %d LDAP entries", num_entries); switch (num_entries) { case 1: /* only one entry, let's proceed */ break; case -1: /* an error occured */ LOG(LOG_ERR, "External Lookup: %s: %s", ERR_EXT_LOOKUP_SEARCH_FAIL, ldap_err2string(ldap_result2error(ld, result, 1))); ldap_unbind( ld ); return NULL ; case 0: /* no entries found */ LOGDEBUG("External Lookup: %s: no entries found.", ERR_EXT_LOOKUP_SEARCH_FAIL); ldap_msgfree( result ); ldap_unbind( ld ); return NULL ; default: /* more than one entry returned */ LOG(LOG_ERR, "External Lookup: %s: more than one entry returned.", ERR_EXT_LOOKUP_SEARCH_FAIL); ldap_msgfree( result ); ldap_unbind( ld ); return NULL; } /* for each entry print out name + all attrs and values */ for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) { if ( (dn = ldap_get_dn( ld, e )) != NULL ) { ldap_memfree( dn ); } for ( a = ldap_first_attribute( ld, e, &ber ); a != NULL; a = ldap_next_attribute( ld, e, ber ) ) { if ((vals = ldap_get_values( ld, e, a)) != NULL ) { for ( i = 0; vals[i] != NULL; i++ ) { external_uid = strdup(vals[i]); } ldap_value_free( vals ); } ldap_memfree( a ); } if ( ber != NULL ) { ber_free( ber, 0 ); } } ldap_msgfree( result ); ldap_unbind( ld ); return external_uid; } #endif char* program_lookup(config_t agent_config, const char *username, char *external_uid) { pid_t wstatus, pid; int i, status; int fd[2]; char *output = malloc (1024); char **args = malloc (1024); char *token; char *saveptr; char *str; char *command_line = 0; /* build proper command line*/ command_line = strdup(transcode_query(_ds_read_attribute(agent_config, "ExtLookupServer"), username, command_line)); if (command_line == NULL) { LOG(LOG_ERR, ERR_EXT_LOOKUP_MISCONFIGURED); free(output); free(args); return NULL; } LOGDEBUG("command line is %s", command_line); /* break the command line into arguments */ for (i = 0, str = command_line; ; i++, str = NULL) { token = strtok_r(str, " ", &saveptr); if (token == NULL) break; args[i] = token; LOGDEBUG("args[%d] = %s",i,token); } args[i] = (char *) 0; if (pipe(fd) == -1) { LOG(LOG_ERR, "%s: errno=%i (%s)", ERR_EXT_LOOKUP_INIT_FAIL, errno, strerror(errno)); free(output); free(args); return NULL; } switch(pid=fork()) { case -1: /* couldn't fork - something went wrong */ LOG(LOG_ERR, "%s: errno=%i (%s)", ERR_EXT_LOOKUP_INIT_FAIL, errno, strerror(errno)); free(output); free(args); return NULL; case 0: /* execute the command and write to fd */ close(fd[0]); dup2(fd[1], fileno(stdout)); execve(args[0], args, 0); exit(EXIT_FAILURE); default: /* read from fd the first output line */ do { wstatus = waitpid( pid, &status, WUNTRACED | WCONTINUED); if (wstatus == -1) { LOGDEBUG("waitpid() exited with an error: %s: errno=%i", strerror(errno), errno); free(output); free(args); return NULL; } if (WIFEXITED(status)) { LOGDEBUG("exited, status=%d\n", WEXITSTATUS(status)); if (WEXITSTATUS(status)) { LOGDEBUG("Error running %s. Check path and permissions.\n", args[0]); } } else if (WIFSIGNALED(status)) { LOGDEBUG("killed by signal %d\n", WTERMSIG(status)); } else if (WIFSTOPPED(status)) { LOGDEBUG("stopped by signal %d\n", WSTOPSIG(status)); } else if (WIFCONTINUED(status)) { LOGDEBUG("continued\n"); } } while (!WIFEXITED(status) && !WIFSIGNALED(status)); close(fd[1]); /* just in case there's no line break at the end of the return... */ memset(output, 0, 1024); if (read(fd[0], output, 1024) == -1) { LOG(LOG_ERR, "%s: errno=%i (%s)", ERR_EXT_LOOKUP_INIT_FAIL, errno, strerror(errno)); free(output); free(args); return NULL; } close(fd[0]); } if (strlen(output) == 0) { free(output); free(args); return NULL; } /* terminate the output string at the first \n */ token = strchr(output, '\n'); if (token != NULL) *token = '\0'; external_uid = strdup(output); free(output); free(command_line); free(args); return external_uid; }
int main (int argc, char **argv) { #ifndef _WIN32 #ifdef TRUSTED_USER_SECURITY struct passwd *p = getpwuid (getuid ()); #endif #endif struct _pgsql_drv_storage *store; char file[PATH_MAX+1]; int i, ch; #ifndef HAVE_GETOPT int optind = 1; #endif /* Read dspam.conf */ agent_config = read_config(NULL); if (!agent_config) { LOG(LOG_ERR, ERR_AGENT_READ_CONFIG); exit(EXIT_FAILURE); } if (!_ds_read_attribute(agent_config, "Home")) { LOG(LOG_ERR, ERR_AGENT_DSPAM_HOME); _ds_destroy_config(agent_config); exit(EXIT_FAILURE); } #ifndef _WIN32 #ifdef TRUSTED_USER_SECURITY if (!_ds_match_attribute(agent_config, "Trust", p->pw_name) && p->pw_uid) { fprintf(stderr, ERR_TRUSTED_MODE "\n"); _ds_destroy_config(agent_config); exit(EXIT_FAILURE); } #endif #endif for(i=0; i<argc; i++) { if (!strncmp (argv[i], "--profile=", 10)) { if (!_ds_match_attribute(agent_config, "Profile", argv[i]+10)) { LOG(LOG_ERR, ERR_AGENT_NO_SUCH_PROFILE, argv[i]+10); _ds_destroy_config(agent_config); exit(EXIT_FAILURE); } else { _ds_overwrite_attribute(agent_config, "DefaultProfile", argv[i]+10); } break; } } open_ctx = open_mtx = NULL; signal (SIGINT, dieout); signal (SIGPIPE, dieout); signal (SIGTERM, dieout); /* Process command line */ ch = 0; #ifdef HAVE_GETOPT while((ch = getopt(argc, argv, "h")) != -1) #else while ( argv[optind] && argv[optind][0] == '-' && (ch = argv[optind][1]) && argv[optind][2] == '\0' ) #endif { switch(ch) { case 'h': /* print help, and then exit. usage exits for us */ usage(); break; #ifndef HAVE_GETOPT default: fprintf(stderr, "%s: unknown option \"%s\".\n", argv[0], argv[optind] + 1); usage(); #endif } #ifndef HAVE_GETOPT optind++; #endif } /* reset our option array and index to where we are after getopt */ argv += optind; argc -= optind; if (argc == 0) { fprintf(stderr,"Must specify an output file\n"); usage(); } memset((void *)file, 0, PATH_MAX+1); strncpy(file, argv[0], PATH_MAX); open_ctx = dspam_create(NULL,NULL,_ds_read_attribute(agent_config, "Home"), DSM_TOOLS, 0); if (open_ctx == NULL) { fprintf(stderr, "Could not initialize context: %s\n", strerror (errno)); exit(EXIT_FAILURE); } set_libdspam_attributes(open_ctx); if (dspam_attach(open_ctx, NULL)) { fprintf(stderr,"Failed to init link to PostgreSQL\n"); dspam_destroy(open_ctx); exit(EXIT_FAILURE); } store = (struct _pgsql_drv_storage *)(open_ctx->storage); GenSQL(store->dbh,file); //PQfinish(store->dbh); OutputMessage(open_ctx,file); if (open_ctx != NULL) dspam_destroy (open_ctx); if (open_mtx != NULL) dspam_destroy (open_mtx); _ds_destroy_config(agent_config); exit (EXIT_SUCCESS); }
int daemon_listen(DRIVER_CTX *DTX) { struct sockaddr_in local_addr, remote_addr; THREAD_CTX *TTX = NULL; fd_set master, read_fds; pthread_attr_t attr; struct timeval tv; int fdmax, yes = 1; int domain = 0; /* listening on domain socket? */ int listener; /* listener fd */ int i; int port = 24, queue = 32; /* default port and queue size */ signal(SIGPIPE, SIG_IGN); signal(SIGINT, process_signal); signal(SIGTERM, process_signal); signal(SIGHUP, process_signal); if (_ds_read_attribute(agent_config, "ServerPort")) port = atoi(_ds_read_attribute(agent_config, "ServerPort")); if (_ds_read_attribute(agent_config, "ServerQueueSize")) queue = atoi(_ds_read_attribute(agent_config, "ServerQueueSize")); if (_ds_read_attribute(agent_config, "ServerDomainSocketPath")) domain = 1; /* initialize */ FD_ZERO(&master); FD_ZERO(&read_fds); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); /* Bind (domain socket) */ if (domain) { struct sockaddr_un saun; char *address = strdup(_ds_read_attribute(agent_config, "ServerDomainSocketPath")); mode_t mask; int len; mask = umask (000); if (_ds_match_attribute(agent_config, "ServerDomainSymlink", "on")) { /* treat ServerDomainSymlink as a symlink path */ char* old_address = address; address = malloc(sizeof(char) * (strlen(address) + 8)); sprintf(address, "%s.%d", old_address, getpid()); free(old_address); /* address now points to the actual socket, ending in pid */ } listener = socket(AF_UNIX, SOCK_STREAM, 0); if (listener == -1) { LOG(LOG_CRIT, ERR_DAEMON_SOCKET, strerror(errno)); umask (mask); return(EFAILURE); } memset(&saun, 0, sizeof(struct sockaddr_un)); saun.sun_family = AF_UNIX; strcpy(saun.sun_path, address); unlink(address); len = sizeof(saun.sun_family) + strlen(saun.sun_path) + 1; LOGDEBUG(INFO_DAEMON_DOMAINSOCK, address); if (bind(listener, (struct sockaddr *) &saun, len)<0) { close(listener); LOG(LOG_CRIT, INFO_DAEMON_DOMAINSOCK, address, strerror(errno)); umask (mask); return EFAILURE; } if (_ds_match_attribute(agent_config, "ServerDomainSymlink", "on")) { /* install symlink for the domain socket */ char *link_address = _ds_read_attribute(agent_config, "ServerDomainSocketPath"); /* to atomically replace the socket symlink, make the symlink elsewhere then move it */ char* new_link_address = malloc(sizeof(char) * (strlen(link_address) + 4)); sprintf(new_link_address, "%s.new", link_address); symlink(address, new_link_address); rename(new_link_address, link_address); free(new_link_address); } umask (mask); free(address); /* Bind to a TCP socket */ } else { listener = socket(AF_INET, SOCK_STREAM, 0); if (listener == -1) { LOG(LOG_CRIT, ERR_DAEMON_SOCKET, strerror(errno)); return(EFAILURE); } if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) { close(listener); LOG(LOG_CRIT, ERR_DAEMON_SOCKOPT, "SO_REUSEADDR", strerror(errno)); return(EFAILURE); } memset(&local_addr, 0, sizeof(struct sockaddr_in)); local_addr.sin_family = AF_INET; local_addr.sin_port = htons(port); local_addr.sin_addr.s_addr = INADDR_ANY; LOGDEBUG(INFO_DAEMON_BIND, port); if (bind(listener, (struct sockaddr *)&local_addr, sizeof(struct sockaddr)) == -1) { close(listener); LOG(LOG_CRIT, ERR_DAEMON_BIND, port, strerror(errno)); return(EFAILURE); } } /* Listen */ if (listen(listener, queue) == -1) { close(listener); LOG(LOG_CRIT, ERR_DAEMON_LISTEN, strerror(errno)); return(EFAILURE); } FD_SET(listener, &master); fdmax = listener; /* Process new connections (until death or reload) */ for(;;) { read_fds = master; tv.tv_sec = 2; tv.tv_usec = 0; if (__daemon_run == 0) { close(listener); char* address = _ds_read_attribute(agent_config, "ServerDomainSocketPath"); if (address) { if (_ds_match_attribute(agent_config, "ServerDomainSymlink", "on")) { /* unlink the actual domain socket */ char* real_address = malloc(sizeof(char) * (strlen(address) + 8)); sprintf(real_address, "%s.%d", address, getpid()); unlink (real_address); /* remove the symlink only if we still own it */ char link_target[256]; int ret = readlink(address, link_target, sizeof(link_target)-1); if (ret >= 0) { /* readlink doesn't null-terminate */ link_target[ret] = '\0'; if (!strcmp(link_target, real_address)) { unlink (address); } } free(real_address); } else unlink (address); } return 0; } if (select(fdmax+1, &read_fds, NULL, NULL, &tv)>0) { /* Process read-ready connections */ for(i=0;i<=fdmax;i++) { if (FD_ISSET(i, &read_fds)) { /* Accept new connections */ if (i == listener) { int newfd; int addrlen = sizeof(remote_addr); if ((newfd = accept(listener, (struct sockaddr *)&remote_addr, (socklen_t *) &addrlen)) == -1) { LOG(LOG_WARNING, ERR_DAEMON_ACCEPT, strerror(errno)); continue; #ifdef DEBUG } else if (!domain) { char buff[32]; LOGDEBUG("connection id %d from %s.", newfd, inet_ntoa_r(remote_addr.sin_addr, buff, sizeof(buff))); #endif } fcntl(newfd, F_SETFL, O_RDWR); setsockopt(newfd,SOL_SOCKET,TCP_NODELAY,&yes,sizeof(int)); /* * Since processing time varies, each new connection gets its own * thread, so we create a new thread context and send it on its way * */ TTX = calloc(1, sizeof(THREAD_CTX)); if (TTX == NULL) { LOG(LOG_CRIT, ERR_MEM_ALLOC); close(newfd); continue; } else { TTX->sockfd = newfd; TTX->DTX = DTX; memcpy(&TTX->remote_addr, &remote_addr, sizeof(remote_addr)); increment_thread_count(); if (pthread_create(&TTX->thread, &attr, process_connection, (void *) TTX)) { decrement_thread_count(); LOG(LOG_CRIT, ERR_DAEMON_THREAD, strerror(errno)); close(TTX->sockfd); free(TTX); continue; } } } /* if i == listener */ } /* if FD_SET else */ } /* for(i.. */ } /* if (select)... */ } /* for(;;) */ /* Shutdown - we should never get here, but who knows */ close(listener); pthread_attr_destroy(&attr); return 0; }
int main (int argc, char *argv[]) { DSPAM_CTX *CTX = NULL, *CTX2; char *user; int do_sigs = 0; int do_probs = 0; int do_unused = 0; int age_sigs = 14; int age_probs = 30; int age_unused[4] = { 90, 30, 15, 15 }; int i, help = 0; struct nt *users = NULL; struct nt_node *node = NULL; #ifndef _WIN32 #ifdef TRUSTED_USER_SECURITY struct passwd *p = getpwuid (getuid ()); #endif #endif /* Read dspam.conf */ agent_config = read_config(NULL); if (!agent_config) { LOG(LOG_ERR, ERR_AGENT_READ_CONFIG); fprintf (stderr, ERR_AGENT_READ_CONFIG "\n"); exit(EXIT_FAILURE); } if (!_ds_read_attribute(agent_config, "Home")) { LOG(LOG_ERR, ERR_AGENT_DSPAM_HOME); fprintf (stderr, ERR_AGENT_DSPAM_HOME "\n"); goto bail; } if (libdspam_init(_ds_read_attribute(agent_config, "StorageDriver")) != 0) { LOG(LOG_ERR, ERR_DRV_INIT); fprintf (stderr, ERR_DRV_INIT "\n"); _ds_destroy_config(agent_config); exit(EXIT_FAILURE); } #ifndef _WIN32 #ifdef TRUSTED_USER_SECURITY if (!_ds_match_attribute(agent_config, "Trust", p->pw_name) && p->pw_uid) { fprintf(stderr, ERR_TRUSTED_MODE "\n"); goto bail; } #endif #endif for(i=0;i<argc;i++) { if (!strncmp (argv[i], "--profile=", 10)) { if (!_ds_match_attribute(agent_config, "Profile", argv[i]+10)) { LOG(LOG_ERR, ERR_AGENT_NO_SUCH_PROFILE, argv[i]+10); fprintf (stderr, ERR_AGENT_NO_SUCH_PROFILE "\n", argv[i]+10); goto bail; } else { _ds_overwrite_attribute(agent_config, "DefaultProfile", argv[i]+10); } break; } } #ifdef DEBUG fprintf (stdout, "dspam_clean starting\n"); #endif if (_ds_read_attribute(agent_config, "PurgeSignatures") && !_ds_match_attribute(agent_config, "PurgeSignatures", "off")) { do_sigs = 1; age_sigs = atoi(_ds_read_attribute(agent_config, "PurgeSignatures")); } if (_ds_read_attribute(agent_config, "PurgeNeutral") && !_ds_match_attribute(agent_config, "PurgeNeutral", "off")) { do_probs = 1; age_probs = atoi(_ds_read_attribute(agent_config, "PurgeNeutral")); } if (_ds_read_attribute(agent_config, "PurgeUnused") && !_ds_match_attribute(agent_config, "PurgeUnused", "off")) { int i; do_unused = 1; age_unused[0] = atoi(_ds_read_attribute(agent_config, "PurgeUnused")); age_unused[1] = atoi(_ds_read_attribute(agent_config, "PurgeHapaxes")); age_unused[2] = atoi(_ds_read_attribute(agent_config, "PurgeHits1S")); age_unused[3] = atoi(_ds_read_attribute(agent_config, "PurgeHits1I")); for(i=0;i<4;i++) if (age_unused[i]==0) do_unused = 0; } users = nt_create(NT_CHAR); if (users == NULL) { fprintf(stderr, "%s", ERR_MEM_ALLOC); goto bail; } for(i=0;i<argc;i++) { if (!strncmp(argv[i], "-p", 2)) { do_probs = 1; if (strlen(argv[i])>2) age_probs = atoi(argv[i]+2); } else if (!strncmp(argv[i], "-s", 2)) { do_sigs = 1; if (strlen(argv[i])>2) age_sigs = atoi(argv[i]+2); } else if (!strncmp(argv[i], "-u", 2)) { do_unused = 1; if (strlen(argv[i])>2) { char *c = strdup(argv[i]+2); char *d = strtok(c, ","); int j = 0; while(d != NULL && j<4) { age_unused[j] = atoi(d); j++; d = strtok(NULL, ","); } free(c); } } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) help = 1; else if (i>0) nt_add(users, argv[i]); } if (help || (!do_probs && !do_sigs && !do_unused)) { fprintf(stderr, "%s", CLEANSYNTAX); _ds_destroy_config(agent_config); nt_destroy(users); libdspam_shutdown(); if (help) { exit(EXIT_SUCCESS); } exit(EXIT_FAILURE); } open_ctx = open_mtx = NULL; signal (SIGINT, dieout); signal (SIGPIPE, dieout); signal (SIGTERM, dieout); dspam_init_driver (NULL); if (users->items == 0) { CTX = dspam_create (NULL, NULL, _ds_read_attribute(agent_config, "Home"), DSM_TOOLS, 0); open_ctx = CTX; if (CTX == NULL) { fprintf (stderr, "Could not initialize context: %s\n", strerror (errno)); dspam_shutdown_driver (NULL); goto bail; } set_libdspam_attributes(CTX); if (dspam_attach(CTX, NULL)) { LOG (LOG_WARNING, "unable to attach dspam context"); fprintf (stderr, "Unable to attach DSPAM context\n"); goto bail; } user = _ds_get_nextuser (CTX); } else { node = users->first; if (node != NULL) user = node->ptr; else goto bail; } while (user != NULL) { #ifdef DEBUG printf ("PROCESSING USER: %s\n", user); #endif CTX2 = dspam_create (user, NULL, _ds_read_attribute(agent_config, "Home"), DSM_TOOLS, 0); open_mtx = CTX2; if (CTX2 == NULL) { fprintf (stderr, "Could not initialize context: %s\n", strerror (errno)); return EUNKNOWN; } set_libdspam_attributes(CTX2); if (dspam_attach(CTX2, NULL)) { LOG (LOG_WARNING, "unable to attach dspam context"); fprintf (stderr, "Unable to attach DSPAM context\n"); goto bail; } if (do_sigs) process_sigs(CTX2, age_sigs); if (do_probs) process_probs(CTX2, age_probs); if (do_unused) process_unused(CTX2, age_unused[0], age_unused[1], age_unused[2], age_unused[3]); dspam_destroy (CTX2); open_mtx = NULL; if (users->items == 0) { user = _ds_get_nextuser (CTX); } else { if (node == NULL || node->next == NULL) { node = NULL; user = NULL; } else { node = node->next; user = node->ptr; } } } if (users->items == 0) { dspam_destroy (CTX); open_ctx = NULL; } dspam_shutdown_driver (NULL); _ds_destroy_config(agent_config); nt_destroy(users); libdspam_shutdown(); exit (EXIT_SUCCESS); bail: if (open_ctx) dspam_destroy(open_ctx); if (open_mtx) dspam_destroy(open_mtx); _ds_destroy_config(agent_config); nt_destroy(users); libdspam_shutdown(); exit(EXIT_FAILURE); }