Home | History | Annotate | Line # | Download | only in smtp
      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