static void SPF_server_new_common_post(SPF_server_t *sp) { SPF_response_t *spf_response; SPF_errcode_t err; spf_response = NULL; err = SPF_server_set_explanation(sp, SPF_DEFAULT_EXP, &spf_response); if (err != SPF_E_SUCCESS) SPF_errorf("Error code %d compiling default explanation", err); if (spf_response) { /* XXX Print the errors?! */ if (SPF_response_messages(spf_response) > 0) SPF_error("Response errors compiling default explanation"); SPF_response_free(spf_response); } spf_response = NULL; err = SPF_server_set_localpolicy(sp, "", 0, &spf_response); if (err != SPF_E_SUCCESS) SPF_errorf("Error code %d compiling default whitelist", err); if (spf_response) { /* XXX Print the errors?! */ if (SPF_response_messages(spf_response) > 0) SPF_error("Response errors compiling default whitelist"); SPF_response_free(spf_response); } #if HAVE_UNBOUND_H // set the default dns timeout to be 5 seconds SPF_server_set_dns_timeout(sp, 5.0); #endif }
CAMLprim value caml_spf_request_query_mailfrom(value req_val) { CAMLparam1(req_val); CAMLlocal3(ret, cmt, res); const char *s; SPF_request_t *req = (SPF_request_t *)req_val; SPF_response_t *resp; SPF_result_t result; caml_enter_blocking_section(); SPF_request_query_mailfrom(req, &resp); caml_leave_blocking_section(); ret = caml_alloc(5, 0); result = SPF_response_result(resp); res = caml_alloc(1, tag_of_result(result)); switch (result) { case SPF_RESULT_FAIL: case SPF_RESULT_SOFTFAIL: case SPF_RESULT_NEUTRAL: cmt = caml_alloc(2, 0); Store_field(cmt, 0, caml_copy_string(SPF_response_get_smtp_comment(resp))); Store_field(cmt, 1, caml_copy_string(SPF_response_get_explanation(resp))); res = caml_alloc(1, tag_of_result(result)); Store_field(res, 0, cmt); Store_field(ret, 0, res); break; case SPF_RESULT_INVALID: case SPF_RESULT_PASS: case SPF_RESULT_TEMPERROR: case SPF_RESULT_PERMERROR: case SPF_RESULT_NONE: Store_field(ret, 0, Val_int(tag_of_result(result))); break; } Store_field(ret, 1, Val_int(SPF_response_reason(resp))); /* For buggy libspf2 - avoid a segfault */ #define BUG_HEADER_COMMENT "internal error" #define BUG_RECEIVED_SPF_VALUE "none (" BUG_HEADER_COMMENT ")" #define BUG_RECEIVED_SPF "Received-SPF: " BUG_RECEIVED_SPF_VALUE s = SPF_response_get_received_spf(resp); Store_field(ret, 2, caml_copy_string(s ? s : BUG_RECEIVED_SPF)); s = SPF_response_get_received_spf_value(resp); Store_field(ret, 3, caml_copy_string(s ? s : BUG_RECEIVED_SPF_VALUE)); s = SPF_response_get_header_comment(resp); Store_field(ret, 4, caml_copy_string(s ? s : BUG_HEADER_COMMENT)); SPF_response_free(resp); CAMLreturn(ret); }
int main() { int spf; char *me, *remote, *helo, *sender, *spf_env; const char *header; SPF_server_t *spf_server; SPF_request_t *spf_request; SPF_response_t *spf_response; /** * env variables **/ if (getenv("RELAYCLIENT") || /* known user */ !(spf_env = getenv("SPF"))) return 0; /* plugin disabled */ spf = atoi(spf_env); if (spf < 1 || spf > 6) { if (spf > 6) fprintf(stderr, "spf: ERROR: invalid value (%d) of SPF variable\n", spf); return 0; } remote = getenv("TCPREMOTEIP"); me = getenv("TCPLOCALHOST"); if (!me) me = getenv("TCPLOCALIP"); if (!remote || !me) { /* should never happen */ fprintf(stderr, "spf: ERROR: can't get tcpserver variables\n"); if(!remote) fprintf(stderr, "spf: can't read TCPREMOTEIP\n"); else fprintf(stderr, "spf: can't read TCPLOCALHOST nor TCPLOCALIP\n"); return 0; } sender = getenv("SMTPMAILFROM"); if (!sender) { /* should never happen */ fprintf(stderr, "spf: ERROR: can't get envelope sender address\n"); fprintf(stderr, "spf: can't read SMTPMAILFROM\n"); return 0; } if (!*sender) return 0; /* null sender mail */ helo = getenv("SMTPHELOHOST"); /** * SPF **/ spf_server = SPF_server_new(SPF_DNS_CACHE, 0); if (!spf_server) { fprintf(stderr, "spf: ERROR: can't initialize libspf2\n"); return 0; } spf_request = SPF_request_new(spf_server); if (!spf_request) { fprintf(stderr, "spf: ERROR: can't initialize libspf2\n"); return 0; } if (SPF_request_set_ipv4_str(spf_request, remote)) { fprintf(stderr, "spf: can't parse TCPREMOTEIP\n"); return 0; } SPF_server_set_rec_dom(spf_server, me); if (helo) SPF_request_set_helo_dom(spf_request, helo); SPF_request_set_env_from(spf_request, sender); /* Perform actual lookup */ SPF_request_query_mailfrom(spf_request, &spf_response); /* check whether mail needn`t to be blocked */ switch (SPF_response_result(spf_response)) { case SPF_RESULT_PASS: break; case SPF_RESULT_FAIL: if (spf > 0) { block(spf_response); return 0; } break; case SPF_RESULT_SOFTFAIL: if (spf > 1) { block(spf_response); return 0; } break; case SPF_RESULT_NEUTRAL: if (spf > 2) { block(spf_response); return 0; } break; case SPF_RESULT_NONE: if (spf > 3) { block(spf_response); return 0; } break; case SPF_RESULT_TEMPERROR: case SPF_RESULT_PERMERROR: if (spf > 4) { block(spf_response); return 0; } break; #if 0 case SPF_RESULT_UNKNOWN: if (spf > 5) { block(spf_response); return 0; } break; case SPF_RESULT_UNMECH: break; #else // FIXME: UNKNOWN and UNMECH above map how? // INVALID should not ever occur, it indicates a bug. case SPF_RESULT_INVALID: if (spf > 5) { block(spf_response); return 0; } break; #endif } /* add header */ header = SPF_response_get_received_spf(spf_response); if (header) printf("H%s\n", header); else { fprintf(stderr, "spf: libspf2 library failed to produce Received-SPF header\n", SPF_strerror(SPF_response_errcode(spf_response))); /* Example taken from libspf2: */ /* fprintf ( stderr, "spf: diag: result = %s (%d)\n", SPF_strresult(SPF_response_result(spf_response)), SPF_response_result(spf_response)); fprintf ( stderr, "spf: diag: err = %s (%d)\n", SPF_strerror(SPF_response_errcode(spf_response)), SPF_response_errcode(spf_response)); for (i = 0; i < SPF_response_messages(spf_response); i++) { SPF_error_t *err = SPF_response_message(spf_response, i); fprintf ( stderr, "spf: diag: %s_msg = (%d) %s\n", (SPF_error_errorp(err) ? "warn" : "err"), SPF_error_code(err), SPF_error_message(err)); } */ } SPF_response_free(spf_response); SPF_request_free(spf_request); SPF_server_free(spf_server); return 0; }
SPF_errcode_t SPF_record_interpret(SPF_record_t *spf_record, SPF_request_t *spf_request, SPF_response_t *spf_response, int depth) { SPF_server_t *spf_server; /* Temporaries */ int i, j; int m; /* Mechanism iterator */ SPF_mech_t *mech; SPF_data_t *data; SPF_data_t *data_end; /* XXX Replace with size_t data_len */ /* Where to insert the local policy (whitelist) */ SPF_mech_t *local_policy; /* Not the local policy */ int found_all; /* A crappy temporary. */ char *buf = NULL; size_t buf_len = 0; ns_type fetch_ns_type; const char *lookup; SPF_dns_rr_t *rr_a; SPF_dns_rr_t *rr_aaaa; SPF_dns_rr_t *rr_ptr; SPF_dns_rr_t *rr_mx; SPF_errcode_t err; SPF_dns_server_t*resolver; /* An SPF record for subrequests - replaces c_results */ SPF_record_t *spf_record_subr; SPF_response_t *save_spf_response; SPF_response_t *spf_response_subr; const char *save_cur_dom; struct in_addr addr4; struct in6_addr addr6; int max_ptr; int max_mx; int max_exceeded; char ip4_buf[ INET_ADDRSTRLEN ]; char ip6_buf[ INET6_ADDRSTRLEN ]; /* * make sure we were passed valid data to work with */ SPF_ASSERT_NOTNULL(spf_record); SPF_ASSERT_NOTNULL(spf_request); SPF_ASSERT_NOTNULL(spf_response); spf_server = spf_record->spf_server; SPF_ASSERT_NOTNULL(spf_server); SPF_ASSERT_NOTNULL(spf_response->spf_record_exp); if (depth > 20) return DONE_PERMERR(SPF_E_RECURSIVE); if ( spf_request->client_ver != AF_INET && spf_request->client_ver != AF_INET6 ) return DONE_PERMERR(SPF_E_NOT_CONFIG); if (spf_request->cur_dom == NULL) return DONE_PERMERR(SPF_E_NOT_CONFIG); /* * localhost always gets a free ride */ #if 0 /* This should have been done already before we got here. */ if ( SPF_request_is_loopback( spf_request ) ) return DONE(SPF_RESULT_PASS,SPF_REASON_LOCALHOST,SPF_E_SUCCESS); #endif /* * Do some start up stuff if we haven't recursed yet */ local_policy = NULL; if ( spf_request->use_local_policy ) { /* * find the location for the whitelist execution * * Philip Gladstone says: * * I think that the localpolicy should only be inserted if the * final mechanism is '-all', and it should be inserted after * the last mechanism which is not '-'. * * Thus for the case of 'v=spf1 +a +mx -all', this would be * interpreted as 'v=spf1 +a +mx +localpolicy -all'. Whereas * 'v=spf1 -all' would remain the same (no non-'-' * mechanism). 'v=spf1 +a +mx -exists:%stuff -all' would * become 'v=spf1 +a +mx +localpolicy -exists:%stuff -all'. */ if ( spf_server->local_policy ) { mech = spf_record->mech_first; found_all = FALSE; for(m = 0; m < spf_record->num_mech; m++) { if ( mech->mech_type == MECH_ALL && (mech->prefix_type == PREFIX_FAIL || mech->prefix_type == PREFIX_UNKNOWN || mech->prefix_type == PREFIX_SOFTFAIL ) ) found_all = TRUE; if ( mech->prefix_type != PREFIX_FAIL && mech->prefix_type != PREFIX_SOFTFAIL ) local_policy = mech; mech = SPF_mech_next( mech ); } if ( !found_all ) local_policy = NULL; } } /* * evaluate the mechanisms */ #define SPF_ADD_DNS_MECH() do { spf_response->num_dns_mech++; } while(0) #define SPF_MAYBE_SKIP_CIDR() \ do { \ if ( data < data_end && data->dc.parm_type == PARM_CIDR ) \ data = SPF_data_next( data ); \ } while(0) #define SPF_GET_LOOKUP_DATA() \ do { \ if ( data == data_end ) \ lookup = spf_request->cur_dom; \ else { \ err = SPF_record_expand_data( spf_server, \ spf_request, spf_response, \ data, ((char *)data_end - (char *)data), \ &buf, &buf_len ); \ if (err == SPF_E_NO_MEMORY) { \ SPF_FREE_LOOKUP_DATA(); \ return DONE_TEMPERR(err); \ } \ if (err) { \ SPF_FREE_LOOKUP_DATA(); \ return DONE_PERMERR(err); \ } \ lookup = buf; \ } \ } while(0) #define SPF_FREE_LOOKUP_DATA() \ do { if (buf != NULL) { free(buf); buf = NULL; } } while(0) resolver = spf_server->resolver; mech = spf_record->mech_first; for (m = 0; m < spf_record->num_mech; m++) { /* This is as good a place as any. */ /* XXX Rip this out and put it into a macro which can go into inner loops. */ if (spf_response->num_dns_mech > spf_server->max_dns_mech) { SPF_FREE_LOOKUP_DATA(); return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, SPF_E_BIG_DNS); } data = SPF_mech_data(mech); data_end = SPF_mech_end_data(mech); switch (mech->mech_type) { case MECH_A: SPF_ADD_DNS_MECH(); SPF_MAYBE_SKIP_CIDR(); SPF_GET_LOOKUP_DATA(); if (spf_request->client_ver == AF_INET) fetch_ns_type = ns_t_a; else fetch_ns_type = ns_t_aaaa; rr_a = SPF_dns_lookup(resolver, lookup, fetch_ns_type, TRUE); if (spf_server->debug) SPF_debugf("found %d A records for %s (herrno: %d)", rr_a->num_rr, lookup, rr_a->herrno); if (rr_a->herrno == TRY_AGAIN) { SPF_dns_rr_free(rr_a); SPF_FREE_LOOKUP_DATA(); return DONE_TEMPERR(SPF_E_DNS_ERROR); /* REASON_MECH */ } for (i = 0; i < rr_a->num_rr; i++) { /* XXX Should this be hoisted? */ if (rr_a->rr_type != fetch_ns_type) continue; if (spf_request->client_ver == AF_INET) { if (SPF_i_match_ip4(spf_server, spf_request, mech, rr_a->rr[i]->a)) { SPF_dns_rr_free(rr_a); SPF_FREE_LOOKUP_DATA(); return DONE_MECH(mech->prefix_type); } } else { if (SPF_i_match_ip6(spf_server, spf_request, mech, rr_a->rr[i]->aaaa)) { SPF_dns_rr_free(rr_a); SPF_FREE_LOOKUP_DATA(); return DONE_MECH(mech->prefix_type); } } } SPF_dns_rr_free(rr_a); break; case MECH_MX: SPF_ADD_DNS_MECH(); SPF_MAYBE_SKIP_CIDR(); SPF_GET_LOOKUP_DATA(); rr_mx = SPF_dns_lookup(resolver, lookup, ns_t_mx, TRUE); if (spf_server->debug) SPF_debugf("found %d MX records for %s (herrno: %d)", rr_mx->num_rr, lookup, rr_mx->herrno); if (rr_mx->herrno == TRY_AGAIN) { SPF_dns_rr_free(rr_mx); SPF_FREE_LOOKUP_DATA(); return DONE_TEMPERR(SPF_E_DNS_ERROR); } /* The maximum number of MX records we will inspect. */ max_mx = rr_mx->num_rr; max_exceeded = 0; if (max_mx > spf_server->max_dns_mx) { max_exceeded = 1; max_mx = SPF_server_get_max_dns_mx(spf_server); } for (j = 0; j < max_mx; j++) { /* XXX Should this be hoisted? */ if (rr_mx->rr_type != ns_t_mx) continue; if (spf_request->client_ver == AF_INET) fetch_ns_type = ns_t_a; else fetch_ns_type = ns_t_aaaa; rr_a = SPF_dns_lookup(resolver, rr_mx->rr[j]->mx, fetch_ns_type, TRUE ); if (spf_server->debug) SPF_debugf("%d: found %d A records for %s (herrno: %d)", j, rr_a->num_rr, rr_mx->rr[j]->mx, rr_a->herrno); if (rr_a->herrno == TRY_AGAIN) { SPF_dns_rr_free(rr_mx); SPF_dns_rr_free(rr_a); SPF_FREE_LOOKUP_DATA(); return DONE_TEMPERR(SPF_E_DNS_ERROR); } for (i = 0; i < rr_a->num_rr; i++) { /* XXX Should this be hoisted? */ if (rr_a->rr_type != fetch_ns_type) continue; if (spf_request->client_ver == AF_INET) { if (SPF_i_match_ip4(spf_server, spf_request, mech, rr_a->rr[i]->a)) { SPF_dns_rr_free(rr_mx); SPF_dns_rr_free(rr_a); SPF_FREE_LOOKUP_DATA(); return DONE(mech->prefix_type, SPF_REASON_MECH, SPF_E_SUCCESS); } } else { if (SPF_i_match_ip6(spf_server, spf_request, mech, rr_a->rr[i]->aaaa)) { SPF_dns_rr_free(rr_mx); SPF_dns_rr_free(rr_a); SPF_FREE_LOOKUP_DATA(); return DONE(mech->prefix_type, SPF_REASON_MECH, SPF_E_SUCCESS); } } } SPF_dns_rr_free(rr_a); } SPF_dns_rr_free( rr_mx ); if (max_exceeded) { SPF_FREE_LOOKUP_DATA(); return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, SPF_E_BIG_DNS); } break; case MECH_PTR: SPF_ADD_DNS_MECH(); SPF_GET_LOOKUP_DATA(); if (spf_request->client_ver == AF_INET) { rr_ptr = SPF_dns_rlookup(resolver, spf_request->ipv4, ns_t_ptr, TRUE); if (spf_server->debug) { INET_NTOP(AF_INET, &spf_request->ipv4.s_addr, ip4_buf, sizeof(ip4_buf)); SPF_debugf("got %d PTR records for %s (herrno: %d)", rr_ptr->num_rr, ip4_buf, rr_ptr->herrno); } if (rr_ptr->herrno == TRY_AGAIN) { SPF_dns_rr_free(rr_ptr); SPF_FREE_LOOKUP_DATA(); return DONE_TEMPERR(SPF_E_DNS_ERROR); } /* The maximum number of PTR records we will inspect. */ max_ptr = rr_ptr->num_rr; max_exceeded = 0; if (max_ptr > spf_server->max_dns_ptr) { max_exceeded = 1; max_ptr = SPF_server_get_max_dns_ptr(spf_server); } for (i = 0; i < max_ptr; i++) { /* XXX MX has a 'continue' case here which should be hoisted. */ rr_a = SPF_dns_lookup(resolver, rr_ptr->rr[i]->ptr, ns_t_a, TRUE); if (spf_server->debug) SPF_debugf( "%d: found %d A records for %s (herrno: %d)", i, rr_a->num_rr, rr_ptr->rr[i]->ptr, rr_a->herrno ); if (rr_a->herrno == TRY_AGAIN) { SPF_dns_rr_free(rr_ptr); SPF_dns_rr_free(rr_a); SPF_FREE_LOOKUP_DATA(); return DONE_TEMPERR( SPF_E_DNS_ERROR ); } for (j = 0; j < rr_a->num_rr; j++) { /* XXX MX has a 'continue' case here which should be hoisted. */ if (spf_server->debug) { INET_NTOP(AF_INET, &rr_a->rr[j]->a.s_addr, ip4_buf, sizeof(ip4_buf)); SPF_debugf("%d: %d: found %s", i, j, ip4_buf); } if (rr_a->rr[j]->a.s_addr == spf_request->ipv4.s_addr) { if (SPF_i_match_domain(spf_server, rr_ptr->rr[i]->ptr, lookup)) { SPF_dns_rr_free(rr_ptr); SPF_dns_rr_free(rr_a); SPF_FREE_LOOKUP_DATA(); return DONE_MECH(mech->prefix_type); } } } SPF_dns_rr_free(rr_a); } SPF_dns_rr_free(rr_ptr); if (max_exceeded) { SPF_FREE_LOOKUP_DATA(); return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, SPF_E_BIG_DNS); } } else if ( spf_request->client_ver == AF_INET6 ) { rr_ptr = SPF_dns_rlookup6(resolver, spf_request->ipv6, ns_t_ptr, TRUE); if ( spf_server->debug ) { INET_NTOP( AF_INET6, &spf_request->ipv6.s6_addr, ip6_buf, sizeof( ip6_buf ) ); SPF_debugf( "found %d PTR records for %s (herrno: %d)", rr_ptr->num_rr, ip6_buf, rr_ptr->herrno ); } if( rr_ptr->herrno == TRY_AGAIN ) { SPF_dns_rr_free(rr_ptr); SPF_FREE_LOOKUP_DATA(); return DONE_TEMPERR( SPF_E_DNS_ERROR ); } max_ptr = rr_ptr->num_rr; max_exceeded = 0; if (max_ptr > spf_server->max_dns_ptr) { max_ptr = SPF_server_get_max_dns_ptr(spf_server); max_exceeded = 1; } for (i = 0; i < max_ptr; i++) { /* XXX MX has a 'continue' case here which should be hoisted. */ rr_aaaa = SPF_dns_lookup(resolver, rr_ptr->rr[i]->ptr, ns_t_aaaa, TRUE); if ( spf_server->debug ) SPF_debugf("%d: found %d AAAA records for %s (herrno: %d)", i, rr_aaaa->num_rr, rr_ptr->rr[i]->ptr, rr_aaaa->herrno); if( rr_aaaa->herrno == TRY_AGAIN ) { SPF_dns_rr_free(rr_ptr); SPF_dns_rr_free(rr_aaaa); SPF_FREE_LOOKUP_DATA(); return DONE_TEMPERR( SPF_E_DNS_ERROR ); } for( j = 0; j < rr_aaaa->num_rr; j++ ) { /* XXX MX has a 'continue' case here which should be hoisted. */ if ( spf_server->debug ) { INET_NTOP(AF_INET6, &rr_aaaa->rr[j]->aaaa.s6_addr, ip6_buf, sizeof(ip6_buf)); SPF_debugf( "%d: %d: found %s", i, j, ip6_buf ); } if (memcmp(&rr_aaaa->rr[j]->aaaa, &spf_request->ipv6, sizeof(spf_request->ipv6)) == 0) { if (SPF_i_match_domain(spf_server, rr_ptr->rr[i]->ptr, lookup)) { SPF_dns_rr_free( rr_ptr ); SPF_dns_rr_free(rr_aaaa); SPF_FREE_LOOKUP_DATA(); return DONE_MECH( mech->prefix_type ); } } } SPF_dns_rr_free(rr_aaaa); } SPF_dns_rr_free(rr_ptr); if (max_exceeded) { SPF_FREE_LOOKUP_DATA(); return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, SPF_E_BIG_DNS); } } break; case MECH_INCLUDE: case MECH_REDIRECT: SPF_ADD_DNS_MECH(); err = SPF_record_expand_data(spf_server, spf_request, spf_response, SPF_mech_data(mech), SPF_mech_data_len(mech), &buf, &buf_len ); if ( err == SPF_E_NO_MEMORY ) { SPF_FREE_LOOKUP_DATA(); return DONE_TEMPERR( err ); } if ( err ) { SPF_FREE_LOOKUP_DATA(); return DONE_PERMERR( err ); } lookup = buf; /* XXX Maintain a stack depth here. Limit at 10. */ if (strcmp(lookup, spf_request->cur_dom) == 0) { SPF_FREE_LOOKUP_DATA(); return DONE_PERMERR( SPF_E_RECURSIVE ); } /* * get the (compiled) SPF record */ spf_record_subr = NULL; /* Remember to reset this. */ save_cur_dom = spf_request->cur_dom; spf_request->cur_dom = lookup; err = SPF_server_get_record(spf_server, spf_request, spf_response, &spf_record_subr); if ( spf_server->debug > 0 ) SPF_debugf( "include/redirect: got SPF record: %s", SPF_strerror( err ) ); if (err != SPF_E_SUCCESS) { spf_request->cur_dom = save_cur_dom; if (spf_record_subr) SPF_record_free(spf_record_subr); SPF_FREE_LOOKUP_DATA(); if (err == SPF_E_DNS_ERROR) return DONE_TEMPERR( err ); else return DONE_PERMERR( err ); } SPF_ASSERT_NOTNULL(spf_record_subr); /* * If we are a redirect which is not within the scope * of any include. */ if (mech->mech_type == MECH_REDIRECT) { save_spf_response = NULL; if (spf_response->spf_record_exp == spf_record) spf_response->spf_record_exp = spf_record_subr; SPF_ASSERT_NOTNULL(spf_response->spf_record_exp); } else { save_spf_response = spf_response; spf_response = SPF_response_new(spf_request); if (! spf_response) { if (spf_record_subr) SPF_record_free(spf_record_subr); SPF_FREE_LOOKUP_DATA(); return DONE_TEMPERR(SPF_E_NO_MEMORY); } spf_response->spf_record_exp = spf_record; SPF_ASSERT_NOTNULL(spf_response->spf_record_exp); } /* * find out whether this configuration passes */ err = SPF_record_interpret(spf_record_subr, spf_request, spf_response, depth + 1); spf_request->cur_dom = save_cur_dom; /* Now, if we were a redirect, the child called done() * and used spf_record_exp. In that case, we need not * worry that spf_record_subr is invalid after the free. * If we were not a redirect, then spf_record_subr * is still the record it was in the first place. * Thus we do not need to reset it now. */ SPF_record_free(spf_record_subr); spf_record_subr = NULL; if ( spf_server->debug > 0 ) SPF_debugf( "include/redirect: executed SPF record: %s result: %s reason: %s", SPF_strerror( err ), SPF_strresult( spf_response->result ), SPF_strreason( spf_response->reason ) ); if (mech->mech_type == MECH_REDIRECT) { SPF_FREE_LOOKUP_DATA(); return err; /* One way or the other */ } else { // if (spf_response->result != SPF_RESULT_INVALID) { /* Set everything up properly again. */ spf_response_subr = spf_response; spf_response = save_spf_response; save_spf_response = NULL; /* Rewrite according to prefix of include */ switch (SPF_response_result(spf_response_subr)) { case SPF_RESULT_PASS: /* Pass */ SPF_FREE_LOOKUP_DATA(); SPF_response_free(spf_response_subr); return DONE_MECH( mech->prefix_type ); case SPF_RESULT_FAIL: case SPF_RESULT_SOFTFAIL: case SPF_RESULT_NEUTRAL: /* No match */ SPF_response_free(spf_response_subr); break; case SPF_RESULT_TEMPERROR: /* Generate TempError */ err = SPF_response_errcode(spf_response_subr); SPF_FREE_LOOKUP_DATA(); SPF_response_free(spf_response_subr); return DONE_TEMPERR( err ); case SPF_RESULT_NONE: /* Generate PermError */ SPF_FREE_LOOKUP_DATA(); SPF_response_free(spf_response_subr); return DONE_PERMERR(SPF_E_INCLUDE_RETURNED_NONE); case SPF_RESULT_PERMERROR: case SPF_RESULT_INVALID: /* Generate PermError */ err = SPF_response_errcode(spf_response_subr); SPF_FREE_LOOKUP_DATA(); SPF_response_free(spf_response_subr); return DONE_PERMERR( err ); } #if 0 SPF_FREE_LOOKUP_DATA(); return err; /* The sub-interpret called done() */ #endif } break; case MECH_IP4: memcpy(&addr4, SPF_mech_ip4_data(mech), sizeof(addr4)); if ( SPF_i_match_ip4( spf_server, spf_request, mech, addr4 ) ) { SPF_FREE_LOOKUP_DATA(); return DONE_MECH( mech->prefix_type ); } break; case MECH_IP6: memcpy(&addr6, SPF_mech_ip6_data(mech), sizeof(addr6)); if ( SPF_i_match_ip6( spf_server, spf_request, mech, addr6 ) ) { SPF_FREE_LOOKUP_DATA(); return DONE_MECH( mech->prefix_type ); } break; case MECH_EXISTS: SPF_ADD_DNS_MECH(); err = SPF_record_expand_data(spf_server, spf_request, spf_response, SPF_mech_data(mech),SPF_mech_data_len(mech), &buf, &buf_len); if (err != SPF_E_SUCCESS) { SPF_FREE_LOOKUP_DATA(); return DONE_TEMPERR( err ); } lookup = buf; rr_a = SPF_dns_lookup(resolver, lookup, ns_t_a, FALSE ); if ( spf_server->debug ) SPF_debugf( "found %d A records for %s (herrno: %d)", rr_a->num_rr, lookup, rr_a->herrno ); if( rr_a->herrno == TRY_AGAIN ) { SPF_dns_rr_free(rr_a); SPF_FREE_LOOKUP_DATA(); return DONE_TEMPERR(SPF_E_DNS_ERROR); } if ( rr_a->num_rr > 0 ) { SPF_dns_rr_free(rr_a); SPF_FREE_LOOKUP_DATA(); return DONE_MECH(mech->prefix_type); } SPF_dns_rr_free(rr_a); break; case MECH_ALL: SPF_FREE_LOOKUP_DATA(); if (mech->prefix_type == PREFIX_UNKNOWN) return DONE_PERMERR(SPF_E_UNKNOWN_MECH); return DONE_MECH(mech->prefix_type); break; default: SPF_FREE_LOOKUP_DATA(); return DONE_PERMERR(SPF_E_UNKNOWN_MECH); break; } /* * execute the local policy */ if ( mech == local_policy ) { err = SPF_record_interpret(spf_server->local_policy, spf_request, spf_response, depth + 1); if ( spf_server->debug > 0 ) SPF_debugf( "local_policy: executed SPF record: %s result: %s reason: %s", SPF_strerror( err ), SPF_strresult( spf_response->result ), SPF_strreason( spf_response->reason ) ); if (spf_response->result != SPF_RESULT_INVALID) { SPF_FREE_LOOKUP_DATA(); return err; } } mech = SPF_mech_next( mech ); } SPF_FREE_LOOKUP_DATA(); /* falling off the end is the same as ?all */ return DONE( SPF_RESULT_NEUTRAL, SPF_REASON_DEFAULT, SPF_E_SUCCESS ); }
int spfc(thread_pool_t *info, thread_ctx_t *thread_ctx, edict_t *edict) { struct timespec ts, start, now, timeleft; chkresult_t *result; grey_tuple_t *request; SPF_server_t *spf_server = NULL; SPF_request_t *spf_request = NULL; SPF_response_t *spf_response = NULL; SPF_response_t *spf_response_2mx = NULL; const char *smtp_error; int ret; logstr(GLOG_DEBUG, "spfc called"); request = (grey_tuple_t *)edict->job; assert(request); result = (chkresult_t *)Malloc(sizeof(chkresult_t)); memset(result, 0, sizeof(*result)); result->judgment = J_UNDEFINED; result->checkname = "spf"; /* initialize if we are not yet initialized */ if (NULL == thread_ctx->state) { /* Initialize */ spf_server = SPF_server_new(SPF_DNS_CACHE, SPF_DEBUG_LEVEL); if (NULL == spf_server) { logstr(GLOG_ERROR, "SPF_server_new failed"); goto FINISH; } thread_ctx->state = spf_server; thread_ctx->cleanup = &cleanup_spfc; } else { spf_server = (SPF_server_t *) thread_ctx->state; } /* Now we are ready to query */ ret = SPF_server_set_explanation(spf_server, "Please see http://www.openspf.org/Why?id=%{S}&ip=%{C}", &spf_response); if (ret) logstr(GLOG_ERROR, "SPF: setting explanation failed"); spf_request = SPF_request_new(spf_server); ret = SPF_request_set_ipv4_str(spf_request, request->client_address); if (ret) { logstr(GLOG_ERROR, "invalid IP address %s", request->client_address); goto CLEANUP; } if (request->helo_name) { ret = SPF_request_set_helo_dom(spf_request, request->helo_name); if (ret) { logstr(GLOG_ERROR, "invalid HELO domain: %s.", request->helo_name); goto CLEANUP; } } ret = SPF_request_set_env_from(spf_request, request->sender); if (ret) { logstr(GLOG_ERROR, "invalid envelope sender address %s", request->sender); goto CLEANUP; } ret = SPF_request_query_mailfrom(spf_request, &spf_response); switch (ret) { case SPF_E_SUCCESS: case SPF_E_NOT_SPF: break; default: logstr(GLOG_ERROR, "spf: sender based query failed: %s", SPF_strerror(ret)); goto CLEANUP; } /* XXX: do we need 2mx checks? */ ret = SPF_response_result(spf_response); switch (ret) { case SPF_RESULT_FAIL: result->judgment = J_BLOCK; logstr(GLOG_DEBUG, "SPF: fail"); smtp_error = SPF_response_get_smtp_comment(spf_response); if (smtp_error) result->reason = strdup(smtp_error); else result->reason = strdup("SPF: policy violation: (no message available)"); break; case SPF_RESULT_SOFTFAIL: result->judgment = J_SUSPICIOUS; logstr(GLOG_DEBUG, "SPF softfail"); result->weight = 1; /* FIXME: configurable */ break; case SPF_RESULT_PASS: result->judgment = J_UNDEFINED; logstr(GLOG_DEBUG, "SPF: pass"); break; case SPF_RESULT_NEUTRAL: result->judgment = J_UNDEFINED; logstr(GLOG_DEBUG, "SPF: neutral"); break; case SPF_RESULT_NONE: result->judgment = J_UNDEFINED; logstr(GLOG_DEBUG, "SPF: no record"); break; default: logstr(GLOG_DEBUG, "Unexpected SPF result (%d)", ret); } CLEANUP: if (spf_request) SPF_request_free(spf_request); if (spf_response) SPF_response_free(spf_response); FINISH: send_result(edict, result); logstr(GLOG_DEBUG, "spfc returning"); request_unlink(request); 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; }
static int spf(milter_stage_t stage, char *name, var_t *attrs) { SPF_request_t *req = NULL; SPF_response_t *res = NULL; SPF_response_t *res_2mx = NULL; char *helo; char *envfrom; char from[321]; char *envrcpt; char rcpt[321]; char *spfstr; char *spfreason; struct sockaddr_storage *ss; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; int r; if (acl_symbol_dereference(attrs, "hostaddr", &ss, "envfrom", &envfrom, "envrcpt", &envrcpt, "helo", &helo, NULL)) { log_error("spf: acl_symbol_dereference failed"); goto error; } sin = (struct sockaddr_in *) ss; sin6 = (struct sockaddr_in6 *) ss; if (util_strmail(from, sizeof from, envfrom) == -1 || util_strmail(rcpt, sizeof rcpt, envrcpt) == -1) { log_error("spf: util_strmail failed"); goto error; } req = SPF_request_new(spf_server); if (req == NULL) { log_error("spf: SPF_request_new failed"); goto error; } /* * Set client address */ if (ss->ss_family == AF_INET6) { r = SPF_request_set_ipv6(req, sin6->sin6_addr); } else { r = SPF_request_set_ipv4(req, sin->sin_addr); } if (r) { log_error("spf: SPF_request_set_ip failed"); goto error; } /* * Set helo */ r = SPF_request_set_helo_dom(req, helo); if (r) { log_error("spf: SPF_request_set_helo_dom failed"); goto error; } /* * Set envelope from */ r = SPF_request_set_env_from(req, from); if (r) { log_error("spf_query: SPF_request_set_env_from failed"); goto error; } /* * Perform SPF query */ SPF_request_query_mailfrom(req, &res); if(SPF_response_result(res) == SPF_RESULT_PASS) { goto result; } /* * If SPF fails check if we received the email from a secondary mx. */ SPF_request_query_rcptto(req, &res_2mx, rcpt); if(SPF_response_result(res_2mx) != SPF_RESULT_PASS) { goto result; } /* * Secondary mx */ log_notice("spf: \"%s\" is a secodary mx for \"%s\"", helo, rcpt); goto exit; result: spfstr = (char *) SPF_strresult(SPF_response_result(res)); if (spfstr == NULL) { log_error("spf: SPF_strresult failed"); goto error; } spfreason = (char *) SPF_strreason(SPF_response_result(res)); if (spfreason == NULL) { log_error("spf: SPF_strreason failed"); goto error; } log_message(LOG_ERR, attrs, "spf: helo=%s from=%s spf=%s", helo, from, spfstr); if (vtable_setv(attrs, VT_STRING, "spf", spfstr, VF_KEEP, VT_STRING, "spf_reason", spfreason, VF_KEEP, VT_NULL)) { log_error("spf: vtable_setv failed"); goto error; } exit: SPF_request_free(req); SPF_response_free(res); if(res_2mx) { SPF_response_free(res_2mx); } return 0; error: if(req) { SPF_request_free(req); } if(res) { SPF_response_free(res); } if(res_2mx) { SPF_response_free(res_2mx); } return -1; }