summaryrefslogtreecommitdiffstats
path: root/src/postscreen/postscreen_misc.c
blob: f07c9ac119d1011122d0d3b445f5e9a0bd949686 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*++
/* NAME
/*	postscreen_misc 3
/* SUMMARY
/*	postscreen misc routines
/* SYNOPSIS
/*	#include <postscreen.h>
/*
/*	char	*psc_format_delta_time(buf, tv, delta)
/*	VSTRING	*buf;
/*	struct timeval tv;
/*	DELTA_TIME *delta;
/*
/*	void	psc_conclude(state)
/*	PSC_STATE *state;
/*
/*	void	psc_hangup_event(state)
/*	PSC_STATE *state;
/* DESCRIPTION
/*	psc_format_delta_time() computes the time difference between
/*	tv (past) and the present, formats the time difference with
/*	sub-second resolution in a human-readable way, and returns
/*	the integer time difference in seconds through the delta
/*	argument.
/*
/*	psc_conclude() logs when a client passes all necessary tests,
/*	updates the postscreen cache for any testes that were passed,
/*	and either forwards the connection to a real SMTP server or
/*	replies with the text in state->error_reply and hangs up the
/*	connection (by default, state->error_reply is set to a
/*	default 421 reply).
/*
/*	psc_hangup_event() cleans up after a client connection breaks
/*	unexpectedly. If logs the test where the break happened,
/*	and how much time as spent in that test before the connection
/*	broke.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System library. */

#include <sys_defs.h>

/* Utility library. */

#include <msg.h>
#include <vstring.h>
#include <iostuff.h>
#include <format_tv.h>

/* Global library. */

#include <mail_params.h>

/* Application-specific. */

#include <postscreen.h>

/* psc_format_delta_time - pretty-formatted delta time */

char   *psc_format_delta_time(VSTRING *buf, struct timeval tv,
			              DELTA_TIME *delta)
{
    DELTA_TIME pdelay;
    struct timeval now;

    GETTIMEOFDAY(&now);
    PSC_CALC_DELTA(pdelay, now, tv);
    VSTRING_RESET(buf);
    format_tv(buf, pdelay.dt_sec, pdelay.dt_usec, SIG_DIGS, var_delay_max_res);
    *delta = pdelay;
    return (STR(buf));
}

/* psc_conclude - bring this session to a conclusion */

void    psc_conclude(PSC_STATE *state)
{
    const char *myname = "psc_conclude";

    if (msg_verbose)
	msg_info("flags for %s: %s",
		 myname, psc_print_state_flags(state->flags, myname));

    /*
     * Handle clients that passed at least one test other than permanent
     * allowlisting, and that didn't fail any test including permanent
     * denylisting. There may still be unfinished tests; those tests will
     * need to be completed when the client returns in a later session.
     */
    if (state->flags & PSC_STATE_MASK_ANY_FAIL)
	state->flags &= ~PSC_STATE_MASK_ANY_PASS;

    /*
     * Log our final blessing when all unfinished tests were completed.
     */
    if ((state->flags & PSC_STATE_MASK_ANY_PASS) != 0
	&& (state->flags & PSC_STATE_MASK_ANY_PASS) ==
	PSC_STATE_FLAGS_TODO_TO_PASS(state->flags & PSC_STATE_MASK_ANY_TODO))
	msg_info("PASS %s [%s]:%s", (state->flags & PSC_STATE_FLAG_NEW) == 0
		 || state->client_info->pass_new_count++ > 0 ?
		 "OLD" : "NEW", PSC_CLIENT_ADDR_PORT(state));

    /*
     * Update the postscreen cache. This still supports a scenario where a
     * client gets allowlisted in the course of multiple sessions, as long as
     * that client does not "fail" any test. Don't try to optimize away cache
     * updates; we want cached information to be up-to-date even if a test
     * result is renewed during overlapping SMTP sessions, and even if
     * 'postfix reload' happens in the middle of that.
     */
    if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) != 0
	&& psc_cache_map != 0) {
	psc_print_tests(psc_temp, state);
	psc_cache_update(psc_cache_map, state->smtp_client_addr, STR(psc_temp));
    }

    /*
     * Either hand off the socket to a real SMTP engine, or say bye-bye.
     */
    if ((state->flags & PSC_STATE_FLAG_NOFORWARD) == 0) {
	psc_send_socket(state);
    } else {
	if ((state->flags & PSC_STATE_FLAG_HANGUP) == 0)
	    (void) PSC_SEND_REPLY(state, state->final_reply);
	msg_info("DISCONNECT [%s]:%s", PSC_CLIENT_ADDR_PORT(state));
	psc_free_session_state(state);
    }
}

/* psc_hangup_event - handle unexpected disconnect */

void    psc_hangup_event(PSC_STATE *state)
{
    DELTA_TIME elapsed;

    /*
     * Sessions can break at any time, even after the client passes all tests
     * (some MTAs including Postfix don't send QUIT when connection reuse is
     * enabled). This must not be treated as a protocol test failure.
     * 
     * Log the current test phase, and the elapsed time after the start of that
     * phase.
     */
    state->flags |= PSC_STATE_FLAG_HANGUP;
    msg_info("HANGUP after %s from [%s]:%s in %s",
	     psc_format_delta_time(psc_temp, state->start_time, &elapsed),
	     PSC_CLIENT_ADDR_PORT(state), state->test_name);
    state->flags |= PSC_STATE_FLAG_NOFORWARD;
    psc_conclude(state);
}