Here is an updated diff integrating different suggestions I received. - use -T (for TLS) instead of -O - use getsubopt(3) which I didn't know - manpage tweaks
Eric. Index: smtp.1 =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtp.1,v retrieving revision 1.9 diff -u -p -r1.9 smtp.1 --- smtp.1 13 Feb 2021 08:07:48 -0000 1.9 +++ smtp.1 20 May 2021 09:06:20 -0000 @@ -27,6 +27,7 @@ .Op Fl F Ar from .Op Fl H Ar helo .Op Fl s Ar server +.Op Fl T Ar params .Op Ar recipient ... .Sh DESCRIPTION The @@ -91,6 +92,26 @@ SMTP session with forced TLS on connecti .Pp Defaults to .Dq smtp://localhost:25 . +.It Fl T Ar params +Set specific parameters for TLS sessions. +The +.Ar params +string is a comma or space separated list of options. +The available options are: +.Bl -tag -width Ds +.It protocols Ns = Ns Ar value +Specify the protocols to use. +Refer to +.Xr tls_config_parse_protocols 3 +for +.Ar value . +.It ciphers Ns = Ns Ar value +Specify the allowed ciphers. +Refer to +.Xr tls_config_set_ciphers 3 +for +.Ar value . +.El .It Fl v Be more verbose. This option can be specified multiple times. Index: smtpc.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtpc.c,v retrieving revision 1.15 diff -u -p -r1.15 smtpc.c --- smtpc.c 10 Apr 2021 10:19:19 -0000 1.15 +++ smtpc.c 20 May 2021 08:57:16 -0000 @@ -48,22 +48,57 @@ static struct smtp_mail mail; static const char *servname = NULL; static struct tls_config *tls_config; +static const char *protocols = NULL; +static const char *ciphers = NULL; + static void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-Chnv] [-a authfile] [-F from] [-H helo] " - "[-s server] [recipient ...]\n", __progname); + "[-s server] [-T params] [recipient ...]\n", __progname); exit(1); } +static void +parse_tls_options(char *opt) +{ + static char * const tokens[] = { +#define CIPHERS 0 + "ciphers", +#define PROTOCOLS 1 + "protocols", + NULL }; + char *value; + + while (*opt) { + switch (getsubopt(&opt, tokens, &value)) { + case CIPHERS: + if (value == NULL) + fatalx("missing value for ciphers"); + ciphers = value; + break; + case PROTOCOLS: + if (value == NULL) + fatalx("missing value for protocols"); + protocols = value; + break; + case -1: + if (suboptarg) + fatalx("invalid TLS option \"%s\"", suboptarg); + fatalx("missing TLS option"); + } + } +} + int main(int argc, char **argv) { char hostname[256]; FILE *authfile; int ch, i; + uint32_t protos; char *server = "localhost"; char *authstr = NULL; size_t alloc = 0; @@ -91,7 +126,7 @@ main(int argc, char **argv) memset(&mail, 0, sizeof(mail)); mail.from = pw->pw_name; - while ((ch = getopt(argc, argv, "CF:H:S:a:hns:v")) != -1) { + while ((ch = getopt(argc, argv, "CF:H:S:T:a:hns:v")) != -1) { switch (ch) { case 'C': params.tls_verify = 0; @@ -105,6 +140,9 @@ main(int argc, char **argv) case 'S': servname = optarg; break; + case 'T': + parse_tls_options(optarg); + break; case 'a': if ((authfile = fopen(optarg, "r")) == NULL) fatal("%s: open", optarg); @@ -159,6 +197,17 @@ main(int argc, char **argv) tls_config = tls_config_new(); if (tls_config == NULL) fatal("tls_config_new"); + + if (protocols) { + if (tls_config_parse_protocols(&protos, protocols) == -1) + fatalx("failed to parse protocol '%s'", protocols); + if (tls_config_set_protocols(tls_config, protos) == -1) + fatalx("tls_config_set_protocols: %s", + tls_config_error(tls_config)); + } + if (ciphers && tls_config_set_ciphers(tls_config, ciphers) == -1) + fatalx("tls_config_set_ciphers: %s", + tls_config_error(tls_config)); if (tls_config_set_ca_file(tls_config, tls_default_ca_cert_file()) == -1) fatal("tls_set_ca_file"); if (!params.tls_verify) {