Exemple #1
0
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;
}
Exemple #4
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;  
}