commit 36dd1e7a7bf9f6dccf52a294d7d29221aef265f4 from: rsadowski date: Fri May 15 13:57:24 2026 UTC relayd: allow explicit paths for certificates, keys and OCSP staples Extend the "keypair" keyword in relayd.conf to support optional explicit paths. Previously, relayd enforced a naming convention, looking up files in /etc/ssl and /etc/ssl/private based on the keypair name. This change allows other applications to manage their certificates without having to comply with relayd's internal naming logic. Input and OK kirill@, help form tb@ commit - e64b43bec6df27f7e951b1c8878d1898928966b2 commit + 36dd1e7a7bf9f6dccf52a294d7d29221aef265f4 blob - 6c3d538ed4e09c5b81ac4bf4e384ded17b27ee87 blob + 05cf30a0f5855a076be4af9a203d7805172f6cbd --- parse.y +++ parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.262 2026/04/06 09:14:54 kirill Exp $ */ +/* $OpenBSD: parse.y,v 1.263 2026/05/15 13:57:24 rsadowski Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter @@ -140,6 +140,7 @@ int relay_id(struct relay *); struct relay *relay_inherit(struct relay *, struct relay *); int getservice(char *); int is_if_in_group(const char *, const char *); +static struct keyname *proto_keyname(char *); typedef struct { union { @@ -1342,21 +1343,94 @@ tlsflags : SESSION TICKETS { proto->tickets = 1; } free($3); } | KEYPAIR STRING { - struct keyname *name; + struct keyname *kname = NULL; - if (strlen($2) >= PATH_MAX) { - yyerror("keypair name too long"); + if ((kname = proto_keyname($2)) == NULL) { free($2); YYERROR; } - if ((name = calloc(1, sizeof(*name))) == NULL) { - yyerror("calloc"); + free($2); + } + | KEYPAIR STRING CERTIFICATE STRING { + struct keyname *kname = NULL; + + if ((kname = proto_keyname($2)) == NULL) { free($2); + free($4); YYERROR; } - name->name = $2; - TAILQ_INSERT_TAIL(&proto->tlscerts, name, entry); + + if (strlen($4) >= PATH_MAX) { + yyerror("keypair cert too long"); + free($2); + free($4); + YYERROR; + } + if (strlcpy(kname->certificate, $4, + sizeof(kname->certificate)) >= + sizeof(kname->certificate)) { + yyerror("keypair certificate truncated"); + free($2); + free($4); + YYERROR; + } + free($2); + free($4); } + | KEYPAIR STRING KEY STRING { + struct keyname *kname = NULL; + + if ((kname = proto_keyname($2)) == NULL) { + free($2); + free($4); + YYERROR; + } + + if (strlen($4) >= PATH_MAX) { + yyerror("keypair certificate key too long"); + free($2); + free($4); + YYERROR; + } + + if (strlcpy(kname->key, $4, + sizeof(kname->key)) >= + sizeof(kname->key)) { + yyerror("keypair certificate key truncated"); + free($2); + free($4); + YYERROR; + } + free($2); + free($4); + } + | KEYPAIR STRING OCSP STRING { + struct keyname *kname = NULL; + + if ((kname = proto_keyname($2)) == NULL) { + free($2); + free($4); + YYERROR; + } + + if (strlen($4) >= PATH_MAX) { + yyerror("keypair ocsp file too long"); + free($2); + free($4); + YYERROR; + } + + if (strlcpy(kname->ocsp, $4, + sizeof(kname->ocsp)) >= + sizeof(kname->ocsp)) { + yyerror("ocsp truncated"); + free($2); + free($4); + YYERROR; + } + free($2); + free($4); + } | CLIENT CA STRING { if (strlcpy(proto->tlsclientca, $3, sizeof(proto->tlsclientca)) >= @@ -1850,7 +1924,7 @@ relay : RELAY STRING { } '{' optnl relayopts_l '}' { struct relay *r; struct relay_config *rlconf = &rlay->rl_conf; - struct keyname *name; + struct keyname *kname; if (relay_findbyname(conf, rlconf->name) != NULL || relay_findbyaddr(conf, rlconf) != NULL) { @@ -1888,11 +1962,11 @@ relay : RELAY STRING { rlay->rl_conf.name); YYERROR; } - TAILQ_FOREACH(name, &rlay->rl_proto->tlscerts, entry) { + TAILQ_FOREACH(kname, &rlay->rl_proto->tlscerts, entry) { if (relay_load_certfiles(conf, - rlay, name->name) == -1) { + rlay, kname) == -1) { yyerror("cannot load keypair %s" - " for relay %s", name->name, + " for relay %s", kname->name, rlay->rl_conf.name); YYERROR; } @@ -3452,7 +3526,7 @@ relay_inherit(struct relay *ra, struct relay *rb) goto err; } TAILQ_FOREACH(name, &rb->rl_proto->tlscerts, entry) { - if (relay_load_certfiles(conf, rb, name->name) == -1) { + if (relay_load_certfiles(conf, rb, name) == -1) { yyerror("cannot load keypair %s for relay %s", name->name, rb->rl_conf.name); goto err; @@ -3551,3 +3625,31 @@ end: close(s); return (ret); } + +struct keyname* +proto_keyname(char *name) +{ + struct keyname *kname = NULL, *key; + + if (strlen(name) >= PATH_MAX) { + yyerror("keypair name too long"); + return NULL; + } + + + TAILQ_FOREACH(key, &proto->tlscerts, entry) { + if (strcmp(key->name, name) == 0) + return key; + } + + if ((kname = calloc(1, sizeof(*kname))) == NULL) { + return NULL; + } + + if ((kname->name = strdup(name)) == NULL) { + free(kname); + return NULL; + } + TAILQ_INSERT_TAIL(&proto->tlscerts, kname, entry); + return kname; +} blob - 9eb1b452d8122be2aaf05dd19dcb84e15b560d49 blob + 92c4320ce130adc612f1a667ea292a357b7dd874 --- relayd.c +++ relayd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.c,v 1.197 2026/03/02 19:28:01 rsadowski Exp $ */ +/* $OpenBSD: relayd.c,v 1.198 2026/05/15 13:57:24 rsadowski Exp $ */ /* * Copyright (c) 2007 - 2016 Reyk Floeter @@ -1335,14 +1335,14 @@ relay_load_fd(int fd, off_t *len) } int -relay_load_certfiles(struct relayd *env, struct relay *rlay, const char *name) +relay_load_certfiles(struct relayd *env, struct relay *rlay, const struct keyname *name) { char certfile[PATH_MAX]; char hbuf[PATH_MAX]; struct protocol *proto = rlay->rl_proto; struct relay_cert *cert; int useport = htons(rlay->rl_conf.port); - int cert_fd = -1, key_fd = -1, ocsp_fd = -1; + int cert_fd = -1, key_fd = -1, ocsp_fd = -1, ret = 0; if (rlay->rl_conf.flags & F_TLSCLIENT) { if (strlen(proto->tlsca) && rlay->rl_tls_ca_fd == -1) { @@ -1385,15 +1385,29 @@ relay_load_certfiles(struct relayd *env, struct relay print_host(&rlay->rl_conf.ss, hbuf, sizeof(hbuf)) == NULL) goto fail; else if (name != NULL && - strlcpy(hbuf, name, sizeof(hbuf)) >= sizeof(hbuf)) + strlcpy(hbuf, name->name, sizeof(hbuf)) >= sizeof(hbuf)) goto fail; - if (snprintf(certfile, sizeof(certfile), - "/etc/ssl/%s:%u.crt", hbuf, useport) == -1) - goto fail; + if (name != NULL && strcmp(name->certificate, "") != 0) { + if (strlcpy(certfile, name->certificate, sizeof(certfile)) + >= sizeof(certfile)) { + log_warnx("certificate truncated"); + goto fail; + } + } + else { + ret = snprintf(certfile, sizeof(certfile), + "/etc/ssl/%s:%u.crt", hbuf, useport); + + if (ret < 0 || (size_t)ret >= sizeof(certfile)) + goto fail; + } if ((cert_fd = open(certfile, O_RDONLY)) == -1) { - if (snprintf(certfile, sizeof(certfile), - "/etc/ssl/%s.crt", hbuf) == -1) + + ret = snprintf(certfile, sizeof(certfile), + "/etc/ssl/%s.crt", hbuf); + + if (ret < 0 || (size_t)ret >= sizeof(certfile)) goto fail; if ((cert_fd = open(certfile, O_RDONLY)) == -1) goto fail; @@ -1401,28 +1415,57 @@ relay_load_certfiles(struct relayd *env, struct relay } log_debug("%s: using certificate %s", __func__, certfile); - if (useport) { - if (snprintf(certfile, sizeof(certfile), - "/etc/ssl/private/%s:%u.key", hbuf, useport) == -1) + if (name != NULL && strcmp(name->key, "") != 0) { + if (strlcpy(certfile, name->key, sizeof(certfile)) + >= sizeof(certfile)) { + log_warnx("certificate key truncated"); goto fail; - } else { - if (snprintf(certfile, sizeof(certfile), - "/etc/ssl/private/%s.key", hbuf) == -1) - goto fail; + } } + else { + if (useport) { + ret = snprintf(certfile, sizeof(certfile), + "/etc/ssl/private/%s:%u.key", + hbuf, useport); + + if (ret < 0 || (size_t)ret >= sizeof(certfile)) + goto fail; + } else { + ret = snprintf(certfile, sizeof(certfile), + "/etc/ssl/private/%s.key", hbuf); + + if (ret < 0 || (size_t)ret >= sizeof(certfile)) + goto fail; + } + } if ((key_fd = open(certfile, O_RDONLY)) == -1) goto fail; log_debug("%s: using private key %s", __func__, certfile); - if (useport) { - if (snprintf(certfile, sizeof(certfile), - "/etc/ssl/%s:%u.ocsp", hbuf, useport) == -1) + if (name != NULL && strcmp(name->ocsp, "") != 0) { + if (strlcpy(certfile, name->ocsp, sizeof(certfile)) + >= sizeof(certfile)) { + log_warnx("certificate ocsp truncated"); goto fail; - } else { - if (snprintf(certfile, sizeof(certfile), - "/etc/ssl/%s.ocsp", hbuf) == -1) - goto fail; + } + } + else { + if (useport) { + ret = snprintf(certfile, sizeof(certfile), + "/etc/ssl/%s:%u.ocsp", + hbuf, useport); + + if (ret < 0 || (size_t)ret >= sizeof(certfile)) + goto fail; + } else { + ret = snprintf(certfile, sizeof(certfile), + "/etc/ssl/%s.ocsp", hbuf); + + if (ret < 0 || (size_t)ret >= sizeof(certfile)) + goto fail; + } + } if ((ocsp_fd = open(certfile, O_RDONLY)) != -1) log_debug("%s: using OCSP staple file %s", __func__, certfile); blob - a63dd4e3a41c38d9399b068d271ce69a342febce blob + f51bf540fa6ed755484a9b290e57de2668fb7337 --- relayd.conf.5 +++ relayd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: relayd.conf.5,v 1.215 2026/02/18 22:27:03 kirill Exp $ +.\" $OpenBSD: relayd.conf.5,v 1.216 2026/05/15 13:57:24 rsadowski Exp $ .\" .\" Copyright (c) 2006 - 2016 Reyk Floeter .\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: February 18 2026 $ +.Dd $Mdocdate: May 15 2026 $ .Dt RELAYD.CONF 5 .Os .Sh NAME @@ -998,7 +998,16 @@ is used. The default is .Ic no edh . .It Ic keypair Ar name -The relay will attempt to look up a private key in +.It Ic keypair Ar name Op Ic cert Ar "path" +.It Ic keypair Ar name Op Ic key Ar "path" +.It Ic keypair Ar name Op Ic ocsp Ar "path" +The relay will attempt to look up the TLS assets associated with +.Ar name . +The optional +.Ar path +arguments must be enclosed in double quotes and specify the absolute path to +the respective file. +By default, it searches for a private key in .Pa /etc/ssl/private/name:port.key and a public certificate in .Pa /etc/ssl/name:port.crt , @@ -1009,6 +1018,16 @@ If these files are not present, the relay will continu .Pa /etc/ssl/private/name.key and .Pa /etc/ssl/name.crt . +.Pp +If the +.Ic cert , +.Ic key , +or +.Ic ocsp +keywords are followed by an explicit +.Ar path , +that file will be used instead of the default location. +.Pp This option can be specified multiple times for TLS Server Name Indication. If not specified, a keypair will be loaded using the specified IP address of the relay as @@ -1017,8 +1036,10 @@ See .Xr ssl 8 for details about TLS server certificates. .Pp -An optional OCSP staple file will be used during TLS handshakes with -this server if it is found as a non-empty file in +An optional OCSP staple file will be used during TLS handshakes. +If no explicit +.Ic ocsp Ar path +is given, it will be searched as a non-empty file in .Pa /etc/ssl/name:port.ocsp or .Pa /etc/ssl/name.ocsp . blob - a5363989f4b4cbcca6c899b12de4ccd3cdf4dfcb blob + 5536b4789e4a72220ebafef985a5ab8fc8e945b8 --- relayd.h +++ relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.278 2026/03/02 19:28:01 rsadowski Exp $ */ +/* $OpenBSD: relayd.h,v 1.279 2026/05/15 13:57:24 rsadowski Exp $ */ /* * Copyright (c) 2006 - 2016 Reyk Floeter @@ -726,6 +726,9 @@ struct relay_ticket_key { struct keyname { TAILQ_ENTRY(keyname) entry; char *name; + char certificate[PATH_MAX]; + char key[PATH_MAX]; + char ocsp[PATH_MAX]; }; TAILQ_HEAD(keynamelist, keyname); @@ -1323,7 +1326,7 @@ struct relay_cert *cert_add(struct relayd *, objid_t); struct relay_cert *cert_find(struct relayd *, objid_t); char *relay_load_fd(int, off_t *); int relay_load_certfiles(struct relayd *, struct relay *, - const char *); + const struct keyname *); int expand_string(char *, size_t, const char *, const char *); void translate_string(char *); void purge_key(char **, off_t);