Hi, sorry for being a moron.

I realize it’s already optional by not specifying client ca… sorry about the 
noise!


> On Dec 16, 2021, at 9:35 PM, Brian Brombacher <[email protected]> wrote:
> 
> Hi, not to interrupt development …
> 
> Can you make this completely optional from the servers perspective?  I don’t 
> want my endpoints validating anonymous client certificates when I run a 
> public endpoint.
> 
> I’ll just hack it out otherwise, but I think this opens a vector that should 
> be completely optional from a relay service.
> 
> 
>> On Dec 16, 2021, at 4:25 PM, rivo nurges <[email protected]> wrote:
>> 
>> Hi!
>> 
>> Here comes the support for relayd client certificate validation.
>> Full certificate chain, subject and issuer can be passed over in http 
>> headers.
>> It supports mandatory validation and optional validation(if client chooses to
>> provide certificate it will be validated).
>> 
>> Part of my sample config.
>> 
>> http protocol test {
>> match header set "CS_SUBJECT" value "$CLIENT_CERT_SUBJECT"
>> match header set "CS_ISSUER" value "$CLIENT_CERT_ISSUER"
>> match header set "CS_CERT" value "$CLIENT_CERT_CHAIN"
>> pass
>> tls {client ca "/tmp/easyrsa3/pki/ca.crt" optional }
>> }
>> 
>> This uses code from the patches submitted by Ashe Connor.
>> 
>> Rivo
>> 
>> diff refs/heads/master refs/heads/relay-clc3
>> blob - a2f1c130d6b45e3082048218c11537dca485998a
>> blob + 5070a7d48f58403f53d818231e1676db749aa9d7
>> --- usr.sbin/relayd/config.c
>> +++ usr.sbin/relayd/config.c
>> @@ -954,6 +954,15 @@ config_setrelay(struct relayd *env, struct relay *rlay
>>                       rlay->rl_conf.name);
>>                   return (-1);
>>               }
>> +                if (rlay->rl_tls_client_ca_fd != -1 &&
>> +                    config_setrelayfd(ps, id, n, 0,
>> +                    rlay->rl_conf.id, RELAY_FD_CLIENTCACERT,
>> +                    rlay->rl_tls_client_ca_fd) == -1) {
>> +                    log_warn("%s: fd passing failed for "
>> +                        "`%s'", __func__,
>> +                        rlay->rl_conf.name);
>> +                    return (-1);
>> +                }
>>               /* Prevent fd exhaustion in the parent. */
>>               if (proc_flush_imsg(ps, id, n) == -1) {
>>                   log_warn("%s: failed to flush "
>> @@ -987,6 +996,10 @@ config_setrelay(struct relayd *env, struct relay *rlay
>>       close(rlay->rl_s);
>>       rlay->rl_s = -1;
>>   }
>> +    if (rlay->rl_tls_client_ca_fd != -1) {
>> +        close(rlay->rl_tls_client_ca_fd);
>> +        rlay->rl_tls_client_ca_fd = -1;
>> +    }
>>   if (rlay->rl_tls_cacert_fd != -1) {
>>       close(rlay->rl_tls_cacert_fd);
>>       rlay->rl_tls_cacert_fd = -1;
>> @@ -1012,6 +1025,10 @@ config_setrelay(struct relayd *env, struct relay *rlay
>>           cert->cert_ocsp_fd = -1;
>>       }
>>   }
>> +    if (rlay->rl_tls_client_ca_fd != -1) {
>> +        close(rlay->rl_tls_client_ca_fd);
>> +        rlay->rl_tls_client_ca_fd = -1;
>> +    }
>>     return (0);
>> }
>> @@ -1034,6 +1051,7 @@ config_getrelay(struct relayd *env, struct imsg *imsg)
>>   rlay->rl_s = imsg->fd;
>>   rlay->rl_tls_ca_fd = -1;
>>   rlay->rl_tls_cacert_fd = -1;
>> +    rlay->rl_tls_client_ca_fd = -1;
>>     if (ps->ps_what[privsep_process] & CONFIG_PROTOS) {
>>       if (rlay->rl_conf.proto == EMPTY_ID)
>> @@ -1163,6 +1181,9 @@ config_getrelayfd(struct relayd *env, struct imsg *ims
>>   case RELAY_FD_CAFILE:
>>       rlay->rl_tls_cacert_fd = imsg->fd;
>>       break;
>> +    case RELAY_FD_CLIENTCACERT:
>> +        rlay->rl_tls_client_ca_fd = imsg->fd;
>> +        break;
>>   }
>>     DPRINTF("%s: %s %d received relay fd %d type %d for relay %s", __func__,
>> blob - 22beb857229a16e5b2c17a25a2944231d41e7e08
>> blob + fe5e8ff4dfed10e8f09e3226bdfe33f8bc031c8e
>> --- usr.sbin/relayd/parse.y
>> +++ usr.sbin/relayd/parse.y
>> @@ -172,14 +172,14 @@ typedef struct {
>> %token    CODE COOKIE DEMOTE DIGEST DISABLE ERROR EXPECT PASS BLOCK EXTERNAL
>> %token    FILENAME FORWARD FROM HASH HEADER HEADERLEN HOST HTTP ICMP INCLUDE 
>> INET
>> %token    INET6 INTERFACE INTERVAL IP KEYPAIR LABEL LISTEN VALUE LOADBALANCE 
>> LOG
>> -%token    LOOKUP METHOD MODE NAT NO DESTINATION NODELAY NOTHING ON PARENT 
>> PATH
>> +%token    LOOKUP METHOD MODE NAT NO DESTINATION NODELAY NOTHING ON OPTIONAL 
>> PARENT PATH
>> %token    PFTAG PORT PREFORK PRIORITY PROTO QUERYSTR REAL REDIRECT RELAY 
>> REMOVE
>> %token    REQUEST RESPONSE RETRY QUICK RETURN ROUNDROBIN ROUTE SACK SCRIPT 
>> SEND
>> %token    SESSION SOCKET SPLICE SSL STICKYADDR STRIP STYLE TABLE TAG TAGGED 
>> TCP
>> %token    TIMEOUT TLS TO ROUTER RTLABEL TRANSPARENT URL WITH TTL RTABLE
>> %token    MATCH PARAMS RANDOM LEASTSTATES SRCHASH KEY CERTIFICATE PASSWORD 
>> ECDHE
>> %token    EDH TICKETS CONNECTION CONNECTIONS CONTEXT ERRORS STATE CHANGES 
>> CHECKS
>> -%token    WEBSOCKETS
>> +%token    WEBSOCKETS CLIENT
>> %token    <v.string>    STRING
>> %token  <v.number>    NUMBER
>> %type    <v.string>    context hostname interface table value path
>> @@ -188,6 +188,7 @@ typedef struct {
>> %type    <v.number>    opttls opttlsclient
>> %type    <v.number>    redirect_proto relay_proto match
>> %type    <v.number>    action ruleaf key_option
>> +%type    <v.number>    clientcaopt
>> %type    <v.port>    port
>> %type    <v.host>    host
>> %type    <v.addr>    address rulesrc ruledst addrprefix
>> @@ -244,6 +245,10 @@ opttlsclient    : /*empty*/    { $$ = 0; }
>>       | WITH ssltls    { $$ = 1; }
>>       ;
>> +clientcaopt    : /*empty*/     { $$ = 0; }
>> +        | OPTIONAL      { $$ = 1; }
>> +        ;
>> +
>> http_type    : HTTP        { $$ = 0; }
>>       | STRING    {
>>           if (strcmp("https", $1) == 0) {
>> @@ -1353,6 +1358,19 @@ tlsflags    : SESSION TICKETS { proto->tickets = 1; }
>>           name->name = $2;
>>           TAILQ_INSERT_TAIL(&proto->tlscerts, name, entry);
>>       }
>> +        | CLIENT CA STRING clientcaopt        {
>> +            if (strlcpy(proto->tlsclientca, $3,
>> +                sizeof(proto->tlsclientca)) >=
>> +                sizeof(proto->tlsclientca)) {
>> +                yyerror("tlsclientca truncated");
>> +                free($3);
>> +                YYERROR;
>> +                }
>> +                        if ($4) {
>> +                proto->tlsflags  |= TLSFLAG_CLIENT_OPTIONAL;
>> +                        }
>> +            free($3);
>> +        }
>>       | NO flag            { proto->tlsflags &= ~($2); }
>>       | flag                { proto->tlsflags |= $1; }
>>       ;
>> @@ -1824,6 +1842,7 @@ relay        : RELAY STRING    {
>>           r->rl_conf.dstretry = 0;
>>           r->rl_tls_ca_fd = -1;
>>           r->rl_tls_cacert_fd = -1;
>> +            r->rl_tls_client_ca_fd = -1;
>>           TAILQ_INIT(&r->rl_tables);
>>           if (last_relay_id == INT_MAX) {
>>               yyerror("too many relays defined");
>> @@ -2413,6 +2432,7 @@ lookup(char *s)
>>       { "check",        CHECK },
>>       { "checks",        CHECKS },
>>       { "ciphers",        CIPHERS },
>> +        { "client",        CLIENT },
>>       { "code",        CODE },
>>       { "connection",        CONNECTION },
>>       { "context",        CONTEXT },
>> @@ -2458,6 +2478,7 @@ lookup(char *s)
>>       { "nodelay",        NODELAY },
>>       { "nothing",        NOTHING },
>>       { "on",            ON },
>> +        { "optional",        OPTIONAL },
>>       { "params",        PARAMS },
>>       { "parent",        PARENT },
>>       { "pass",        PASS },
>> @@ -3399,6 +3420,7 @@ relay_inherit(struct relay *ra, struct relay *rb)
>>   if (!(rb->rl_conf.flags & F_TLS)) {
>>       rb->rl_tls_cacert_fd = -1;
>>       rb->rl_tls_ca_fd = -1;
>> +        rb->rl_tls_client_ca_fd = -1;
>>   }
>>   TAILQ_INIT(&rb->rl_tables);
>> blob - da4a1aa0cc1158b22506c6d81e4d36b8810c025c
>> blob + 2d16b9d91e594a06d4b1b2bfc791c7f0c861fc57
>> --- usr.sbin/relayd/relay.c
>> +++ usr.sbin/relayd/relay.c
>> @@ -2255,6 +2255,30 @@ relay_tls_ctx_create(struct relay *rlay)
>>       }
>>       rlay->rl_tls_cacert_fd = -1;
>> +        if (rlay->rl_tls_client_ca_fd != -1) {
>> +            if ((buf = relay_load_fd(rlay->rl_tls_client_ca_fd,
>> +                &len)) ==
>> +                NULL) {
>> +                log_warn(
>> +                    "failed to read tls client CA certificate");
>> +                goto err;
>> +            }
>> +
>> +            if (tls_config_set_ca_mem(tls_cfg, buf, len) != 0) {
>> +                log_warnx(
>> +                    "failed to set tls client CA cert: %s",
>> +                    tls_config_error(tls_cfg));
>> +                goto err;
>> +            }
>> +            purge_key(&buf, len);
>> +
>> +            if (rlay->rl_proto->tlsflags & TLSFLAG_CLIENT_OPTIONAL)
>> +                tls_config_verify_client_optional(tls_cfg);
>> +            else
>> +                tls_config_verify_client(tls_cfg);
>> +        }
>> +        rlay->rl_tls_client_ca_fd = -1;
>> +
>>       tls = tls_server();
>>       if (tls == NULL) {
>>           log_warnx("unable to allocate TLS context");
>> blob - d493c238813cfc692d83f65a88d4556b2fa35b0f
>> blob + 58ba35c16ea8d80b36796d977ad7920d3bed3a9c
>> --- usr.sbin/relayd/relay_http.c
>> +++ usr.sbin/relayd/relay_http.c
>> @@ -78,6 +78,7 @@ int         relay_match_actions(struct ctl_relay_event *,
>>           struct relay_table **);
>> void         relay_httpdesc_free(struct http_descriptor *);
>> char *         server_root_strip(char *, int);
>> +char        *url_encode(const char *);
>> static struct relayd    *env = NULL;
>> @@ -1279,7 +1280,32 @@ relay_expand_http(struct ctl_relay_event *cre, char *v
>>       if (expand_string(buf, len, "$TIMEOUT", ibuf) != 0)
>>           return (NULL);
>>   }
>> -
>> +    if (strstr(val, "$CLIENT_CERT_") != NULL && 
>> tls_peer_cert_provided(cre->tls)) {
>> +        if (strstr(val, "$CLIENT_CERT_SUBJECT") != NULL) {
>> +            if (expand_string(buf, len,
>> +                "$CLIENT_CERT_SUBJECT", tls_peer_cert_subject(cre->tls)) != 
>> 0)
>> +                return (NULL);
>> +        }
>> +        if (strstr(val, "$CLIENT_CERT_ISSUER") != NULL) {
>> +            if (expand_string(buf, len,
>> +                "$CLIENT_CERT_ISSUER", tls_peer_cert_issuer(cre->tls)) != 0)
>> +                return (NULL);
>> +        }
>> +        if (strstr(val, "$CLIENT_CERT_CHAIN") != NULL) {
>> +            const char *pem;
>> +            char *cbuf;
>> +            size_t plen;
>> +            pem = tls_peer_cert_chain_pem(cre->tls, &plen);
>> +            cbuf = malloc(plen);
>> +            sprintf(cbuf, "%.*s", (int)plen - 1, pem);
>> +            if (expand_string(buf, len,
>> +                "$CLIENT_CERT_CHAIN", url_encode(cbuf)) != 0) {
>> +                    free(cbuf);
>> +                return (NULL);
>> +            } else
>> +                free(cbuf);
>> +        }
>> +    }
>>   return (buf);
>> }
>> @@ -2045,3 +2071,27 @@ server_root_strip(char *path, int n)
>>   return (path);
>> }
>> +char *
>> +url_encode(const char *src)
>> +{
>> +    static char     hex[] = "0123456789ABCDEF";
>> +    char        *dp, *dst;
>> +    unsigned char     c;
>> +
>> +    /* We need 3 times the memory if every letter is encoded. */
>> +    if ((dst = calloc(3, strlen(src) + 1)) == NULL)
>> +        return (NULL);
>> +
>> +    for (dp = dst; *src != 0; src++) {
>> +        c = (unsigned char) *src;
>> +        if (c == ' ' || c == '#' || c == '%' || c == '?' || c == '"' ||
>> +            c == '&' || c == '<' || c <= 0x1f || c >= 0x7f) {
>> +            *dp++ = '%';
>> +            *dp++ = '%';
>> +            *dp++ = hex[c >> 4];
>> +            *dp++ = hex[c & 0x0f];
>> +        } else
>> +            *dp++ = *src;
>> +    }
>> +    return (dst);
>> +}
>> blob - 54e26e646fae5804e66d2d3cfeba68e06914ab2b
>> blob + cd99c21d7cdaf9fc5fdc33e5a0ad886afaa9b889
>> --- usr.sbin/relayd/relayd.c
>> +++ usr.sbin/relayd/relayd.c
>> @@ -1360,6 +1360,15 @@ relay_load_certfiles(struct relayd *env, struct relay
>>   if ((rlay->rl_conf.flags & F_TLS) == 0)
>>       return (0);
>> +    if (strlen(proto->tlsclientca) &&
>> +        rlay->rl_tls_client_ca_fd == -1) {
>> +        if ((rlay->rl_tls_client_ca_fd =
>> +            open(proto->tlsclientca, O_RDONLY)) == -1)
>> +            return (-1);
>> +        log_debug("%s: using client ca %s", __func__,
>> +            proto->tlsclientca);
>> +    }
>> +
>>   if (name == NULL &&
>>       print_host(&rlay->rl_conf.ss, hbuf, sizeof(hbuf)) == NULL)
>>       goto fail;
>> blob - cecbae71f87e603b3e30d4c0114bf1c60a82b52a
>> blob + cfb7a314811730723449a5109d500014711db3ae
>> --- usr.sbin/relayd/relayd.conf.5
>> +++ usr.sbin/relayd/relayd.conf.5
>> @@ -948,6 +948,13 @@ will be used (strong crypto cipher suites without anon
>> See the CIPHERS section of
>> .Xr openssl 1
>> for information about SSL/TLS cipher suites and preference lists.
>> +.It Ic client ca Ar path Op optional
>> +Require TLS client certificates whose authenticity can be verified
>> +against the CA certificate(s) in the specified file in order to
>> +proceed beyond the TLS handshake.
>> +If the
>> +.Ic optional
>> +keyword is present, the certificate is verified only if presented.
>> .It Ic client-renegotiation
>> Allow client-initiated renegotiation.
>> To mitigate a potential DoS risk,
>> @@ -1361,6 +1368,12 @@ The value string may contain predefined macros that wi
>> at runtime:
>> .Pp
>> .Bl -tag -width $SERVER_ADDR -offset indent -compact
>> +.It Ic $CLIENT_CERT_CHAIN
>> +The certificate chain of the client certificate.
>> +.It Ic $CLIENT_CERT_ISSUER
>> +The issuer of the client certificate.
>> +.It Ic $CLIENT_CERT_SUBJECT
>> +The subject of the client certificate.
>> .It Ic $HOST
>> The Host header's value of the relay.
>> .It Ic $REMOTE_ADDR
>> blob - 2236d140f7e6b9477bac401cbcdd559db171680b
>> blob + 2a1166599bfd57b0682c4d4bacd15d340ff9b5ad
>> --- usr.sbin/relayd/relayd.h
>> +++ usr.sbin/relayd/relayd.h
>> @@ -139,11 +139,12 @@ struct ctl_relaytable {
>> };
>> enum fd_type {
>> -    RELAY_FD_CERT    = 1,
>> -    RELAY_FD_CACERT    = 2,
>> -    RELAY_FD_CAFILE    = 3,
>> -    RELAY_FD_KEY    = 4,
>> -    RELAY_FD_OCSP    = 5
>> +    RELAY_FD_CERT        = 1,
>> +    RELAY_FD_CACERT        = 2,
>> +    RELAY_FD_CAFILE        = 3,
>> +    RELAY_FD_KEY        = 4,
>> +    RELAY_FD_OCSP        = 5,
>> +    RELAY_FD_CLIENTCACERT    = 6
>> };
>> struct ctl_relayfd {
>> @@ -403,6 +404,7 @@ union hashkey {
>> #define F_TLSINSPECT        0x04000000
>> #define F_HASHKEY        0x08000000
>> #define F_AGENTX_TRAPONLY    0x10000000
>> +#define F_TLSVERIFY        0x20000000
>> #define F_BITS                                \
>>   "\10\01DISABLE\02BACKUP\03USED\04DOWN\05ADD\06DEL\07CHANGED"    \
>> @@ -703,6 +705,7 @@ TAILQ_HEAD(relay_rules, relay_rule);
>> #define TLSFLAG_VERSION                0x1f
>> #define TLSFLAG_CIPHER_SERVER_PREF        0x20
>> #define TLSFLAG_CLIENT_RENEG            0x40
>> +#define    TLSFLAG_CLIENT_OPTIONAL            0x80
>> #define TLSFLAG_DEFAULT                \
>>   (TLSFLAG_TLSV1_2|TLSFLAG_TLSV1_3|TLSFLAG_CIPHER_SERVER_PREF)
>> @@ -746,6 +749,7 @@ struct protocol {
>>   char             tlscacert[PATH_MAX];
>>   char             tlscakey[PATH_MAX];
>>   char            *tlscapass;
>> +    char             tlsclientca[PATH_MAX];
>>   struct keynamelist     tlscerts;
>>   char             name[MAX_NAME_SIZE];
>>   int             tickets;
>> @@ -835,6 +839,7 @@ struct relay {
>>     int             rl_tls_ca_fd;
>>   int             rl_tls_cacert_fd;
>> +    int             rl_tls_client_ca_fd;
>>   EVP_PKEY        *rl_tls_pkey;
>>   X509            *rl_tls_cacertx509;
>>   char            *rl_tls_cakey;
>> 

Reply via email to