1/*++
2/* NAME
3/*	smtp_key 3
4/* SUMMARY
5/*	cache/table lookup key management
6/* SYNOPSIS
7/*	#include "smtp.h"
8/*
9/*	char	*smtp_key_prefix(buffer, delim_na, iterator, context_flags)
10/*	VSTRING	*buffer;
11/*	const char *delim_na;
12/*	SMTP_ITERATOR *iterator;
13/*	int	context_flags;
14/* DESCRIPTION
15/*	The Postfix SMTP server accesses caches and lookup tables,
16/*	using lookup keys that contain information from various
17/*	contexts: per-server configuration, per-request envelope,
18/*	and results from DNS queries.
19/*
20/*	These lookup keys sometimes share the same context information.
21/*	The primary purpose of this API is to ensure that this
22/*	shared context is used consistently, and that its use is
23/*	made explicit (both are needed to verify that there is no
24/*	false cache sharing).
25/*
26/*	smtp_key_prefix() constructs a lookup key prefix from context
27/*	that may be shared with other lookup keys. The user is free
28/*	to append additional application-specific context. The result
29/*	value is a pointer to the result text.
30/*
31/*	Arguments:
32/* .IP buffer
33/*	Storage for the result.
34/* .IP delim_na
35/*	The field delimiter character, and the optional place holder
36/*	character for a) information that is unavailable, b)
37/*	information that is inapplicable, or c) that would result
38/*	in an empty field.  Key fields that contain "delim_na"
39/*	characters will be base64-encoded.
40/*	Do not specify "delim_na" characters that are part of the
41/*	base64 character set.
42/* .IP iterator
43/*	Information that will be selected by the specified flags.
44/* .IP context_flags
45/*	Bit-wise OR of one or more of the following.
46/* .RS
47/* .IP SMTP_KEY_FLAG_SERVICE
48/*	The global service name. This is a proxy for
49/*	destination-independent and request-independent context.
50/* .IP SMTP_KEY_FLAG_SENDER
51/*	The envelope sender address. This is a proxy for sender-dependent
52/*	context, such as per-sender SASL authentication.
53/* .IP SMTP_KEY_FLAG_REQ_NEXTHOP
54/*	The request nexthop destination. This is a proxy for
55/*	destination-dependent, but host-independent context.
56/* .IP SMTP_KEY_FLAG_NEXTHOP
57/*	The current iterator's nexthop destination (request nexthop
58/*	or fallback nexthop, including optional [] and :port). This
59/*	is the form that users specify in a SASL or TLS lookup
60/*	tables.
61/* .IP SMTP_KEY_FLAG_HOSTNAME
62/*	The current iterator's remote hostname.
63/* .IP SMTP_KEY_FLAG_ADDR
64/*	The current iterator's remote address.
65/* .IP SMTP_KEY_FLAG_PORT
66/*	The current iterator's remote port.
67/* .RE
68/* DIAGNOSTICS
69/*	Panic: undefined flag or zero flags. Fatal: out of memory.
70/* LICENSE
71/* .ad
72/* .fi
73/*	The Secure Mailer license must be distributed with this software.
74/* AUTHOR(S)
75/*	Wietse Venema
76/*	IBM T.J. Watson Research
77/*	P.O. Box 704
78/*	Yorktown Heights, NY 10598, USA
79/*--*/
80
81 /*
82  * System library.
83  */
84#include <sys_defs.h>
85#include <netinet/in.h>			/* ntohs() for Solaris or BSD */
86#include <arpa/inet.h>			/* ntohs() for Linux or BSD */
87#include <string.h>
88
89 /*
90  * Utility library.
91  */
92#include <msg.h>
93#include <vstring.h>
94#include <base64_code.h>
95
96 /*
97  * Global library.
98  */
99#include <mail_params.h>
100
101 /*
102  * Application-specific.
103  */
104#include <smtp.h>
105
106 /*
107  * We use a configurable field terminator and optional place holder for data
108  * that is unavailable or inapplicable. We base64-encode content that
109  * contains these characters, and content that needs obfuscation.
110  */
111
112/* smtp_key_append_na - append place-holder key field */
113
114static void smtp_key_append_na(VSTRING *buffer, const char *delim_na)
115{
116    if (delim_na[1] != 0)
117	VSTRING_ADDCH(buffer, delim_na[1]);
118    VSTRING_ADDCH(buffer, delim_na[0]);
119}
120
121/* smtp_key_append_str - append string-valued key field */
122
123static void smtp_key_append_str(VSTRING *buffer, const char *str,
124				        const char *delim_na)
125{
126    if (str == 0 || str[0] == 0) {
127	smtp_key_append_na(buffer, delim_na);
128    } else if (str[strcspn(str, delim_na)] != 0) {
129	base64_encode_opt(buffer, str, strlen(str), BASE64_FLAG_APPEND);
130	VSTRING_ADDCH(buffer, delim_na[0]);
131    } else {
132	vstring_sprintf_append(buffer, "%s%c", str, delim_na[0]);
133    }
134}
135
136/* smtp_key_append_uint - append unsigned-valued key field */
137
138static void smtp_key_append_uint(VSTRING *buffer, unsigned num,
139				         const char *delim_na)
140{
141    vstring_sprintf_append(buffer, "%u%c", num, delim_na[0]);
142}
143
144/* smtp_key_prefix - format common elements in lookup key */
145
146char   *smtp_key_prefix(VSTRING *buffer, const char *delim_na,
147			        SMTP_ITERATOR *iter, int flags)
148{
149    const char myname[] = "smtp_key_prefix";
150    SMTP_STATE *state = iter->parent;	/* private member */
151
152    /*
153     * Sanity checks.
154     */
155    if (state == 0)
156	msg_panic("%s: no parent state", myname);
157    if (flags & ~SMTP_KEY_MASK_ALL)
158	msg_panic("%s: unknown key flags 0x%x",
159		  myname, flags & ~SMTP_KEY_MASK_ALL);
160    if (flags == 0)
161	msg_panic("%s: zero flags", myname);
162
163    /*
164     * Initialize.
165     */
166    VSTRING_RESET(buffer);
167
168    /*
169     * Per-service and per-request context.
170     */
171    if (flags & SMTP_KEY_FLAG_SERVICE)
172	smtp_key_append_str(buffer, state->service, delim_na);
173    if (flags & SMTP_KEY_FLAG_SENDER)
174	smtp_key_append_str(buffer, state->request->sender, delim_na);
175
176    /*
177     * Per-destination context, non-canonicalized form.
178     */
179    if (flags & SMTP_KEY_FLAG_REQ_NEXTHOP)
180	smtp_key_append_str(buffer, STR(iter->request_nexthop), delim_na);
181    if (flags & SMTP_KEY_FLAG_NEXTHOP)
182	smtp_key_append_str(buffer, STR(iter->dest), delim_na);
183
184    /*
185     * Per-host context, canonicalized form.
186     */
187    if (flags & SMTP_KEY_FLAG_HOSTNAME)
188	smtp_key_append_str(buffer, STR(iter->host), delim_na);
189    if (flags & SMTP_KEY_FLAG_ADDR)
190	smtp_key_append_str(buffer, STR(iter->addr), delim_na);
191    if (flags & SMTP_KEY_FLAG_PORT)
192	smtp_key_append_uint(buffer, ntohs(iter->port), delim_na);
193
194    /* Similarly, provide unique TLS fingerprint when applicable. */
195
196    VSTRING_TERMINATE(buffer);
197
198    return STR(buffer);
199}
200