- Added openssl application-layer protocol negotiation

- Added restconf tests for http/1.0 and http/2
This commit is contained in:
Olof hagsand 2021-05-09 17:55:13 +02:00
parent ad08200931
commit 6ed2202638
2 changed files with 96 additions and 2 deletions

View file

@ -188,6 +188,8 @@
/* Forward */
static int restconf_connection(int s, void* arg);
static int session_id_context = 1;
/*! Get restconf native handle
* @param[in] h Clicon handle
* @retval rh Restconf native handle
@ -740,7 +742,66 @@ restconf_verify_certs(int preverify_ok,
return preverify_ok;
}
static int session_id_context = 1;
/*! Debug print of all incoming alpn alternatives, eg h2 and http/1.1
*/
static int
dump_alpn_proto_list(const unsigned char *in,
unsigned int inlen)
{
unsigned char *inp;
unsigned char len;
char *str;
inp = (unsigned char*)in;
while ((len = *inp) != 0) {
inp++;
if ((str = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
return -1;
}
strncpy(str, (const char*)inp, len);
str[len] = '\0';
clicon_debug(1, "%s %s", __FUNCTION__, str);
free(str);
inp += len;
}
return 0;
}
/*! Application-layer Protocol Negotiation (alpn) callback
* The value of the out, outlen vector should be set to the value of a single protocol selected from
* the in, inlen vector. The out buffer may point directly into in, or to a buffer that outlives the
* handshake.
*/
static int
alpn_select_proto_cb(SSL *ssl,
const unsigned char **out,
unsigned char *outlen,
const unsigned char *in,
unsigned int inlen,
void *arg)
{
unsigned char *inp;
unsigned char len;
if (clicon_debug_get())
dump_alpn_proto_list(in, inlen);
inp = (unsigned char*)in;
/* select http/1.1 */
inp = (unsigned char*)in;
while ((len = *inp) != 0) {
inp++;
if (len == 8 && strncmp((char*)inp, "http/1.1", len) == 0)
// if (len == 2 && strncmp((char*)inp, "h2", len) == 0)
break;
inp += len;
}
if (len == 0)
return SSL_TLSEXT_ERR_NOACK;
*outlen = len;
*out = inp;
return SSL_TLSEXT_ERR_OK;
}
/*
* see restconf_config ->cv_evhtp_init(x2) -> cx_evhtp_socket ->
@ -768,6 +829,8 @@ restconf_ssl_context_create(clicon_handle h)
SSL_CTX_set_options(ctx, SSL_MODE_RELEASE_BUFFERS | SSL_OP_NO_COMPRESSION);
// SSL_CTX_set_timeout(ctx, cfg->ssl_ctx_timeout); /* default 300s */
/* Application Layer Protocol Negotiation (alpn) callback */
SSL_CTX_set_alpn_select_cb(ctx, alpn_select_proto_cb, h);
done:
return ctx;
}
@ -1145,6 +1208,8 @@ restconf_accept_client(int fd,
int er;
int readmore;
X509 *peercert;
const unsigned char *alpn = NULL;
unsigned int alpnlen = 0;
clicon_debug(1, "%s %d", __FUNCTION__, fd);
if (rsock == NULL){
@ -1293,6 +1358,17 @@ restconf_accept_client(int fd,
goto ok;
}
}
/* Sets data and len to point to the client's requested protocol for this connection. */
SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
if (alpn == NULL) {
/* Returns a pointer to the selected protocol in data with length len. */
SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
}
clicon_debug(1, "%s ALPN: %d %s", __FUNCTION__, alpnlen, alpn);
if (alpn == NULL || alpnlen != 8 || memcmp("http/1.1", alpn, 8) != 0) {
clicon_err(OE_RESTCONF, 0, "Protocol http/1.1 not selected: %s", alpn);
goto done;
}
/* Get the actual peer, XXX this maybe could be done in ca-auth client-cert code ?
* Note this _only_ works if SSL_set1_host() was set previously,...
*/

View file

@ -166,6 +166,24 @@ function testrun()
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
if [ "${WITH_RESTCONF}" = "native" ]; then # XXX does not work with nginx
new "restconf GET http/1.0 - returns 1.0"
expectpart "$(curl $CURLOPTS --http1.0 -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.0 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
fi
new "restconf GET http/1.1"
expectpart "$(curl $CURLOPTS --http1.1 -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
new "restconf GET http/2"
expectpart "$(curl $CURLOPTS --http2 -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
if [ $proto = http ]; then # see (2) https to http port in restconf_main_native.c
new "restconf GET http/2 prior-knowledge (http)"
expectpart "$(curl $CURLOPTS --http2-prior-knowledge -X GET $proto://$addr/.well-known/host-meta 2>&1)" "16 55" # "Error in the HTTP2 framing layer" "Connection reset by peer"
else
new "restconf GET http/2 prior-knowledge (https)"
expectpart "$(curl $CURLOPTS --http2-prior-knowledge -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
fi
# Negative test GET datastore
if [ $proto = http ]; then # see (2) https to http port in restconf_main_native.c
new "Wrong proto=https on http port, expect err 35 wrong version number"