Ejemplo n.º 1
0
    int
env_unlink( struct envelope *env )
{
    char		efile_fname[ MAXPATHLEN + 1 ];

    sprintf( efile_fname, "%s/E%s", env->e_dir, env->e_id );

    if ( unlink( efile_fname ) != 0 ) {
	syslog( LOG_ERR, "Syserror: env_unlink unlink %s: %m", efile_fname );
	return( -1 );
    }

    env->e_flags = ( env->e_flags & ( ~ENV_FLAG_EFILE ));

    if ( env->e_dir == simta_dir_fast ) {
	simta_fast_files--;
	simta_debuglog( 3, "env_unlink env <%s> fast_files decrement %d",
		env->e_id, simta_fast_files );
    }

    env_dfile_unlink( env );

    simta_debuglog( 2, "env_unlink env <%s> %s: unlinked", env->e_dir, env->e_id );

    return( 0 );
}
Ejemplo n.º 2
0
    int
env_jail_set( struct envelope *e, int val )
{
    char			*s;

    if ( simta_debug > 2 ) {
	switch ( val ) {
	default:
	    s = "Unknown";
	    break;

	case ENV_JAIL_NO_CHANGE:
	    s = "JAIL_NO_CHANGE";
	    break;

	case ENV_JAIL_PAROLEE:
	    s = "JAIL_PAROLEE";
	    break;

	case ENV_JAIL_PRISONER:
	    s = "JAIL_PRISONER";
	    break;
	}

	simta_debuglog( 3, "Jail %s: value %d (%s)", e->e_id, val, s );
    }

    e->e_jail = val;

    return( 0 );
}
Ejemplo n.º 3
0
    int
env_tfile_unlink( struct envelope *e )
{
    char		tf[ MAXPATHLEN + 1 ];

    simta_debuglog( 3, "env_tfile_unlink %s", e->e_id );

    sprintf( tf, "%s/t%s", e->e_dir, e->e_id );

    if ( unlink( tf ) != 0 ) {
	syslog( LOG_ERR, "Syserror: env_tfile_unlink unlink %s: %m", tf );
	return( -1 );
    }

    e->e_flags = ( e->e_flags & ( ~ENV_FLAG_TFILE ));

    return( 0 );
}
Ejemplo n.º 4
0
    struct spf *
spf_lookup( const char *helo, const char *email, const struct sockaddr *addr )
{
    char		    *p;
    struct spf		    *s;

    s = calloc( 1, sizeof( struct spf ));
    s->spf_queries = 0;
    s->spf_sockaddr = addr;
    s->spf_helo = yaslauto( helo );

    if ( strlen( email ) == 0 ) {
	/* RFC 7208 2.4 The "MAIL FROM" Identity
	 * When the reverse-path is null, this document defines the "MAIL FROM"
	 * identity to be the mailbox composed of the local-part "postmaster"
	 * and the "HELO" identity
	 */
	s->spf_domain = yaslauto( helo );
	s->spf_localpart = yaslauto( "postmaster" );
    } else if (( p = strrchr( email, '@' )) != NULL ) {
	s->spf_domain = yaslauto( p + 1 );
	s->spf_localpart = yaslnew( email, (size_t) (p - email ));
    } else {
	/* RFC 7208 4.3 Initial Processing
	 * If the <sender> has no local-part, substitute the string
	 * "postmaster" for the local-part.
	 */
	s->spf_domain = yaslauto( email );
	s->spf_localpart = yaslauto( "postmaster" );
    }

    simta_debuglog( 2, "SPF %s: localpart %s", s->spf_domain, s->spf_localpart );

    s->spf_result = spf_check_host( s, s->spf_domain );

    return( s );
}
Ejemplo n.º 5
0
    int
env_efile( struct envelope *e )
{
    char		tf[ MAXPATHLEN + 1 ];
    char		ef[ MAXPATHLEN + 1 ];
    char		df[ MAXPATHLEN + 1 ];
    struct timeval	tv_now;
    struct dll_entry	*e_dll;

    sprintf( tf, "%s/t%s", e->e_dir, e->e_id );
    sprintf( ef, "%s/E%s", e->e_dir, e->e_id );
    sprintf( df, "%s/D%s", e->e_dir, e->e_id );

    if ( rename( tf, ef ) < 0 ) {
	syslog( LOG_ERR, "Syserror: env_efile rename %s %s: %m", tf, ef );
	unlink( tf );
	return( -1 );
    }

    if ( e->e_dir == simta_dir_fast ) {
	simta_fast_files++;
	simta_debuglog( 2, "Envelope env <%s> fast_files increment %d",
		e->e_id, simta_fast_files );
    }

    simta_debuglog( 3, "env_efile %s %s %s", e->e_dir, e->e_id,
	    e->e_hostname ? e->e_hostname : "" );

    e->e_flags = ( e->e_flags & ( ~ENV_FLAG_TFILE ));
    e->e_flags |= ENV_FLAG_EFILE;

    if ( simta_gettimeofday( &tv_now ) != 0 ) {
	return( -1 );
    }

    e->e_etime.tv_sec = tv_now.tv_sec;

    if ( simta_sync ) {
	env_fsync( ef );
	env_fsync( df );
	/* fsync() does not ensure that the directory entries for the files
	 * have been synced, so we must explicitly sync the directory.
	 */
	env_fsync( e->e_dir );
    }

    if ( simta_mid_list_enable != 0 ) {
	if (( e_dll = dll_lookup_or_create( &simta_env_list,
		e->e_id, 0 )) == NULL ) {
	    return( 1 );
	}

	if ( e_dll->dll_data == NULL ) {
	    e_dll->dll_data = e;
	    e->e_env_list_entry = e_dll;
	}
    }

    if ( simta_sender_list_enable != 0 ) {
	if ( sender_list_add( e ) != 0 ) {
	    return( 1 );
	}
    }

    return( 0 );
}
Ejemplo n.º 6
0
    int
env_tfile( struct envelope *e )
{
    int						fd;
    struct recipient				*r;
    FILE					*tff;
    char					tf[ MAXPATHLEN + 1 ];
    int						version_to_write;

    assert( e->e_dir != NULL );
    assert( e->e_id != NULL );

    sprintf( tf, "%s/t%s", e->e_dir, e->e_id );

    /* make tfile */
    if (( fd = open( tf, O_WRONLY | O_CREAT | O_EXCL, 0600 )) < 0 ) {
	syslog( LOG_ERR, "Syserror: env_tfile open %s: %m", tf );
	return( -1 );
    }

    if (( tff = fdopen( fd, "w" )) == NULL ) {
	close( fd );
	syslog( LOG_ERR, "Syserror: env_tfile fdopen: %m" );
	unlink( tf );
	return( -1 );
    }

    /* VSIMTA_EFILE_VERSION */
    version_to_write = SIMTA_EFILE_VERSION;
#if 0
    if (( !e->e_attributes ) && ( !e->e_jail )) {
	version_to_write = 3;
    }
#endif

    if ( fprintf( tff, "V%d\n", version_to_write ) < 0 ) {
	syslog( LOG_ERR, "Syserror: env_tfile fprintf: %m" );
	goto cleanup;
    }

    /* Emessage-id */
    if ( fprintf( tff, "E%s\n", e->e_id ) < 0 ) {
	syslog( LOG_ERR, "Syserror: env_tfile fprintf: %m" );
	goto cleanup;
    }

    /* Idinode */
    if ( e->e_dinode <= 0 ) {
	panic( "env_tfile: bad dinode" );
    }
    if ( fprintf( tff, "I%lu\n", e->e_dinode ) < 0 ) {
	syslog( LOG_ERR, "Syserror: env_tfile fprintf: %m" );
	goto cleanup;
    }

    simta_debuglog( 3, "env_tfile %s: Dinode %d", e->e_id, (int)e->e_dinode );

    /* Xpansion Level */
    if ( fprintf( tff, "X%d\n", e->e_n_exp_level ) < 0 ) {
	syslog( LOG_ERR, "Syserror: env_tfile fprintf: %m" );
	goto cleanup;
    }

    /* Jail Level */
    if (( version_to_write < 5 )) {
    } else if ( fprintf( tff, "J%d\n", e->e_jail ) < 0 ) {
	syslog( LOG_ERR, "Syserror: env_tfile fprintf: %m" );
	goto cleanup;
    }

    /* Hdestination-host */
    if (( e->e_hostname != NULL ) && ( e->e_dir != simta_dir_dead )) {
	if ( fprintf( tff, "H%s\n", e->e_hostname ) < 0 ) {
	    syslog( LOG_ERR, "Syserror: env_tfile fprintf: %m" );
	    goto cleanup;
	}

    } else {
	if ( fprintf( tff, "H\n" ) < 0 ) {
	    syslog( LOG_ERR, "Syserror: env_tfile fprintf: %m" );
	    goto cleanup;
	}
    }

    if (( version_to_write < 4 )) {
    } else if ( fprintf( tff, "D%u\n", e->e_attributes ) < 0 ) {
	syslog( LOG_ERR, "Syserror: env_tfile fprintf: %m" );
	goto cleanup;
    }

    /* [email protected] */
    if ( e->e_mail != NULL ) {
	if ( fprintf( tff, "F%s\n", e->e_mail ) < 0 ) {
	    syslog( LOG_ERR, "Syserror: env_tfile fprintf: %m" );
	    goto cleanup;
	}

    } else {
	if ( fprintf( tff, "F\n" ) < 0 ) {
	    syslog( LOG_ERR, "Syserror: env_tfile fprintf: %m" );
	    goto cleanup;
	}
    }

    /* [email protected] */
    if ( e->e_rcpt != NULL ) {
	for ( r = e->e_rcpt; r != NULL; r = r->r_next ) {
	    if ( fprintf( tff, "R%s\n", r->r_rcpt ) < 0 ) {
		syslog( LOG_ERR, "Syserror: env_tfile fprintf: %m" );
		goto cleanup;
	    }
	}

    } else {
	syslog( LOG_ERR, "Envelope env <%s>: no recipients while writing tfile",
		e->e_id );
	goto cleanup;
    }

    if ( fclose( tff ) != 0 ) {
	syslog( LOG_ERR, "Syserror: env_tfile fclose: %m" );
	unlink( tf );
	return( -1 );
    }

    e->e_flags |= ENV_FLAG_TFILE;

    return( 0 );

cleanup:
    fclose( tff );
    unlink( tf );
    return( -1 );
}
Ejemplo n.º 7
0
    int
env_move( struct envelope *env, char *target_dir )
{
    char                        dfile_new[ MAXPATHLEN ];
    char                        efile_new[ MAXPATHLEN ];
    char                        dfile_old[ MAXPATHLEN ];
    char                        efile_old[ MAXPATHLEN ];

    /* only move messages to slow or fast */
    assert(( target_dir == simta_dir_slow ) ||
	    ( target_dir == simta_dir_fast ));

    /* move message to target_dir if it isn't there already */
    if ( env->e_dir != target_dir ) {
	sprintf( efile_old, "%s/E%s", env->e_dir, env->e_id );
	sprintf( dfile_old, "%s/D%s", env->e_dir, env->e_id );
	sprintf( dfile_new, "%s/D%s", target_dir, env->e_id );
	sprintf( efile_new, "%s/E%s", target_dir, env->e_id );

	if ( link( dfile_old, dfile_new ) != 0 ) {
	    syslog( LOG_ERR, "Syserror: env_move link %s %s: %m", dfile_old,
		    dfile_new );
	    return( -1 );
	}

	if ( link( efile_old, efile_new ) != 0 ) {
	    syslog( LOG_ERR, "Syserror: env_move link %s %s: %m", efile_old,
		    efile_new );
	    if ( unlink( dfile_new ) != 0 ) {
		syslog( LOG_ERR, "Syserror: env_move unlink %s: %m",
			dfile_new );
	    }
	    return( -1 );
	}

	if ( target_dir == simta_dir_fast ) {
	    simta_fast_files++;
	    simta_debuglog( 3, "env_move env <%s> fast_files increment %d",
		    env->e_id, simta_fast_files );
	}

	if ( env_unlink( env ) != 0 ) {
	    if ( unlink( efile_new ) != 0 ) {
		syslog( LOG_ERR, "env_move unlink %s: %m", efile_new );
	    } else {
		if ( target_dir == simta_dir_fast ) {
		    simta_fast_files--;
		    simta_debuglog( 3, "env_move %s fast_files decrement %d",
			    env->e_id, simta_fast_files );
		}

		if ( unlink( dfile_new ) != 0 ) {
		    syslog( LOG_ERR, "env_move unlink %s: %m", dfile_new );
		}
	    }
	    return( -1 );
	}

	env->e_dir = target_dir;
	env->e_flags |= ENV_FLAG_EFILE;

	simta_debuglog( 1, "Envelope env <%s>: moved to %s",
		env->e_id, env->e_dir );
    }

    return( 0 );
}
Ejemplo n.º 8
0
Archivo: expand.c Proyecto: simta/simta
    int
expand( struct envelope *unexpanded_env )
{
    struct expand               exp;
    struct envelope             *base_error_env;
    struct envelope             *env_dead = NULL;
    struct envelope             *env;
    struct envelope             **env_p;
    struct recipient            *rcpt;
    struct expand_output        *host_stab = NULL;
    struct expand_output        *eo;
    struct expand_output        *eo_free;
    struct exp_addr             *e_addr;
    struct exp_addr             *next_e_addr;
    struct simta_red            *hq_red;
    char                        *domain;
    SNET                        *snet = NULL;
    int                         n_rcpts;
    int                         return_value = 1;
    int                         env_out = 0;
    int                         fast_file_start;
    int                         sendermatch;
    char                        e_original[ MAXPATHLEN ];
    char                        d_original[ MAXPATHLEN ];
    char                        d_out[ MAXPATHLEN ];
    /* RFC 5321 4.5.3.1.3.  Path
     * The maximum total length of a reverse-path or forward-path is 256
     * octets (including the punctuation and element separators).
     */
    char                        header[ 270 ];
#ifdef HAVE_LDAP
    char                        *p;
    int                         loop_color = 1;
    struct exp_link             *memonly;
    struct exp_link             *parent;
#endif /* HAVE_LDAP */

    if ( unexpanded_env->e_hostname != NULL ) {
        syslog( LOG_INFO, "Expand env <%s>: already expanded for host %s",
                unexpanded_env->e_id, unexpanded_env->e_hostname );
        return_value = 0;
        goto done;
    }

    memset( &exp, 0, sizeof( struct expand ));
    exp.exp_env = unexpanded_env;
    fast_file_start = simta_fast_files;

    /* call address_expand on each address in the expansion list.
     *
     * if an address is expandable, the address(es) that it expands to will
     * be added to the expansion list. These non-terminal addresses must
     * have their st_data set to NULL to specify that they are not to be
     * included in the terminal expansion list.
     *
     * Any address in the expansion list whose st_data is not NULL is
     * considered a terminal address and will be written out as one
     * of the addresses in expanded envelope(s).
     */

    if (( base_error_env = address_bounce_create( &exp )) == NULL ) {
        syslog( LOG_ERR, "Expand env <%s>: address_bounce_create: %m",
                unexpanded_env->e_id );
        goto done;
    }

    if ( env_recipient( base_error_env, unexpanded_env->e_mail ) != 0 ) {
        syslog( LOG_ERR, "Expand env <%s>: env_recipient: %m",
                unexpanded_env->e_id );
        goto done;
    }

    /* add all of the original recipients to the expansion list */
    for ( rcpt = unexpanded_env->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) {
        if ( add_address( &exp, rcpt->r_rcpt, base_error_env,
                ADDRESS_TYPE_EMAIL, exp.exp_env->e_mail ) != 0 ) {
            /* add_address syslogs errors */
            goto cleanup1;
        }
    }

    /* process the expansion list */
    for ( exp.exp_addr_cursor = exp.exp_addr_head;
            exp.exp_addr_cursor != NULL;
            exp.exp_addr_cursor = exp.exp_addr_cursor->e_addr_next ) {
        switch ( address_expand( &exp )) {
        case ADDRESS_EXCLUDE:
            exp.exp_addr_cursor->e_addr_terminal = 0;
            /* the address is not a terminal local address */
            break;

        case ADDRESS_FINAL:
            exp.exp_addr_cursor->e_addr_terminal = 1;
            break;

        case ADDRESS_SYSERROR:
            goto cleanup1;

        default:
            panic( "Expand: address_expand out of range" );
        }
    }

#ifdef HAVE_LDAP
    /* Members-only processing */
    for ( memonly = exp.exp_memonly; memonly != NULL;
            memonly = memonly->el_next ) {
        if ((( p = parent_permitted( memonly->el_exp_addr )) != NULL ) ||
                ( sender_is_child( memonly->el_exp_addr->e_addr_children,
                loop_color++ ))) {
            if ( p != NULL ) {
                syslog( LOG_INFO, "Expand env <%s>: members-only group %s OK: "
                        "parent %s permitted",
                        unexpanded_env->e_id, memonly->el_exp_addr->e_addr, p );

            } else {
                syslog( LOG_INFO, "Expand env <%s>: members-only group %s OK: "
                        "sender is child",
                        unexpanded_env->e_id, memonly->el_exp_addr->e_addr );
            }
            memonly->el_exp_addr->e_addr_ldap_flags =
                    ( memonly->el_exp_addr->e_addr_ldap_flags &
                    ( ~STATUS_LDAP_MEMONLY ));
            if ( memonly->el_exp_addr->e_addr_env_moderated != NULL ) {
                env_free( memonly->el_exp_addr->e_addr_env_moderated );
                memonly->el_exp_addr->e_addr_env_moderated = NULL;
            }

        } else {
            syslog( LOG_NOTICE,
                    "Expand env <%s>: members-only group %s suppressed",
                    unexpanded_env->e_id, memonly->el_exp_addr->e_addr );
            memonly->el_exp_addr->e_addr_ldap_flags |= STATUS_LDAP_SUPPRESSOR;
            suppress_addrs( memonly->el_exp_addr->e_addr_children,
                    loop_color++ );
        }
    }
#endif /* HAVE_LDAP */

    sprintf( d_original, "%s/D%s", unexpanded_env->e_dir,
            unexpanded_env->e_id );

    /* Create one expanded envelope for every host we expanded address for */
    for ( e_addr = exp.exp_addr_head; e_addr != NULL;
            e_addr = e_addr->e_addr_next ) {

#ifdef HAVE_LDAP
        if ((( e_addr->e_addr_ldap_flags & STATUS_LDAP_SUPPRESSED ) != 0 ) &&
                ( !unblocked_path_to_root( e_addr, loop_color++ ))) {
            if ( simta_expand_debug != 0 ) {
                printf( "Suppressed: %s\n", e_addr->e_addr );
            }
            continue;
        }
        if ( e_addr->e_addr_env_gmailfwd != NULL ) {
            e_addr->e_addr_env_gmailfwd->e_attributes =
                    unexpanded_env->e_attributes | ENV_ATTR_ARCHIVE_ONLY;

            if ( simta_expand_debug != 0 ) {
                printf( "Group mail forwarding: %s\n", e_addr->e_addr );
                env_stdout( e_addr->e_addr_env_gmailfwd );
                continue;
            }

            sprintf( d_out, "%s/D%s", e_addr->e_addr_env_gmailfwd->e_dir,
                    e_addr->e_addr_env_gmailfwd->e_id );
            if ( env_dfile_copy( e_addr->e_addr_env_gmailfwd, d_original,
                    NULL ) == 0 ) {
                syslog( LOG_ERR, "Expand env <%s>: %s: env_dfile_copy failed",
                        unexpanded_env->e_id,
                        e_addr->e_addr_env_gmailfwd->e_id );
                goto cleanup3;
            }

            simta_debuglog( 2, "Expand env <%s>: group mail env %s dinode %d",
                    unexpanded_env->e_id,
                    e_addr->e_addr_env_gmailfwd->e_id,
                    (int)e_addr->e_addr_env_gmailfwd->e_dinode );

            sendermatch = !strcasecmp( unexpanded_env->e_mail,
                    e_addr->e_addr_env_gmailfwd->e_mail );

            n_rcpts = 0;
            for ( rcpt = e_addr->e_addr_env_gmailfwd->e_rcpt; rcpt != NULL;
                    rcpt = rcpt->r_next ) {
                n_rcpts++;
                if ( sendermatch ) {
                    syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s>",
                            unexpanded_env->e_id,
                            e_addr->e_addr_env_gmailfwd->e_id, rcpt->r_rcpt,
                            e_addr->e_addr_env_gmailfwd->e_mail );
                } else {
                    syslog( LOG_INFO,
                            "Expand env <%s>: %s: To <%s> From <%s> (%s)",
                            unexpanded_env->e_id,
                            e_addr->e_addr_env_gmailfwd->e_id, rcpt->r_rcpt,
                            e_addr->e_addr_env_gmailfwd->e_mail,
                            unexpanded_env->e_mail );
                }

            }
            syslog( LOG_INFO,
                    "Expand env <%s>: %s: Expanded %d group mail forwarders",
                    unexpanded_env->e_id, e_addr->e_addr_env_gmailfwd->e_id,
                    n_rcpts );

            if ( env_outfile( e_addr->e_addr_env_gmailfwd ) != 0 ) {
                /* env_outfile syslogs errors */
                if ( unlink( d_out ) != 0 ) {
                    syslog( LOG_ERR, "Syserror: expand unlink %s: %m", d_out );
                }
                goto cleanup3;
            }
            env_out++;
            queue_envelope( e_addr->e_addr_env_gmailfwd );
            continue;

        }

        if ( e_addr->e_addr_env_moderated != NULL ) {
            e_addr->e_addr_env_moderated->e_attributes =
                    unexpanded_env->e_attributes;

            if ( simta_expand_debug != 0 ) {
                printf( "Moderated: %s\n", e_addr->e_addr );
                env_stdout( e_addr->e_addr_env_moderated );
                continue;
            }

            sprintf( d_out, "%s/D%s", e_addr->e_addr_env_moderated->e_dir,
                    e_addr->e_addr_env_moderated->e_id );
            if ( env_dfile_copy( e_addr->e_addr_env_moderated, d_original,
                    NULL ) == 0 ) {
                syslog( LOG_ERR, "Expand env <%s>: %s: env_dfile_copy failed",
                        unexpanded_env->e_id,
                        e_addr->e_addr_env_moderated->e_id );
                goto cleanup3;
            }

            simta_debuglog( 2, "Expand env <%s>: %s: moderation env dinode %d",
                    unexpanded_env->e_id, e_addr->e_addr_env_moderated->e_id,
                    (int)e_addr->e_addr_env_moderated->e_dinode );

            sendermatch = !strcasecmp( unexpanded_env->e_mail,
                    e_addr->e_addr_env_moderated->e_mail );

            n_rcpts = 0;
            for ( rcpt = e_addr->e_addr_env_moderated->e_rcpt; rcpt != NULL;
                    rcpt = rcpt->r_next ) {
                n_rcpts++;
                if ( sendermatch ) {
                    syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s>",
                            unexpanded_env->e_id,
                            e_addr->e_addr_env_moderated->e_id, rcpt->r_rcpt,
                            e_addr->e_addr_env_moderated->e_mail );
                } else {
                    syslog( LOG_INFO,
                            "Expand env <%s>: %s: To <%s> From <%s> (%s)",
                            unexpanded_env->e_id,
                            e_addr->e_addr_env_moderated->e_id, rcpt->r_rcpt,
                            e_addr->e_addr_env_moderated->e_mail,
                            unexpanded_env->e_mail );
                }

            }
            syslog( LOG_INFO,
                    "Expand env <%s>: %s: Expanded %d moderators",
                    unexpanded_env->e_id, e_addr->e_addr_env_moderated->e_id,
                    n_rcpts );

            if ( env_outfile( e_addr->e_addr_env_moderated ) != 0 ) {
                /* env_outfile syslogs errors */
                if ( unlink( d_out ) != 0 ) {
                    syslog( LOG_ERR, "expand unlink %s: %m", d_out );
                }
                goto cleanup3;
            }
            env_out++;
            queue_envelope( e_addr->e_addr_env_moderated );
            continue;

        } else if ( e_addr->e_addr_ldap_flags & STATUS_LDAP_SUPPRESSOR ) {
            for ( parent = e_addr->e_addr_parents; parent != NULL;
                    parent = parent->el_next ) {
                if ( parent->el_exp_addr == NULL ) {
                    if ( bounce_text( base_error_env, TEXT_ERROR,
                            "Members only group conditions not met: ",
                            e_addr->e_addr, NULL ) != 0 ) {
                        goto cleanup3;
                    }

                    if ( bounce_text( base_error_env, TEXT_ERROR,
            "If you have any questions, please contact the group owner: ",
                            e_addr->e_addr_owner, NULL ) != 0 ) {
                        goto cleanup3;
                    }

                } else if (( e_addr->e_addr_ldap_flags &
                        STATUS_LDAP_PRIVATE ) == 0 ) {
                    if ( bounce_text( parent->el_exp_addr->e_addr_errors,
                            TEXT_ERROR,
                            "Members only group conditions not met: ",
                            e_addr->e_addr, NULL ) != 0 ) {
                        goto cleanup3;
                    }

                    if ( bounce_text( parent->el_exp_addr->e_addr_errors,
                            TEXT_ERROR,
            "If you have any questions, please contact the group owner: ",
                            e_addr->e_addr_owner, NULL ) != 0 ) {
                        goto cleanup3;
                    }
                }
            }

            continue;
        }
#endif /* HAVE_LDAP */

        if ( e_addr->e_addr_terminal == 0 ) {
            if ( simta_expand_debug != 0 ) {
                printf( "Non-terminal: %s\n", e_addr->e_addr );
            }
            /* not a terminal expansion, do not add */
            continue;
        }

        if ( simta_expand_debug != 0 ) {
            printf( "Terminal: %s\n", e_addr->e_addr );
        }

        switch ( e_addr->e_addr_type ) {
        case ADDRESS_TYPE_EMAIL:
            if (( domain = strchr( e_addr->e_addr, '@' )) == NULL ) {
                syslog( LOG_ERR, "Expand env <%s>: strchr blivet",
                        unexpanded_env->e_id );
                goto cleanup3;
            }
            domain++;
            env = eo_lookup( host_stab, domain, e_addr->e_addr_from );
            break;

        case ADDRESS_TYPE_DEAD:
            domain = NULL;
            env = env_dead;
            break;

        default:
            panic( "expand: address type out of range" );
        }

        if ( env == NULL ) {
            /* Create envelope and add it to list */
            if (( env = env_create( domain ? simta_dir_fast : simta_dir_dead,
                    NULL, e_addr->e_addr_from, unexpanded_env )) == NULL ) {
                syslog( LOG_ERR, "Expand env <%s>: env_create: %m",
                        unexpanded_env->e_id );
                goto cleanup3;
            }

            simta_debuglog( 2, "Expand env <%s>: %s: expansion env dinode %d",
                    unexpanded_env->e_id, env->e_id, (int)env->e_dinode );

            /* fill in env */
            env->e_attributes = unexpanded_env->e_attributes;
            if ( domain != NULL ) {
                if ( env_hostname( env, domain ) != 0 ) {
                    env_free( env );
                    goto cleanup3;
                }
            } else {
                env_dead = env;
            }

            /* Add env to host_stab */
            if ( eo_insert( &host_stab, env ) != 0 ) {
                syslog( LOG_ERR, "Expand env <%s>: eo_insert %s failed: %m",
                        unexpanded_env->e_id, env->e_id );
                env_free( env );
                goto cleanup3;
            }
        }

        if ( env_recipient( env, e_addr->e_addr ) != 0 ) {
            goto cleanup3;
        }

        syslog( LOG_NOTICE,
                "Expand env <%s>: %s: recipient <%s> added to env for host %s",
                unexpanded_env->e_id, env->e_id, e_addr->e_addr,
                env->e_hostname ? env->e_hostname : "NULL" );
    }

    /* Write out all expanded envelopes and place them in to the host_q */
    for ( eo = host_stab; eo != NULL; eo = eo->eo_next ) {
        env = eo->eo_env;

        if ( simta_expand_debug == 0 ) {
            sprintf( d_out, "%s/D%s", env->e_dir, env->e_id );

            /* RFC 5321 4.4 Trace Information
             * When the delivery SMTP server makes the "final delivery" of a
             * message, it inserts a return-path line at the beginning of the
             * mail data.  This use of return-path is required; mail systems
             * MUST support it.  The return-path line preserves the
             * information in the <reverse-path> from the MAIL command.
             * Here, final delivery means the message has left the SMTP
             * environment.
             */
            if ((( hq_red = red_host_lookup( eo->eo_hostname )) != NULL ) &&
                    ( hq_red->red_deliver_type == RED_DELIVER_BINARY )) {
                if ( snprintf( header, 270,
                        "Return-Path: <%s>", env->e_mail ) >= 270 ) {
                    syslog( LOG_ERR,
                            "Expand env <%s>: %s: return path is too large",
                            unexpanded_env->e_id, env->e_id );
                }
                if ( env_dfile_copy( env, d_original, header ) == 0 ) {
                    syslog( LOG_ERR, "Expand env <%s>: %s: env_dfile_copy failed",
                            unexpanded_env->e_id, env->e_id );
                    goto cleanup4;
                }
            } else {
                /* Dfile: link Dold_id env->e_dir/Dnew_id */
                if ( link( d_original, d_out ) != 0 ) {
                    syslog( LOG_ERR, "Syserror: expand link %s %s: %m",
                            d_original, d_out );
                    goto cleanup4;
                }
            }

            sendermatch = !strcasecmp( unexpanded_env->e_mail, env->e_mail );

            n_rcpts = 0;
            for ( rcpt = env->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) {
                n_rcpts++;
                if ( sendermatch ) {
                    syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s>",
                            unexpanded_env->e_id, env->e_id, rcpt->r_rcpt,
                            env->e_mail );
                } else {
                    syslog( LOG_INFO,
                            "Expand env <%s>: %s: To <%s> From <%s> (%s)",
                            unexpanded_env->e_id, env->e_id, rcpt->r_rcpt,
                            env->e_mail, unexpanded_env->e_mail );
                }
            }

            syslog( LOG_INFO,
                    "Expand env <%s>: %s: Expanded %d recipients",
                    unexpanded_env->e_id, env->e_id, n_rcpts );

            /* Efile: write env->e_dir/Enew_id for all recipients at host */
            syslog( LOG_NOTICE, "Expand env <%s>: %s: writing Efile for %s",
                    unexpanded_env->e_id, env->e_id,
                    env->e_hostname ? env->e_hostname : "NULL" );
            if ( env_outfile( env ) != 0 ) {
                /* env_outfile syslogs errors */
                if ( unlink( d_out ) != 0 ) {
                    syslog( LOG_ERR, "Syserror: expand unlink %s: %m", d_out );
                }
                goto cleanup4;
            }

            env_out++;
            queue_envelope( env );

        } else {
            printf( "\n" );
            env_stdout( env );
        }
    }

    if ( env_out == 0 ) {
        syslog( LOG_NOTICE, "Expand env <%s>: no terminal recipients, "
                "deleting message", unexpanded_env->e_id );
    }

    /* write errors out to disk */
    env_p = &(exp.exp_errors);
    while (( env = *env_p ) != NULL ) {
        if ( simta_expand_debug == 0 ) {
            if ( env->e_error != 0 ) {
                env_p = &(env->e_next);

                if ( snet == NULL ) {
                    if (( snet = snet_open( d_original, O_RDONLY, 0,
                            1024 * 1024 )) == NULL ) {
                        syslog( LOG_ERR, "Liberror: expand snet_open %s: %m",
                                d_original );
                        goto cleanup5;
                    }
                } else {
                    if ( lseek( snet_fd( snet ), (off_t)0, SEEK_SET ) != 0 ) {
                        syslog( LOG_ERR, "Syserror: q_deliver lseek: %m" );
                        panic( "q_deliver lseek fail" );
                    }
                }

                /* write out error text, get Dfile inode */
                if ( bounce_dfile_out( env, snet ) == 0 ) {
                    if ( snet != NULL ) {
                        if ( snet_close( snet ) != 0 ) {
                            syslog( LOG_ERR,
                                    "Liberror: expand snet_close %s: %m",
                                    d_original );
                        }
                    }

                    goto cleanup5;
                }

                simta_debuglog( 2, "Expand env <%s>: %s: errors env dinode %d",
                        unexpanded_env->e_id, env->e_id, (int)env->e_dinode );

                line_file_free( env->e_err_text );
                env->e_err_text = NULL;
                env->e_error = 0;

                if ( env_outfile( env ) != 0 ) {
                    /* env_outfile syslogs errors */
                    sprintf( d_out, "%s/D%s", env->e_dir, env->e_id );
                    if ( unlink( d_out ) != 0 ) {
                        syslog( LOG_ERR, "Syserror: expand unlink %s: %m",
                                d_out );
                    }
                    goto cleanup5;
                }

                sendermatch = !strcasecmp( unexpanded_env->e_mail,
                        env->e_mail );

                n_rcpts = 0;
                for ( rcpt = env->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) {
                    n_rcpts++;
                    if ( sendermatch ) {
                        syslog( LOG_INFO,
                                "Expand env <%s>: %s: To <%s> From <%s>",
                                unexpanded_env->e_id, env->e_id, rcpt->r_rcpt,
                                env->e_mail );
                    } else {
                        syslog( LOG_INFO,
                                "Expand env <%s>: %s: To <%s> From <%s> (%s)",
                                unexpanded_env->e_id, env->e_id, rcpt->r_rcpt,
                                env->e_mail, unexpanded_env->e_mail );
                    }
                }

                syslog( LOG_NOTICE, "Expand env <%s>: %s: Expanded %d bounces",
                        unexpanded_env->e_id, env->e_id, n_rcpts );

                queue_envelope( env );

            } else {
                *env_p = env->e_next;
                env_free( env );
            }

        } else {
            *env_p = env->e_next;
            bounce_stdout( env );
            env_free( env );
        }
    }

    if ( snet != NULL ) {
        if ( snet_close( snet ) != 0 ) {
            syslog( LOG_ERR, "Liberror: expand snet_close %s: %m", d_original );
            sprintf( d_out, "%s/D%s", env->e_dir, env->e_id );
            if ( unlink( d_out ) != 0 ) {
                syslog( LOG_ERR, "Syserror: expand unlink %s: %m", d_out );
            }
            goto cleanup5;
        }
        snet = NULL;
    }

    syslog( LOG_INFO, "Expand env <%s>: Metric %d entries %d levels",
            unexpanded_env->e_id, exp.exp_entries, exp.exp_max_level );

    if ( simta_expand_debug != 0 ) {
        return_value = 0;
        goto cleanup2;
    }

    if ( utime( d_original, NULL ) != 0 ) {
        syslog( LOG_ERR, "Syserror: expand utime %s: %m", d_original );
        goto cleanup5;
    }

    if ( unexpanded_env->e_dir != simta_dir_fast ) {
        /* truncate orignal Efile */
        sprintf( e_original, "%s/E%s", unexpanded_env->e_dir,
                unexpanded_env->e_id );

        if ( truncate( e_original, (off_t)0 ) != 0 ) {
            syslog( LOG_ERR, "Syserror: expand truncate %s: %m", e_original );
            goto cleanup5;
        }
    }

    /* delete original message */
    if ( env_unlink( unexpanded_env ) != 0 ) {
        syslog( LOG_ERR,
                "Expand env <%s>: Expansion complete, can't delete message",
                unexpanded_env->e_id );
    } else {
        syslog( LOG_INFO,
                "Expand env <%s>: Expansion complete, message deleted",
                unexpanded_env->e_id );
    }

    return_value = 0;
    goto cleanup2;

cleanup5:
    cleanup_envelope_list( &exp.exp_errors );
#ifdef HAVE_LDAP
    cleanup_envelope_list( &exp.exp_gmailfwding );
#endif /* HAVE_LDAP */

cleanup4:
    for ( eo = host_stab; eo != NULL; eo = eo->eo_next ) {
        env = eo->eo_env;
        eo->eo_env = NULL;

        if (( env->e_flags & ENV_FLAG_EFILE ) != 0 ) {
            queue_remove_envelope( env );
            if ( env_unlink( env ) == 0 ) {
                syslog( LOG_WARNING, "Expand env <%s>: Message Deleted: "
                        "System error, unwinding expansion", env->e_id );
            } else {
                syslog( LOG_ERR, "Expand env <%s>: "
                        "System error, can't unwind expansion", env->e_id );
            }
        }

        env_free( env );
    }

cleanup3:
#ifdef HAVE_LDAP
    for ( memonly = exp.exp_memonly; memonly != NULL;
            memonly = memonly->el_next ) {
        if (( memonly->el_exp_addr->e_addr_env_moderated != NULL ) &&
                (( memonly->el_exp_addr->e_addr_env_moderated->e_flags &
                ENV_FLAG_EFILE ) != 0 )) {
            env_unlink( memonly->el_exp_addr->e_addr_env_moderated );
            env_free( memonly->el_exp_addr->e_addr_env_moderated );
            memonly->el_exp_addr->e_addr_env_moderated = NULL;
        }
    }
#endif /* HAVE_LDAP */

    if ( simta_fast_files != fast_file_start ) {
        syslog( LOG_ERR, "Expand env <%s>: could not unwind expansion",
                unexpanded_env->e_id );
        return_value = 1;
    }

cleanup2:
    /* free host_stab */
    eo = host_stab;
    while ( eo != NULL ) {
        eo_free = eo;
        eo = eo->eo_next;
        free( eo_free );
    }

cleanup1:
#ifdef HAVE_LDAP
    exp_addr_link_free( exp.exp_memonly );
#endif /* HAVE_LDAP */

    /* free the expansion list */
    for ( e_addr = exp.exp_addr_head; e_addr != NULL; e_addr = next_e_addr ) {
        next_e_addr = e_addr->e_addr_next;

#ifdef HAVE_LDAP
        exp_addr_link_free( e_addr->e_addr_parents );
        exp_addr_link_free( e_addr->e_addr_children );
        permitted_destroy( e_addr );
        if (( e_addr->e_addr_env_moderated != NULL ) &&
                (( e_addr->e_addr_env_moderated->e_flags &
                ENV_FLAG_EFILE ) == 0 )) {
            env_free( e_addr->e_addr_env_moderated );
        }

        if ( e_addr->e_addr_owner ) {
            free( e_addr->e_addr_owner );
        }

        if ( e_addr->e_addr_dn ) {
            free( e_addr->e_addr_dn );
        }
#endif

        free( e_addr->e_addr );
        free( e_addr->e_addr_from );
        free( e_addr );
    }

done:
    if ( return_value != 0 ) {
        syslog( LOG_ERR, "Expand env <%s>: Expansion failed",
                unexpanded_env->e_id );
    }

    return( return_value );
}
Ejemplo n.º 9
0
    int
spf_check_host( struct spf *s, const yastr domain )
{
    int			    i, j, rc, qualifier, ret = SPF_RESULT_NONE;
    struct dnsr_result	    *dnsr_res, *dnsr_res_mech = NULL;
    struct dnsr_string      *txt;
    yastr		    record = NULL, redirect = NULL, domain_spec, tmp;
    size_t		    tok_count = 0;
    yastr		    *split = NULL;
    char		    *p;
    unsigned long	    cidr, cidr6;
    int			    mech_queries = 0;

    /* RFC 7208 3.1 DNS Resource Records
     * SPF records MUST be published as a DNS TXT (type 16) Resource Record
     * (RR) [RFC1035] only.
     */
    if (( dnsr_res = get_txt( domain )) == NULL ) {
	syslog( LOG_WARNING, "SPF %s [%s]: TXT lookup %s failed",
		s->spf_domain, domain, domain );
	return( SPF_RESULT_TEMPERROR );
    }

    for ( i = 0 ; i < dnsr_res->r_ancount ; i++ ) {
	if ( dnsr_res->r_answer[ i ].rr_type == DNSR_TYPE_TXT ) {
	    txt = dnsr_res->r_answer[ i ].rr_txt.txt_data;
	    /* RFC 7208 4.5 Selecting Records
	     * Starting with the set of records that were returned by the
	     * lookup, discard records that do not begin with a version section
	     * of exactly "v=spf1".  Note that the version section is
	     * terminated by either an SP character or the end of the record.
	     */
	    if (( strncasecmp( txt->s_string, "v=spf1", 6 ) == 0 ) &&
		    (( txt->s_string[ 6 ] == ' ' ) ||
		    ( txt->s_string[ 6 ] == '\0' ))) {
		if ( record != NULL ) {
		    /* RFC 7208 3.2 Multiple DNS Records
		     * A domain name MUST NOT have multiple records that would
		     * cause an authorization check to select more than one
		     * record.
		     */
		    syslog( LOG_ERR,
			    "SPF %s [%s]: multiple v=spf1 records found",
			    s->spf_domain, domain );
		    ret = SPF_RESULT_PERMERROR;
		    goto cleanup;
		}
		record = yaslempty( );
		/* RFC 7208 3.3 Multiple Strings in a Single DNS Record
		 * If a published record contains multiple character-strings,
		 * then the record MUST be treated as if those strings are
		 * concatenated together without adding spaces.
		 */
		for ( ; txt != NULL ; txt = txt->s_next ) {
		    record = yaslcat( record, txt->s_string );
		}
	    }
	}
    }

    if ( record == NULL ) {
	simta_debuglog( 1, "SPF %s [%s]: no SPF record found",
		s->spf_domain, domain );
	goto cleanup;
    }

    simta_debuglog( 2, "SPF %s [%s]: record: %s", s->spf_domain, domain, record );

    split = yaslsplitlen( record, yasllen( record ), " ", 1, &tok_count );

    /* Start at 1, 0 is v=spf1 */
    for ( i = 1 ; i < tok_count ; i++ ) {
	/* multiple spaces in a record will result in empty elements */
	if ( yasllen( split[ i ] ) == 0 ) {
	    continue;
	}


	/* RFC 7208 4.6.4 DNS Lookup Limits
	 * Some mechanisms and modifiers (collectively, "terms") cause DNS
	 * queries at the time of evaluation [...] SPF implementations MUST
	 * limit the total number of those terms to 10 during SPF evaluation,
	 * to avoid unreasonable load on the DNS.  If this limit is exceeded,
	 * the implementation MUST return "permerror".
	 */
	/* In real life strictly enforcing a limit of ten will break SPF
	 * evaluation of multiple major domains, so we use a higher limit.
	 */
	if ( s->spf_queries > 25 ) {
	    syslog( LOG_WARNING, "SPF %s [%s]: DNS lookup limit exceeded",
		    s->spf_domain, domain );
	    ret = SPF_RESULT_PERMERROR;
	    goto cleanup;
	}

	/* RFC 7208 4.6.2 Mechanisms
	 * The possible qualifiers, and the results they cause check_host() to
	 * return, are as follows:
	 *
	 * "+" pass
	 * "-" fail
	 * "~" softfail
	 * "?" neutral
	 *
	 * The qualifier is optional and defaults to "+".
	 */
	switch ( *split[ i ] ) {
	case '+':
	    qualifier = SPF_RESULT_PASS;
	    yaslrange( split[ i ], 1, -1 );
	    break;
	case '-':
	    qualifier = SPF_RESULT_FAIL;
	    yaslrange( split[ i ], 1, -1 );
	    break;
	case '~':
	    qualifier = SPF_RESULT_SOFTFAIL;
	    yaslrange( split[ i ], 1, -1 );
	    break;
	case '?':
	    qualifier = SPF_RESULT_NEUTRAL;
	    yaslrange( split[ i ], 1, -1 );
	    break;
	default:
	    qualifier = SPF_RESULT_PASS;
	    break;
	}

	if ( strncasecmp( split[ i ], "redirect=", 9 ) == 0 ) {
	    s->spf_queries++;
	    redirect = split[ i ];
	    yaslrange( redirect, 9, -1 );
	    simta_debuglog( 2, "SPF %s [%s]: redirect to %s",
		    s->spf_domain, domain, redirect );

	/* RFC 7208 5.1 "all"
	 * The "all" mechanism is a test that always matches.
	 */
	} else if ( strcasecmp( split[ i ], "all" ) == 0 ) {
	    simta_debuglog( 2, "SPF %s [%s]: matched all: %s",
		    s->spf_domain, domain, spf_result_str( qualifier ));
	    ret = qualifier;
	    goto cleanup;

	/* RFC 7208 5.2 "include"
	 * The "include" mechanism triggers a recursive evaluation of
	 * check_host().
	 */
	} else if ( strncasecmp( split[ i ], "include:", 8 ) == 0 ) {
	    s->spf_queries++;
	    yaslrange( split[ i ], 8, -1 );
	    simta_debuglog( 2, "SPF %s [%s]: include %s",
		    s->spf_domain, domain, split[ i ] );
	    rc = spf_check_host( s, split[ i ] );
	    switch ( rc ) {
	    case SPF_RESULT_NONE:
		ret = SPF_RESULT_PERMERROR;
		goto cleanup;
	    case SPF_RESULT_PASS:
		ret = qualifier;
		goto cleanup;
	    case SPF_RESULT_TEMPERROR:
	    case SPF_RESULT_PERMERROR:
		ret = rc;
		goto cleanup;
	    }

	/* RFC 7208 5.3 "a" */
	} else if (( strcasecmp( split[ i ], "a" ) == 0 ) ||
		( strncasecmp( split[ i ], "a:", 2 ) == 0 ) ||
		( strncasecmp( split[ i ], "a/", 2 ) == 0 )) {
	    s->spf_queries++;
	    yaslrange( split[ i ], 1, -1 );

	    if (( domain_spec = spf_parse_domainspec_cidr( s, domain,
		    split[ i ], &cidr, &cidr6 )) == NULL ) {
		/* Macro expansion failed, probably a syntax problem. */
		ret = SPF_RESULT_PERMERROR;
		goto cleanup;
	    }

	    rc = spf_check_a( s, domain, cidr, cidr6, domain_spec );

	    switch( rc ) {
	    case SPF_RESULT_PASS:
		simta_debuglog( 2, "SPF %s [%s]: matched a %s/%ld/%ld: %s",
			s->spf_domain, domain, domain_spec, cidr, cidr6,
			spf_result_str( qualifier ));
		yaslfree( domain_spec );
		ret = qualifier;
		goto cleanup;
	    case SPF_RESULT_TEMPERROR:
		yaslfree( domain_spec );
		ret = rc;
		goto cleanup;
	    default:
		break;
	    }

	    yaslfree( domain_spec );

	/* RFC 7208 5.4 "mx" */
	} else if (( strcasecmp( split[ i ], "mx" ) == 0 ) ||
		( strncasecmp( split[ i ], "mx:", 3 ) == 0 ) ||
		( strncasecmp( split[ i ], "mx/", 3 ) == 0 )) {
	    s->spf_queries++;
	    mech_queries = 0;
	    yaslrange( split[ i ], 2, -1 );

	    if (( domain_spec = spf_parse_domainspec_cidr( s, domain,
		    split[ i ], &cidr, &cidr6 )) == NULL ) {
		/* Macro expansion failed, probably a syntax problem. */
		ret = SPF_RESULT_PERMERROR;
		goto cleanup;
	    }

	    if (( dnsr_res_mech = get_mx( domain_spec )) == NULL ) {
		syslog( LOG_WARNING, "SPF %s [%s]: MX lookup %s failed",
			s->spf_domain, domain, domain_spec );
		yaslfree( domain_spec );
		ret = SPF_RESULT_TEMPERROR;
		goto cleanup;
	    }

	    for ( j = 0 ; j < dnsr_res_mech->r_ancount ; j++ ) {
		if ( dnsr_res_mech->r_answer[ j ].rr_type == DNSR_TYPE_MX ) {
		    /* RFC 7208 4.6.4 DNS Lookup Limits
		     * When evaluating the "mx" mechanism, the number of "MX"
		     * resource records queried is included in the overall
		     * limit of 10 mechanisms/modifiers that cause DNS lookups
		     */
		    s->spf_queries++;
		    rc = spf_check_a( s, domain, cidr, cidr6,
			    dnsr_res_mech->r_answer[ j ].rr_mx.mx_exchange );
		    switch( rc ) {
		    case SPF_RESULT_PASS:
			simta_debuglog( 2,
				"SPF %s [%s]: matched mx %s/%ld/%ld: %s",
				s->spf_domain, domain, domain_spec, cidr, cidr6,
				spf_result_str( qualifier ));
			ret = qualifier;
			dnsr_free_result( dnsr_res_mech );
			yaslfree( domain_spec );
			goto cleanup;
		    case SPF_RESULT_PERMERROR:
		    case SPF_RESULT_TEMPERROR:
			ret = rc;
			dnsr_free_result( dnsr_res_mech );
			yaslfree( domain_spec );
			goto cleanup;
		    default:
			break;
		    }
		}
	    }

	    dnsr_free_result( dnsr_res_mech );
	    yaslfree( domain_spec );

	/* RFC 7208 5.5 "ptr" (do not use) */
	} else if (( strcasecmp( split[ i ], "ptr" ) == 0 ) ||
		( strncasecmp( split[ i ], "ptr:", 4 ) == 0 )) {
	    s->spf_queries++;
	    mech_queries = 0;
	    if (( dnsr_res_mech = get_ptr( s->spf_sockaddr )) == NULL ) {
		/* RFC 7208 5.5 "ptr" (do not use )
		 * If a DNS error occurs while doing the PTR RR lookup,
		 * then this mechanism fails to match.
		 */
		continue;
	    }

	    if ( dnsr_res_mech->r_ancount == 0 ) {
		dnsr_free_result( dnsr_res_mech );
		continue;
	    }

	    if ( split[ i ][ 3 ] == ':' ) {
		domain_spec = yaslnew(( split[ i ] + 4 ),
			( yasllen( split[ i ] ) - 4 ));
	    } else {
		domain_spec = yasldup( domain );
	    }

	    for ( j = 0 ; j < dnsr_res_mech->r_ancount ; j++ ) {
		if ( dnsr_res_mech->r_answer[ j ].rr_type != DNSR_TYPE_PTR ) {
		    continue;
		}
		/* We only care if it's a pass; like the initial PTR query,
		 * DNS errors are treated as a non-match rather than an error.
		 */
		/* RFC 7208 4.6.4 DNS Lookup Limits
		 * the evaluation of each "PTR" record MUST NOT result in
		 * querying more than 10 address records -- either "A" or
		 * "AAAA" resource records.  If this limit is exceeded, all
		 * records  other than the first 10 MUST be ignored.
		 */
		if (( mech_queries++ < 10 ) && ( spf_check_a( s, domain, 32,
			128, dnsr_res_mech->r_answer[ j ].rr_dn.dn_name ) ==
			SPF_RESULT_PASS )) {
		    tmp = yaslauto(
			    dnsr_res_mech->r_answer[ j ].rr_dn.dn_name );
		    while (( yasllen( tmp ) > yasllen( domain_spec )) &&
			    ( p = strchr( tmp, '.' ))) {
			yaslrange( tmp, ( p - tmp + 1 ), -1 );
		    }
		    rc = strcasecmp( tmp, domain_spec );
		    yaslfree( tmp );
		    if ( rc == 0 ) {
			simta_debuglog( 2,
				"SPF %s [%s]: matched ptr %s (%s): %s",
				s->spf_domain, domain, domain_spec,
				dnsr_res_mech->r_answer[ j ].rr_dn.dn_name,
				spf_result_str( qualifier ));
			ret = qualifier;
			yaslfree( domain_spec );
			dnsr_free_result( dnsr_res_mech );
			goto cleanup;
		    }
		}
	    }

	    yaslfree( domain_spec );
	    dnsr_free_result( dnsr_res_mech );

	/* RFC 7208 5.6 "ip4" and "ip6"
	 * These mechanisms test whether <ip> is contained within a given
	 * IP network.
	 */
	} else if ( strncasecmp( split[ i ], "ip4:", 4 ) == 0 ) {
	    if ( s->spf_sockaddr->sa_family != AF_INET ) {
		continue;
	    }

	    yaslrange( split[ i ], 4, -1 );
	    if (( p = strchr( split[ i ], '/' )) != NULL ) {
		errno = 0;
		cidr = strtoul( p + 1, NULL, 10 );
		if ( errno ) {
		    syslog( LOG_WARNING,
			    "SPF %s [%s]: failed parsing CIDR mask %s: %m",
			    s->spf_domain, domain, p + 1 );
		    ret = SPF_RESULT_PERMERROR;
		    goto cleanup;
		}
		if ( cidr > 32 ) {
		    syslog( LOG_WARNING, "SPF %s [%s]: invalid CIDR mask: %ld",
			    s->spf_domain, domain, cidr );
		    ret = SPF_RESULT_PERMERROR;
		    goto cleanup;
		}
		yaslrange( split[ i ], 0, p - split[ i ] - 1 );
	    } else {
		cidr = 32;
	    }

	    if (( rc = simta_cidr_compare( cidr, s->spf_sockaddr, NULL,
		    split[ i ] )) < 0 ) {
		syslog( LOG_WARNING,
			"SPF %s [%s]: simta_cidr_compare failed for %s",
			s->spf_domain, domain, split[ i ] );
		ret = SPF_RESULT_PERMERROR;
		goto cleanup;
	    } else if ( rc == 0 ) {
		simta_debuglog( 2, "SPF %s [%s]: matched ip4 %s/%ld: %s",
			s->spf_domain, domain, split[ i ], cidr,
			spf_result_str( qualifier ));
		ret = qualifier;
		goto cleanup;
	    }

	} else if ( strncasecmp( split[ i ], "ip6:", 4 ) == 0 ) {
	    if ( s->spf_sockaddr->sa_family != AF_INET6 ) {
		continue;
	    }

	    yaslrange( split[ i ], 4, -1 );
	    if (( p = strchr( split[ i ], '/' )) != NULL ) {
		errno = 0;
		cidr = strtoul( p + 1, NULL, 10 );
		if ( errno ) {
		    syslog( LOG_WARNING,
			    "SPF %s [%s]: failed parsing CIDR mask %s: %m",
			    s->spf_domain, domain, p + 1 );
		}
		if ( cidr > 128 ) {
		    syslog( LOG_WARNING, "SPF %s [%s]: invalid CIDR mask: %ld",
			    s->spf_domain, domain, cidr );
		    ret = SPF_RESULT_PERMERROR;
		    goto cleanup;
		}
		yaslrange( split[ i ], 0, p - split[ i ] - 1 );
	    } else {
		cidr = 128;
	    }

	    if (( rc = simta_cidr_compare( cidr, s->spf_sockaddr, NULL,
		    split[ i ] )) < 0 ) {
		syslog( LOG_WARNING,
			"SPF %s [%s]: simta_cidr_compare failed for %s",
			s->spf_domain, domain, split[ i ] );
		ret = SPF_RESULT_PERMERROR;
		goto cleanup;
	    } else if ( rc == 0 ) {
		simta_debuglog( 2, "SPF %s [%s]: matched ip6 %s/%ld: %s",
			s->spf_domain, domain, split[ i ], cidr,
			spf_result_str( qualifier ));
		ret = qualifier;
		goto cleanup;
	    }

	/* RFC 7208 5.7 "exists" */
	} else if ( strncasecmp( split[ i ], "exists:", 7 ) == 0 ) {
	    s->spf_queries++;
	    yaslrange( split[ i ], 7, -1 );
	    if (( domain_spec =
		    spf_macro_expand( s, domain, split[ i ] )) == NULL ) {
		/* Macro expansion failed, probably a syntax problem. */
		ret = SPF_RESULT_PERMERROR;
		goto cleanup;
	    }

	    if (( dnsr_res_mech = get_a( domain_spec )) == NULL ) {
		syslog( LOG_WARNING, "SPF %s [%s]: A lookup %s failed",
			s->spf_domain, domain, domain_spec );
		yaslfree( domain_spec );
		ret = SPF_RESULT_TEMPERROR;
		goto cleanup;
	    }

	    if ( dnsr_res_mech->r_ancount > 0 ) {
		simta_debuglog( 2, "SPF %s [%s]: matched exists %s: %s",
			s->spf_domain, domain, domain_spec,
			spf_result_str( qualifier ));
		dnsr_free_result( dnsr_res_mech );
		yaslfree( domain_spec );
		ret = qualifier;
		goto cleanup;
	    }

	    yaslfree( domain_spec );
	    dnsr_free_result( dnsr_res_mech );

	} else {
	    for ( p = split[ i ] ; isalnum( *p ) ; p++ );

	    if ( *p == '=' ) {
		/* RFC 7208 6 Modifier Definitions
		 * Unrecognized modifiers MUST be ignored
		 */
		simta_debuglog( 1, "SPF %s [%s]: %s unknown modifier %s",
			s->spf_domain, domain, spf_result_str( qualifier ),
			split[ i ] );
	    } else {
		syslog( LOG_WARNING, "SPF %s [%s]: %s unknown mechanism %s",
			s->spf_domain, domain, spf_result_str( qualifier ),
			split[ i ] );
		ret = SPF_RESULT_PERMERROR;
		goto cleanup;
	    }
	}
    }

    if ( redirect != NULL ) {
	if (( domain_spec = spf_macro_expand( s, domain, redirect )) == NULL ) {
	    /* Macro expansion failed, probably a syntax problem. */
	    ret = SPF_RESULT_PERMERROR;
	} else {
	    ret = spf_check_host( s, domain_spec );
	    yaslfree( domain_spec );
	}
	if ( ret == SPF_RESULT_NONE ) {
	    ret = SPF_RESULT_PERMERROR;
	}

    } else {
	/* RFC 7208 4.7 Default Result
	 * If none of the mechanisms match and there is no "redirect" modifier,
	 * then the check_host() returns a result of "neutral", just as if
	 * "?all" were specified as the last directive.
	 */
	ret = SPF_RESULT_NEUTRAL;
	simta_debuglog( 2, "SPF %s [%s]: default result: %s", s->spf_domain,
		domain, spf_result_str( ret ));
    }

cleanup:
    if ( split != NULL ) {
	yaslfreesplitres( split, tok_count );
    }
    yaslfree( record );
    dnsr_free_result( dnsr_res );
    return( ret );
}
Ejemplo n.º 10
0
    static yastr
spf_macro_expand( struct spf *s, const yastr domain, const yastr macro )
{
    int			urlescape, rtransform;
    long		dtransform, i, j;
    char		*p, *pp;
    char		delim;
    yastr		expanded, tmp, escaped;
    yastr		*split;
    size_t		tok_count;

    expanded = yaslempty( );
    escaped = yaslempty( );
    tmp = yaslempty( );

    for ( p = macro ; *p != '\0' ; p++ ) {
	if ( *p != '%' ) {
	    expanded = yaslcatlen( expanded, p, 1 );
	    continue;
	}
	p++;
	switch( *p ) {
	case '%':
	    expanded = yaslcat( expanded, "%" );
	    break;
	case '_':
	    expanded = yaslcat( expanded, "_" );
	    break;
	case '-':
	    expanded = yaslcat( expanded, "%20" );
	    break;
	case '{':
	    p++;
	    /* RFC 7208 7.3 Macro Processing Details
	     * Uppercase macros expand exactly as their lowercase equivalents,
	     * and are then URL escaped.
	     */
	    urlescape = isupper( *p );
	    switch( *p ) {
		case 'S':
		case 's':
		    yaslclear( tmp );
		    tmp = yaslcatprintf( tmp, "%s@%s", s->spf_localpart,
			    s->spf_domain );
		    break;
		case 'L':
		case 'l':
		    tmp = yaslcpy( tmp, s->spf_localpart );
		    break;
		case 'O':
		case 'o':
		    tmp = yaslcpy( tmp, s->spf_domain );
		    break;
		case 'D':
		case 'd':
		    tmp = yaslcpy( tmp, domain );
		    break;
		case 'I':
		case 'i':
		    if ( s->spf_sockaddr->sa_family == AF_INET ) {
			tmp = yaslgrowzero( tmp, INET_ADDRSTRLEN );
			if ( inet_ntop( s->spf_sockaddr->sa_family,
				&((struct sockaddr_in *)s->spf_sockaddr)->sin_addr,
				tmp, (socklen_t)yasllen( tmp )) == NULL ) {
			    goto error;
			}
			yaslupdatelen( tmp );
		    }
		    break;
		case 'P':
		case 'p':
		    /* This is overly complex and should not be used,
		     * so we're not going to implement it. */
		    tmp = yaslcpy( tmp, "unknown" );
		    break;
		case 'V':
		case 'v':
		    tmp = yaslcpy( tmp, ( s->spf_sockaddr->sa_family == AF_INET6 ) ?
			    "ip6" : "in-addr" );
		    break;
		case 'H':
		case 'h':
		    tmp = yaslcpy( tmp, s->spf_helo );
		    break;
		default:
		    syslog( LOG_WARNING,
			    "SPF %s [%s]: invalid macro-letter: %c",
			    s->spf_domain, domain, *p );
		    goto error;
	    }

	    if ( urlescape ) {
		/* RFC 7208 7.3 Macro Processing Details
		 * Uppercase macros expand exactly as their lowercase
		 * equivalents, and are then URL escaped. URL escaping MUST be
		 * performed for characters not in the "unreserved" set, which
		 * is defined in [RFC3986].
		 */
		yaslclear( escaped );
		for ( pp = tmp ; *pp != '\0' ; pp++ ) {
		    /* RFC 3986 2.3 Unreserved Characters
		     * Characters that are allowed in a URI but do not have a
		     * reserved purpose are called unreserved.  These include
		     * uppercase and lowercase letters, decimal digits, hyphen,
		     * period, underscore, and tilde.
		     */
		    if ( isalnum( *pp ) || *pp == '-' || *pp == '.' ||
			    *pp == '_' || *pp == '~' ) {
			escaped = yaslcatlen( escaped, pp, 1 );
		    } else {
			/* Reserved */
			escaped = yaslcatprintf( escaped, "%%%X", *pp );
		    }
		}
		tmp = yaslcpylen( tmp, escaped, yasllen( escaped ));
	    }
	    p++;

	    /* Check for transformers */
	    dtransform = 0;
	    rtransform = 0;
	    if ( isdigit( *p )) {
		dtransform = strtoul( p, &pp, 10 );
		p = pp;
	    }

	    if ( *p == 'r' ) {
		rtransform = 1;
		p++;
	    }

	    delim = '\0';
	    for ( pp = p ; *pp != '\0' ; pp++ ) {
		if ( *pp == '}' ) {
		    break;
		}
		switch( *pp ) {
		/* RFC 7208 7.1 Formal Specification
		 * delimiter        = "." / "-" / "+" / "," / "/" / "_" / "="
		 */
		case '.':
		case '-':
		case '+':
		case ',':
		case '/':
		case '_':
		case '=':
			if ( delim != '\0' ) {
			    tmp = yaslmapchars( tmp, pp, &delim, 1 );
			} else {
			    delim = *pp;
			}
		    break;
		default:
		    syslog( LOG_WARNING, "SPF %s [%s]: invalid delimiter: %c",
			    s->spf_domain, domain, *pp );
		    goto error;
		}
	    }

	    if (( rtransform == 1 ) || ( dtransform > 0 ) ||
		    ( delim != '\0' )) {
		if ( delim == '\0' ) {
		    delim = '.';
		}
		split = yaslsplitlen( tmp, yasllen( tmp ), &delim, 1,
		    &tok_count );
		yaslclear( tmp );
		if ( rtransform == 1 ) {
		    if (( dtransform > 0 ) && ( tok_count > dtransform )) {
			j = tok_count - dtransform;
		    } else {
			j = 0;
		    }
		    for ( i = tok_count - 1 ; i >= j ; i-- ) {
			if ( yasllen( tmp ) > 0 ) {
			    tmp = yaslcat( tmp, "." );
			}
			tmp = yaslcatyasl( tmp, split [ i ] );
		    }
		} else {
		    if (( dtransform > 0 ) && (tok_count > dtransform )) {
			j = dtransform;
		    } else {
			j = tok_count;
		    }
		    for ( i = 0 ; i < j ; i++ ) {
			if ( yasllen( tmp ) > 0 ) {
			    tmp = yaslcat( tmp, "." );
			}
			tmp = yaslcatyasl( tmp, split[ i ] );
		    }
		}
		yaslfreesplitres( split, tok_count );
	    }
	    expanded = yaslcatyasl( expanded, tmp );
	    break;
	default:
	    syslog( LOG_WARNING, "SPF %s [%s]: invalid macro-expand: %s",
		    s->spf_domain, domain, p );
	    goto error;
	}
    }

    if ( yaslcmp( macro, expanded )) {
	simta_debuglog( 3, "SPF %s [%s]: expanded %s to %s",
		s->spf_domain, domain, macro, expanded );
    }

    yaslfree( tmp );
    yaslfree( escaped );
    return( expanded );

error:
    yaslfree( tmp );
    yaslfree( escaped );
    yaslfree( expanded );
    return( NULL );
}