main.c

Go to the documentation of this file.
00001 /* libspf - Sender Policy Framework library 00002 * 00003 * ANSI C implementation of spf-draft-200405.txt 00004 * 00005 * Author: James Couzens <jcouzens@codeshare.ca> 00006 * Author: Sean Comeau <scomeau@obscurity.org> 00007 * 00008 * File: spf.c 00009 * Desc: Main library functionality 00010 * 00011 * License: 00012 * 00013 * The libspf Software License, Version 1.0 00014 * 00015 * Copyright (c) 2004 James Couzens & Sean Comeau All rights 00016 * reserved. 00017 * 00018 * Redistribution and use in source and binary forms, with or without 00019 * modification, are permitted provided that the following conditions 00020 * are met: 00021 * 00022 * 1. Redistributions of source code must retain the above copyright 00023 * notice, this list of conditions and the following disclaimer. 00024 * 00025 * 2. Redistributions in binary form must reproduce the above copyright 00026 * notice, this list of conditions and the following disclaimer in 00027 * the documentation and/or other materials provided with the 00028 * distribution. 00029 * 00030 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 00031 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00032 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00033 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS MAKING USE OF THIS LICESEN 00034 * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00035 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00036 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 00037 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 00038 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 00039 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 00040 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00041 * SUCH DAMAGE. 00042 * 00043 */ 00044 00045 #ifdef _WITH_PTHREADS 00046 #include <pthread.h> /* pthread_mutex */ 00047 #endif /* _WITH_PTHREDS */ 00048 00049 #include "../../config.h" /* autoconf */ 00050 #include "main.h" /* our header */ 00051 #include "util.h" /* Utility functions */ 00052 #include "dns.h" /* DNS functions */ 00053 #include "macro.h" /* MACRO parsing functions */ 00054 00055 #undef VERSION /* autoconf */ 00056 00057 spf_config_t confg; /* global config struct */ 00058 00059 int h_errno; /* error handling */ 00060 00061 #ifdef _WITH_PTHREADS 00062 /* mutex for _DNS_gethostbyname_r wrapper */ 00063 extern pthread_mutex_t dns_mutex; 00064 #endif /* _WITH_PTHREADS */ 00065 00066 static SPF_BOOL _SPF_clear_holdbufs(peer_info_t *); 00067 00068 /* SPF_init 00069 * 00070 * Author: James Couzens <jcouzens@codeshare.ca> 00071 * 00072 * Date: 12/25/03 00073 * 00074 * Desc: 00075 * Applies config options and returns allocated memory to a peer_info_t 00076 * structure. Malloc returns NULL on a failure, and as such this function exits 00077 * with NULL upon failure to allocate memory which there by results in a 00078 * complete failure of the entire library. 00079 * 00080 */ 00081 peer_info_t *SPF_init(const char *local, const char *rip, const char *expl, 00082 const char *tf, const char *guess, u_int32_t use_trust, u_int32_t use_guess) 00083 { 00084 time_t curtime; /* time */ 00085 00086 char *cur_utc_time; /* time */ 00087 00088 peer_info_t *p = NULL; /* return structure buffer */ 00089 00090 /* 00091 * spf_result struct including header texts. This structure is used 00092 * during message header generation and has intended future purposes. 00093 * Each string has associated with it its actual size which is used 00094 * to provide some measure of protection against overflowing the strings 00095 * as they are (currently) statically defined in util.h 00096 */ 00097 static spf_result_t spf_result[] = 00098 {{SIZEOF(HR_PASS), HR_PASS, SPF_PASS, SIZEOF(HDR_PASS), HDR_PASS, '+' }, 00099 {SIZEOF(HR_NONE), HR_NONE, SPF_NONE, SIZEOF(HDR_NONE), HDR_NONE, '\0'}, 00100 {SIZEOF(HR_S_FAIL), HR_S_FAIL, SPF_S_FAIL, SIZEOF(HDR_S_FAIL), HDR_S_FAIL, '~' }, 00101 {SIZEOF(HR_H_FAIL), HR_H_FAIL, SPF_H_FAIL, SIZEOF(HDR_H_FAIL), HDR_H_FAIL, '-' }, 00102 {SIZEOF(HR_ERROR), HR_ERROR, SPF_ERROR, SIZEOF(HDR_ERROR), HDR_ERROR, '\0'}, 00103 {SIZEOF(HR_NEUTRAL), HR_NEUTRAL, SPF_NEUTRAL, SIZEOF(HDR_NEUTRAL), HDR_NEUTRAL, '?' }, 00104 {SIZEOF(HR_UNKNOWN), HR_UNKNOWN, SPF_UNKNOWN, SIZEOF(HDR_UNKNOWN), HDR_UNKNOWN, '\0'}, 00105 {SIZEOF(HR_UNMECH), HR_UNMECH, SPF_UNMECH, SIZEOF(HDR_UNMECH), HDR_UNMECH, '\0'}}; 00106 00107 /* 00108 * This structure here is the heart and soul or effectively the 'context' 00109 * of libSPF. This structure is passed back and forth between nearly 00110 * every function which will operate on it in one way or another. 00111 */ 00112 p = xmalloc(SIZEOF(peer_info_t)); 00113 00114 p->spf_result = spf_result; 00115 00116 00117 /* 00118 * Set the SPF query recursion level to zero. This counter is 00119 * incremeneted to a maximum of SPF_MAX_RECURSE (default value of 00120 * that macro is 20) to provide a limit of recursiveness. Not only does 00121 * it astound me to see people creating infinite loops unintentionally 00122 * through the use of the 'include' and 'redirect' facilities within SPF, 00123 * but there is also the greater than average chance that a skriptkiddie 00124 * or some other individual with malicious intentions would use a lack of 00125 * such a level (or a poorly set one, you REALLY should not raise this 00126 * above 20) to faciliate a very effective DoS or dDoS attack against your 00127 * server using SPF checks. 00128 */ 00129 p->spf_rlevel = 0; 00130 00131 /* 00132 * RFC2821 (pre-DATA) related variables 00133 * 00134 */ 00135 00136 p->helo = NULL; 00137 p->ehlo = NULL; 00138 p->from = NULL; 00139 00140 if (local != NULL && (*local && *(local + 1))) 00141 { 00142 p->mta_hname = xstrndup(local, SPF_MAX_LOCAL_PART); 00143 p->r_vhname = xstrndup(local, SPF_MAX_LOCAL_PART); 00144 } 00145 else 00146 { 00147 p->mta_hname = NULL; 00148 p->r_vhname = NULL; 00149 xepprintf("Warning: Invalid local-part detected (DSN or NULL?)\n"); 00150 } 00151 00152 /* don't change this, this is for Solaris which can't print NULL strings */ 00153 xvprintf("Called with: (%s) (%s) (%s) (%s) (%s) %u:%u\n", 00154 local ? local : "NULL", 00155 rip ? rip : "NULL", 00156 expl ? expl : "NULL", 00157 tf ? tf : "NULL", 00158 guess ? guess : "NULL", 00159 use_trust, use_guess); 00160 00161 /* 00162 * Set the default SPF version level to zero. This is important because 00163 * the lack of a valid SPF version here is relied upon within SPF_parse_policy 00164 * to determine an appropriate exit enforcing the RFC requirement that each 00165 * SPF record publish a valid version within their SPF query. 00166 */ 00167 p->spf_ver = 0; 00168 00169 /* 00170 * "Trusted Forwarder" enabled? This is also what you could use if you wanted to 00171 * operate an 'in-house' whitelist for your server. For more information about 00172 * the officla "Trusted Forwarder" service visit http://trustedforwarder.org 00173 */ 00174 if (use_trust == SPF_TRUE) 00175 { 00176 p->use_trust = SPF_TRUE; 00177 } 00178 else 00179 { 00180 p->use_trust = SPF_FALSE; 00181 } 00182 00183 /* 00184 * "Best Guess" enabled? This is a great way to provide a "generic" SPF check 00185 * against domains who do not publish SPF either through ignorance or refusal 00186 * to do so. The default best guess is defined in spf.h and is comprised of 00187 * SPF syntax facilitating 'a', 'mx', and 'ptr' checks against the connected 00188 * host when enabled in an attempt to validate them 00189 */ 00190 if (use_guess == SPF_TRUE) 00191 { 00192 p->use_guess = SPF_TRUE; 00193 } 00194 else 00195 { 00196 p->use_guess = SPF_FALSE; 00197 } 00198 00199 p->ALL = SPF_FALSE; 00200 p->p = NULL; 00201 00202 /* 00203 * SPF Explanation. This can be optionally pre-pended to the RFC2822 00204 * message body as a means of informing a client when rejecting their 00205 * message for a failure to appropriate pass a desired SPF level. 00206 */ 00207 if (expl != NULL && (*expl && *(expl + 1))) 00208 { 00209 p->explain = xstrndup(expl, (strlen(expl) + 1)); 00210 } 00211 else 00212 { 00213 p->explain = NULL; 00214 } 00215 00216 if (guess != NULL && (*guess && *(guess + 1))) 00217 { 00218 p->guess = xstrndup(guess, (strlen(guess) + 1)); 00219 } 00220 else 00221 { 00222 p->guess = xstrndup(SPF_GUESS, (SIZEOF(SPF_GUESS) + 1)); 00223 } 00224 00225 if ((tf != NULL) && (*tf && *(tf + 1))) 00226 { 00227 p->trusted = xstrndup(tf, (strlen(tf) + 1)); 00228 } 00229 else 00230 { 00231 p->trusted = xstrndup(SPF_TRUSTED, (SIZEOF(SPF_TRUSTED) + 1)); 00232 } 00233 00234 p->ptr_mhost = NULL; /* MTA hostname */ 00235 p->current_domain = NULL; /* Current domain name (of client) */ 00236 p->cur_eaddr = NULL; /* Current email-address (of client) */ 00237 00238 /* 00239 * inet_pton is used to convert the IP address of the remotely connected 00240 * client into a network address structure which will be used during 00241 * and SPF parse to perform CIDR calculations against a variety of SPF 00242 * mechanisms ('ip4', 'ip6', etc.). Currently hard set for IPv4. 00243 * 00244 * A character version of the remote peer is retained within the 00245 * peer_info_t structure by reason that there is no sense not doign 00246 * so since we will need to reference it in a human readable form, 00247 * and since the MTA has already performed the labour necessary to 00248 * get it into this form, we might as well keep it. 00249 */ 00250 if (((rip != NULL) && (*rip && *(rip + 1))) && (inet_pton(AF_INET, rip, 00251 &p->addr) >= 0)) 00252 { 00253 p->r_ip = xstrndup(rip, SPF_MAX_IP_ADDR); 00254 } 00255 else 00256 { 00257 xepprintf("Warning: Unable to execute inet_print (bad passed ipaddr?)\n"); 00258 SPF_close(p); 00259 00260 return(NULL); 00261 } 00262 00263 /* 00264 * 'ip_ver' is the Internet Address protocol version being used 00265 * at any given time during an SPF parse. Whilst this is intended to 00266 * support IPv6 at this time it is hard set to IPv4 (AF_INET) 00267 */ 00268 snprintf(p->ip_ver, SPF_MAX_IP_ADDR, "in-addr"); 00269 00270 /* 00271 * Obtain the number of seconds since the UNIX Epoch or UTC time 00272 * which we may optionally be used when pre-pending the Received-SPF 00273 * header after an SPF parse 00274 */ 00275 cur_utc_time = xmalloc(SPF_MAX_DATETIME); 00276 snprintf(cur_utc_time, SPF_MAX_DATETIME, "%lu", time(&curtime)); 00277 memcpy(p->utc_time, cur_utc_time, SPF_MAX_DATETIME); 00278 xfree(cur_utc_time); 00279 00280 /* 00281 * As per the SPF RFC, localhost shall always be considered exempt from 00282 * any SPF checks. There is no facility to override this, nor should 00283 * you. 00284 */ 00285 if ((strcmp(rip, "127.0.0.1") == 0) || (strcmp(rip, "localhost") == 0)) 00286 { 00287 UTIL_assoc_prefix(p, SPF_PASS, NULL); 00288 } 00289 else 00290 { 00291 UTIL_assoc_prefix(p, SPF_NEUTRAL, NULL); 00292 } 00293 00294 xprintf("libspf initialized succesfully. (%i bytes allocated)\n", 00295 SIZEOF(peer_info_t)); 00296 00297 return(p); 00298 } 00299 00300 00301 /* SPF_close 00302 * 00303 * Author: James Couzens <jcouzens@codeshare.ca> 00304 * 00305 * Date: 12/25/03 00306 * 00307 * Desc: 00308 * Free memory associated with passed peer_info_t structure. 00309 * p _MUST_ be nulled, checks in the various MTA code rely 00310 * on it to be NULL for various checks. 00311 * 00312 */ 00313 peer_info_t *SPF_close(peer_info_t *p) 00314 { 00315 if (p == NULL) 00316 { 00317 xepprintf("peer structure null. Aborting!\n"); 00318 00319 return(NULL); 00320 } 00321 00322 xfree(p->mta_hname); 00323 xfree(p->helo); 00324 xfree(p->from); 00325 xfree(p->current_domain); 00326 xfree(p->r_ip); 00327 xfree(p->ptr_mhost); 00328 xfree(p->cur_eaddr); 00329 xfree(p->trusted); 00330 xfree(p->guess); 00331 xfree(p->explain); 00332 xfree(p->r_vhname); 00333 00334 xfree(p); 00335 p = NULL; 00336 00337 return(p); 00338 } 00339 00340 00341 /* SPF_policy_main 00342 * 00343 * Author: James Couzens <jcouzens@codeshare.ca> 00344 * 00345 * Date: 12/10/03 00346 * Date: 01/24/04 by James <jcouzens@codeshare.ca> 00347 * Date: 05/02/04 by Teddy <teddy@teddy.ch> 00348 * 00349 * Desc: 00350 * This calls only the recursive version SPF_policy_main. 00351 * In earlier versions, SPF_policy_main was not able to handle CNAME 00352 * 00353 */ 00354 /* 00355 SPF_RESULT SPF_policy_main(peer_info_t *p) 00356 { 00357 return(SPF_policy_main(p, 0)); 00358 } 00359 */ 00360 00361 00362 /* SPF_policy_main 00363 * 00364 * Author: James Couzens <jcouzens@codeshare.ca> 00365 * 00366 * Date: 09/08/04 by James <jcouzens@codeshare.ca> 00367 * 00368 * Desc: 00369 * This is the primary SPF function from which effectively all underlying 00370 * operation of the library is manipulated. Passed a 'peer_info_t' structure when 00371 * properly populated DNS queries of T_TXT and T_CNAME are made to obtain and 00372 * recusrively follow SPF records through DNS up to a limit hard set through 00373 * the macro 'SPF_MAX_RECURSE'. 00374 * 00375 * As per the RFC 'include' and 'redirect' are cached until all other mechanisms 00376 * and modifiers of a particular SPF query are exhausted before being attempted 00377 * as they are the most expensive SPF operations. 00378 * 00379 * Returns 'SPF_RESULT' which describes the overall outcome of however many 00380 * actual parses may have been involved. Based on this outcome the calling 00381 * function is expected to further call SPF_build_header and SPF_get_explain, 00382 * and finally SPF_close to finalize their SPF query. 00383 * 00384 * SPF return types are often represented by single characters however not all 00385 * SPF_RESULT return types may be references this way. The characters are 00386 * as follows: 00387 * 00388 * '+' - SPF_PASS (pass) 00389 * '-' - SPF_HARD_FAIL (fail) 00390 * '~' - SPF_SOFT_FAIL (softfail) 00391 * '?' - SPF_NEUTRAL (neutral) 00392 * 00393 * These chars and default error messages related to them are all managed 00394 * through the structure spf_result_t so if you wish to modify them 00395 * you will need to seek out the various macro's that are referenced 00396 * through this structure (which is declared within the SPF_init function). 00397 * 00398 */ 00399 SPF_RESULT SPF_policy_main(peer_info_t *p) 00400 { 00401 SPF_RESULT res; /* result of an SPF parse */ 00402 00403 char *tmp = NULL; /* temporary pointer */ 00404 char *cname_buf = NULL; /* tmp cname placeholder */ 00405 char *include_buf = NULL; /* tmp include placeholder */ 00406 char *redirect_buf = NULL; /* tmp redirect placeholder */ 00407 00408 00409 if (p == NULL) 00410 { 00411 xepprintf("Unable to continue with a NULL peer_info_t structure!\n"); 00412 00413 return(SPF_UNKNOWN); 00414 } 00415 00416 if (p->spf_rlevel >= SPF_MAX_RECURSE) 00417 { 00418 xvprintf("recursion level exceeded (%u) levels; Aborting.\n", 00419 p->spf_rlevel); 00420 00421 return(SPF_UNKNOWN); 00422 } 00423 00424 xvprintf("[%i] current domain: (%s)\n", p->spf_rlevel, p->current_domain); 00425 xvprintf("[%i] include buffer: (%s)\n", p->spf_rlevel, p->include_buf); 00426 xvprintf("[%i] redirect buffer: (%s)\n", p->spf_rlevel, p->redirect_buf); 00427 00428 /* increment recursion count */ 00429 p->spf_rlevel++; 00430 00431 /* 00432 * ensure that our buffer placeholders are clean within our 00433 * peer_info_t structure otherwise you will get exceptionally 00434 * negative and unpredictable results from the rest of this 00435 * function! 00436 */ 00437 _SPF_clear_holdbufs(p); 00438 00439 /* fire off a peer_info_t structure for an SPF policy parse */ 00440 res = SPF_policy_main_rec(p); 00441 00442 xvprintf("result of SPF parse is %i\n", res); 00443 00444 /* store these here for now, we'll take them back in a moment */ 00445 cname_buf = p->cname_buf; 00446 include_buf = p->include_buf; 00447 redirect_buf = p->redirect_buf; 00448 00449 /* play it safe */ 00450 p->cname_buf = NULL; 00451 p->include_buf = NULL; 00452 p->redirect_buf = NULL; 00453 00454 if (res != SPF_PASS) 00455 { 00456 /* store this here for now, we'll take it back in a moment */ 00457 tmp = p->current_domain; 00458 00459 xvprintf("tmp is holding the current domain: (%s)\n", tmp); 00460 00461 /* 00462 * First the CNAME is looked at, and separate from the 'include' 00463 * and 'redirect' buffer placeholders because its not possible 00464 * to have either 'include' or 'redirect' if you have a CNAME! 00465 */ 00466 if (cname_buf != NULL) 00467 { 00468 p->current_domain = cname_buf; 00469 00470 xvprintf("Current domain: (%s); CNAME Buffer: (%s)\n", 00471 p->current_domain, cname_buf); 00472 00473 res = SPF_policy_main(p); 00474 } 00475 else 00476 { 00477 /* 00478 * In the event that during our original parse we happend 00479 * upon either the 'include' or 'redirect' markers, we would 00480 * have saved their contents in the placeholder buffer within 00481 * the peer_info_t structure until after the entire other 00482 * items within the SPF language have been exhausted. Once 00483 * this has taken place we perform a brand new parse with the 00484 * contents of either of the two buffers shouldl they have 00485 * any data. 00486 */ 00487 if (include_buf != NULL) 00488 { 00489 p->current_domain = include_buf; 00490 00491 xvprintf("current domain is now (%s) from INCLUDE\n", 00492 p->current_domain); 00493 00494 res = SPF_policy_main(p); 00495 00496 xvprintf("result of SPF parse was %i\n", res); 00497 00498 /* 00499 * this is quick hack and should go somewhere else but I 00500 * don't have time to do that right now. If an include 00501 * results in SPF_NONE then the RFC states we must return 00502 * 'unknown' 00503 */ 00504 if (res == SPF_NONE) 00505 { 00506 xpprintf("INCLUDE resulted in SPF_NONE, returning UNKNOWN\n"); 00507 res = SPF_UNKNOWN; 00508 UTIL_assoc_prefix(p, SPF_UNKNOWN, NULL); 00509 } 00510 } 00511 00512 /* 00513 * The 'include_buf' induced SPF parse may have resulted in 00514 * an SPF_PASS result. In the event that it didn't we then 00515 * examine the 'redirect_buf' and send it off if necessary 00516 * trying again with a brand new SPF query (effectively). 00517 */ 00518 if ((res != SPF_PASS) && (redirect_buf != NULL)) 00519 { 00520 xvprintf("found a redirect buffer with valid data (%s)\n", 00521 redirect_buf); 00522 00523 p->current_domain = redirect_buf; 00524 00525 xvprintf("current domain is now: (%s) from REDIRECT\n", 00526 p->current_domain); 00527 00528 res = SPF_policy_main(p); 00529 00530 xvprintf("result of SPF parse was %i\n", res); 00531 } 00532 } 00533 00534 /* reset our original domain */ 00535 p->current_domain = tmp; 00536 00537 xvprintf("current domain is now: (%s); received back from tmp\n", 00538 p->current_domain); 00539 } 00540 00541 /* place the old values back into our peer_info_t structure */ 00542 p->cname_buf = cname_buf; 00543 p->include_buf = include_buf; 00544 p->redirect_buf = redirect_buf; 00545 00546 _SPF_clear_holdbufs(p); 00547 00548 xvprintf("leaving function; returning with value: (%i)\n", res); 00549 00550 return(res); 00551 } 00552 00553 00554 00555 /* SPF_policy_main 00556 * 00557 * Author: James Couzens <jcouzens@codeshare.ca> 00558 * 00559 * Date: 12/10/03 00560 * Date: 01/24/04 by James <jcouzens@codeshare.ca> 00561 * Date: 05/02/04 by Teddy <teddy@teddy.ch> 00562 * 00563 * Desc: 00564 * Fetches the SERVER_POLICY records from the FQDN's NS server 00565 * and returns an SPF_RESULT enumeration indicating the level of 00566 * compliance with an SPF policy (match, fail, error etc.. see spf.h) 00567 * Recursion inserted by Teddy 00568 * 00569 */ 00570 SPF_RESULT SPF_policy_main_rec(peer_info_t *p) 00571 { 00572 SPF_RESULT res = SPF_NEUTRAL; /* SPF parse result */ 00573 char *rr_data = NULL; /* record */ 00574 00575 00576 if (p == NULL) 00577 { 00578 xepprintf("Error peer structure is NULL. Aborting\n"); 00579 00580 UTIL_assoc_prefix(p, SPF_ERROR, NULL); 00581 00582 #ifdef _SPF_LOGFILE_STATS 00583 UTIL_log_result(p); 00584 #endif /* _SPF_LOGFILE_STATS */ 00585 00586 00587 xepprintf("leaving function; returning SPF_ERROR\n"); 00588 00589 return(SPF_ERROR); 00590 } 00591 else if (p->addr.s_addr <= 0) 00592 { 00593 xepprintf("Error no idea who the remote peer is. Abort!\n"); 00594 00595 UTIL_assoc_prefix(p, SPF_ERROR, NULL); 00596 00597 #ifdef _SPF_LOGFILE_STATS 00598 UTIL_log_result(p); 00599 #endif /* _SPF_LOGFILE_STATS */ 00600 00601 xepprintf("leaving function; returning SPF_ERROR\n"); 00602 00603 return(SPF_ERROR); 00604 } 00605 00606 /* 00607 * As is stated in SPF_init, localhost is always exempt from SPF 00608 * checks. A check for this was done in SPF_init and to keep 00609 * actions being brought about by the proper functions p->RES 00610 * would have been intentionally set to SPF_PASS and we check 00611 * for that here. 00612 */ 00613 if (p->RES == SPF_PASS) 00614 { 00615 #ifdef _SPF_LOGFILE_STATS 00616 UTIL_log_result(p); 00617 #endif /* _SPF_LOGFILE_STATS */ 00618 00619 xpprintf("localhost exempt from SPF checks; returning SPF_PASS\n"); 00620 00621 return(SPF_PASS); 00622 } 00623 00624 if ((rr_data = DNS_query(p, p->current_domain, T_TXT, NULL)) != NULL) 00625 { 00626 xprintf("DNS_query returned with answer: (%s)\n", rr_data); 00627 00628 SPF_parse_policy(p, rr_data); 00629 00630 xfree(rr_data); 00631 } 00632 else if ((rr_data = DNS_query(p, p->current_domain, T_CNAME, NULL)) != NULL) 00633 { 00634 xvprintf("domain (%s) is CNAME of (%s). Restarting SPF_policy_main.", 00635 p->current_domain, rr_data); 00636 00637 /* store the result of the CNAME lookup in the CNAME buf */ 00638 p->cname_buf = rr_data; 00639 } /* if rr_data */ 00640 00641 /* 00642 * If either of the three placeholder buffers have anything in 00643 * them check the peer_info_t structure for an SPF parse result 00644 * and return it. 00645 */ 00646 if (p->cname_buf || p->include_buf || p->redirect_buf) 00647 { 00648 if (p->RES != SPF_PASS) 00649 { 00650 xprintf("leaving function; returning (%i)\n", p->RES); 00651 00652 return(p->RES); 00653 } 00654 } 00655 00656 /* 00657 * this is intentionally outside of the above statements so it can be 00658 * called if the user has decided to implement best guess but has 00659 * disabled trusted forwarder. 00660 */ 00661 if ((p->RES != SPF_PASS) && (p->use_trust == SPF_TRUE)) 00662 { 00663 xvprintf("Error obtaining record, trying Trusted Fwdr (%s)\n", 00664 p->trusted); 00665 00666 res = SPF_parse_policy(p, p->trusted); 00667 00668 xvprintf("SPF_parse_policy returned %i\n", res); 00669 } 00670 00671 if ((p->RES != SPF_PASS) && (p->include_buf || p->redirect_buf)) 00672 { 00673 return(p->RES); 00674 } 00675 00676 if ((p->RES != SPF_PASS) && (p->use_guess == SPF_TRUE)) 00677 { 00678 xvprintf("Attempting a best guess effort as a last resort " 00679 "using: (%s)\n", p->guess); 00680 00681 res = SPF_parse_policy(p, p->guess); 00682 00683 xvprintf("SPF_parse_policy returned %i\n", res); 00684 } 00685 00686 if ((p->RES != SPF_PASS) && (p->include_buf || p->redirect_buf)) 00687 { 00688 return(p->RES); 00689 } 00690 00691 xprintf("Return policy %i on mech: (%s) with outcome: (%s)\n", 00692 p->RES, p->last_m, p->rs); 00693 00694 /* 00695 * If logging is enabled this will write to a statistics log 00696 * at (by default) /var/log/spflog.txt where you can over time 00697 * get a good idea of how SPF performs on your network. 00698 * 00699 */ 00700 #ifdef _SPF_LOGFILE_STATS 00701 UTIL_log_result(p); 00702 #endif /* _SPF_LOGFILE_STATS */ 00703 00704 return(p->RES); 00705 } 00706 00707 00708 /* SPF_parse_policy 00709 * 00710 * Author: James Couzens <jcouzens@codeshare.ca> 00711 * 00712 * Date: 12/19/03 00713 * 00714 * Desc: 00715 * Parses the passed 'policy' and interprets the policies 00716 * contained within it to decide if based on various comparisons against 00717 * r_ip (remote peer's ipaddress) whether or not it meets the SPF 00718 * policy. 00719 * 00720 */ 00721 SPF_BOOL SPF_parse_policy(peer_info_t *p, const char *policy) 00722 { 00723 SPF_MECHANISM POLICY_TYPE; /* SPF policy mechanism type */ 00724 00725 SPF_RESULT PREFIX_TYPE; /* SPF mechanism prefix type */ 00726 00727 SPF_BOOL POLICY_MATCH; /* T / F on a policy match */ 00728 SPF_BOOL POLICY_ERROR; /* T / F for errors to return early */ 00729 00730 int16_t pos; /* position in the policy */ 00731 int16_t s_pos; /* position in a policy token */ 00732 00733 size_t p_len; /* length of passed policy string */ 00734 00735 char *copy; /* storage container for passed policy */ 00736 char *cp; /* working pointer to passed policy */ 00737 00738 char *token; /* token pointer to piece of policy */ 00739 char *token_ptr; /* working pointer for token */ 00740 00741 char *tmp_ptr; /* temporary utility pointer */ 00742 char *tmp_ptr2; /* 2nd temporary utility pointer */ 00743 char *macro; /* expanded macro storage container */ 00744 00745 /* include */ 00746 peer_info_t *i_p; /* copy of p with replaced domain */ 00747 00748 policy_addr_t *policy_ip; /* ip address of a policy record */ 00749 00750 00751 POLICY_MATCH = SPF_FALSE; 00752 POLICY_ERROR = SPF_FALSE; 00753 PREFIX_TYPE = SPF_NEUTRAL; 00754 00755 policy_ip = NULL; 00756 i_p = NULL; 00757 pos = 0; 00758 token = NULL; 00759 token_ptr = NULL; 00760 macro = NULL; 00761 00762 if ((policy == NULL) || (p == NULL)) 00763 { 00764 xepprintf("Unable to continue called with NULL structure\n"); 00765 00766 return(SPF_FALSE); 00767 } 00768 00769 if (p->spf_rlevel >= SPF_MAX_RECURSE) 00770 { 00771 xvprintf("recursion breach (%i levels); Terminated.\n", 00772 p->spf_rlevel); 00773 00774 snprintf(p->error, SPF_MAX_ERROR, "Recursion loop, terminated."); 00775 UTIL_assoc_prefix(p, SPF_UNKNOWN, NULL); 00776 00777 return(SPF_FALSE); 00778 } 00779 00780 p_len = strlen(policy); 00781 00782 xprintf("about to parse (%s) of len: %i (%s)\n", 00783 policy, p_len, p->spf_result[p->RES].s); 00784 00785 /* allocate memory and assign working pointer for policy */ 00786 cp = copy = xstrndup(policy, (p_len + 1)); 00787 00788 /* loops through the entire policy string until its exhausted */ 00789 while (*cp) 00790 { 00791 /* skip white space */ 00792 while ((*cp == ' ') && (*(cp + 1) == ' ')) 00793 { 00794 cp++; 00795 } 00796 pos = UTIL_index(cp, ' '); 00797 00798 if (!*(cp + 1)) 00799 { 00800 xfree(copy); 00801 xpprintf("leaving function; nothing more to parse, returning SPF_TRUE\n"); 00802 00803 return(SPF_TRUE); 00804 } 00805 00806 token = xstrndup(cp, (pos + 1)); 00807 token_ptr = token; 00808 s_pos = 0; 00809 00810 /* look for a mechanism modifier prefix */ 00811 if (UTIL_is_spf_result(*token_ptr) || 00812 (strncmp(token_ptr, "default", 7) == 0)) 00813 { 00814 PREFIX_TYPE = UTIL_get_mech_prefix(p, token_ptr); 00815 snprintf(p->last_m, SPF_MAX_MECHANISM, "%s", token_ptr); 00816 00817 if (*token_ptr != 'd') 00818 { 00819 /* move ahead one because a prefix was specified */ 00820 token_ptr++; 00821 } 00822 } 00823 else 00824 { 00825 snprintf(p->last_m, SPF_MAX_MECHANISM, "%s", token_ptr); 00826 PREFIX_TYPE = SPF_PASS; 00827 } 00828 00829 /* figure out what sort of mechanism we're working with */ 00830 POLICY_TYPE = UTIL_get_policy_mech(token_ptr); 00831 00832 xprintf("SPF Policy Mechanism: %i (token: %s) (pos: %i)\n", 00833 POLICY_TYPE, token_ptr, pos); 00834 00835 switch (POLICY_TYPE) 00836 { 00837 00838 /* 00839 * no policy set 00840 */ 00841 case NO_POLICY: 00842 break; 00843 00844 /* 00845 * unrecognized mechanism 00846 */ 00847 case UNMECH: 00848 UTIL_assoc_prefix(p, SPF_UNMECH, NULL); 00849 00850 xvprintf("Unrecognized mechanism (%s); returning SPF_FALSE\n", token); 00851 00852 xfree(token); 00853 xfree(copy); 00854 00855 return(SPF_FALSE); 00856 00857 /* 00858 * 'version' mechanism 00859 */ 00860 case VERSION: 00861 xprintf("policy mechanism is version (%s)\n", token_ptr); 00862 00863 if ((s_pos = UTIL_index(token_ptr, '=')) > 0) 00864 { 00865 token_ptr += s_pos + 4; /* skip =spf */ 00866 00867 if (atoi(token_ptr) > SPF_VERSION) 00868 { 00869 UTIL_assoc_prefix(p, SPF_NONE, NULL); 00870 00871 xfree(token); 00872 xfree(copy); 00873 00874 return(SPF_FALSE); 00875 } 00876 00877 p->spf_ver = atoi(token_ptr); 00878 00879 xvprintf("SPF Version defined as: %u\n", p->spf_ver); 00880 } 00881 else 00882 { 00883 xvprintf("SPF version redefined! (%u)\n", p->spf_ver); 00884 UTIL_assoc_prefix(p, SPF_S_FAIL, NULL); 00885 00886 xfree(token); 00887 xfree(copy); 00888 00889 return(SPF_FALSE); 00890 } 00891 00892 break; 00893 00894 /* 00895 * 'all' mechanism 00896 */ 00897 case ALL: 00898 xprintf("policy mechanism is all (%s) policy: (%i)\n", 00899 token_ptr, POLICY_TYPE); 00900 00901 UTIL_assoc_prefix(p, PREFIX_TYPE, NULL); 00902 POLICY_MATCH = SPF_TRUE; 00903 p->ALL = SPF_TRUE; 00904 00905 break; 00906 00907 /* 00908 * 'default' policy 00909 */ 00910 case DEFAULT: 00911 xprintf("policy mechanism is default (%s) policy: (%i)\n", 00912 token_ptr, POLICY_TYPE); 00913 00914 POLICY_MATCH = SPF_TRUE; 00915 p->ALL = SPF_TRUE; 00916 00917 break; 00918 00919 /* 00920 * 'include' mechanism 00921 */ 00922 case INCLUDE: 00923 xprintf("policy mechanism is include (%s)\n", token_ptr); 00924 00925 if ((tmp_ptr = strstr(token_ptr, ":")) != NULL) 00926 { 00927 tmp_ptr++; 00928 00929 if (UTIL_is_macro(tmp_ptr) == SPF_TRUE) 00930 { 00931 xvprintf("this INCLUDE mechanism contained macros (%s)\n", tmp_ptr); 00932 00933 if (p->include_buf == NULL) 00934 { 00935 if ((macro = MACRO_expand(p, tmp_ptr)) != NULL) 00936 { 00937 p->include_buf = xstrndup(macro, SPF_MAX_STR); 00938 00939 xfree(macro); 00940 } 00941 } 00942 else 00943 { 00944 xvprintf("Got include=%s, but include already set. Ignoring.\n", 00945 tmp_ptr); 00946 } 00947 } 00948 else 00949 { 00950 xvprintf("storing INCLUDE (%s) for later...\n", tmp_ptr); 00951 00952 if (p->include_buf == NULL) 00953 { 00954 p->include_buf = xstrndup(tmp_ptr, SPF_MAX_STR); 00955 } 00956 else 00957 { 00958 xvprintf("Got include=%s, but include already set. Ignoring.\n", 00959 tmp_ptr); 00960 } 00961 } 00962 } 00963 else 00964 { 00965 /* 'include' with no options returns unknown */ 00966 POLICY_MATCH = SPF_TRUE; 00967 UTIL_assoc_prefix(p, SPF_UNKNOWN, token_ptr); 00968 00969 xfree(token); 00970 xfree(copy); 00971 00972 return(SPF_TRUE); 00973 } 00974 break; 00975 00976 /* 00977 * 'a' mechanism 00978 */ 00979 case A: 00980 xprintf("policy mechanism is A (%s)\n", token_ptr); 00981 00982 if ((tmp_ptr = strstr(token_ptr, "/")) != NULL) 00983 { 00984 tmp_ptr++; 00985 POLICY_MATCH = UTIL_a_cmp(p, token_ptr, atoi(tmp_ptr)); 00986 } 00987 else 00988 { 00989 POLICY_MATCH = UTIL_a_cmp(p, token_ptr, 32); 00990 } 00991 00992 /* mechanism was supplied with a - indicating a failure on */ 00993 if ((PREFIX_TYPE == SPF_H_FAIL) && (POLICY_MATCH == SPF_TRUE)) 00994 { 00995 xpprintf("Found a match on a negative prefix. Halting parse.\n"); 00996 UTIL_assoc_prefix(p, SPF_H_FAIL, token_ptr); 00997 00998 xfree(token); 00999 xfree(copy); 01000 01001 return(SPF_FALSE); 01002 } 01003 01004 break; 01005 01006 /* 01007 * 'mx' mechanism 01008 */ 01009 case MX: 01010 xprintf("policy mechanism is mx (%s)\n", token_ptr); 01011 01012 /* we've been supplied an MX to use instead.. grab it */ 01013 /* use tmp_ptr2 so we can use tmp_ptr again */ 01014 if ((tmp_ptr = strstr(token_ptr, ":")) != NULL) 01015 { 01016 tmp_ptr++; 01017 tmp_ptr2 = tmp_ptr; 01018 } 01019 else 01020 { 01021 tmp_ptr2 = p->current_domain; 01022 } 01023 01024 if ((tmp_ptr = strstr(token_ptr, "/")) != NULL) 01025 { 01026 tmp_ptr++; 01027 POLICY_MATCH = UTIL_mx_cmp(p, tmp_ptr2, atoi(tmp_ptr)); 01028 } 01029 else 01030 { 01031 POLICY_MATCH = UTIL_mx_cmp(p, tmp_ptr2, 32); 01032 } 01033 01034 /* mechanism was supplied with a - indicating a failure on */ 01035 if ((PREFIX_TYPE == SPF_H_FAIL) && (POLICY_MATCH == SPF_TRUE)) 01036 { 01037 xpprintf("Found a match on a negative prefix. Halting parse.\n"); 01038 01039 UTIL_assoc_prefix(p, SPF_H_FAIL, token_ptr); 01040 01041 xfree(token); 01042 xfree(copy); 01043 01044 return(SPF_FALSE); 01045 } 01046 01047 break; 01048 01049 /* 01050 * 'ptr' mechanism 01051 */ 01052 case PTR: 01053 xprintf("policy mechanism is ptr (%s)\n", token_ptr); 01054 01055 POLICY_MATCH = UTIL_ptr_cmp(p, token_ptr); 01056 01057 /* mechanism was supplied with a - indicating a failure on */ 01058 if ((PREFIX_TYPE == SPF_H_FAIL) && (POLICY_MATCH == SPF_TRUE)) 01059 { 01060 xpprintf("Found a match on a negative prefix. Halting parse.\n"); 01061 01062 break; 01063 } 01064 01065 break; 01066 01067 01068 /* 01069 * 'ip4' mechanism 01070 */ 01071 case IP4: 01072 xprintf("policy mechanism is ip4 (%s)\n", token_ptr); 01073 01074 if ((policy_ip = UTIL_expand_ip(token_ptr)) == NULL) 01075 { 01076 xpprintf("ERROR: inet_pton unable to convert ip to binary\n"); 01077 01078 break; 01079 } 01080 01081 xvprintf("POL: %lu (%s) PEER: %lu (%s)\n", 01082 policy_ip->addr.s_addr, token_ptr, p->addr.s_addr, p->r_ip); 01083 01084 POLICY_MATCH = UTIL_cidr_cmp(policy_ip, &p->addr); 01085 xfree(policy_ip); 01086 01087 /* mechanism was supplied with a - indicating a failure on */ 01088 if ((PREFIX_TYPE == SPF_H_FAIL) && (POLICY_MATCH == SPF_TRUE)) 01089 { 01090 xpprintf("Found a match on a negative prefix. Halting parse.\n"); 01091 UTIL_assoc_prefix(p, SPF_H_FAIL, token_ptr); 01092 01093 xfree(token); 01094 xfree(copy); 01095 01096 return(SPF_FALSE); 01097 } 01098 01099 break; 01100 01101 /* 01102 * 'ip6' mechanism 01103 */ 01104 case IP6: 01105 xprintf("policy mechanism is ip6 (%s)\n", token_ptr); 01106 01107 break; 01108 01109 /* 01110 * 'exists' mechanism 01111 */ 01112 case EXISTS: 01113 xprintf("policy mechanism is exists (%s)\n", token_ptr); 01114 01115 if ((tmp_ptr = strstr(token_ptr, ":")) != NULL) 01116 { 01117 tmp_ptr++; 01118 01119 if ((macro = MACRO_expand(p, tmp_ptr)) == NULL) 01120 { 01121 xvprintf("Unable to expand macro (%s). Aborting.\n", tmp_ptr); 01122 01123 break; 01124 } 01125 01126 if (DNS_query(p, macro, T_A, NULL) == (char *)SPF_TRUE) 01127 { 01128 POLICY_MATCH = SPF_TRUE; 01129 } 01130 01131 xfree(macro); 01132 } 01133 break; 01134 01135 /* 01136 * 'redirect' modifier 01137 */ 01138 case REDIRECT: 01139 xprintf("modifier is redirect (%s)\n", token_ptr); 01140 01141 if ((tmp_ptr = strstr(token_ptr, "=")) != NULL) 01142 { 01143 tmp_ptr++; 01144 01145 if (UTIL_is_macro(tmp_ptr) == SPF_TRUE) 01146 { 01147 if (p->redirect_buf == NULL) 01148 { 01149 if ((macro = MACRO_expand(p, tmp_ptr)) != NULL) 01150 { 01151 p->redirect_buf = xstrndup(macro, SPF_MAX_STR); 01152 xfree(macro); 01153 } 01154 } 01155 else 01156 { 01157 xvprintf("Got redir=%s, but redirect already set. Ignoring.\n", 01158 tmp_ptr); 01159 } 01160 } 01161 else 01162 { 01163 if (p->redirect_buf == NULL) 01164 { 01165 xvprintf("setting redirect_buf to %s\n", tmp_ptr); 01166 p->redirect_buf = xstrndup(tmp_ptr, SPF_MAX_STR); 01167 } 01168 else 01169 { 01170 xvprintf("Got redir=%s, but redirect already set. Ignoring.\n", 01171 tmp_ptr); 01172 } 01173 } 01174 } 01175 break; 01176 01177 /* 01178 * 'explain' mechanism 01179 */ 01180 case EXPLAIN: 01181 xprintf("policy mechanism is explain (%s)\n", token_ptr); 01182 01183 if ((tmp_ptr = strstr(token_ptr, "=")) != NULL) 01184 { 01185 tmp_ptr++; 01186 01187 if ((macro = MACRO_expand(p, tmp_ptr)) == NULL) 01188 { 01189 xvprintf("Unable to expand macro (%s). Aborting.\n", tmp_ptr); 01190 01191 break; 01192 } 01193 01194 p->explain = xstrndup(macro, (strlen(macro) + 1)); 01195 xfree(macro); 01196 } 01197 else 01198 { 01199 xpprintf("EXPLAIN modifier must be accompanied " \ 01200 "by arguments and I found none."); 01201 01202 /* 'include' with no options returns unknown */ 01203 POLICY_MATCH = SPF_TRUE; 01204 UTIL_assoc_prefix(p, SPF_UNKNOWN, token_ptr); 01205 01206 xfree(token); 01207 xfree(copy); 01208 01209 return(SPF_TRUE); 01210 } 01211 01212 break; 01213 } /* switch */ 01214 01215 xfree(token); 01216 cp += pos + 1; /* move over to the next mechanism */ 01217 01218 if ((POLICY_MATCH == SPF_TRUE) && (p->spf_ver > 0)) 01219 { 01220 /*p->RES = SPF_PASS;*/ 01221 UTIL_assoc_prefix(p, PREFIX_TYPE, p->last_m); 01222 p->RES_P = p->RES; 01223 01224 xpprintf("returning SPF_TRUE\n"); 01225 xfree(copy); 01226 01227 return(SPF_TRUE); 01228 } 01229 } /* while parsing policy */ 01230 01231 xfree(copy); 01232 01233 return(SPF_FALSE); 01234 } 01235 01236 01237 /* SPF_result 01238 * 01239 * Author: James Couzens <jcouzens@codeshare.ca> 01240 * 01241 * Date: 02/02/04 01242 * 01243 * Desc: 01244 * Returns a string of allocated memory detailing a response 01245 * that the MTA will return to the client based on the result 01246 * received from an SPF query. 01247 * 01248 */ 01249 char *SPF_result(peer_info_t *p) 01250 { 01251 char *buf; /* return buffer */ 01252 01253 01254 buf = xmalloc(SPF_MAX_HEADER); 01255 01256 switch (p->RES) 01257 { 01258 case SPF_PASS: 01259 snprintf(buf, SPF_MAX_SMTP_RES, RES_PASS, p->from, p->r_ip); 01260 break; 01261 01262 case SPF_NONE: 01263 snprintf(buf, SPF_MAX_SMTP_RES, RES_NONE, p->from); 01264 break; 01265 01266 case SPF_S_FAIL: 01267 snprintf(buf, SPF_MAX_SMTP_RES, RES_S_FAIL, p->from, p->r_ip); 01268 break; 01269 01270 case SPF_H_FAIL: 01271 snprintf(buf, SPF_MAX_SMTP_RES, RES_H_FAIL, p->from, p->r_ip); 01272 break; 01273 01274 case SPF_ERROR: 01275 snprintf(buf, SPF_MAX_SMTP_RES, RES_ERROR, p->from); 01276 break; 01277 01278 case SPF_NEUTRAL: 01279 snprintf(buf, SPF_MAX_SMTP_RES, RES_NEUTRAL, p->r_ip, p->from); 01280 break; 01281 01282 case SPF_UNKNOWN: 01283 snprintf(buf, SPF_MAX_SMTP_RES, RES_UNKNOWN, p->from); 01284 break; 01285 01286 case SPF_UNMECH: 01287 snprintf(buf, SPF_MAX_SMTP_RES, RES_UNMECH, p->from); 01288 break; 01289 } 01290 01291 xprintf("response: (%s)\n", buf); 01292 01293 return(buf); 01294 } 01295 01296 01297 /* SPF_get_explain 01298 * 01299 * Author: James Couzens <jcouzens@codeshare.ca> 01300 * 01301 * Date: 01/25/04 01302 * 01303 * Desc: 01304 * Takes the contents of a returned explain parse (if any), 01305 * allocates memory for it and returns that memory with the contents of 01306 * that string. Calling function is required to free this memory. 01307 * 01308 * 01309 */ 01310 char *SPF_get_explain(peer_info_t *p) 01311 { 01312 char *buf = NULL; /* return buffer */ 01313 01314 01315 if (p->explain != NULL) 01316 { 01317 if ((buf = MACRO_expand(p, SPF_EXPLAIN)) != NULL) 01318 { 01319 xprintf("Prepending explain: (%s)\n", buf); 01320 01321 return(buf); 01322 } 01323 } 01324 01325 return(NULL); 01326 } 01327 01328 01329 /* SPF_build_header 01330 * 01331 * Author: James Couzens <jcouzens@codeshare.ca> 01332 * 01333 * Date: 01/25/04 01334 * 01335 * Desc: 01336 * Allocates memory and builds a string which contains a 01337 * Received-SPF header. The calling function is responsible to free 01338 * this memory. 01339 * 01340 * 01341 */ 01342 char *SPF_build_header(peer_info_t *p) 01343 { 01344 char *buf; /* return buffer */ 01345 01346 buf = xmalloc(SPF_MAX_HEADER); 01347 01348 switch (p->RES) 01349 { 01350 /* */ 01351 case SPF_PASS: 01352 snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_PASS].h, 01353 p->mta_hname, p->from, p->r_ip, p->mta_hname, 01354 p->r_ip, p->from); 01355 break; 01356 01357 /* */ 01358 case SPF_NONE: 01359 snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_NONE].h, 01360 p->mta_hname, p->from); 01361 break; 01362 01363 /* */ 01364 case SPF_S_FAIL: 01365 snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_S_FAIL].h, 01366 p->mta_hname, p->from, p->r_ip, p->mta_hname, 01367 p->r_ip, p->from); 01368 break; 01369 01370 /* */ 01371 case SPF_H_FAIL: 01372 snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_H_FAIL].h, 01373 p->mta_hname, p->from, p->r_ip, p->mta_hname, 01374 p->r_ip, p->from); 01375 break; 01376 01377 /* */ 01378 case SPF_ERROR: 01379 snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_ERROR].h, 01380 p->mta_hname, p->from, p->error); 01381 break; 01382 01383 /* */ 01384 case SPF_NEUTRAL: 01385 snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_NEUTRAL].h, 01386 p->mta_hname, p->from, p->r_ip); 01387 break; 01388 01389 /* */ 01390 case SPF_UNKNOWN: 01391 snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_UNKNOWN].h, 01392 p->mta_hname, p->from, p->current_domain, p->last_m); 01393 break; 01394 01395 /* */ 01396 case SPF_UNMECH: 01397 snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_UNMECH].h, 01398 p->last_m, p->mta_hname, p->from); 01399 break; 01400 } 01401 01402 xvprintf("Prepending header string: (%s)\n", buf); 01403 01404 return(buf); 01405 } 01406 01407 01408 /* SPF_smtp_helo 01409 * 01410 * Author: Sean Comeau <scomeau@obscurity.org> 01411 * James Couzens <jcouzens@codeshare.ca> 01412 * Patrick Earl (http://patearl.net/) 01413 * 01414 * Date: 12/10/03 01415 * 12/25/03 (modified / renamed) 01416 * 02/04/04 (modified) 01417 * 01418 * Desc: 01419 * Fetches the HELO from MTA and places into the global 01420 * p structure. 01421 * 01422 */ 01423 SPF_BOOL SPF_smtp_helo(peer_info_t *p, const char *s) 01424 { 01425 01426 xprintf("called with (%s)\n", s); 01427 01428 if (p->helo) 01429 { 01430 xfree(p->helo); 01431 } 01432 01433 p->helo = xstrdup(s); 01434 p->ehlo = p->helo; 01435 01436 return (strlen(p->helo) > 0); 01437 } 01438 01439 01440 /* SPF_smtp_from 01441 * 01442 * Author: James Couzens <jcouzens@codeshare.ca> 01443 * Patrick Earl (http://patearl.net/) 01444 * 01445 * Date: 12/10/03 01446 * Date: 12/25/03 James Couzens <jcouzens@codeshare.ca> (rewrite) 01447 * Date: 02/04/04 Patrick Earl (http://patearl.net) (modified) 01448 * Date: 02/05/04 Teddy <teddy@teddy.ch> 01449 * 01450 * Desc: 01451 * Fetches the FROM from MTA and places into the global 01452 * peer_info_t structure. 01453 * 01454 * 01455 */ 01456 SPF_BOOL SPF_smtp_from(peer_info_t *p, const char *s) 01457 { 01458 int len; /* utility */ 01459 01460 char *pos; /* position in string */ 01461 01462 01463 xfree(p->from); 01464 xfree(p->current_domain); 01465 01466 if (s == NULL) 01467 { 01468 if (*(p->helo) == '\0') 01469 { 01470 p->from = xstrndup("unknown", 7); 01471 } 01472 else 01473 { 01474 p->from = xstrndup(p->helo, SPF_MAX_ENV_HELO); 01475 } 01476 01477 xvprintf("NULL or invalid MAIL FROM rcvd. Using %s from now on.\n", 01478 p->from ? p->from : p->helo); 01479 01480 return(SPF_FALSE); 01481 } 01482 01483 p->from = xstrndup(s, SPF_MAX_STR); 01484 01485 /* strips < and > from E-Mail-Address */ 01486 if (*p->from == 60) 01487 { 01488 pos = p->from; 01489 (*(p->from + strlen(p->from) - 1)) = '\0'; 01490 p->from++; 01491 p->from = xstrndup(p->from, SPF_MAX_STR); 01492 01493 xfree(pos); 01494 } 01495 01496 if (*p->from == '\0') 01497 { 01498 if (*(p->helo) == '\0') 01499 { 01500 p->from = xstrndup("unknown", 7); 01501 } 01502 else 01503 { 01504 p->from = xstrndup(p->helo, SPF_MAX_ENV_HELO); 01505 } 01506 } 01507 01508 pos = p->from; 01509 01510 xprintf("MAIL-FROM: (%s) (called with: %s)\n", p->from, s); 01511 01512 if ((pos = strstr(pos, "@")) != NULL) 01513 { 01514 len = (pos - p->from); 01515 01516 if (len >= SPF_MAX_LOCAL_PART) 01517 { 01518 xvprintf("%i is >= %i causing data overrun\n", len, SPF_MAX_LOCAL_PART); 01519 } 01520 01521 /* Copy everything up to the @ into local_part. Do not include the @. */ 01522 memcpy(p->local_part, p->from, len); 01523 p->local_part[len] = '\0'; 01524 01525 pos++; /* skip past the @ */ 01526 p->current_domain = xstrndup(pos, SPF_MAX_STR); 01527 01528 xprintf("Current domain: (%s)\n", p->current_domain); 01529 01530 } 01531 else 01532 { 01533 snprintf(p->local_part, 11, "postmaster"); 01534 p->current_domain = xstrndup(p->from, SPF_MAX_STR); 01535 } 01536 01537 xvprintf("local-part: (%s); domain: (%s); sender: (%s)\n", 01538 p->local_part, p->current_domain, p->from); 01539 01540 return(SPF_TRUE); 01541 } 01542 01543 01544 /* _SPF_clear_holdbufs 01545 * 01546 * Author: Travis Anderson <tanderson@codeshare.ca> 01547 * Author: James Couzens <jcouzens@codeshare.ca> 01548 * 01549 * Date: 09/11/04 01550 * 01551 * Desc: 01552 * There are three possible 'holding' buffers that are used 01553 * only after an original parse comes back negative (ie anything other 01554 * that SPF_PASS), and this function simply goes through and free's 01555 * any memory they might have associated and assigns them a NULL 01556 * pointer. 01557 * 01558 * This function is *PRIVATE* and you will never need to call it, 01559 * nor can it be referenced outside of this file, its purely 01560 * written for the benefit for SPF_parse_policy when dealing with 01561 * recursion for INCLUDE, REDIRECT, and CNAME. 01562 * 01563 */ 01564 static SPF_BOOL _SPF_clear_holdbufs(peer_info_t *p) 01565 { 01566 if (p == NULL) 01567 { 01568 xepprintf("peer_info_t structure was NULL! Aborting!\n"); 01569 01570 return(SPF_FALSE); 01571 } 01572 01573 if (p->cname_buf != NULL) 01574 { 01575 xfree(p->cname_buf); 01576 p->cname_buf = NULL; 01577 } 01578 01579 if (p->include_buf != NULL) 01580 { 01581 xfree(p->include_buf); 01582 p->include_buf = NULL; 01583 } 01584 01585 if (p->redirect_buf) 01586 { 01587 xfree(p->redirect_buf); 01588 p->redirect_buf = NULL; 01589 } 01590 01591 return(SPF_TRUE); 01592 } 01593 01594 /* end spf.c */

Generated on Thu Sep 16 18:10:46 2004 for libSPF v1.0 by doxygen 1.3.8