Example #1
0
static SPF_errcode_t
SPF_i_set_header_comment(SPF_response_t *spf_response)
{
	SPF_server_t	*spf_server;
	SPF_request_t	*spf_request;
	char			*spf_source;

	size_t			 len;

	char			 ip4_buf[ INET_ADDRSTRLEN ];
	char			 ip6_buf[ INET6_ADDRSTRLEN ];
	const char		*ip;

	char			*buf;
	char			*sender_dom;
	char			*p, *p_end;

	SPF_ASSERT_NOTNULL(spf_response);
	spf_request = spf_response->spf_request;
	SPF_ASSERT_NOTNULL(spf_request);
	spf_server = spf_request->spf_server;
	SPF_ASSERT_NOTNULL(spf_server);

	if (spf_response->header_comment)
		free(spf_response->header_comment);
	spf_response->header_comment = NULL;

	/* Is this cur_dom? */
	sender_dom = spf_request->env_from_dp;
	if (sender_dom == NULL)
		sender_dom = spf_request->helo_dom;

	if ( spf_response->reason == SPF_REASON_LOCAL_POLICY ) {
		spf_source = strdup( "local policy" );
	}
	else if ( spf_response->reason == SPF_REASON_2MX ) {
		if ( spf_request->rcpt_to_dom == NULL  || spf_request->rcpt_to_dom[0] == '\0' )
			SPF_error( "RCPT TO domain is NULL" );

		spf_source = strdup( spf_request->rcpt_to_dom );
	}
	else if ( sender_dom == NULL ) {
		spf_source = strdup( "unknown domain" );
	}
	else {
		len = strlen( sender_dom ) + sizeof( "domain of " );
		spf_source = malloc( len );
		if ( spf_source )
			snprintf( spf_source, len, "domain of %s", sender_dom );
	}

	if ( spf_source == NULL )
		return SPF_E_INTERNAL_ERROR;

	ip = NULL;
	if ( spf_request->client_ver == AF_INET ) {
		ip = inet_ntop( AF_INET, &spf_request->ipv4,
						ip4_buf, sizeof( ip4_buf ) );
	}
	else if (spf_request->client_ver == AF_INET6 ) {
		ip = inet_ntop( AF_INET6, &spf_request->ipv6,
						ip6_buf, sizeof( ip6_buf ) );
	}
	if ( ip == NULL )
		ip = "(unknown ip address)";

	len = strlen( SPF_request_get_rec_dom(spf_request) ) + strlen( spf_source ) + strlen( ip ) + 80;
	buf = malloc( len );
	if ( buf == NULL ) {
		free( spf_source );
		return SPF_E_INTERNAL_ERROR;
	}

	p = buf;
	p_end = p + len;

	/* create the stock header comment */
	p += snprintf( p, p_end - p, "%s: ",  SPF_request_get_rec_dom(spf_request) );

	switch(spf_response->result)
	{
	case SPF_RESULT_PASS:
		if ( spf_response->reason == SPF_REASON_LOCALHOST )
			snprintf( p, p_end - p, "localhost is always allowed." );
		else if ( spf_response->reason == SPF_REASON_2MX )
			snprintf( p, p_end - p, "message received from %s which is an MX secondary for %s.",
					  ip, spf_source );
		else
			snprintf( p, p_end - p, "%s designates %s as permitted sender",
					  spf_source, ip );
		break;

	case SPF_RESULT_FAIL:
		snprintf( p, p_end - p, "%s does not designate %s as permitted sender",
				  spf_source, ip );
		break;

	case SPF_RESULT_SOFTFAIL:
		snprintf( p, p_end - p, "transitioning %s does not designate %s as permitted sender",
				  spf_source, ip );
		break;

	case SPF_RESULT_PERMERROR:
		snprintf(p, p_end - p, "error in processing during lookup of %s: %s",
					  spf_source, SPF_strerror(spf_response->err));
		break;

	case SPF_RESULT_NEUTRAL:
		snprintf(p, p_end - p, "%s is neither permitted nor denied by %s",
				ip, spf_source);
		break;
	case SPF_RESULT_NONE:
		snprintf(p, p_end - p, "%s does not provide an SPF record",
				spf_source);
		break;

	case SPF_RESULT_TEMPERROR:
		snprintf(p, p_end - p, "encountered temporary error during SPF processing of %s",
				spf_source );
		break;


	default:
		snprintf( p, p_end - p, "error: unknown SPF result %d encountered while checking %s for %s",
				  spf_response->result, ip, spf_source );
		break;
	}

	if (spf_source)
		free(spf_source);

	spf_response->header_comment = SPF_sanitize(spf_server, buf);

	return SPF_E_SUCCESS;
}
Example #2
0
/**
 * This could better collect errors, like the compiler does.
 * This requires that *bufp be either malloced to *buflenp, or NULL
 * This may realloc *bufp.
 */
SPF_errcode_t
SPF_record_expand_data(SPF_server_t *spf_server,
				SPF_request_t *spf_request,
				SPF_response_t *spf_response,
				SPF_data_t *data, size_t data_len,
				char **bufp, size_t *buflenp)
{
	SPF_data_t	*d, *data_end;

	size_t		 len;
	const char	*p_err;	// XXX Check this value, when returned.
	char		*p, *p_end;
	const char	*p_read;
	const char	*p_read_end;
	char		*p_write;
	char		*p2, *p2_end;


	const char	*var;
	char		*munged_var = NULL;
	char		*url_var = NULL;

			/* Pretty-printing buffers. */
	char		ip4_buf[ INET_ADDRSTRLEN ];
	char		ip6_buf[ INET6_ADDRSTRLEN ];
			/* Hex buffer for ipv6 (size in nibbles) */
	char		ip6_rbuf[ sizeof( struct in6_addr ) * 4 + 1 ];

	char		time_buf[ sizeof( "4294967296" ) ]; /* 2^32 seconds max		*/

	int			num_found;
	int			i;
	size_t		buflen;
	int			compute_length;
	SPF_errcode_t	 err;


	/*
	 * make sure we were passed valid data to work with
	 */
	SPF_ASSERT_NOTNULL(spf_server);
	SPF_ASSERT_NOTNULL(data);
	SPF_ASSERT_NOTNULL(bufp);
	SPF_ASSERT_NOTNULL(buflenp);

	buflen = 1;	/* For the terminating '\0' */
	compute_length = 1;
	p = NULL;
	p_end = NULL;

	/* data_end = SPF_mech_end_data( mech ); */ /* doesn't work for mods */
	data_end = (SPF_data_t *)((char *)data + data_len);

top:
#ifdef DEBUG
	fprintf(stderr, "Pass start compute_length=%d\n", compute_length);
#endif
	/*
	 * expand the data
	 */
	for (d = data; d < data_end; d = SPF_data_next(d)) {
#ifdef DEBUG
		fprintf(stderr, " Item type=%d at %p\n", d->dc.parm_type, d);
#endif
		if (d->dc.parm_type == PARM_CIDR)
			continue;

		if (d->ds.parm_type == PARM_STRING) {
			if (compute_length) {
				buflen += d->ds.len;
				continue;
			}
			/* This should NEVER happen now. */
			if (p_end - (p + d->ds.len) <= 0)
					SPF_error("Failed to allocate enough memory "
								"to expand string.");
			memcpy(p, SPF_data_str(d), d->ds.len);
			p += d->ds.len;
			continue;
		}

		/* Otherwise, it's a variable. */

		var = NULL;
		switch (d->dv.parm_type) {
		case PARM_LP_FROM:		/* local-part of envelope-sender */
			var = spf_request->env_from_lp;
			break;

		case PARM_ENV_FROM:		/* envelope-sender				*/
			var = spf_request->env_from;
			break;

		case PARM_DP_FROM:		/* envelope-domain				*/
			var = spf_request->env_from_dp;
			break;

		case PARM_CUR_DOM:		/* current-domain				*/
			var = spf_request->cur_dom;
			break;

		case PARM_CLIENT_IP:		/* SMTP client IP				*/
			if (compute_length) {
				len = sizeof(ip6_buf);
				if (d->dv.url_encode)
					len *= 3;
				buflen += len;
				continue;
			}
			if (spf_request->client_ver == AF_INET) {
				p_err = inet_ntop(AF_INET, &spf_request->ipv4,
								   ip4_buf, sizeof(ip4_buf));
				var = ip4_buf;
			}
			else if (spf_request->client_ver == AF_INET6) {
				p2 = ip6_rbuf;
				p2_end = p2 + sizeof(ip6_rbuf);

				for (i = 0; i < array_elem(spf_request->ipv6.s6_addr); i++) {
					p2 += snprintf(p2, p2_end - p2, "%.1x.%.1x.",
									spf_request->ipv6.s6_addr[i] >> 4,
									spf_request->ipv6.s6_addr[i] & 0xf);
				}

				/* squash the final '.' */
				ip6_rbuf[sizeof(struct in6_addr) * 4 - 1] = '\0';

				var = ip6_rbuf;
			}
			break;

		case PARM_CLIENT_IP_P:		/* SMTP client IP (pretty)		*/
			if (compute_length) {
				len = sizeof(ip6_buf);
				if (d->dv.url_encode)
					len *= 3;
				buflen += len;
				continue;
			}
			if (spf_request->client_ver == AF_INET) {
				p_err = inet_ntop(AF_INET, &spf_request->ipv4,
								   ip4_buf, sizeof(ip4_buf));
				var = ip4_buf;
			}
			else if (spf_request->client_ver == AF_INET6) {
				p_err = inet_ntop(AF_INET6, &spf_request->ipv6,
								   ip6_buf, sizeof(ip6_buf));
				var = ip6_buf;
			}
			break;

		case PARM_TIME:				/* time in UTC epoch secs		*/
			if (compute_length) {
				len = sizeof(time_buf);
				/* This never gets bigger using URL encoding. */
				buflen += len;
				continue;
			}
			snprintf(time_buf, sizeof(time_buf), "%ld",
					  (long)time(NULL));
			var = time_buf;
			break;

		case PARM_CLIENT_DOM:		/* SMTP client domain name		*/
			var = SPF_request_get_client_dom(spf_request);
			if (! var)
				return SPF_E_NO_MEMORY;
			break;

		case PARM_CLIENT_VER:		/* IP ver str - in-addr/ip6		*/
			if (spf_request->client_ver == AF_INET)
				var = client_ver_ipv4;
			else if (spf_request->client_ver == AF_INET6)
				var = client_ver_ipv6;
			break;

		case PARM_HELO_DOM:		/* HELO/EHLO domain				*/
			var = spf_request->helo_dom;
			break;

		case PARM_REC_DOM:		/* receiving domain				*/
			var = SPF_request_get_rec_dom(spf_request);
			break;

		default:
#ifdef DEBUG
			fprintf(stderr, "Invalid variable %d\n", d->dv.parm_type);
#endif
			return SPF_E_INVALID_VAR;
			break;
		}

		if (var == NULL)
			return SPF_E_UNINIT_VAR;

		len = strlen(var);
		if (compute_length) {
			if (d->dv.url_encode)
				len *= 3;
			buflen += len;
			continue;
		}

		/* Now we put 'var' through the munging procedure. */
		munged_var = (char *)malloc(len + 1);
		if (munged_var == NULL)
			return SPF_E_NO_MEMORY;
		memset(munged_var, 0, len + 1);

		p_read_end = var + len;
		p_write = munged_var;

		/* reverse */

/* The following code confuses both me and Coverity. Shevek. */

		if (d->dv.rev) {
			p_read = p_read_end - 1;

			while ( p_read >= var ) {
				if ( SPF_delim_valid(d, *p_read) ) {
					/* Subtract 1 because p_read points to delim, and
					 * p_read_end points to the following delim. */
					len = p_read_end - p_read - 1;
					memcpy( p_write, p_read + 1, len );
					p_write += len;
					*p_write++ = '.';

					p_read_end = p_read;
				}
				p_read--;
			}

			/* Now p_read_end should point one before the start of the
			 * string. p_read_end might also point there if the string
			 * starts with a delimiter. */
			if (p_read_end >= p_read) {
				len = p_read_end - p_read - 1;
				memcpy( p_write, p_read + 1, len );
				p_write += len;
				*p_write++ = '.';
			}

			/* p_write always points to the 'next' character. */
			p_write--;
			*p_write = '\0';
		}
		else {
			p_read = var;

			while (p_read < p_read_end) {
				if (SPF_delim_valid(d, *p_read))
					*p_write++ = '.';
				else
					*p_write++ = *p_read;
				p_read++;
			}

			*p_write = '\0';
		}

		/* Now munged_var is a copy of var, possibly reversed, and
		 * thus len == strlen(munged_var). However, we continue to
		 * manipulate the underlying munged_var since var is const. */

		/* truncate, from the right hand side. */
		if (d->dv.num_rhs > 0) {
			p_read_end = munged_var + len;		/* const, at '\0' */
			p_write = munged_var + len - 1;
			num_found = 0;
			while (p_write > munged_var) {
				if (*p_write == '.')
					num_found++;
				if (num_found == d->dv.num_rhs)
					break;
				p_write--;
			}
			p_write++;		/* Move to just after the '.' */
			/* This moves the '\0' as well. */
			len = p_read_end - p_write;
			memmove(munged_var, p_write, len + 1);
		}

		var = munged_var;
		/* Now, we have 'var', of length 'len' */

		/* URL encode */

		if (d->dv.url_encode) {
			url_var = malloc(len * 3 + 1);
			if (url_var == NULL) {
				if (munged_var)
					free(munged_var);
				return SPF_E_NO_MEMORY;
			}

			p_read = var;
			p_write = url_var;

			/* escape non-uric characters (rfc2396) */
			while ( *p_read != '\0' )
			{
				if ( isalnum( (unsigned char)( *p_read  ) ) )
					*p_write++ = *p_read++;
				else
				{
					switch( *p_read )
					{
					case '-':
					case '_':
					case '.':
					case '!':
					case '~':
					case '*':
					case '\'':
					case '(':
					case ')':
						*p_write++ = *p_read++;
						break;

					default:
						/* No point doing snprintf with a const '4'
						 * because we know we're going to get 4
						 * characters anyway. */
						sprintf( p_write, "%%%02x", *p_read );
						p_write += 3;
						p_read++;
						break;
					}
				}
			}
			*p_write = '\0';

			var = url_var;
			len = p_write - url_var;		/* Not actually used. */
		}


		/* finish up */
		len = snprintf(p, p_end - p, "%s", var);
		p += len;
		if (p_end - p <= 0) {
			if (munged_var)
				free(munged_var);
			if (url_var)
				free(url_var);
			return SPF_E_INTERNAL_ERROR;
		}

		if (munged_var)
			free(munged_var);
		munged_var = NULL;
		if (url_var)
			free(url_var);
		url_var = NULL;
	}
Example #3
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;  
}