gnutls.c 34.3 KB
Newer Older
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1
/*****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
2
 * gnutls.c
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
3
 *****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
4
 * Copyright (C) 2004-2006 Rémi Denis-Courmont
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
5
 * $Id$
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
6
 *
7
 * Authors: Rémi Denis-Courmont <rem # videolan.org>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
22
23
24
25
 *****************************************************************************/

/*
 * TODO:
26
 * - fix FIXMEs
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
27
28
 */

zorglub's avatar
zorglub committed
29

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
30
31
32
33
/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
34
#include <errno.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
35
36
#include <vlc/vlc.h>

37
38
39
40
41
42
43
44
45
46
47
48
49
#include <sys/types.h>
#include <errno.h>
#ifdef HAVE_DIRENT_H
# include <dirent.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
# ifdef HAVE_UNISTD_H
#  include <unistd.h>
# endif
#endif


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
50
#include "vlc_tls.h"
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
51
#include "charset.h"
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
52

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
53
#include <gcrypt.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
54
#include <gnutls/gnutls.h>
55
#include <gnutls/x509.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
56

57
58
59
#define DH_BITS             1024
#define CACHE_EXPIRATION    3600
#define CACHE_SIZE          64
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
60
61
62
63
64
65
66
67
68

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

#define DH_BITS_TEXT N_("Diffie-Hellman prime bits")
#define DH_BITS_LONGTEXT N_( \
69
70
71
    "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." )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
72

73
74
#define CACHE_EXPIRATION_TEXT N_("Expiration time for resumed TLS sessions")
#define CACHE_EXPIRATION_LONGTEXT N_( \
75
76
    "It is possible to cache the resumed TLS sessions. This is the expiration "\
    "time of the sessions stored in this cache, in seconds." )
77
78
79

#define CACHE_SIZE_TEXT N_("Number of resumed TLS sessions")
#define CACHE_SIZE_LONGTEXT N_( \
80
    "This is the maximum number of resumed TLS sessions that " \
81
82
    "the cache will hold." )

83
84
#define CHECK_CERT_TEXT N_("Check TLS/SSL server certificate validity")
#define CHECK_CERT_LONGTEXT N_( \
85
86
    "This ensures that the server certificate is valid " \
    "(i.e. signed by an approved Certification Authority)." )
87
88
89

#define CHECK_HOSTNAME_TEXT N_("Check TLS/SSL server hostname in certificate")
#define CHECK_HOSTNAME_LONGTEXT N_( \
90
91
    "This ensures that the server hostname in certificate matches the " \
    "requested host name." )
92

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
93
vlc_module_begin();
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
94
    set_shortname( "GnuTLS" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
95
96
97
    set_description( _("GnuTLS TLS encryption layer") );
    set_capability( "tls", 1 );
    set_callbacks( Open, Close );
zorglub's avatar
zorglub committed
98
99
    set_category( CAT_ADVANCED );
    set_subcategory( SUBCAT_ADVANCED_MISC );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
100

101
    add_bool( "tls-check-cert", VLC_TRUE, NULL, CHECK_CERT_TEXT,
102
              CHECK_CERT_LONGTEXT, VLC_FALSE );
103
    add_bool( "tls-check-hostname", VLC_TRUE, NULL, CHECK_HOSTNAME_TEXT,
104
105
              CHECK_HOSTNAME_LONGTEXT, VLC_FALSE );

106
    add_integer( "gnutls-dh-bits", DH_BITS, NULL, DH_BITS_TEXT,
107
                 DH_BITS_LONGTEXT, VLC_TRUE );
108
    add_integer( "gnutls-cache-expiration", CACHE_EXPIRATION, NULL,
109
                 CACHE_EXPIRATION_TEXT, CACHE_EXPIRATION_LONGTEXT, VLC_TRUE );
110
    add_integer( "gnutls-cache-size", CACHE_SIZE, NULL, CACHE_SIZE_TEXT,
111
                 CACHE_SIZE_LONGTEXT, VLC_TRUE );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
112
113
114
vlc_module_end();


115
116
117
118
119
120
121
122
123
124
125
126
#define MAX_SESSION_ID    32
#define MAX_SESSION_DATA  1024

typedef struct saved_session_t
{
    char id[MAX_SESSION_ID];
    char data[MAX_SESSION_DATA];

    unsigned i_idlen;
    unsigned i_datalen;
} saved_session_t;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
127
128
129

typedef struct tls_server_sys_t
{
130
131
132
133
134
135
136
    gnutls_certificate_credentials  x509_cred;
    gnutls_dh_params                dh_params;

    struct saved_session_t          *p_cache;
    struct saved_session_t          *p_store;
    int                             i_cache_size;
    vlc_mutex_t                     cache_lock;
137
138

    int                             (*pf_handshake2)( tls_session_t * );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
139
140
141
} tls_server_sys_t;


142
143
144
typedef struct tls_session_sys_t
{
    gnutls_session  session;
145
    char            *psz_hostname;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
146
    vlc_bool_t      b_handshaked;
147
148
149
} tls_session_sys_t;


150
151
typedef struct tls_client_sys_t
{
152
    struct tls_session_sys_t       session;
153
154
155
156
    gnutls_certificate_credentials x509_cred;
} tls_client_sys_t;


157
static int
158
_get_Int( vlc_object_t *p_this, const char *var )
159
160
161
162
163
164
165
166
167
168
169
170
{
    vlc_value_t value;

    if( var_Get( p_this, var, &value ) != VLC_SUCCESS )
    {
        var_Create( p_this, var, VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
        var_Get( p_this, var, &value );
    }

    return value.i_int;
}

171
172
173
174
175
176
177
178
179
180
181
182
183
184
static int
_get_Bool( vlc_object_t *p_this, const char *var )
{
    vlc_value_t value;

    if( var_Get( p_this, var, &value ) != VLC_SUCCESS )
    {
        var_Create( p_this, var, VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
        var_Get( p_this, var, &value );
    }

    return value.b_bool;
}

185
#define get_Int( a, b ) _get_Int( (vlc_object_t *)(a), (b) )
186
#define get_Bool( a, b ) _get_Bool( (vlc_object_t *)(a), (b) )
187
188


189
/**
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
190
 * Sends data through a TLS session.
191
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
192
static int
193
gnutls_Send( void *p_session, const void *buf, int i_length )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
194
195
{
    int val;
196
    tls_session_sys_t *p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
197

198
199
200
201
    p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);

    val = gnutls_record_send( p_sys->session, buf, i_length );
    /* TODO: handle fatal error */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
202
203
204
205
    return val < 0 ? -1 : val;
}


206
/**
207
 * Receives data through a TLS session.
208
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
209
static int
210
gnutls_Recv( void *p_session, void *buf, int i_length )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
211
212
{
    int val;
213
214
215
    tls_session_sys_t *p_sys;

    p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
216

217
218
    val = gnutls_record_recv( p_sys->session, buf, i_length );
    /* TODO: handle fatal error */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
219
220
221
222
223
    return val < 0 ? -1 : val;
}


/*****************************************************************************
224
 * tls_Session(Continue)?Handshake:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
225
 *****************************************************************************
226
 * Establishes TLS session with a peer through socket <fd>.
227
228
229
 * Returns -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.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
230
 *****************************************************************************/
231
static int
232
gnutls_ContinueHandshake( tls_session_t *p_session)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
233
{
234
    tls_session_sys_t *p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
235
236
    int val;

237
238
    p_sys = (tls_session_sys_t *)(p_session->p_sys);

239
     /* TODO: handle fatal error */
240
241
242
#ifdef WIN32
    WSASetLastError( 0 );
#endif
243
244
245
    val = gnutls_handshake( p_sys->session );
    if( ( val == GNUTLS_E_AGAIN ) || ( val == GNUTLS_E_INTERRUPTED ) )
        return 1 + gnutls_record_get_direction( p_sys->session );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
246
247
248

    if( val < 0 )
    {
249
250
251
#ifdef WIN32
        msg_Dbg( p_session, "Winsock error %d", WSAGetLastError( ) );
#endif
252
        msg_Err( p_session, "TLS handshake failed: %s",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
253
                 gnutls_strerror( val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
254
        p_session->pf_close( p_session );
255
        return -1;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
256
    }
257

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
258
    p_sys->b_handshaked = VLC_TRUE;
259
260
261
    return 0;
}

262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
static int
gnutls_VerifyHostname( vlc_object_t *p_this, gnutls_session session,
                       const char *psz_hostname )
{
    const gnutls_datum *p_data;
    gnutls_x509_crt cert;
    unsigned status;
    int val;

    /* certificate (host)name verification */
    p_data = gnutls_certificate_get_peers( session, &status );
    if( p_data == NULL )
    {
        msg_Err( p_this, "TLS peer certificate not available" );
        return -1;
    }

    val = gnutls_x509_crt_init( &cert );
    if( val )
    {
        msg_Err( p_this, "x509 fatal error: %s", gnutls_strerror( val ) );
        return -1;
    }

    val = gnutls_x509_crt_import( cert, p_data, GNUTLS_X509_FMT_DER );
    if( val )
    {
        msg_Err( p_this, "x509 certificate import error: %s",
                gnutls_strerror( val ) );
        gnutls_x509_crt_deinit( cert );
        return -1;
    }

    if( gnutls_x509_crt_check_hostname( cert, psz_hostname ) == 0 )
    {
        msg_Err( p_this, "x509 certificate does not match \"%s\"",
                psz_hostname );
        gnutls_x509_crt_deinit( cert );
        return -1;
    }

    gnutls_x509_crt_deinit( cert );
    msg_Dbg( p_this, "x509 hostname matches %s", psz_hostname );
    return 0;
}

308
static int
309
310
311
312
313
314
315
gnutls_HandshakeAndValidate( tls_session_t *p_session )
{
    int val;

    val = gnutls_ContinueHandshake( p_session );
    if( val == 0 )
    {
316
        unsigned status;
317
        tls_session_sys_t *p_sys;
318

319
320
321
        p_sys = (tls_session_sys_t *)(p_session->p_sys);
        /* certificates chain verification */
        val = gnutls_certificate_verify_peers2( p_sys->session, &status );
322
323
324

        if( val )
        {
325
            msg_Err( p_session, "TLS certificate verification failed: %s",
326
327
328
329
330
331
332
                     gnutls_strerror( val ) );
            p_session->pf_close( p_session );
            return -1;
        }

        if( status )
        {
333
            msg_Err( p_session, "TLS session: access denied" );
334
            if( status & GNUTLS_CERT_INVALID )
335
                msg_Warn( p_session, "certificate could not be verified" );
336
            if( status & GNUTLS_CERT_REVOKED )
337
                msg_Warn( p_session, "certificate was revoked" );
338
            if( status & GNUTLS_CERT_SIGNER_NOT_FOUND )
339
                msg_Warn( p_session, "certificate's signer was not found" );
340
            if( status & GNUTLS_CERT_SIGNER_NOT_CA )
341
342
343
                msg_Warn( p_session, "certificate's signer is not a CA" );
            if( status & GNUTLS_CERT_INSECURE_ALGORITHM )
                msg_Warn( p_session, "insecure certificate signature algorithm" );
344
345
346
347
348
            p_session->pf_close( p_session );
            return -1;
        }

        msg_Dbg( p_session, "TLS certificate verified" );
349
350
351
        if( ( p_sys->psz_hostname != NULL )
         && gnutls_VerifyHostname( (vlc_object_t *)p_session, p_sys->session,
                                   p_sys->psz_hostname ) )
352
353
354
355
        {
            p_session->pf_close( p_session );
            return -1;
        }
356
357
358
359
360
361
        return 0;
    }

    return val;
}

362
363
364
365
366
367
368
369
370
371
/**
 * Starts negociation of a TLS session.
 *
 * @param fd stream socket already connected with the peer.
 * @param psz_hostname if not NULL, hostname to mention as a Server Name.
 *
 * @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.
 */
372
373
static int
gnutls_BeginHandshake( tls_session_t *p_session, int fd,
374
                       const char *psz_hostname )
375
376
377
378
379
{
    tls_session_sys_t *p_sys;

    p_sys = (tls_session_sys_t *)(p_session->p_sys);

380
    gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)(intptr_t)fd);
381

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
382
    if( psz_hostname != NULL )
383
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
384
385
        gnutls_server_name_set( p_sys->session, GNUTLS_NAME_DNS, psz_hostname,
                                strlen( psz_hostname ) );
386
387
388
389
390
391
392
393
394
395
        if( get_Bool( p_session, "tls-check-hostname" ) )
        {
            p_sys->psz_hostname = strdup( psz_hostname );
            if( p_sys->psz_hostname == NULL )
            {
                p_session->pf_close( p_session );
                return -1;
            }
        }
    }
396

397
    return p_session->pf_handshake2( p_session );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
398
399
}

400
/**
401
 * Terminates TLS session and releases session data.
402
403
 * You still have to close the socket yourself.
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
404
405
406
static void
gnutls_SessionClose( tls_session_t *p_session )
{
407
    tls_session_sys_t *p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
408

409
    p_sys = (tls_session_sys_t *)(p_session->p_sys);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
410

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
411
412
    if( p_sys->b_handshaked == VLC_TRUE )
        gnutls_bye( p_sys->session, GNUTLS_SHUT_WR );
413
    gnutls_deinit( p_sys->session );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
414

415
416
417
    if( p_sys->psz_hostname != NULL )
        free( p_sys->psz_hostname );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
418
419
420
    vlc_object_detach( p_session );
    vlc_object_destroy( p_session );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
421
    free( p_sys );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
422
423
424
425
426
427
}

static void
gnutls_ClientDelete( tls_session_t *p_session )
{
    /* On the client-side, credentials are re-allocated per session */
428
429
430
    gnutls_certificate_credentials x509_cred =
                        ((tls_client_sys_t *)(p_session->p_sys))->x509_cred;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
431
    gnutls_SessionClose( p_session );
432
433
434

    /* credentials must be free'd *after* gnutls_deinit() */
    gnutls_certificate_free_credentials( x509_cred );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
435
436
}

437
438
439
440
441
442

static int
gnutls_Addx509File( vlc_object_t *p_this,
                    gnutls_certificate_credentials cred,
                    const char *psz_path, vlc_bool_t b_priv );

443
static int
444
445
446
gnutls_Addx509Directory( vlc_object_t *p_this,
                         gnutls_certificate_credentials cred,
                         const char *psz_dirname,
447
                         vlc_bool_t b_priv )
448
449
{
    DIR* dir;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
450
    const char *psz_dirent;
451
452
453
454

    if( *psz_dirname == '\0' )
        psz_dirname = ".";

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
455
    dir = utf8_opendir( psz_dirname );
456
457
    if( dir == NULL )
    {
458
        msg_Warn( p_this, "cannot open directory (%s): %s", psz_dirname,
459
460
461
                  strerror( errno ) );
        return VLC_EGENERIC;
    }
462
463
464
465
466
467
468
469
470
471
472
#ifdef S_ISLNK
    else
    {
        struct stat st1, st2;
        int fd = dirfd( dir );

        /*
         * Gets stats for the directory path, checks that it is not a
         * symbolic link (to avoid possibly infinite recursion), and verifies
         * that the inode is still the same, to avoid TOCTOU race condition.
         */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
473
474
        if( ( fd == -1)
         || fstat( fd, &st1 ) || utf8_lstat( psz_dirname, &st2 )
475
476
477
478
479
480
481
         || S_ISLNK( st2.st_mode ) || ( st1.st_ino != st2.st_ino ) )
        {
            closedir( dir );
            return VLC_EGENERIC;
        }
    }
#endif
482

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
483
    while( ( psz_dirent = utf8_readdir( dir ) ) != NULL )
484
485
    {
        char *psz_filename;
486
487
488
489
490
491
492
        int check;

        if( ( strcmp( ".", psz_dirent ) == 0 )
         || ( strcmp( "..", psz_dirent ) == 0 ) )
            continue;

        check = asprintf( &psz_filename, "%s/%s", psz_dirname,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
493
494
495
                              psz_dirent );
        LocaleFree( psz_dirent );
        if( check == -1 )
496
            continue;
497

498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
        gnutls_Addx509File( p_this, cred, psz_filename, b_priv );
        free( psz_filename );
    }

    closedir( dir );
    return VLC_SUCCESS;
}


static int
gnutls_Addx509File( vlc_object_t *p_this,
                    gnutls_certificate_credentials cred,
                    const char *psz_path, vlc_bool_t b_priv )
{
    struct stat st;

    if( utf8_stat( psz_path, &st ) == 0 )
    {
        if( S_ISREG( st.st_mode ) )
517
        {
518
519
            char *psz_localname = ToLocale( psz_path );
            int i = b_priv
520
                    ? gnutls_certificate_set_x509_key_file( cred,
521
522
523
524
525
526
527
                    psz_localname,  psz_localname, GNUTLS_X509_FMT_PEM )
                : gnutls_certificate_set_x509_trust_file( cred,
                        psz_localname, GNUTLS_X509_FMT_PEM );
            LocaleFree( psz_localname );

            if( i < 0 )
            {
528
                msg_Warn( p_this, "cannot add x509 credentials (%s): %s",
529
530
                          psz_path, gnutls_strerror( i ) );
                return VLC_EGENERIC;
531
            }
532
            else
533
            {
534
                msg_Dbg( p_this, "added x509 credentials (%s)",
535
536
                         psz_path );
                return VLC_SUCCESS;
537
538
            }
        }
539
540
541
        else if( S_ISDIR( st.st_mode ) )
        {
            msg_Dbg( p_this,
542
                     "looking recursively for x509 credentials in %s",
543
544
545
                     psz_path );
            return gnutls_Addx509Directory( p_this, cred, psz_path, b_priv);
        }
546
    }
547
    else
548
        msg_Warn( p_this, "cannot add x509 credentials (%s): %s",
549
                  psz_path, strerror( errno ) );
550
    return VLC_EGENERIC;
551
552
}

553

554
555
556
/**
 * Initializes a client-side TLS session.
 */
557
static tls_session_t *
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
558
gnutls_ClientCreate( tls_t *p_tls )
559
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
560
561
    tls_session_t *p_session = NULL;
    tls_client_sys_t *p_sys = NULL;
562
563
564
565
566
567
568
569
570
571
    int i_val;
    const int cert_type_priority[3] =
    {
        GNUTLS_CRT_X509,
        0
    };

    p_sys = (tls_client_sys_t *)malloc( sizeof(struct tls_client_sys_t) );
    if( p_sys == NULL )
        return NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
572

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
573
574
575
576
577
578
579
580
581
582
583
    p_session = (struct tls_session_t *)vlc_object_create ( p_tls, sizeof(struct tls_session_t) );
    if( p_session == NULL )
    {
        free( p_sys );
        return NULL;
    }

    p_session->p_sys = p_sys;
    p_session->sock.p_sys = p_session;
    p_session->sock.pf_send = gnutls_Send;
    p_session->sock.pf_recv = gnutls_Recv;
584
    p_session->pf_handshake = gnutls_BeginHandshake;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
585
586
587
    p_session->pf_close = gnutls_ClientDelete;

    p_sys->session.b_handshaked = VLC_FALSE;
588
    p_sys->session.psz_hostname = NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
589
590

    vlc_object_attach( p_session, p_tls );
591
592
593
594

    i_val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
    if( i_val != 0 )
    {
595
        msg_Err( p_tls, "cannot allocate X509 credentials: %s",
596
                 gnutls_strerror( i_val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
597
        goto error;
598
599
    }

600
    if( get_Bool( p_tls, "tls-check-cert" ) )
601
    {
602
603
        char *psz_path;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
604
        if( asprintf( &psz_path, "%s/"CONFIG_DIR"/ssl/certs",
605
                      p_tls->p_vlc->psz_homedir ) != -1 )
606
        {
607
608
609
            gnutls_Addx509Directory( (vlc_object_t *)p_session,
                                     p_sys->x509_cred, psz_path, VLC_FALSE );
            free( psz_path );
610
        }
611

612
613
614
615
616
617
618
        if( asprintf( &psz_path, "%s/ca-certificates.crt",
            config_GetDataDir ( (vlc_object_t *)p_session) ) != -1 )
        {
            gnutls_Addx509File( (vlc_object_t *)p_session,
                                p_sys->x509_cred, psz_path, VLC_FALSE );
            free( psz_path );
        }
619
        p_session->pf_handshake2 = gnutls_HandshakeAndValidate;
620
    }
621
622
623
    else
        p_session->pf_handshake2 = gnutls_ContinueHandshake;

624
625
626
    {
        char *psz_path;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
627
628
        if( asprintf( &psz_path, "%s/"CONFIG_DIR"/ssl/private",
                      p_tls->p_vlc->psz_homedir ) == -1 )
629
630
631
632
633
634
635
636
637
638
639
        {
            gnutls_certificate_free_credentials( p_sys->x509_cred );
            goto error;
        }

        gnutls_Addx509Directory( (vlc_object_t *)p_session, p_sys->x509_cred,
                                 psz_path, VLC_TRUE );

        free( psz_path );
    }

640
    i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT );
641
642
    if( i_val != 0 )
    {
643
        msg_Err( p_tls, "cannot initialize TLS session: %s",
644
645
                 gnutls_strerror( i_val ) );
        gnutls_certificate_free_credentials( p_sys->x509_cred );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
646
        goto error;
647
648
    }

649
    i_val = gnutls_set_default_priority( p_sys->session.session );
650
651
    if( i_val < 0 )
    {
652
        msg_Err( p_tls, "cannot set ciphers priorities: %s",
653
                 gnutls_strerror( i_val ) );
654
        gnutls_deinit( p_sys->session.session );
655
        gnutls_certificate_free_credentials( p_sys->x509_cred );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
656
        goto error;
657
658
    }

659
660
    i_val = gnutls_certificate_type_set_priority( p_sys->session.session,
                                                  cert_type_priority );
661
662
    if( i_val < 0 )
    {
663
        msg_Err( p_tls, "cannot set certificate type priorities: %s",
664
                 gnutls_strerror( i_val ) );
665
        gnutls_deinit( p_sys->session.session );
666
        gnutls_certificate_free_credentials( p_sys->x509_cred );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
667
        goto error;
668
669
    }

670
671
    i_val = gnutls_credentials_set( p_sys->session.session,
                                    GNUTLS_CRD_CERTIFICATE,
672
673
674
                                    p_sys->x509_cred );
    if( i_val < 0 )
    {
675
        msg_Err( p_tls, "cannot set TLS session credentials: %s",
676
                 gnutls_strerror( i_val ) );
677
        gnutls_deinit( p_sys->session.session );
678
        gnutls_certificate_free_credentials( p_sys->x509_cred );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
679
        goto error;
680
681
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
682
    return p_session;
683

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
684
685
686
687
error:
    vlc_object_detach( p_session );
    vlc_object_destroy( p_session );
    free( p_sys );
688

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
689
    return NULL;
690
691
692
}


693
694
695
/**
 * TLS session resumption callbacks (server-side)
 */
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
static int cb_store( void *p_server, gnutls_datum key, gnutls_datum data )
{
    tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;

    if( ( p_sys->i_cache_size == 0 )
     || ( key.size > MAX_SESSION_ID )
     || ( data.size > MAX_SESSION_DATA ) )
        return -1;

    vlc_mutex_lock( &p_sys->cache_lock );

    memcpy( p_sys->p_store->id, key.data, key.size);
    memcpy( p_sys->p_store->data, data.data, data.size );
    p_sys->p_store->i_idlen = key.size;
    p_sys->p_store->i_datalen = data.size;

    p_sys->p_store++;
    if( ( p_sys->p_store - p_sys->p_cache ) == p_sys->i_cache_size )
        p_sys->p_store = p_sys->p_cache;

    vlc_mutex_unlock( &p_sys->cache_lock );

    return 0;
}


static const gnutls_datum err_datum = { NULL, 0 };

static gnutls_datum cb_fetch( void *p_server, gnutls_datum key )
{
    tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
    saved_session_t *p_session, *p_end;

    p_session = p_sys->p_cache;
    p_end = p_session + p_sys->i_cache_size;

    vlc_mutex_lock( &p_sys->cache_lock );

    while( p_session < p_end )
    {
        if( ( p_session->i_idlen == key.size )
         && !memcmp( p_session->id, key.data, key.size ) )
        {
            gnutls_datum data;

            data.size = p_session->i_datalen;

            data.data = gnutls_malloc( data.size );
            if( data.data == NULL )
            {
                vlc_mutex_unlock( &p_sys->cache_lock );
                return err_datum;
            }

            memcpy( data.data, p_session->data, data.size );
            vlc_mutex_unlock( &p_sys->cache_lock );
            return data;
        }
        p_session++;
    }

    vlc_mutex_unlock( &p_sys->cache_lock );

    return err_datum;
}


static int cb_delete( void *p_server, gnutls_datum key )
{
    tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
    saved_session_t *p_session, *p_end;

    p_session = p_sys->p_cache;
    p_end = p_session + p_sys->i_cache_size;

    vlc_mutex_lock( &p_sys->cache_lock );

    while( p_session < p_end )
    {
        if( ( p_session->i_idlen == key.size )
         && !memcmp( p_session->id, key.data, key.size ) )
        {
            p_session->i_datalen = p_session->i_idlen = 0;
            vlc_mutex_unlock( &p_sys->cache_lock );
            return 0;
        }
        p_session++;
    }

    vlc_mutex_unlock( &p_sys->cache_lock );

    return -1;
}


791
792
793
/**
 * Initializes a server-side TLS session.
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
794
795
796
797
static tls_session_t *
gnutls_ServerSessionPrepare( tls_server_t *p_server )
{
    tls_session_t *p_session;
798
    tls_server_sys_t *p_server_sys;
799
    gnutls_session session;
800
    int i_val;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
801

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
802
803
804
    p_session = vlc_object_create( p_server, sizeof (struct tls_session_t) );
    if( p_session == NULL )
        return NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
805

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
806
807
808
809
810
811
812
813
814
    p_session->p_sys = malloc( sizeof(struct tls_session_sys_t) );
    if( p_session->p_sys == NULL )
    {
        vlc_object_destroy( p_session );
        return NULL;
    }

    vlc_object_attach( p_session, p_server );

815
    p_server_sys = (tls_server_sys_t *)p_server->p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
816
817
818
    p_session->sock.p_sys = p_session;
    p_session->sock.pf_send = gnutls_Send;
    p_session->sock.pf_recv = gnutls_Recv;
819
    p_session->pf_handshake = gnutls_BeginHandshake;
820
    p_session->pf_handshake2 = p_server_sys->pf_handshake2;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
821
822
823
    p_session->pf_close = gnutls_SessionClose;

    ((tls_session_sys_t *)p_session->p_sys)->b_handshaked = VLC_FALSE;
824
    ((tls_session_sys_t *)p_session->p_sys)->psz_hostname = NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
825

826
    i_val = gnutls_init( &session, GNUTLS_SERVER );
827
    if( i_val != 0 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
828
    {
829
        msg_Err( p_server, "cannot initialize TLS session: %s",
830
                 gnutls_strerror( i_val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
831
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
832
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
833
834
835

    ((tls_session_sys_t *)p_session->p_sys)->session = session;

836
    i_val = gnutls_set_default_priority( session );
837
    if( i_val < 0 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
838
    {
839
        msg_Err( p_server, "cannot set ciphers priorities: %s",
840
                 gnutls_strerror( i_val ) );
841
        gnutls_deinit( session );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
842
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
843
844
    }

845
    i_val = gnutls_credentials_set( session, GNUTLS_CRD_CERTIFICATE,
846
                                    p_server_sys->x509_cred );
847
    if( i_val < 0 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
848
    {
849
        msg_Err( p_server, "cannot set TLS session credentials: %s",
850
                 gnutls_strerror( i_val ) );
851
        gnutls_deinit( session );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
852
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
853
854
    }

855
856
    if( p_session->pf_handshake2 == gnutls_HandshakeAndValidate )
        gnutls_certificate_server_set_request( session, GNUTLS_CERT_REQUIRE );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
857

858
    gnutls_dh_set_prime_bits( session, get_Int( p_server, "gnutls-dh-bits" ) );
859

860
    /* Session resumption support */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
861
    gnutls_db_set_cache_expiration( session, get_Int( p_server,
862
                                    "gnutls-cache-expiration" ) );
863
864
865
866
    gnutls_db_set_retrieve_function( session, cb_fetch );
    gnutls_db_set_remove_function( session, cb_delete );
    gnutls_db_set_store_function( session, cb_store );
    gnutls_db_set_ptr( session, p_server );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
867
868

    return p_session;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
869
870
871
872
873
874

error:
    free( p_session->p_sys );
    vlc_object_detach( p_session );
    vlc_object_destroy( p_session );
    return NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
875
876
877
}


878
879
880
/**
 * Releases data allocated with tls_ServerCreate().
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
881
882
883
static void
gnutls_ServerDelete( tls_server_t *p_server )
{
884
885
886
887
    tls_server_sys_t *p_sys;
    p_sys = (tls_server_sys_t *)p_server->p_sys;

    vlc_mutex_destroy( &p_sys->cache_lock );
888
    free( p_sys->p_cache );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
889
890
891
892

    vlc_object_detach( p_server );
    vlc_object_destroy( p_server );

893
894
895
    /* all sessions depending on the server are now deinitialized */
    gnutls_certificate_free_credentials( p_sys->x509_cred );
    gnutls_dh_params_deinit( p_sys->dh_params );
896
    free( p_sys );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
897
898
899
}


900
/**
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
901
 * Adds one or more certificate authorities.
902
903
904
905
 *
 * @param psz_ca_path (Unicode) path to an x509 certificates list.
 *
 * @return -1 on error, 0 on success.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
906
907
908
909
 *****************************************************************************/
static int
gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
{
910
    tls_server_sys_t *p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
911
912
    char *psz_local_path;
    int val;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
913

914
915
    p_sys = (tls_server_sys_t *)(p_server->p_sys);

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
916
    psz_local_path = ToLocale( psz_ca_path );
917
    val = gnutls_certificate_set_x509_trust_file( p_sys->x509_cred,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
918
                                                  psz_local_path,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
919
                                                  GNUTLS_X509_FMT_PEM );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
920
    LocaleFree( psz_local_path );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
921
922
    if( val < 0 )
    {
923
        msg_Err( p_server, "cannot add trusted CA (%s): %s", psz_ca_path,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
924
                 gnutls_strerror( val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
925
926
        return VLC_EGENERIC;
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
927
    msg_Dbg( p_server, " %d trusted CA added (%s)", val, psz_ca_path );
928
929
930
931

    /* enables peer's certificate verification */
    p_sys->pf_handshake2 = gnutls_HandshakeAndValidate;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
932
933
934
935
    return VLC_SUCCESS;
}


936
/**
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
937
 * Adds a certificates revocation list to be sent to TLS clients.
938
939
940
941
942
 *
 * @param psz_crl_path (Unicode) path of the CRL file.
 *
 * @return -1 on error, 0 on success.
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
943
944
945
946
static int
gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path )
{
    int val;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
947
    char *psz_local_path = ToLocale( psz_crl_path );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
948
949
950

    val = gnutls_certificate_set_x509_crl_file( ((tls_server_sys_t *)
                                                (p_server->p_sys))->x509_cred,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
951
                                                psz_local_path,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
952
                                                GNUTLS_X509_FMT_PEM );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
953
    LocaleFree( psz_crl_path );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
954
955
    if( val < 0 )
    {
956
        msg_Err( p_server, "cannot add CRL (%s): %s", psz_crl_path,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
957
                 gnutls_strerror( val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
958
959
        return VLC_EGENERIC;
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
960
    msg_Dbg( p_server, "%d CRL added (%s)", val, psz_crl_path );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
961
962
    return VLC_SUCCESS;
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
963

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
964

965
/**
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
966
 * Allocates a whole server's TLS credentials.
967
968
969
 *
 * @return NULL on error.
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
970
static tls_server_t *
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
971
972
gnutls_ServerCreate( tls_t *p_tls, const char *psz_cert_path,
                     const char *psz_key_path )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
973
974
{
    tls_server_t *p_server;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
975
    tls_server_sys_t *p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
976
    char *psz_local_key, *psz_local_cert;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
977
978
    int val;

979
    msg_Dbg( p_tls, "creating TLS server" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
980

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
981
982
    p_sys = (tls_server_sys_t *)malloc( sizeof(struct tls_server_sys_t) );
    if( p_sys == NULL )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
983
984
        return NULL;

985
    p_sys->i_cache_size = get_Int( p_tls, "gnutls-cache-size" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
986
987
988
989
990
991
992
993
994
995
996
    p_sys->p_cache = (struct saved_session_t *)calloc( p_sys->i_cache_size,
                                           sizeof( struct saved_session_t ) );
    if( p_sys->p_cache == NULL )
    {
        free( p_sys );
        return NULL;
    }
    p_sys->p_store = p_sys->p_cache;

    p_server = vlc_object_create( p_tls, sizeof(struct tls_server_t) );
    if( p_server == NULL )
997
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
998
999
        free( p_sys->p_cache );
        free( p_sys );
1000
1001
        return NULL;
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1002
1003
1004
1005
1006
1007
1008
1009
1010

    vlc_object_attach( p_server, p_tls );

    p_server->p_sys = p_sys;
    p_server->pf_delete = gnutls_ServerDelete;
    p_server->pf_add_CA = gnutls_ServerAddCA;
    p_server->pf_add_CRL = gnutls_ServerAddCRL;
    p_server->pf_session_prepare = gnutls_ServerSessionPrepare;

1011
1012
1013
    /* No certificate validation by default */
    p_sys->pf_handshake2 = gnutls_ContinueHandshake;

1014
    /* FIXME: check for errors */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1015
    vlc_mutex_init( p_server, &p_sys->cache_lock );
1016

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1017
    /* Sets server's credentials */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1018
    val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1019
1020
    if( val != 0 )
    {
1021
        msg_Err( p_server, "cannot allocate X509 credentials: %s",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1022
                 gnutls_strerror( val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1023
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1024
1025
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1026
1027
    psz_local_cert = ToLocale( psz_cert_path );
    psz_local_key = ToLocale( psz_key_path );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1028
    val = gnutls_certificate_set_x509_key_file( p_sys->x509_cred,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1029
                                                psz_local_cert, psz_local_key,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1030
                                                GNUTLS_X509_FMT_PEM );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1031
1032
    LocaleFree( psz_cert_path );
    LocaleFree( psz_key_path );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1033
1034
    if( val < 0 )
    {
1035
        msg_Err( p_server, "cannot set certificate chain or private key: %s",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1036
                 gnutls_strerror( val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1037
1038
        gnutls_certificate_free_credentials( p_sys->x509_cred );
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1039
1040
    }

1041
1042
1043
1044
    /* FIXME:
     * - regenerate these regularly
     * - support other ciper suites
     */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1045
    val = gnutls_dh_params_init( &p_sys->dh_params );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1046
1047
    if( val >= 0 )
    {
1048
        msg_Dbg( p_server, "computing Diffie Hellman ciphers parameters" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1049
        val = gnutls_dh_params_generate2( p_sys->dh_params,
1050
                                          get_Int( p_tls, "gnutls-dh-bits" ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1051
1052
1053
    }
    if( val < 0 )
    {
1054
        msg_Err( p_server, "cannot initialize DH cipher suites: %s",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1055
                 gnutls_strerror( val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1056
1057
        gnutls_certificate_free_credentials( p_sys->x509_cred );
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1058
    }
1059
    msg_Dbg( p_server, "ciphers parameters computed" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1060

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1061
    gnutls_certificate_set_dh_params( p_sys->x509_cred, p_sys->dh_params);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1062
1063

    return p_server;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1064
1065
1066
1067
1068
1069
1070

error:
    vlc_mutex_destroy( &p_sys->cache_lock );
    vlc_object_detach( p_server );
    vlc_object_destroy( p_server );
    free( p_sys );
    return NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1071
1072
1073
}


1074
1075
1076
/**
 * gcrypt thread option VLC implementation
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1077
1078
vlc_object_t *__p_gcry_data;

1079
static int gcry_vlc_mutex_init( void **p_sys )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1080
1081
{
    int i_val;
1082
    vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc( sizeof( vlc_mutex_t ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1083
1084
1085
1086

    if( p_lock == NULL)
        return ENOMEM;

1087
1088
1089
    i_val = vlc_mutex_init( __p_gcry_data, p_lock );
    if( i_val )
        free( p_lock );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1090
1091
1092
1093
1094
    else
        *p_sys = p_lock;
    return i_val;
}

1095
static int gcry_vlc_mutex_destroy( void **p_sys )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1096
1097
1098
1099
{
    int i_val;
    vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys;