]> git.sesse.net Git - vlc/blobdiff - modules/misc/gnutls.c
* English grammar fixes and beautifications for the intf_UserFatal messages
[vlc] / modules / misc / gnutls.c
index 8af649f355649e890c40e801bb7cdbd345379574..d5677b268dd33fdf6d8eaac2cd3e128fce3dc3db 100644 (file)
 #include <gnutls/gnutls.h>
 #include <gnutls/x509.h>
 
-#define DH_BITS           1024
 #define CACHE_TIMEOUT     3600
 #define CACHE_SIZE          64
 
+#include "dhparams.h"
+
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
@@ -61,12 +62,6 @@ static void CloseClient (vlc_object_t *);
 static int  OpenServer  (vlc_object_t *);
 static void CloseServer (vlc_object_t *);
 
-#define DH_BITS_TEXT N_("Diffie-Hellman prime bits")
-#define DH_BITS_LONGTEXT N_( \
-    "This allows you to modify the Diffie-Hellman prime's number of bits, " \
-    "used for TLS or SSL-based server-side encryption. This is generally " \
-    "not needed." )
-
 #define CACHE_TIMEOUT_TEXT N_("Expiration time for resumed TLS sessions")
 #define CACHE_TIMEOUT_LONGTEXT N_( \
     "It is possible to cache the resumed TLS sessions. This is the expiration "\
@@ -77,11 +72,6 @@ static void CloseServer (vlc_object_t *);
     "This is the maximum number of resumed TLS sessions that " \
     "the cache will hold." )
 
-#define CHECK_CERT_TEXT N_("Check TLS/SSL server certificate validity")
-#define CHECK_CERT_LONGTEXT N_( \
-    "This ensures that the server certificate is valid " \
-    "(i.e. signed by an approved Certification Authority)." )
-
 vlc_module_begin();
     set_shortname( "GnuTLS" );
     set_description( _("GnuTLS transport layer security") );
@@ -90,8 +80,7 @@ vlc_module_begin();
     set_category( CAT_ADVANCED );
     set_subcategory( SUBCAT_ADVANCED_MISC );
 
-    add_bool( "tls-check-cert", VLC_TRUE, NULL, CHECK_CERT_TEXT,
-              CHECK_CERT_LONGTEXT, VLC_FALSE );
+    add_obsolete_bool( "tls-check-cert" );
     add_obsolete_bool( "tls-check-hostname" );
 
     add_submodule();
@@ -101,8 +90,7 @@ vlc_module_begin();
         set_subcategory( SUBCAT_ADVANCED_MISC );
         set_callbacks( OpenServer, CloseServer );
 
-        add_integer( "gnutls-dh-bits", DH_BITS, NULL, DH_BITS_TEXT,
-                    DH_BITS_LONGTEXT, VLC_TRUE );
+        add_obsolete_integer( "gnutls-dh-bits" );
         add_integer( "gnutls-cache-timeout", CACHE_TIMEOUT, NULL,
                     CACHE_TIMEOUT_TEXT, CACHE_TIMEOUT_LONGTEXT, VLC_TRUE );
         add_integer( "gnutls-cache-size", CACHE_SIZE, NULL, CACHE_SIZE_TEXT,
@@ -178,8 +166,7 @@ static int gnutls_Init (vlc_object_t *p_this)
 {
     int ret = VLC_EGENERIC;
 
-    vlc_mutex_t *lock = var_GetGlobalMutex ("gnutls_mutex");
-    vlc_mutex_lock (lock);
+    vlc_mutex_t *lock = var_AcquireMutex ("gnutls_mutex");
 
     /* This should probably be removed/fixed. It will screw up with multiple
      * LibVLC instances. */
@@ -216,8 +203,7 @@ error:
  */
 static void gnutls_Deinit (vlc_object_t *p_this)
 {
-    vlc_mutex_t *lock = var_GetGlobalMutex( "gnutls_mutex" );
-    vlc_mutex_lock (lock);
+    vlc_mutex_t *lock = var_AcquireMutex( "gnutls_mutex" );
 
     gnutls_global_deinit ();
     msg_Dbg (p_this, "GnuTLS deinitialized");
@@ -262,9 +248,9 @@ static int gnutls_Error (vlc_object_t *obj, int val)
 
 struct tls_session_sys_t
 {
-    gnutls_session  session;
-    char                          *psz_hostname;
-    vlc_bool_t      b_handshaked;
+    gnutls_session_t session;
+    char            *psz_hostname;
+    vlc_bool_t       b_handshaked;
 };
 
 
@@ -301,9 +287,11 @@ gnutls_Recv( void *p_session, void *buf, int i_length )
 
 
 /**
- * @return -1 on error (you need not and must not call tls_SessionClose())
- * 0 on succesful handshake completion, 1 if more would-be blocking recv is
- * needed, 2 if more would-be blocking send is required.
+ * Starts or continues the TLS handshake.
+ *
+ * @return -1 on fatal error, 0 on succesful handshake completion,
+ * 1 if more would-be blocking recv is needed,
+ * 2 if more would-be blocking send is required.
  */
 static int
 gnutls_ContinueHandshake (tls_session_t *p_session)
@@ -325,7 +313,6 @@ gnutls_ContinueHandshake (tls_session_t *p_session)
 #endif
         msg_Err( p_session, "TLS handshake error: %s",
                  gnutls_strerror( val ) );
-        p_session->pf_close( p_session );
         return -1;
     }
 
@@ -373,7 +360,7 @@ gnutls_HandshakeAndValidate( tls_session_t *session )
     {
         msg_Err( session, "Certificate verification failed: %s",
                  gnutls_strerror( val ) );
-        goto error;
+        return -1;
     }
 
     if( status )
@@ -392,24 +379,24 @@ gnutls_HandshakeAndValidate( tls_session_t *session )
             msg_Err( session,
                      "unknown certificate error (you found a bug in VLC)" );
 
-        goto error;
+        return -1;
     }
 
     /* certificate (host)name verification */
-    const gnutls_datum *data = gnutls_certificate_get_peers( p_sys->session,
-                                                             &(unsigned){0} );
+    const gnutls_datum_t *data;
+    data = gnutls_certificate_get_peers (p_sys->session, &(unsigned){0});
     if( data == NULL )
     {
         msg_Err( session, "Peer certificate not available" );
-        goto error;
+        return -1;
     }
 
-    gnutls_x509_crt cert;
+    gnutls_x509_crt_t cert;
     val = gnutls_x509_crt_init( &cert );
     if( val )
     {
         msg_Err( session, "x509 fatal error: %s", gnutls_strerror( val ) );
-        goto error;
+        return -1;
     }
 
     val = gnutls_x509_crt_import( cert, data, GNUTLS_X509_FMT_DER );
@@ -417,61 +404,48 @@ gnutls_HandshakeAndValidate( tls_session_t *session )
     {
         msg_Err( session, "Certificate import error: %s",
                  gnutls_strerror( val ) );
-        goto crt_error;
+        goto error;
     }
 
-    if( p_sys->psz_hostname != NULL )
+    assert( p_sys->psz_hostname != NULL );
+    if ( !gnutls_x509_crt_check_hostname( cert, p_sys->psz_hostname ) )
     {
-        if ( !gnutls_x509_crt_check_hostname( cert, p_sys->psz_hostname ) )
-        {
-            msg_Err( session, "Certificate does not match \"%s\"",
-                     p_sys->psz_hostname );
-            goto crt_error;
-        }
+        msg_Err( session, "Certificate does not match \"%s\"",
+                 p_sys->psz_hostname );
+        goto error;
     }
-    else
-        msg_Warn( session, "Certificate and hostname were not verified" );
 
     if( gnutls_x509_crt_get_expiration_time( cert ) < time( NULL ) )
     {
         msg_Err( session, "Certificate expired" );
-        goto crt_error;
+        goto error;
     }
 
     if( gnutls_x509_crt_get_activation_time( cert ) > time ( NULL ) )
     {
         msg_Err( session, "Certificate not yet valid" );
-        goto crt_error;
+        goto error;
     }
 
     gnutls_x509_crt_deinit( cert );
     msg_Dbg( session, "TLS/x509 certificate verified" );
     return 0;
 
-crt_error:
-    gnutls_x509_crt_deinit( cert );
 error:
-    session->pf_close( session );
+    gnutls_x509_crt_deinit( cert );
     return -1;
 }
 
 /**
- * Starts negociation of a TLS session.
+ * Sets the operating system file descriptor backend for the TLS sesison.
  *
  * @param fd stream socket already connected with the peer.
- *
- * @return -1 on error (you need not and must not call tls_SessionClose),
- * 0 on succesful handshake completion, 1 if more would-be blocking recv is
- * needed, 2 if more would-be blocking send is required.
  */
-static int
-gnutls_BeginHandshake( tls_session_t *p_session, int fd )
+static void
+gnutls_SetFD (tls_session_t *p_session, int fd)
 {
-    tls_session_sys_t *p_sys = p_session->p_sys;
-
-    gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)(intptr_t)fd);
-
-    return p_session->pf_handshake2( p_session );
+    gnutls_transport_set_ptr (p_session->p_sys->session,
+                              (gnutls_transport_ptr_t)(intptr_t)fd);
 }
 
 typedef int (*tls_prio_func) (gnutls_session_t, const int *);
@@ -580,12 +554,12 @@ gnutls_SessionPrioritize (vlc_object_t *obj, gnutls_session_t session)
 
 static int
 gnutls_Addx509File( vlc_object_t *p_this,
-                    gnutls_certificate_credentials cred,
+                    gnutls_certificate_credentials_t cred,
                     const char *psz_path, vlc_bool_t b_priv );
 
 static int
 gnutls_Addx509Directory( vlc_object_t *p_this,
-                         gnutls_certificate_credentials cred,
+                         gnutls_certificate_credentials_t cred,
                          const char *psz_dirname,
                          vlc_bool_t b_priv )
 {
@@ -597,8 +571,16 @@ gnutls_Addx509Directory( vlc_object_t *p_this,
     dir = utf8_opendir( psz_dirname );
     if( dir == NULL )
     {
-        msg_Warn( p_this, "cannot open directory (%s): %m", psz_dirname );
-        return VLC_EGENERIC;
+        if (errno != ENOENT)
+        {
+            msg_Err (p_this, "cannot open directory (%s): %m", psz_dirname);
+            return VLC_EGENERIC;
+        }
+
+        msg_Dbg (p_this, "creating empty certificate directory: %s",
+                 psz_dirname);
+        utf8_mkdir (psz_dirname, b_priv ? 0700 : 0755);
+        return VLC_SUCCESS;
     }
 #ifdef S_ISLNK
     else
@@ -691,8 +673,8 @@ gnutls_Addx509File( vlc_object_t *p_this,
 /** TLS client session data */
 typedef struct tls_client_sys_t
 {
-    struct tls_session_sys_t       session;
-    gnutls_certificate_credentials x509_cred;
+    struct tls_session_sys_t         session;
+    gnutls_certificate_credentials_t x509_cred;
 } tls_client_sys_t;
 
 
@@ -718,18 +700,16 @@ static int OpenClient (vlc_object_t *obj)
     p_session->sock.p_sys = p_session;
     p_session->sock.pf_send = gnutls_Send;
     p_session->sock.pf_recv = gnutls_Recv;
-    p_session->pf_handshake = gnutls_BeginHandshake;
-    p_session->pf_close = NULL;
+    p_session->pf_set_fd = gnutls_SetFD;
 
     p_sys->session.b_handshaked = VLC_FALSE;
-    p_sys->session.psz_hostname = NULL;
 
     const char *homedir = obj->p_libvlc->psz_datadir,
                *datadir = config_GetDataDir ();
     size_t l1 = strlen (homedir), l2 = strlen (datadir);
-    char path[((l1 > l2) ? l1 : l2) + sizeof ("/ssl/private")];
+    char path[((l1 > l2) ? l1 : l2) + sizeof ("/ca-certificates.crt")];
+    //                              > sizeof ("/ssl/private")
     //                              > sizeof ("/ssl/certs")
-    //                              > sizeof ("/ca-certificates.crt")
 
     i_val = gnutls_certificate_allocate_credentials (&p_sys->x509_cred);
     if (i_val != 0)
@@ -739,19 +719,18 @@ static int OpenClient (vlc_object_t *obj)
         goto error;
     }
 
-    if (var_CreateGetBool (obj, "tls-check-cert"))
-    {
-        sprintf (path, "%s/ssl/certs", homedir);
-        gnutls_Addx509Directory (VLC_OBJECT (p_session),
-                                  p_sys->x509_cred, path, VLC_FALSE);
-
-        sprintf (path, "%s/ca-certificates.crt", datadir);
-        gnutls_Addx509File (VLC_OBJECT (p_session),
-                            p_sys->x509_cred, path, VLC_FALSE);
-        p_session->pf_handshake2 = gnutls_HandshakeAndValidate;
-    }
-    else
-        p_session->pf_handshake2 = gnutls_ContinueHandshake;
+    sprintf (path, "%s/ssl", homedir);
+    utf8_mkdir (path, 0755);
+
+    sprintf (path, "%s/ssl/certs", homedir);
+    gnutls_Addx509Directory (VLC_OBJECT (p_session),
+                             p_sys->x509_cred, path, VLC_FALSE);
+
+    sprintf (path, "%s/ca-certificates.crt", datadir);
+    gnutls_Addx509File (VLC_OBJECT (p_session),
+                        p_sys->x509_cred, path, VLC_FALSE);
+    p_session->pf_handshake = gnutls_HandshakeAndValidate;
+    /*p_session->pf_handshake = gnutls_ContinueHandshake;*/
 
     sprintf (path, "%s/ssl/private", homedir);
     gnutls_Addx509Directory (VLC_OBJECT (p_session), p_sys->x509_cred,
@@ -770,6 +749,9 @@ static int OpenClient (vlc_object_t *obj)
                                   p_sys->session.session))
         goto s_error;
 
+    /* minimum DH prime bits */
+    gnutls_dh_set_prime_bits (p_sys->session.session, 1024);
+
     i_val = gnutls_credentials_set (p_sys->session.session,
                                     GNUTLS_CRD_CERTIFICATE,
                                     p_sys->x509_cred);
@@ -781,12 +763,12 @@ static int OpenClient (vlc_object_t *obj)
     }
 
     char *servername = var_GetNonEmptyString (p_session, "tls-server-name");
-    if (servername != NULL )
-    {
-        p_sys->session.psz_hostname = servername;
-        gnutls_server_name_set (p_sys->session.session, GNUTLS_NAME_DNS,
-                                servername, strlen (servername));
-    }
+    if (servername == NULL )
+        msg_Err (p_session, "server name missing for TLS session");
+
+    p_sys->session.psz_hostname = servername;
+    gnutls_server_name_set (p_sys->session.session, GNUTLS_NAME_DNS,
+                            servername, strlen (servername));
 
     return VLC_SUCCESS;
 
@@ -822,15 +804,15 @@ static void CloseClient (vlc_object_t *obj)
  */
 struct tls_server_sys_t
 {
-    gnutls_certificate_credentials  x509_cred;
-    gnutls_dh_params                dh_params;
+    gnutls_certificate_credentials_t x509_cred;
+    gnutls_dh_params_t               dh_params;
 
     struct saved_session_t          *p_cache;
     struct saved_session_t          *p_store;
-    int                             i_cache_size;
-    vlc_mutex_t                     cache_lock;
+    int                              i_cache_size;
+    vlc_mutex_t                      cache_lock;
 
-    int                             (*pf_handshake2)( tls_session_t * );
+    int                            (*pf_handshake) (tls_session_t *);
 };
 
 
@@ -878,7 +860,7 @@ static int cb_store( void *p_server, gnutls_datum key, gnutls_datum data )
 
 static gnutls_datum cb_fetch( void *p_server, gnutls_datum key )
 {
-    static const gnutls_datum err_datum = { NULL, 0 };
+    static const gnutls_datum_t err_datum = { NULL, 0 };
     tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
     saved_session_t *p_session, *p_end;
 
@@ -892,7 +874,7 @@ static gnutls_datum cb_fetch( void *p_server, gnutls_datum key )
         if( ( p_session->i_idlen == key.size )
          && !memcmp( p_session->id, key.data, key.size ) )
         {
-            gnutls_datum data;
+            gnutls_datum_t data;
 
             data.size = p_session->i_datalen;
 
@@ -949,9 +931,10 @@ static int cb_delete( void *p_server, gnutls_datum key )
  * You still have to close the socket yourself.
  */
 static void
-gnutls_SessionClose( tls_session_t *p_session )
+gnutls_SessionClose (tls_server_t *p_server, tls_session_t *p_session)
 {
     tls_session_sys_t *p_sys = p_session->p_sys;
+    (void)p_server;
 
     if( p_sys->b_handshaked == VLC_TRUE )
         gnutls_bye( p_sys->session, GNUTLS_SHUT_WR );
@@ -972,7 +955,7 @@ gnutls_ServerSessionPrepare( tls_server_t *p_server )
 {
     tls_session_t *p_session;
     tls_server_sys_t *p_server_sys;
-    gnutls_session session;
+    gnutls_session_t session;
     int i_val;
 
     p_session = vlc_object_create( p_server, sizeof (struct tls_session_t) );
@@ -986,15 +969,12 @@ gnutls_ServerSessionPrepare( tls_server_t *p_server )
         return NULL;
     }
 
-    vlc_object_attach( p_session, p_server );
-
     p_server_sys = p_server->p_sys;
     p_session->sock.p_sys = p_session;
     p_session->sock.pf_send = gnutls_Send;
     p_session->sock.pf_recv = gnutls_Recv;
-    p_session->pf_handshake = gnutls_BeginHandshake;
-    p_session->pf_handshake2 = p_server_sys->pf_handshake2;
-    p_session->pf_close = gnutls_SessionClose;
+    p_session->pf_set_fd = gnutls_SetFD;
+    p_session->pf_handshake = p_server_sys->pf_handshake;
 
     p_session->p_sys->b_handshaked = VLC_FALSE;
     p_session->p_sys->psz_hostname = NULL;
@@ -1025,14 +1005,11 @@ gnutls_ServerSessionPrepare( tls_server_t *p_server )
         goto error;
     }
 
-    if( p_session->pf_handshake2 == gnutls_HandshakeAndValidate )
-        gnutls_certificate_server_set_request( session, GNUTLS_CERT_REQUIRE );
-
-    i_val = config_GetInt (p_server, "gnutls-dh-bits");
-    gnutls_dh_set_prime_bits (session, i_val);
+    if (p_session->pf_handshake == gnutls_HandshakeAndValidate)
+        gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUIRE);
 
     /* Session resumption support */
-    i_val = config_GetInt (p_server, "gnutls-cache-expiration");
+    i_val = config_GetInt (p_server, "gnutls-cache-timeout");
     gnutls_db_set_cache_expiration (session, i_val);
     gnutls_db_set_retrieve_function( session, cb_fetch );
     gnutls_db_set_remove_function( session, cb_delete );
@@ -1079,7 +1056,7 @@ gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
     msg_Dbg( p_server, " %d trusted CA added (%s)", val, psz_ca_path );
 
     /* enables peer's certificate verification */
-    p_sys->pf_handshake2 = gnutls_HandshakeAndValidate;
+    p_sys->pf_handshake = gnutls_HandshakeAndValidate;
 
     return VLC_SUCCESS;
 }
@@ -1143,12 +1120,13 @@ static int OpenServer (vlc_object_t *obj)
 
     p_sys->p_store = p_sys->p_cache;
     p_server->p_sys = p_sys;
-    p_server->pf_add_CA = gnutls_ServerAddCA;
+    p_server->pf_add_CA  = gnutls_ServerAddCA;
     p_server->pf_add_CRL = gnutls_ServerAddCRL;
-    p_server->pf_session_prepare = gnutls_ServerSessionPrepare;
+    p_server->pf_open    = gnutls_ServerSessionPrepare;
+    p_server->pf_close   = gnutls_SessionClose;
 
     /* No certificate validation by default */
-    p_sys->pf_handshake2 = gnutls_ContinueHandshake;
+    p_sys->pf_handshake  = gnutls_ContinueHandshake;
 
     vlc_mutex_init( p_server, &p_sys->cache_lock );
 
@@ -1168,9 +1146,9 @@ static int OpenServer (vlc_object_t *obj)
     val = gnutls_certificate_set_x509_key_file (p_sys->x509_cred,
                                                 psz_local_cert, psz_local_key,
                                                 GNUTLS_X509_FMT_PEM );
-    LocaleFree (psz_key_path);
+    LocaleFree (psz_local_key);
     free (psz_key_path);
-    LocaleFree (psz_cert_path);
+    LocaleFree (psz_local_cert);
     free (psz_cert_path);
 
     if( val < 0 )
@@ -1182,26 +1160,27 @@ static int OpenServer (vlc_object_t *obj)
     }
 
     /* FIXME:
-     * - regenerate these regularly
      * - support other ciper suites
      */
-    val = gnutls_dh_params_init( &p_sys->dh_params );
-    if( val >= 0 )
+    val = gnutls_dh_params_init (&p_sys->dh_params);
+    if (val >= 0)
     {
-        msg_Dbg( p_server, "computing Diffie Hellman ciphers parameters" );
-        val = gnutls_dh_params_generate2 (p_sys->dh_params,
-                                          config_GetInt (obj, "gnutls-dh-bits"));
+        const gnutls_datum_t data = {
+            .data = (unsigned char *)dh_params,
+            .size = sizeof (dh_params) - 1,
+        };
+
+        val = gnutls_dh_params_import_pkcs3 (p_sys->dh_params, &data,
+                                             GNUTLS_X509_FMT_PEM);
+        if (val == 0)
+            gnutls_certificate_set_dh_params (p_sys->x509_cred,
+                                              p_sys->dh_params);
     }
-    if( val < 0 )
+    if (val < 0)
     {
-        msg_Err( p_server, "cannot initialize DH cipher suites: %s",
-                 gnutls_strerror( val ) );
-        gnutls_certificate_free_credentials( p_sys->x509_cred );
-        goto error;
+        msg_Err (p_server, "cannot initialize DHE cipher suites: %s",
+                 gnutls_strerror (val));
     }
-    msg_Dbg( p_server, "ciphers parameters computed" );
-
-    gnutls_certificate_set_dh_params( p_sys->x509_cred, p_sys->dh_params);
 
     return VLC_SUCCESS;