Commit 7c478d55 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

gnutls: add parameter for Application Layer Protocol Negotiation

parent 15427d0c
...@@ -62,8 +62,10 @@ struct vlc_tls_creds ...@@ -62,8 +62,10 @@ struct vlc_tls_creds
module_t *module; module_t *module;
void *sys; void *sys;
int (*open) (vlc_tls_creds_t *, vlc_tls_t *, int fd, const char *host); int (*open) (vlc_tls_creds_t *, vlc_tls_t *, int fd, const char *host,
int (*handshake) (vlc_tls_t *, const char *host, const char *service); const char *const *alpn);
int (*handshake) (vlc_tls_t *, const char *host, const char *service,
char **restrict alp);
void (*close) (vlc_tls_t *); void (*close) (vlc_tls_t *);
}; };
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
# include "config.h" # include "config.h"
#endif #endif
#include <stdlib.h>
#include <string.h>
#include <time.h> #include <time.h>
#include <errno.h> #include <errno.h>
#include <assert.h> #include <assert.h>
...@@ -161,7 +163,8 @@ static int gnutls_Recv (void *opaque, void *buf, size_t length) ...@@ -161,7 +163,8 @@ static int gnutls_Recv (void *opaque, void *buf, size_t length)
} }
static int gnutls_SessionOpen (vlc_tls_t *tls, int type, static int gnutls_SessionOpen (vlc_tls_t *tls, int type,
gnutls_certificate_credentials_t x509, int fd) gnutls_certificate_credentials_t x509, int fd,
const char *const *alpn)
{ {
gnutls_session_t session; gnutls_session_t session;
const char *errp; const char *errp;
...@@ -195,6 +198,31 @@ static int gnutls_SessionOpen (vlc_tls_t *tls, int type, ...@@ -195,6 +198,31 @@ static int gnutls_SessionOpen (vlc_tls_t *tls, int type,
goto error; goto error;
} }
if (alpn != NULL)
{
gnutls_datum_t *protv = NULL;
unsigned protc = 0;
while (*alpn != NULL)
{
gnutls_datum_t *n = realloc(protv, sizeof (*protv) * (protc + 1));
if (unlikely(n == NULL))
{
free(protv);
goto error;
}
protv = n;
protv[protc].data = (void *)*alpn;
protv[protc].size = strlen(*alpn);
protc++;
alpn++;
}
val = gnutls_alpn_set_protocols (session, protv, protc, 0);
free (protv);
}
gnutls_transport_set_int (session, fd); gnutls_transport_set_int (session, fd);
tls->sys = session; tls->sys = session;
...@@ -215,7 +243,7 @@ error: ...@@ -215,7 +243,7 @@ error:
* 1 if more would-be blocking recv is needed, * 1 if more would-be blocking recv is needed,
* 2 if more would-be blocking send is required. * 2 if more would-be blocking send is required.
*/ */
static int gnutls_ContinueHandshake (vlc_tls_t *tls) static int gnutls_ContinueHandshake (vlc_tls_t *tls, char **restrict alp)
{ {
gnutls_session_t session = tls->sys; gnutls_session_t session = tls->sys;
int val; int val;
...@@ -231,7 +259,7 @@ static int gnutls_ContinueHandshake (vlc_tls_t *tls) ...@@ -231,7 +259,7 @@ static int gnutls_ContinueHandshake (vlc_tls_t *tls)
switch (val) switch (val)
{ {
case GNUTLS_E_SUCCESS: case GNUTLS_E_SUCCESS:
return 0; goto done;
case GNUTLS_E_AGAIN: case GNUTLS_E_AGAIN:
case GNUTLS_E_INTERRUPTED: case GNUTLS_E_INTERRUPTED:
/* I/O event: return to caller's poll() loop */ /* I/O event: return to caller's poll() loop */
...@@ -245,6 +273,26 @@ static int gnutls_ContinueHandshake (vlc_tls_t *tls) ...@@ -245,6 +273,26 @@ static int gnutls_ContinueHandshake (vlc_tls_t *tls)
#endif #endif
msg_Err (tls, "TLS handshake error: %s", gnutls_strerror (val)); msg_Err (tls, "TLS handshake error: %s", gnutls_strerror (val));
return -1; return -1;
done:
if (alp != NULL)
{
gnutls_datum_t datum;
val = gnutls_alpn_get_selected_protocol (session, &datum);
if (val == 0)
{
if (memchr (datum.data, 0, datum.size) != NULL)
return -1; /* Other end is doing something fishy?! */
*alp = strndup ((char *)datum.data, datum.size);
if (unlikely(*alp == NULL))
return -1;
}
else
*alp = NULL;
}
return 0;
} }
/** /**
...@@ -262,9 +310,10 @@ static void gnutls_SessionClose (vlc_tls_t *tls) ...@@ -262,9 +310,10 @@ static void gnutls_SessionClose (vlc_tls_t *tls)
} }
static int gnutls_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *tls, static int gnutls_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *tls,
int fd, const char *hostname) int fd, const char *hostname,
const char *const *alpn)
{ {
int val = gnutls_SessionOpen (tls, GNUTLS_CLIENT, crd->sys, fd); int val = gnutls_SessionOpen (tls, GNUTLS_CLIENT, crd->sys, fd, alpn);
if (val != VLC_SUCCESS) if (val != VLC_SUCCESS)
return val; return val;
...@@ -282,9 +331,9 @@ static int gnutls_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *tls, ...@@ -282,9 +331,9 @@ static int gnutls_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *tls,
} }
static int gnutls_ClientHandshake (vlc_tls_t *tls, const char *host, static int gnutls_ClientHandshake (vlc_tls_t *tls, const char *host,
const char *service) const char *service, char **restrict alp)
{ {
int val = gnutls_ContinueHandshake (tls); int val = gnutls_ContinueHandshake (tls, alp);
if (val) if (val)
return val; return val;
...@@ -468,18 +517,19 @@ typedef struct vlc_tls_creds_sys ...@@ -468,18 +517,19 @@ typedef struct vlc_tls_creds_sys
* Initializes a server-side TLS session. * Initializes a server-side TLS session.
*/ */
static int gnutls_ServerSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *tls, static int gnutls_ServerSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *tls,
int fd, const char *hostname) int fd, const char *hostname,
const char *const *alpn)
{ {
vlc_tls_creds_sys_t *sys = crd->sys; vlc_tls_creds_sys_t *sys = crd->sys;
assert (hostname == NULL); assert (hostname == NULL);
return gnutls_SessionOpen (tls, GNUTLS_SERVER, sys->x509_cred, fd); return gnutls_SessionOpen (tls, GNUTLS_SERVER, sys->x509_cred, fd, alpn);
} }
static int gnutls_ServerHandshake (vlc_tls_t *tls, const char *host, static int gnutls_ServerHandshake (vlc_tls_t *tls, const char *host,
const char *service) const char *service, char **restrict alp)
{ {
int val = gnutls_ContinueHandshake (tls); int val = gnutls_ContinueHandshake (tls, alp);
if (val == 0) if (val == 0)
tls->sock.p_sys = tls; tls->sock.p_sys = tls;
......
...@@ -150,7 +150,7 @@ vlc_tls_t *vlc_tls_SessionCreate (vlc_tls_creds_t *crd, int fd, ...@@ -150,7 +150,7 @@ vlc_tls_t *vlc_tls_SessionCreate (vlc_tls_creds_t *crd, int fd,
{ {
vlc_tls_t *session = vlc_custom_create (crd, sizeof (*session), vlc_tls_t *session = vlc_custom_create (crd, sizeof (*session),
"tls session"); "tls session");
int val = crd->open (crd, session, fd, host); int val = crd->open (crd, session, fd, host, NULL);
if (val == VLC_SUCCESS) if (val == VLC_SUCCESS)
return session; return session;
vlc_object_release (session); vlc_object_release (session);
...@@ -162,7 +162,7 @@ int vlc_tls_SessionHandshake (vlc_tls_t *session, const char *host, ...@@ -162,7 +162,7 @@ int vlc_tls_SessionHandshake (vlc_tls_t *session, const char *host,
{ {
vlc_tls_creds_t *crd = (vlc_tls_creds_t *)(session->p_parent); vlc_tls_creds_t *crd = (vlc_tls_creds_t *)(session->p_parent);
return crd->handshake (session, host, service); return crd->handshake (session, host, service, NULL);
} }
void vlc_tls_SessionDelete (vlc_tls_t *session) void vlc_tls_SessionDelete (vlc_tls_t *session)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment