static void log_io_data(spctx_t* ctx, spio_t* io, const char* data, int read) { char buf[MAX_LOG_LINE + 1]; int pos, len; ASSERT(ctx && io && data); for(;;) { data += strspn(data, "\r\n"); if(!*data) break; pos = strcspn(data, "\r\n"); len = pos < MAX_LOG_LINE ? pos : MAX_LOG_LINE; memcpy(buf, data, len); buf[len] = 0; sp_messagex(ctx, LOG_DEBUG, "%s%s%s", GET_IO_NAME(io), read ? " < " : " > ", buf); data += pos; } }
spctx_t* cb_new_context() { spctx_t* ctx = (spctx_t*)calloc(1, sizeof(spctx_t)); if(!ctx) sp_messagex(NULL, LOG_CRIT, "out of memory"); return ctx; }
int cb_parse_option(const char* name, const char* value) { if(strcasecmp("Mode", name) == 0) { if(strcasecmp("sign", value) == 0) mode = MODE_SIGN; else if(strcasecmp("verify", value) == 0) mode = MODE_VERIFY; else sp_messagex(NULL, LOG_ERR, "Unknown Mode setting"); return 1; } if(strcasecmp("Selector", name) == 0) { strncpy(dkim_opts.szSelector, value, sizeof(dkim_opts.szSelector)); return 1; } if(strcasecmp("Domain", name) == 0) { strncpy(dkim_opts.szDomain, value, sizeof(dkim_opts.szDomain)); return 1; } if(strcasecmp("PrivateKey", name) == 0) { keyfile = value; return 1; } if(strcasecmp("Expiry", name) == 0) { time_t t; time(&t); char *pEnd; dkim_opts.expireTime = t + strtol(value, &pEnd, 10); return 1; } return 0; }
int spio_connect(spctx_t* ctx, spio_t* io, const struct sockaddr_any* src, const struct sockaddr_any* sany, const char* addrname) { int ret = 0; int fd; ASSERT(ctx && io && sany && addrname); ASSERT(io->fd == -1); if((fd = socket(SANY_TYPE(*sany), SOCK_STREAM, 0)) == -1) RETURN(-1); if(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &(g_state.timeout), sizeof(g_state.timeout)) == -1 || setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &(g_state.timeout), sizeof(g_state.timeout)) == -1) sp_messagex(ctx, LOG_DEBUG, "%s: couldn't set timeouts on connection", GET_IO_NAME(io)); #ifdef LINUX_TRANSPARENT_PROXY if (src && g_state.tproxy_out) { int true = 1; if(setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, (void *)&true, sizeof(true)) == -1) sp_message(ctx, LOG_WARNING, "%s: couldn't set transparent mode on connection", GET_IO_NAME(io)); else if (bind(fd, &SANY_ADDR(*src), SANY_LEN(*src)) == -1) sp_message(ctx, LOG_WARNING, "%s: couldn't bind foreign address on connection", GET_IO_NAME(io)); }
static int sign_data(spctx_t *ctx) { char ebuf[256]; int n; DKIMContext ctxt; strncpy(dkim_opts.szRequiredHeaders, "From;To;Cc;Subject;Date;", 25); dkim_opts.nHash = DKIM_HASH_SHA256; dkim_opts.nIncludeBodyHash = DKIM_BODYHASH_IETF_1; dkim_opts.nCanon = DKIM_SIGN_RELAXED; if(key_is_set == 0) { FILE* keyFP = fopen( keyfile, "r" ); if ( keyFP == NULL ) { sp_messagex(ctx, LOG_ERR, "Unable to open DKIM key file: %s", keyfile); return -1; } n = fread( keyBuffer, 1, sizeof(keyBuffer), keyFP ); if (n == sizeof(keyBuffer)) /* TC9 */ { sp_messagex(ctx, LOG_ERR, "DKIM key is too large, maximum size is %i", sizeof(keyBuffer)); } keyBuffer[n] = '\0'; fclose(keyFP); sp_messagex(ctx, LOG_DEBUG, "DKIM key read from %s", keyfile); key_is_set = 1; } /* Tell client to start sending data */ if(sp_start_data (ctx) < 0) return -1; /* Message already printed */ n = DKIMSignInit( &ctxt, &dkim_opts ); sp_messagex(ctx, LOG_DEBUG, "DKIM signer initialised"); int len = -1; const char *buffer = 0; do { len = sp_read_data(ctx, &buffer); if(len == -1) { DKIMSignFree(&ctxt); return -1; } if(len > 0) { DKIMSignProcess( &ctxt, buffer, len ); sp_write_data( ctx, buffer, len ); } } while(len > 0); sp_write_data( ctx, NULL, 0 ); char *dkim_signature = NULL; n = DKIMSignGetSig2( &ctxt, keyBuffer, &dkim_signature ); sp_messagex(ctx, LOG_DEBUG, "Signing message"); if( sp_done_data(ctx, dkim_signature) == -1 ) { DKIMSignFree(&ctxt); return -1; } else { DKIMSignFree(&ctxt); return 0; } }
static int verify_data(spctx_t* ctx) { char ebuf[256]; int n; SPF_request_t *spf_request = NULL; char * xforwardaddr = NULL; char * xforwardhelo = NULL; if(spf_server == NULL) { /* redirect errors */ SPF_error_handler = SPF_error_syslog; SPF_warning_handler = SPF_warning_syslog; SPF_info_handler = SPF_info_syslog; SPF_debug_handler = SPF_debug_syslog; spf_server = SPF_server_new(SPF_DNS_CACHE, 1); if (spf_server == NULL) return -1; } /* trim string */ if(ctx->xforwardaddr) xforwardaddr = trim_space(ctx->xforwardaddr); if(ctx->xforwardhelo) xforwardhelo = trim_space(ctx->xforwardhelo); sp_messagex(ctx, LOG_DEBUG, "New connection: ADDR %s - MAIL FROM %s - XF-ADDR %s - XF-HELO %s", ctx->client.peername, ctx->sender, xforwardaddr, xforwardhelo); spf_request = SPF_request_new(spf_server); if( xforwardaddr ) SPF_request_set_ipv4_str( spf_request, xforwardaddr ); else if ( ctx->client.peername ) SPF_request_set_ipv4_str( spf_request, ctx->client.peername ); if( xforwardhelo ) SPF_request_set_helo_dom( spf_request, xforwardhelo ); if( ctx->sender ) SPF_request_set_env_from( spf_request, ctx->sender ); SPF_response_t *spf_response = NULL; SPF_request_query_mailfrom(spf_request, &spf_response); char hostname[100]; strncpy(hostname, SPF_request_get_rec_dom(spf_request), 99); char *result_spf = NULL; switch(SPF_response_result(spf_response)) { case SPF_RESULT_NONE: sp_messagex(ctx, LOG_DEBUG, "No SPF policy found for %s", ctx->sender); result_spf = "none"; break; case SPF_RESULT_NEUTRAL: result_spf = "neutral"; sp_messagex(ctx, LOG_DEBUG, "SPF: NEUTRAL for %s", ctx->sender); break; case SPF_RESULT_SOFTFAIL: result_spf = "softfail"; sp_messagex(ctx, LOG_DEBUG, "SPF: SOFTFAIL for %s", ctx->sender); break; case SPF_RESULT_PASS: result_spf = "pass"; sp_messagex(ctx, LOG_DEBUG, "SPF: PASS for %s", ctx->sender); break; case SPF_RESULT_FAIL: buffer_reject_message("550 SPF Reject", ebuf, sizeof(ebuf)); buffer_reject_message(SPF_response_get_smtp_comment(spf_response), ebuf, sizeof(ebuf)); final_reject_message(ebuf, sizeof(ebuf)); sp_messagex(ctx, LOG_DEBUG, "SPF FAIL for %s, ignore message", ctx->sender); SPF_response_free(spf_response); SPF_request_free(spf_request); if(sp_fail_data(ctx, ebuf) == -1) return -1; else return 0; break; case SPF_RESULT_TEMPERROR: case SPF_RESULT_PERMERROR: case SPF_RESULT_INVALID: buffer_reject_message("450 temporary failure", ebuf, sizeof(ebuf)); final_reject_message(ebuf, sizeof(ebuf)); sp_messagex(ctx, LOG_DEBUG, "TEMP ERROR or INVALID RECORD in SPF for %s", ctx->sender); SPF_response_free(spf_response); SPF_request_free(spf_request); if(sp_fail_data(ctx, ebuf) == -1) return -1; else return 0; break; }; char auth_result_spf[1025]; snprintf(auth_result_spf, 1024, "spf=%s smtp.mailfrom=%s", result_spf, ctx->sender); SPF_response_free(spf_response); SPF_request_free(spf_request); /* Tell client to start sending data */ if(sp_start_data (ctx) < 0) return -1; /* Message already printed */ /* Setup DKIM verifier */ DKIMContext ctxt; DKIMVerifyOptions vopts = {0}; vopts.nCheckPractices = 1; vopts.pfnSelectorCallback = NULL; //SelectorCallback; n = DKIMVerifyInit( &ctxt, &vopts ); /* Read data into verifier */ int len = -1; const char *buffer = 0; do { len = sp_read_data(ctx, &buffer); if(len == -1) return -1; if(len > 0) { DKIMVerifyProcess( &ctxt, buffer, len ); sp_write_data( ctx, buffer, len ); } } while(len > 0); sp_write_data( ctx, NULL, 0 ); /* Verify DKIM */ n = DKIMVerifyResults( &ctxt ); /* Get verification details */ int nSigCount = 0; DKIMVerifyDetails* pDetails; char szPolicy[512]; DKIMVerifyGetDetails(&ctxt, &nSigCount, &pDetails, szPolicy ); /* Proxy based on verification results */ char auth_result_dkim[1025]; if(nSigCount == 0) { sp_messagex(ctx, LOG_DEBUG, "No DKIM signature, passthrough"); snprintf(auth_result_dkim, 1024, "dkim=none"); } else if (n == DKIM_SUCCESS || n == DKIM_PARTIAL_SUCCESS) { sp_messagex(ctx, LOG_DEBUG, "DKIM verification: Success, adding header information"); int strpos = 0; int i=0; for(; i<nSigCount; ++i) { snprintf(&auth_result_dkim[strpos], 1024 - strpos, "%sdkim=%s header.d=%s", (i>0 ? ";\n" : ""), (pDetails[i].nResult == DKIM_SUCCESS ? "pass" : "fail"), pDetails[i].szSignatureDomain); strpos = strlen(auth_result_dkim); } } else { sp_messagex(ctx, LOG_DEBUG, "DKIM verification: Failed, report error and ignore message."); buffer_reject_message("550 DKIM Signature failed verification (http://www.dkim.org/info/dkim-faq.html)", ebuf, sizeof(ebuf)); final_reject_message(ebuf, sizeof(ebuf)); DKIMVerifyFree( &ctxt ); if(sp_fail_data(ctx, ebuf) == -1) return -1; else return 0; } DKIMVerifyFree( &ctxt ); char auth_results_header[1025]; snprintf(auth_results_header, 1024, "Authentication-Results: %s;\n %s;\n %s;", hostname, auth_result_spf, auth_result_dkim); if( sp_done_data(ctx, auth_results_header) == -1) return -1; return 0; }