main.c

Go to the documentation of this file.
00001 /* libspf - Sender Policy Framework library 00002 * 00003 * ANSI C implementation of draft-mengwong-spf-02.9.7.txt 00004 * 00005 * Author: James Couzens <jcouzens@6o4.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 #include "main.h" 00046 #include "util.h" 00047 #include "dns.h" 00048 #include "macro.h" 00049 #include "error.h" 00050 00051 spf_config_t confg; /* global config struct */ 00052 u_int8_t spf_rlevel; /* global recursive counter */ 00053 int h_errno; /* error handling */ 00054 00055 00056 /* SPF_init 00057 * 00058 * Author: James Couzens <jcouzens@6o4.ca> 00059 * 00060 * Date: 12/25/03 00061 * 00062 * Desc: 00063 * Applies config options and returns allocated memory to a peer_info_t 00064 * structure. Malloc returns NULL on a failure, and as such this function exits 00065 * with NULL upon failure to allocate memory which there by results in a 00066 * complete failure of the entire library. 00067 * 00068 */ 00069 peer_info_t *SPF_init(const char *local, const char *rip, const char *expl, 00070 const char *tf, const char *guess, u_int32_t use_trust, u_int32_t use_guess) 00071 { 00072 time_t curtime; /* time */ 00073 char *cur_utc_time; /* time */ 00074 00075 peer_info_t *peer_info; /* peer info structure to be returned */ 00076 00077 /* spf_result struct including header texts */ 00078 static spf_result_t spf_result[] = 00079 { 00080 { sizeof(HR_PASS), HR_PASS, SPF_PASS, sizeof(HDR_PASS), HDR_PASS, '+' }, 00081 { sizeof(HR_NONE), HR_NONE, SPF_NONE, sizeof(HDR_NONE), HDR_NONE, '\0' }, 00082 { sizeof(HR_S_FAIL), HR_S_FAIL, SPF_S_FAIL, sizeof(HDR_S_FAIL), HDR_S_FAIL, '~' }, 00083 { sizeof(HR_H_FAIL), HR_H_FAIL, SPF_H_FAIL, sizeof(HDR_H_FAIL), HDR_H_FAIL, '-' }, 00084 { sizeof(HR_ERROR), HR_ERROR, SPF_ERROR, sizeof(HDR_ERROR), HDR_ERROR, '\0' }, 00085 { sizeof(HR_NEUTRAL), HR_NEUTRAL, SPF_NEUTRAL, sizeof(HDR_NEUTRAL), HDR_NEUTRAL, '?' }, 00086 { sizeof(HR_UNKNOWN), HR_UNKNOWN, SPF_UNKNOWN, sizeof(HDR_UNKNOWN), HDR_UNKNOWN, '\0' }, 00087 { sizeof(HR_UNMECH), HR_UNMECH, SPF_UNMECH, sizeof(HDR_UNMECH), HDR_UNMECH, '\0' } 00088 }; 00089 00090 xvprintf("Called with: (%s) (%s) (%s) (%s) (%s) %u:%u\n", 00091 local, rip, expl, tf, guess, use_trust, use_guess); 00092 00093 /* allocate peer_info structure */ 00094 peer_info = xmalloc(sizeof(peer_info_t)); 00095 00096 if (use_trust == TRUE) 00097 { 00098 peer_info->use_trust = TRUE; 00099 } 00100 else 00101 { 00102 peer_info->use_trust = FALSE; 00103 } 00104 00105 if (use_guess == TRUE) 00106 { 00107 peer_info->use_guess = TRUE; 00108 } 00109 else 00110 { 00111 peer_info->use_guess = FALSE; 00112 } 00113 00114 peer_info->spf_result = spf_result; 00115 00116 peer_info->ALL = FALSE; 00117 peer_info->p = NULL; 00118 00119 peer_info->spf_ver = 0; 00120 00121 peer_info->helo = NULL; 00122 peer_info->ehlo = NULL; 00123 peer_info->from = NULL; 00124 00125 if (expl != NULL && (*expl && *(expl + 1))) 00126 { 00127 peer_info->explain = xstrndup(expl, (strlen(expl) + 1)); 00128 } 00129 else 00130 { 00131 peer_info->explain = NULL; 00132 } 00133 00134 if (guess != NULL && (*guess && *(guess + 1))) 00135 { 00136 peer_info->guess = xstrndup(guess, (strlen(guess) + 1)); 00137 } 00138 else 00139 { 00140 peer_info->guess = xstrndup(SPF_GUESS, (sizeof(SPF_GUESS) + 1)); 00141 } 00142 00143 if (tf != NULL && (*tf && *(tf + 1))) 00144 { 00145 peer_info->trusted = xstrndup(tf, (strlen(tf) + 1)); 00146 } 00147 else 00148 { 00149 peer_info->trusted = xstrndup(SPF_TRUSTED, (sizeof(SPF_TRUSTED) + 1)); 00150 } 00151 00152 peer_info->ptr_mhost = NULL; 00153 peer_info->cur_dom = NULL; 00154 peer_info->cur_eaddr = NULL; 00155 peer_info->mta_hname = xstrndup(local, (strlen(local) + 1)); 00156 00157 00158 if (inet_pton(AF_INET, rip, &peer_info->addr) <= 0) 00159 { 00160 xeprintf("Unable to execute inet_print\n"); 00161 } 00162 00163 peer_info->r_ip = xstrndup(rip, (strlen(rip) + 1)); 00164 peer_info->r_vhname = xstrndup(local, (strlen(local) + 1)); 00165 00166 sprintf(peer_info->ip_ver, "in-addr"); 00167 00168 memset(peer_info->local_part, '\0', LOCAL_PART); 00169 00170 cur_utc_time = xmalloc(22); 00171 snprintf(cur_utc_time, 22, "%lu", time(&curtime)); 00172 memcpy(peer_info->utc_time, cur_utc_time, 22); 00173 xfree(cur_utc_time); 00174 00175 memset(peer_info->last_m, '\0', MAX_MECHANISM); 00176 memset(peer_info->error, '\0', MAX_ERROR); 00177 00178 /* localhost is exempt from SPF so automagical pass ;) */ 00179 if ((strcmp(rip, "127.0.0.1") == 0) || (strcmp(rip, "localhost") == 0)) 00180 { 00181 UTIL_assoc_prefix(peer_info, SPF_PASS, NULL); 00182 } 00183 else 00184 { 00185 UTIL_assoc_prefix(peer_info, SPF_NEUTRAL, NULL); 00186 } 00187 00188 xprintf("libspf initialized succesfully. (%i bytes allocated)\n", 00189 sizeof(peer_info_t)); 00190 00191 return(peer_info); 00192 } 00193 00194 00195 /* SPF_close 00196 * 00197 * Author: James Couzens <jcouzens@6o4.ca> 00198 * 00199 * Date: 12/25/03 00200 * 00201 * Desc: 00202 * Free memory associated with passed peer_info_t structure. 00203 * peer_info _MUST_ be nulled, checks in the various MTA code rely 00204 * on it to be NULL for various checks. 00205 * 00206 */ 00207 peer_info_t *SPF_close(peer_info_t *peer_info) 00208 { 00209 if (peer_info == NULL) 00210 { 00211 xeprintf("peer structure null. Aborting!\n"); 00212 return(NULL); 00213 } 00214 00215 xfree(peer_info->mta_hname); 00216 xfree(peer_info->helo); 00217 xfree(peer_info->from); 00218 xfree(peer_info->cur_dom); 00219 xfree(peer_info->r_ip); 00220 xfree(peer_info->ptr_mhost); 00221 xfree(peer_info->cur_eaddr); 00222 xfree(peer_info->trusted); 00223 xfree(peer_info->guess); 00224 xfree(peer_info->explain); 00225 xfree(peer_info->r_vhname); 00226 00227 xfree(peer_info); 00228 peer_info = NULL; 00229 00230 return(peer_info); 00231 } 00232 00233 00234 /* SPF_policy_main 00235 * 00236 * Author: James Couzens <jcouzens@6o4.ca> 00237 * 00238 * Date: 12/10/03 00239 * Date: 01/24/04 by James <jcouzens@6o4.ca> 00240 * Date: 05/02/04 by Teddy <teddy@teddy.ch> 00241 * 00242 * Desc: 00243 * This calls only the recursive version SPF_policy_main_rec. 00244 * In earlier versions, SPF_policy_main was not able to handle CNAME 00245 * 00246 */ 00247 SPF_RESULT SPF_policy_main(peer_info_t *peer_info) 00248 { 00249 return SPF_policy_main_rec(peer_info, 0); 00250 } 00251 00252 00253 /* SPF_policy_main_rec 00254 * 00255 * Author: James Couzens <jcouzens@6o4.ca> 00256 * 00257 * Date: 12/10/03 00258 * Date: 01/24/04 by James <jcouzens@6o4.ca> 00259 * Date: 05/02/04 by Teddy <teddy@teddy.ch> 00260 * 00261 * Desc: 00262 * Fetches the SERVER_POLICY records from the FQDN's NS server 00263 * and returns an SPF_RESULT enumeration indicating the level of 00264 * compliance with an SPF policy (match, fail, error etc.. see spf.h) 00265 * Recursion inserted by Teddy 00266 * 00267 */ 00268 SPF_RESULT SPF_policy_main_rec(peer_info_t *peer_info, int rec) 00269 { 00270 char *rr_data = NULL; /* record */ 00271 char *domain = NULL; /* original domain */ 00272 SPF_RESULT result; /* result for recursive */ 00273 00274 /* localhost is exempt */ 00275 if (peer_info->RES == SPF_PASS) 00276 { 00277 return(SPF_PASS); 00278 } 00279 00280 if (spf_rlevel >= SPF_RECURSE) 00281 { 00282 snprintf(peer_info->error, MAX_ERROR, "INCLUDE loop, terminated."); 00283 UTIL_assoc_prefix(peer_info, SPF_UNKNOWN, NULL); 00284 return(SPF_UNKNOWN); 00285 } 00286 00287 if (peer_info == NULL) 00288 { 00289 xeprintf("Error peer structure is NULL. Aborting\n"); 00290 } 00291 else if (peer_info->addr.s_addr <= 0) 00292 { 00293 xvprintf("(QID: %u) :: Error no idea who the remote peer is. Abort!\n", 00294 spf_rlevel); 00295 UTIL_assoc_prefix(peer_info, SPF_ERROR, NULL); 00296 return(SPF_ERROR); 00297 } 00298 00299 /* We check if domain name is CNAME */ 00300 if ((rr_data = DNS_query(peer_info, peer_info->cur_dom, T_CNAME, NULL)) != NULL) 00301 { 00302 if (MAX_CNAME > rec) 00303 { 00304 xvprintf("Domain (%s) is CNAME of (%s). Restarting SPF_policy_main.", 00305 peer_info->cur_dom, rr_data); 00306 domain = peer_info->cur_dom; 00307 peer_info->cur_dom = rr_data; 00308 result = SPF_policy_main_rec(peer_info, rec + 1); 00309 xfree(peer_info->cur_dom); 00310 peer_info->cur_dom = domain; 00311 return result; 00312 } 00313 else 00314 { 00315 xvprintf("(QID: %u) :: Error: Maximum of recursion reached. Abort!\n", spf_rlevel); 00316 UTIL_assoc_prefix(peer_info, SPF_ERROR, NULL); 00317 xfree(rr_data); 00318 return(SPF_ERROR); 00319 } 00320 } 00321 if ((rr_data = DNS_query(peer_info, peer_info->cur_dom, T_TXT, NULL)) != NULL) 00322 { 00323 xprintf("(QID: %u) :: Error parsing ANSWER(s)\n", spf_rlevel); 00324 00325 UTIL_assoc_prefix(peer_info, SPF_NONE, NULL); 00326 SPF_parse_policy(peer_info, rr_data); 00327 xfree(rr_data); 00328 } 00329 else if (peer_info->RES == SPF_NONE && peer_info->use_trust == TRUE) 00330 { 00331 xvprintf("(QID: %u) :: Error obtaining record, trying trusted forwarder (%s)\n", 00332 spf_rlevel, peer_info->trusted); 00333 00334 SPF_parse_policy(peer_info, peer_info->trusted); 00335 } 00336 00337 if (peer_info->RES != SPF_PASS && peer_info->use_guess == TRUE) 00338 { 00339 xvprintf("(QID: %u) :: Attempting a best guess effort as a last resort " 00340 "using: (%s)\n", spf_rlevel, peer_info->guess); 00341 00342 SPF_parse_policy(peer_info, peer_info->guess); 00343 } 00344 00345 if (peer_info->RES != SPF_PASS && peer_info->explain == NULL) 00346 { 00347 if ((peer_info->explain = MACRO_expand(peer_info, SPF_EXPLAIN)) == NULL) 00348 { 00349 xeprintf("Unable to obtain explanation\n"); 00350 } 00351 } 00352 00353 xprintf("(QID: %u) :: Return policy %i on mech: (%s) with outcome: (%s)\n", 00354 spf_rlevel, peer_info->RES, peer_info->last_m, peer_info->rs); 00355 00356 UTIL_log_result(peer_info); 00357 00358 return(peer_info->RES); 00359 } 00360 00361 00362 /* SPF_parse_policy 00363 * 00364 * Author: James Couzens <jcouzens@6o4.ca> 00365 * 00366 * Date: 12/19/03 00367 * 00368 * Desc: 00369 * Parses the passed 'policy' and interprets the policies 00370 * contained within it to decide if based on various comparisons against 00371 * r_ip (remote peer's ipaddress) whether or not it meets the SPF 00372 * policy. 00373 * 00374 */ 00375 SPF_BOOL SPF_parse_policy(peer_info_t *peer_info, const char *policy) 00376 { 00377 SPF_MECHANISM POLICY_TYPE; /* SPF policy mechanism type */ 00378 SPF_RESULT PREFIX_TYPE; /* SPF mechanism prefix type */ 00379 SPF_BOOL POLICY_MATCH; /* T / F on a policy match */ 00380 SPF_BOOL POLICY_ERROR; /* T /F for errors to return early */ 00381 00382 /* general use */ 00383 policy_addr_t *policy_ip; /* ip address of a policy record */ 00384 00385 /* include */ 00386 peer_info_t *i_peer_info; /* copy of peer_info with replaced domain */ 00387 00388 size_t p_len; /* length of passed policy string */ 00389 int16_t pos; /* position in the policy */ 00390 int16_t s_pos; /* position in a policy token */ 00391 int16_t i; 00392 00393 char *copy; /* storage container for passed policy */ 00394 char *cp; /* working pointer to passed policy */ 00395 00396 char *token; /* token pointer to piece of policy */ 00397 char *token_ptr; /* working pointer for token */ 00398 00399 char *tmp_ptr; /* temporary utility pointer */ 00400 char *tmp_ptr2; /* 2nd temporary utility pointer */ 00401 char *macro; /* expanded macro storage container */ 00402 00403 u_int8_t ver_c; /* copy of spf version */ 00404 00405 POLICY_MATCH = FALSE; 00406 POLICY_ERROR = FALSE; 00407 PREFIX_TYPE = SPF_NEUTRAL; 00408 00409 policy_ip = NULL; 00410 i_peer_info = NULL; 00411 macro = NULL; 00412 00413 if ((policy == NULL) || (peer_info == NULL)) 00414 { 00415 xeprintf("Unable to continue called with NULL structure\n"); 00416 return(SPF_ERROR); 00417 } 00418 00419 p_len = strlen(policy); 00420 00421 xprintf("(QID: %u) :: about to parse (%s) of len: %i (%s)\n", 00422 spf_rlevel, policy, p_len, peer_info->spf_result[peer_info->RES].s); 00423 00424 /* allocate memory and assign working pointer for policy */ 00425 cp = copy = xstrndup(policy, (p_len + 1)); 00426 00427 i = strlen(cp); 00428 00429 /* loops through the entire policy string until its exhausted */ 00430 while (*cp) 00431 { 00432 while (*(cp + 1) && (pos = UTIL_index(cp, ' ')) == 0) 00433 { 00434 cp++; 00435 } 00436 00437 if (!*(cp + 1)) 00438 { 00439 xfree(copy); 00440 00441 /* implicit return is neutral */ 00442 if (peer_info->spf_ver == SPF_VERSION) 00443 { 00444 UTIL_assoc_prefix(peer_info, SPF_NEUTRAL, NULL); 00445 return(TRUE); 00446 } 00447 00448 return(FALSE); 00449 } 00450 00451 token = xstrndup(cp, (pos + 1)); 00452 token_ptr = token; 00453 s_pos = 0; 00454 00455 /* look for a mechanism modifier prefix */ 00456 if (UTIL_is_spf_result(*token_ptr) || 00457 (strncmp(token_ptr, "default", 7) == 0)) 00458 { 00459 PREFIX_TYPE = UTIL_get_mech_prefix(peer_info, token_ptr); 00460 snprintf(peer_info->last_m, MAX_MECHANISM, "%s", token_ptr); 00461 00462 if (*token_ptr != 'd') 00463 { 00464 /* move ahead one because a prefix was specified */ 00465 token_ptr++; 00466 } 00467 } 00468 else 00469 { 00470 snprintf(peer_info->last_m, MAX_MECHANISM, "%s", token_ptr); 00471 PREFIX_TYPE = SPF_PASS; 00472 } 00473 00474 /* figure out what sort of mechanism we're working with */ 00475 POLICY_TYPE = UTIL_get_policy_mech(token_ptr); 00476 00477 xprintf("(QID: %u) :: SPF Policy Mechanism: %i (token: %s) (pos: %i)\n", 00478 spf_rlevel, POLICY_TYPE, token_ptr, pos); 00479 00480 switch (POLICY_TYPE) 00481 { 00482 case NO_POLICY: 00483 break; 00484 case UNMECH: 00485 UTIL_assoc_prefix(peer_info, SPF_UNMECH, NULL); 00486 xfree(token); 00487 xfree(copy); 00488 return(FALSE); 00489 case VERSION: 00490 xprintf("(QID: %u) :: policy mechanism is version (%s)\n", 00491 spf_rlevel, token_ptr); 00492 00493 if ((s_pos = UTIL_index(token_ptr, '=')) > 0) 00494 { 00495 token_ptr += s_pos + 4; /* skip =spf */ 00496 00497 if (atoi(token_ptr) > SPF_VERSION) 00498 { 00499 UTIL_assoc_prefix(peer_info, SPF_NONE, NULL); 00500 xfree(token); 00501 xfree(copy); 00502 return(FALSE); 00503 } 00504 00505 peer_info->spf_ver = atoi(token_ptr); 00506 00507 xvprintf("(QID: %u) :: SPF Version defined as: %u\n", 00508 spf_rlevel, peer_info->spf_ver); 00509 00510 } 00511 else 00512 { 00513 xvprintf("(QID: %u) :: SPF version redefined! (%u)\n", 00514 spf_rlevel, peer_info->spf_ver); 00515 00516 UTIL_assoc_prefix(peer_info, SPF_S_FAIL, NULL); 00517 xfree(token); 00518 xfree(copy); 00519 return(FALSE); 00520 } 00521 00522 break; 00523 case ALL: 00524 xprintf("(QID: %u) :: policy mechanism is all (%s) policy: (%i)\n", 00525 spf_rlevel, token_ptr, POLICY_TYPE); 00526 00527 UTIL_assoc_prefix(peer_info, PREFIX_TYPE, NULL); 00528 POLICY_MATCH = TRUE; 00529 peer_info->ALL = TRUE; 00530 00531 00532 break; 00533 case DEFAULT: 00534 xprintf("(QID: %u) :: policy mechanism is default (%s) policy: (%i)\n", 00535 spf_rlevel, token_ptr, POLICY_TYPE); 00536 00537 POLICY_MATCH = TRUE; 00538 peer_info->ALL = TRUE; 00539 break; 00540 case INCLUDE: 00541 xprintf("(QID: %u) :: policy mechanism is include (%s)\n", 00542 spf_rlevel, token_ptr); 00543 00544 if ((tmp_ptr = strstr(token_ptr, ":")) != NULL) 00545 { 00546 tmp_ptr++; 00547 00548 ver_c = peer_info->spf_ver; 00549 if (UTIL_is_macro(tmp_ptr) == TRUE) 00550 { 00551 if ((macro = MACRO_expand(peer_info, tmp_ptr)) != NULL) 00552 { 00553 peer_info->cur_dom = xstrndup(macro, (strlen(macro) + 1)); 00554 xfree(macro); 00555 } 00556 } 00557 else 00558 { 00559 peer_info->cur_dom = xstrndup(tmp_ptr, (strlen(tmp_ptr) + 1)); 00560 } 00561 00562 spf_rlevel++; 00563 peer_info->RES = SPF_NEUTRAL; 00564 PREFIX_TYPE = SPF_policy_main(peer_info); 00565 peer_info->spf_ver = ver_c; /* re-assign original proto ver */ 00566 xvprintf("(QID: %u) :: include returned result %i (%s)\n", 00567 spf_rlevel, PREFIX_TYPE, peer_info->last_m); 00568 00569 switch (PREFIX_TYPE) 00570 { 00571 case SPF_PASS: 00572 POLICY_MATCH = TRUE; 00573 break; 00574 case SPF_NONE: 00575 /* 'none' coming back from an include is a permanent error, and 00576 * thus we return unknown 00577 */ 00578 POLICY_ERROR = TRUE; 00579 PREFIX_TYPE = SPF_UNKNOWN; 00580 break; 00581 case SPF_S_FAIL: 00582 break; 00583 case SPF_H_FAIL: 00584 break; 00585 case SPF_ERROR: 00586 POLICY_ERROR = TRUE; 00587 break; 00588 case SPF_NEUTRAL: 00589 break; 00590 case SPF_UNKNOWN: 00591 POLICY_ERROR = TRUE; 00592 break; 00593 case SPF_UNMECH: 00594 break; 00595 } /* switch */ 00596 00597 if (POLICY_ERROR == TRUE) 00598 { 00599 UTIL_assoc_prefix(peer_info, PREFIX_TYPE, token_ptr); 00600 xfree(token); 00601 xfree(copy); 00602 return(FALSE); 00603 } 00604 } 00605 else 00606 { 00607 /* 'include' with no options returns unknown */ 00608 POLICY_MATCH = TRUE; 00609 UTIL_assoc_prefix(peer_info, SPF_UNKNOWN, token_ptr); 00610 xfree(token); 00611 xfree(copy); 00612 return(TRUE); 00613 } 00614 break; 00615 00616 case A: 00617 xprintf("(QID: %u) :: policy mechanism is A (%s)\n", spf_rlevel, token_ptr); 00618 00619 if ((tmp_ptr = strstr(token_ptr, "/")) != NULL) 00620 { 00621 tmp_ptr++; 00622 POLICY_MATCH = UTIL_a_cmp(peer_info, token_ptr, atoi(tmp_ptr)); 00623 } 00624 else 00625 { 00626 POLICY_MATCH = UTIL_a_cmp(peer_info, token_ptr, 32); 00627 } 00628 00629 /* mechanism was supplied with a - indicating a failure on */ 00630 if ((PREFIX_TYPE == SPF_H_FAIL) && (POLICY_MATCH == TRUE)) 00631 { 00632 xvprintf("(QID: %u) :: Found a match on a negative prefix. Halting" 00633 "parse.\n", spf_rlevel); 00634 00635 UTIL_assoc_prefix(peer_info, SPF_H_FAIL, token_ptr); 00636 xfree(token); 00637 xfree(copy); 00638 return(FALSE); 00639 } 00640 00641 break; 00642 00643 case MX: 00644 xprintf("(QID: %u) :: policy mechanism is mx (%s)\n", 00645 spf_rlevel, token_ptr); 00646 00647 /* we've been supplied an MX to use instead.. grab it */ 00648 if ((tmp_ptr = strstr(token_ptr, ":")) != NULL) 00649 { 00650 tmp_ptr++; 00651 tmp_ptr2 = tmp_ptr; 00652 } 00653 else 00654 { 00655 tmp_ptr2 = peer_info->cur_dom; 00656 } 00657 00658 if ((tmp_ptr = strstr(token_ptr, "/")) != NULL) 00659 { 00660 tmp_ptr++; 00661 POLICY_MATCH = UTIL_mx_cmp(peer_info, tmp_ptr2, atoi(tmp_ptr)); 00662 } 00663 else 00664 { 00665 POLICY_MATCH = UTIL_mx_cmp(peer_info, tmp_ptr2, 32); 00666 } 00667 00668 /* mechanism was supplied with a - indicating a failure on */ 00669 if ((PREFIX_TYPE == SPF_H_FAIL) && (POLICY_MATCH == TRUE)) 00670 { 00671 xvprintf("(QID: %u) :: Found a match on a negative prefix. Halting" 00672 "parse.\n", spf_rlevel); 00673 00674 UTIL_assoc_prefix(peer_info, SPF_H_FAIL, token_ptr); 00675 xfree(token); 00676 xfree(copy); 00677 return(FALSE); 00678 } 00679 00680 break; 00681 case PTR: 00682 xprintf("(QID: %u) :: policy mechanism is ptr (%s)\n", 00683 spf_rlevel, token_ptr); 00684 00685 POLICY_MATCH = UTIL_ptr_cmp(peer_info, token_ptr); 00686 00687 /* mechanism was supplied with a - indicating a failure on */ 00688 if ((PREFIX_TYPE == SPF_H_FAIL) && (POLICY_MATCH == TRUE)) 00689 { 00690 xvprintf("(QID: %u) :: Found a match on a negative prefix. Halting" 00691 "parse.\n", spf_rlevel); 00692 00693 break; 00694 } 00695 00696 break; 00697 case IP4: 00698 xprintf("(QID: %u) :: policy mechanism is ip4 (%s)\n", 00699 spf_rlevel, token_ptr); 00700 00701 if ((policy_ip = UTIL_expand_ip(token_ptr)) == NULL) 00702 { 00703 xprintf("(QID: %u) :: ERROR: inet_pton unable to convert ip to " 00704 "binary\n", spf_rlevel); 00705 00706 break; 00707 } 00708 00709 xvprintf("(QID: %i) :: POL: %lu (%s) PEER: %lu (%s)\n", 00710 spf_rlevel, policy_ip->addr.s_addr, token_ptr, peer_info->addr.s_addr, 00711 peer_info->r_ip); 00712 POLICY_MATCH = UTIL_cidr_cmp(peer_info, policy_ip, &peer_info->addr); 00713 xfree(policy_ip); 00714 00715 /* mechanism was supplied with a - indicating a failure on */ 00716 if ((PREFIX_TYPE == SPF_H_FAIL) && (POLICY_MATCH == TRUE)) 00717 { 00718 xvprintf("(QID: %u) :: Found a match on a negative prefix. Halting " 00719 "parse.\n", spf_rlevel); 00720 00721 UTIL_assoc_prefix(peer_info, SPF_H_FAIL, token_ptr); 00722 xfree(token); 00723 xfree(copy); 00724 return(FALSE); 00725 } 00726 00727 break; 00728 case IP6: 00729 xprintf("(QID: %u) :: policy mechanism is ip6 (%s)\n", 00730 spf_rlevel, token_ptr); 00731 break; 00732 case EXISTS: 00733 xprintf("(QID: %u) :: policy mechanism is exists (%s)\n", 00734 spf_rlevel, token_ptr); 00735 00736 if ((tmp_ptr = strstr(token_ptr, ":")) != NULL) 00737 { 00738 tmp_ptr++; 00739 00740 if ((macro = MACRO_expand(peer_info, tmp_ptr)) == NULL) 00741 { 00742 xvprintf("(QID: %u) :: Unable to expand macro (%s). " 00743 "Aborting.\n", spf_rlevel, tmp_ptr); 00744 00745 break; 00746 } 00747 00748 if (DNS_query(peer_info, macro, T_A, NULL) == (char *)TRUE) 00749 { 00750 POLICY_MATCH = TRUE; 00751 } 00752 00753 xfree(macro); 00754 } 00755 break; 00756 case REDIRECT: 00757 xprintf("(QID: %u) :: policy mechanism is redirect (%s)\n", 00758 spf_rlevel, token_ptr); 00759 00760 if ((tmp_ptr = strstr(token_ptr, "=")) != NULL) 00761 { 00762 tmp_ptr++; 00763 00764 ver_c = peer_info->spf_ver; 00765 if (UTIL_is_macro(tmp_ptr) == TRUE) 00766 { 00767 if ((macro = MACRO_expand(peer_info, tmp_ptr)) != NULL) 00768 { 00769 xfree(peer_info->cur_dom); 00770 peer_info->cur_dom = xstrndup(macro, (strlen(macro) + 1)); 00771 xfree(macro); 00772 } 00773 } 00774 else 00775 { 00776 xfree(peer_info->cur_dom); 00777 peer_info->cur_dom = xstrndup(tmp_ptr, (strlen(tmp_ptr) + 1)); 00778 } 00779 00780 spf_rlevel++; 00781 peer_info->RES = SPF_NEUTRAL; 00782 PREFIX_TYPE = SPF_policy_main(peer_info); 00783 peer_info->spf_ver = ver_c; /* re-assign original proto ver */ 00784 xvprintf("(QID: %u) :: redirect returned result %i (%s)\n", 00785 spf_rlevel, PREFIX_TYPE, peer_info->last_m); 00786 00787 switch (PREFIX_TYPE) 00788 { 00789 case SPF_PASS: 00790 POLICY_MATCH = TRUE; 00791 break; 00792 case SPF_NONE: 00793 /* 'none' coming back from an redirect is a permanent error, and 00794 * thus we return unknown 00795 */ 00796 POLICY_ERROR = TRUE; 00797 PREFIX_TYPE = SPF_UNKNOWN; 00798 break; 00799 case SPF_S_FAIL: 00800 break; 00801 case SPF_H_FAIL: 00802 break; 00803 case SPF_ERROR: 00804 POLICY_ERROR = TRUE; 00805 break; 00806 case SPF_NEUTRAL: 00807 break; 00808 case SPF_UNKNOWN: 00809 POLICY_ERROR = TRUE; 00810 break; 00811 case SPF_UNMECH: 00812 break; 00813 } /* switch */ 00814 00815 if (POLICY_ERROR == TRUE) 00816 { 00817 UTIL_assoc_prefix(peer_info, PREFIX_TYPE, token_ptr); 00818 xfree(token); 00819 xfree(copy); 00820 return(FALSE); 00821 } 00822 } 00823 else 00824 { 00825 /* 'include' with no options returns unknown */ 00826 POLICY_MATCH = TRUE; 00827 UTIL_assoc_prefix(peer_info, SPF_UNKNOWN, token_ptr); 00828 xfree(token); 00829 xfree(copy); 00830 return(TRUE); 00831 } 00832 break; 00833 case EXPLAIN: 00834 xprintf("(QID: %u) :: policy mechanism is explain (%s)\n", 00835 spf_rlevel, token_ptr); 00836 00837 if ((tmp_ptr = strstr(token_ptr, ":")) != NULL) 00838 { 00839 tmp_ptr++; 00840 00841 if ((macro = MACRO_expand(peer_info, tmp_ptr)) == NULL) 00842 { 00843 xvprintf("(QID: %u) :: Unable to expand macro (%s). Aborting.\n", 00844 spf_rlevel, tmp_ptr); 00845 00846 break; 00847 } 00848 00849 peer_info->explain = xstrndup(macro, (strlen(macro) + 1)); 00850 xfree(macro); 00851 } 00852 00853 break; 00854 } /* switch */ 00855 00856 xfree(token); 00857 cp += pos + 1; /* move over to the next mechanism */ 00858 00859 if ((POLICY_MATCH == TRUE) && (peer_info->spf_ver > 0)) 00860 { 00861 /*peer_info->RES = SPF_PASS;*/ 00862 UTIL_assoc_prefix(peer_info, PREFIX_TYPE, peer_info->last_m); 00863 peer_info->RES_P = peer_info->RES; 00864 xfree(copy); 00865 return(TRUE); 00866 } 00867 } /* while parsing policy */ 00868 00869 xfree(copy); 00870 return(FALSE); 00871 } 00872 00873 00874 /* SPF_result 00875 * 00876 * Author: James Couzens <jcouzens@6o4.ca> 00877 * 00878 * Date: 02/02/04 00879 * 00880 * Desc: 00881 * Returns a string of allocated memory detailing a response 00882 * that the MTA will return to the client based on the result 00883 * received from an SPF query. 00884 * 00885 */ 00886 char *SPF_result(peer_info_t *peer_info) 00887 { 00888 char *buf; 00889 00890 buf = xmalloc(MAX_HEADER); 00891 memset(buf, '\0', MAX_HEADER); 00892 00893 switch (peer_info->RES) 00894 { 00895 case SPF_PASS: 00896 snprintf(buf, MAX_SMTP_RES, RES_PASS, peer_info->from, 00897 peer_info->r_ip); 00898 break; 00899 case SPF_NONE: 00900 snprintf(buf, MAX_SMTP_RES, RES_NONE, peer_info->from); 00901 break; 00902 case SPF_S_FAIL: 00903 snprintf(buf, MAX_SMTP_RES, RES_S_FAIL, peer_info->from, 00904 peer_info->r_ip); 00905 break; 00906 case SPF_H_FAIL: 00907 snprintf(buf, MAX_SMTP_RES, RES_H_FAIL, peer_info->from, 00908 peer_info->r_ip); 00909 break; 00910 case SPF_ERROR: 00911 snprintf(buf, MAX_SMTP_RES, RES_ERROR, peer_info->from); 00912 break; 00913 case SPF_NEUTRAL: 00914 snprintf(buf, MAX_SMTP_RES, RES_NEUTRAL, peer_info->r_ip, 00915 peer_info->from); 00916 break; 00917 case SPF_UNKNOWN: 00918 snprintf(buf, MAX_SMTP_RES, RES_UNKNOWN, peer_info->from); 00919 break; 00920 case SPF_UNMECH: 00921 snprintf(buf, MAX_SMTP_RES, RES_UNMECH, peer_info->from); 00922 break; 00923 } 00924 00925 xprintf("response: (%s)\n", buf); 00926 00927 return(buf); 00928 } 00929 00930 00931 /* SPF_get_explain 00932 * 00933 * Author: James Couzens <jcouzens@6o4.ca> 00934 * 00935 * Date: 01/25/04 00936 * 00937 * Desc: 00938 * Takes the contents of a returned explain parse (if any), 00939 * allocates memory for it and returns that memory with the contents of 00940 * that string. Calling function is required to free this memory. 00941 * 00942 * 00943 */ 00944 char *SPF_get_explain(peer_info_t *peer_info) 00945 { 00946 char *buf = NULL; 00947 00948 if (peer_info->explain != NULL) 00949 { 00950 if ((buf = MACRO_expand(peer_info, SPF_EXPLAIN)) != NULL) 00951 { 00952 xprintf("Prepending explain: (%s)\n", 00953 buf); 00954 return(buf); 00955 } 00956 } 00957 00958 return(NULL); 00959 } 00960 00961 00962 /* SPF_build_header 00963 * 00964 * Author: James Couzens <jcouzens@6o4.ca> 00965 * 00966 * Date: 01/25/04 00967 * 00968 * Desc: 00969 * Allocates memory and builds a string which contains a 00970 * Received-SPF header. The calling function is responsible to free 00971 * this memory. 00972 * 00973 * 00974 */ 00975 char *SPF_build_header(peer_info_t *peer_info) 00976 { 00977 char *buf; 00978 00979 buf = xmalloc(MAX_HEADER); 00980 memset(buf, '\0', MAX_HEADER); 00981 00982 switch (peer_info->RES) 00983 { 00984 case SPF_PASS: 00985 snprintf(buf, MAX_HEADER, peer_info->spf_result[SPF_PASS].h, 00986 peer_info->mta_hname, peer_info->from, peer_info->r_ip, 00987 peer_info->mta_hname, peer_info->r_ip, peer_info->from); 00988 break; 00989 case SPF_NONE: 00990 snprintf(buf, MAX_HEADER, peer_info->spf_result[SPF_NONE].h, 00991 peer_info->mta_hname, peer_info->from); 00992 break; 00993 case SPF_S_FAIL: 00994 snprintf(buf, MAX_HEADER, peer_info->spf_result[SPF_S_FAIL].h, 00995 peer_info->mta_hname, peer_info->from, peer_info->r_ip, 00996 peer_info->mta_hname, peer_info->r_ip, peer_info->from); 00997 break; 00998 case SPF_H_FAIL: 00999 snprintf(buf, MAX_HEADER, peer_info->spf_result[SPF_H_FAIL].h, 01000 peer_info->mta_hname, peer_info->from, peer_info->r_ip, 01001 peer_info->mta_hname, peer_info->r_ip, peer_info->from); 01002 break; 01003 case SPF_ERROR: 01004 snprintf(buf, MAX_HEADER, peer_info->spf_result[SPF_ERROR].h, 01005 peer_info->mta_hname, peer_info->from, peer_info->error); 01006 break; 01007 case SPF_NEUTRAL: 01008 snprintf(buf, MAX_HEADER, peer_info->spf_result[SPF_NEUTRAL].h, 01009 peer_info->mta_hname, peer_info->from, peer_info->r_ip); 01010 break; 01011 case SPF_UNKNOWN: 01012 snprintf(buf, MAX_HEADER, peer_info->spf_result[SPF_UNKNOWN].h, 01013 peer_info->mta_hname, peer_info->from, peer_info->cur_dom, 01014 peer_info->last_m); 01015 break; 01016 case SPF_UNMECH: 01017 snprintf(buf, MAX_HEADER, peer_info->spf_result[SPF_UNMECH].h, 01018 peer_info->last_m, peer_info->mta_hname, peer_info->from); 01019 break; 01020 } 01021 01022 xvprintf("Prepending header string: (%s)\n", buf); 01023 01024 return(buf); 01025 } 01026 01027 01028 /* SPF_smtp_helo 01029 * 01030 * Author: Sean Comeau <scomeau@obscurity.org> 01031 * James Couzens <jcouzens@6o4.ca> 01032 * Patrick Earl (http://patearl.net/) 01033 * 01034 * Date: 12/10/03 01035 * 12/25/03 (modified / renamed) 01036 * 02/04/04 (modified) 01037 * 01038 * Desc: 01039 * Fetches the HELO from MTA and places into the global 01040 * peer_info structure. 01041 * 01042 */ 01043 SPF_BOOL SPF_smtp_helo(peer_info_t *peer_info, const char *s) 01044 { 01045 01046 xprintf("called with (%s)\n", s); 01047 01048 if (peer_info->helo) 01049 { 01050 xfree(peer_info->helo); 01051 } 01052 01053 peer_info->helo = xstrdup(s); 01054 peer_info->ehlo = peer_info->helo; 01055 01056 return (strlen(peer_info->helo) > 0); 01057 } 01058 01059 01060 /* SPF_smtp_from 01061 * 01062 * Author: Sean Comeau <scomeau@obscurity.org> 01063 * James Couzens <jcouzens@6o4.ca> 01064 * Patrick Earl (http://patearl.net/) 01065 * 01066 * Date: 12/10/03 01067 * 12/25/03 James Couzens <jcouzens@6o4.ca> (rewrite) 01068 * 02/04/04 Patrick Earl (http://patearl.net) (modified) 01069 * Date: 02/05/04 Teddy <teddy@teddy.ch> 01070 * 01071 * Desc: 01072 * Fetches the FROM from MTA and places into the global 01073 * peer_info structure. 01074 * 01075 * 01076 */ 01077 SPF_BOOL SPF_smtp_from(peer_info_t *peer_info, const char *s) 01078 { 01079 int len; /* utility */ 01080 char *pos; /* position in string */ 01081 01082 xfree(peer_info->from); 01083 xfree(peer_info->cur_dom); 01084 01085 if (s == NULL) 01086 { 01087 if (*(peer_info->helo) == '\0') 01088 { 01089 peer_info->from = xstrdup("unknown"); 01090 } 01091 else 01092 { 01093 peer_info->from = xstrdup(peer_info->helo); 01094 } 01095 01096 xvprintf("NULL or invalid MAIL FROM received. Working with %s from now on.\n", 01097 peer_info->from ? peer_info->from : peer_info->helo); 01098 01099 return(FALSE); 01100 } 01101 01102 peer_info->from = xstrdup(s); 01103 01104 /* strips < and > from E-Mail-Address */ 01105 if (*peer_info->from == 60) { 01106 pos = peer_info->from; 01107 *(peer_info->from + strlen(peer_info->from) - 1) = 0; 01108 peer_info->from++; 01109 peer_info->from = xstrdup(peer_info->from); 01110 xfree(pos); 01111 } 01112 01113 if (*peer_info->from == '\0') 01114 { 01115 if (*(peer_info->helo) == '\0') 01116 { 01117 peer_info->from = xstrdup("unknown"); 01118 } 01119 else 01120 { 01121 peer_info->from = xstrdup(peer_info->helo); 01122 } 01123 } 01124 01125 pos = peer_info->from; 01126 01127 xprintf("FROM: (%s) (called with: %s)\n", peer_info->from, s); 01128 01129 if ((pos = strstr(pos, "@"))) 01130 { 01131 len = (pos - peer_info->from); 01132 01133 if (len >= LOCAL_PART) 01134 { 01135 xvprintf("%s is >= %s causing data overrun\n", len, LOCAL_PART); 01136 /*xeprintf("die\n");*/ 01137 } 01138 01139 /* Copy everything up to the @ into local_part. Do not include the @. */ 01140 memcpy(peer_info->local_part, peer_info->from, len); 01141 peer_info->local_part[len] = 0; 01142 01143 pos++; /* skip past the @ */ 01144 peer_info->cur_dom = xstrdup(pos); 01145 01146 xprintf("CUR DOM: (%s)\n", peer_info->cur_dom); 01147 01148 } 01149 else 01150 { 01151 strcpy(peer_info->local_part, "postmaster"); 01152 peer_info->cur_dom = xstrdup(peer_info->from); 01153 } 01154 01155 xvprintf("LOCAL: (%s) DOMAIN (%s) SENDER: (%s)\n", peer_info->local_part, 01156 peer_info->cur_dom, peer_info->from); 01157 01158 return(TRUE); 01159 } 01160 01161 /* end spf.c */

Generated on Thu Jul 1 14:05:44 2004 for libspf v1.0 by doxygen 1.3.7