1 /* $NetBSD: smtp_key.c,v 1.4 2026/05/09 18:49:20 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* smtp_key 3 6 /* SUMMARY 7 /* cache/table lookup key management 8 /* SYNOPSIS 9 /* #include "smtp.h" 10 /* 11 /* char *smtp_key_prefix(buffer, delim_na, iterator, context_flags) 12 /* VSTRING *buffer; 13 /* const char *delim_na; 14 /* SMTP_ITERATOR *iterator; 15 /* int context_flags; 16 /* DESCRIPTION 17 /* The Postfix SMTP server accesses caches and lookup tables, 18 /* using lookup keys that contain information from various 19 /* contexts: per-server configuration, per-request envelope, 20 /* and results from DNS queries. 21 /* 22 /* These lookup keys sometimes share the same context information. 23 /* The primary purpose of this API is to ensure that this 24 /* shared context is used consistently, and that its use is 25 /* made explicit (both are needed to verify that there is no 26 /* false cache sharing). 27 /* 28 /* smtp_key_prefix() constructs a lookup key prefix from context 29 /* that may be shared with other lookup keys. The user is free 30 /* to append additional application-specific context. The result 31 /* value is a pointer to the result text. 32 /* 33 /* Arguments: 34 /* .IP buffer 35 /* Storage for the result. 36 /* .IP delim_na 37 /* The field delimiter character, and the optional place holder 38 /* character for a) information that is unavailable, b) 39 /* information that is inapplicable, or c) that would result 40 /* in an empty field. Key fields that contain "delim_na" 41 /* characters will be base64-encoded. 42 /* Do not specify "delim_na" characters that are part of the 43 /* base64 character set. 44 /* .IP iterator 45 /* Information that will be selected by the specified flags. 46 /* .IP context_flags 47 /* Bit-wise OR of one or more of the following. 48 /* .RS 49 /* .IP SMTP_KEY_FLAG_SERVICE 50 /* The global service name. This is a proxy for 51 /* destination-independent and request-independent context. 52 /* .IP SMTP_KEY_FLAG_SENDER 53 /* The envelope sender address. This is a proxy for sender-dependent 54 /* context, such as per-sender SASL authentication. 55 /* .IP SMTP_KEY_FLAG_REQ_NEXTHOP 56 /* The delivery request nexthop destination, including optional 57 /* [] and :port (the same form that users specify in a SASL 58 /* password or TLS policy lookup table). This is a proxy for 59 /* destination-dependent, but host-independent context. 60 /* .IP SMTP_KEY_FLAG_CUR_NEXTHOP 61 /* The current iterator's nexthop destination (delivery request 62 /* nexthop or fallback nexthop, including optional [] and 63 /* :port). 64 /* .IP SMTP_KEY_FLAG_HOSTNAME 65 /* The current iterator's remote hostname. 66 /* .IP SMTP_KEY_FLAG_ADDR 67 /* The current iterator's remote address. 68 /* .IP SMTP_KEY_FLAG_PORT 69 /* The current iterator's remote port. 70 /* .IP SMTP_KEY_FLAG_TLS_LEVEL 71 /* The requested TLS security level. 72 /* .IP SMTP_KEY_FLAG_REQ_SMTPUTF8 73 /* Whether SMTPUTF8 support is required. 74 /* .IP SMTP_KEY_FLAG_REQTLS_LEVEL 75 /* The REQUIRETLS enforcement level. 76 /* .RE 77 /* DIAGNOSTICS 78 /* Panic: undefined flag or zero flags. Fatal: out of memory. 79 /* LICENSE 80 /* .ad 81 /* .fi 82 /* The Secure Mailer license must be distributed with this software. 83 /* AUTHOR(S) 84 /* Wietse Venema 85 /* IBM T.J. Watson Research 86 /* P.O. Box 704 87 /* Yorktown Heights, NY 10598, USA 88 /* 89 /* Wietse Venema 90 /* Google, Inc. 91 /* 111 8th Avenue 92 /* New York, NY 10011, USA 93 /*--*/ 94 95 /* 96 * System library. 97 */ 98 #include <sys_defs.h> 99 #include <netinet/in.h> /* ntohs() for Solaris or BSD */ 100 #include <arpa/inet.h> /* ntohs() for Linux or BSD */ 101 #include <string.h> 102 103 /* 104 * Utility library. 105 */ 106 #include <msg.h> 107 #include <vstring.h> 108 #include <base64_code.h> 109 110 /* 111 * Global library. 112 */ 113 #include <mail_params.h> 114 #include <smtputf8.h> 115 116 /* 117 * Application-specific. 118 */ 119 #include <smtp.h> 120 121 /* 122 * We use a configurable field terminator and optional place holder for data 123 * that is unavailable or inapplicable. We base64-encode content that 124 * contains these characters, and content that needs obfuscation. 125 */ 126 127 /* smtp_key_append_na - append place-holder key field */ 128 129 static void smtp_key_append_na(VSTRING *buffer, const char *delim_na) 130 { 131 if (delim_na[1] != 0) 132 VSTRING_ADDCH(buffer, delim_na[1]); 133 VSTRING_ADDCH(buffer, delim_na[0]); 134 } 135 136 /* smtp_key_append_str - append string-valued key field */ 137 138 static void smtp_key_append_str(VSTRING *buffer, const char *str, 139 const char *delim_na) 140 { 141 if (str == 0 || str[0] == 0) { 142 smtp_key_append_na(buffer, delim_na); 143 } else if (str[strcspn(str, delim_na)] != 0) { 144 base64_encode_opt(buffer, str, strlen(str), BASE64_FLAG_APPEND); 145 VSTRING_ADDCH(buffer, delim_na[0]); 146 } else { 147 vstring_sprintf_append(buffer, "%s%c", str, delim_na[0]); 148 } 149 } 150 151 /* smtp_key_append_uint - append unsigned-valued key field */ 152 153 static void smtp_key_append_uint(VSTRING *buffer, unsigned num, 154 const char *delim_na) 155 { 156 vstring_sprintf_append(buffer, "%u%c", num, delim_na[0]); 157 } 158 159 /* smtp_key_prefix - format common elements in lookup key */ 160 161 char *smtp_key_prefix(VSTRING *buffer, const char *delim_na, 162 SMTP_ITERATOR *iter, int flags) 163 { 164 static const char myname[] = "smtp_key_prefix"; 165 SMTP_STATE *state = iter->parent; /* private member */ 166 167 /* 168 * Sanity checks. 169 */ 170 if (state == 0) 171 msg_panic("%s: no parent state", myname); 172 if (flags & ~SMTP_KEY_MASK_ALL) 173 msg_panic("%s: unknown key flags 0x%x", 174 myname, flags & ~SMTP_KEY_MASK_ALL); 175 if (flags == 0) 176 msg_panic("%s: zero flags", myname); 177 178 /* 179 * Initialize. 180 */ 181 VSTRING_RESET(buffer); 182 183 /* 184 * Per-service and per-request context. 185 */ 186 if (flags & SMTP_KEY_FLAG_SERVICE) 187 smtp_key_append_str(buffer, state->service, delim_na); 188 if (flags & SMTP_KEY_FLAG_SENDER) 189 smtp_key_append_str(buffer, state->request->sender, delim_na); 190 191 /* 192 * Per-destination context, non-canonicalized form. 193 */ 194 if (flags & SMTP_KEY_FLAG_REQ_NEXTHOP) 195 smtp_key_append_str(buffer, STR(iter->request_nexthop), delim_na); 196 if (flags & SMTP_KEY_FLAG_CUR_NEXTHOP) 197 smtp_key_append_str(buffer, STR(iter->dest), delim_na); 198 199 /* 200 * Per-host context, canonicalized form. 201 */ 202 if (flags & SMTP_KEY_FLAG_HOSTNAME) 203 smtp_key_append_str(buffer, STR(iter->host), delim_na); 204 if (flags & SMTP_KEY_FLAG_ADDR) 205 smtp_key_append_str(buffer, STR(iter->addr), delim_na); 206 if (flags & SMTP_KEY_FLAG_PORT) 207 smtp_key_append_uint(buffer, ntohs(iter->port), delim_na); 208 209 /* 210 * Requested TLS level, if applicable. TODO(tlsproxy) should the lookup 211 * engine also try the requested TLS level and 'stronger', in case a 212 * server hosts multiple domains with different TLS requirements? 213 */ 214 if (flags & SMTP_KEY_FLAG_TLS_LEVEL) 215 #ifdef USE_TLS 216 smtp_key_append_uint(buffer, state->tls->level, delim_na); 217 #else 218 smtp_key_append_na(buffer, delim_na); 219 #endif 220 221 /* 222 * REQUIRETLS enforcement level, if applicable. TODO(tlsproxy) should the 223 * lookup engine also try the requested TLS level and 'stronger', in case 224 * a server hosts multiple domains with different TLS requirements? 225 */ 226 if (flags & SMTP_KEY_FLAG_REQTLS_LEVEL) 227 #ifdef USE_TLS 228 smtp_key_append_uint(buffer, state->reqtls_level, delim_na); 229 #else 230 smtp_key_append_na(buffer, delim_na); 231 #endif 232 233 /* 234 * Require SMTPUTF8 support, if applicable. TODO(wietse) if a delivery 235 * request does not need SMTPUTF8, should we also search the connection 236 * cache for a connection that is known to support it? No, because the 237 * connection would be saved back under a key that does not require 238 * SMTPUTF8 support. 239 */ 240 if (flags & SMTP_KEY_FLAG_REQ_SMTPUTF8) 241 smtp_key_append_uint(buffer, 242 DELIVERY_REQUIRES_SMTPUTF8(state->request), 243 delim_na); 244 else 245 smtp_key_append_na(buffer, delim_na); 246 247 VSTRING_TERMINATE(buffer); 248 249 return STR(buffer); 250 } 251