gnutls.c 35.8 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
26
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
27
28

#include <vlc/vlc.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
29
#include <stdlib.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
30
#include <errno.h>
31
#include <time.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
32

33
34
35
36
37
38
39
40
41
42
43
44
45
#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
46
#include "vlc_tls.h"
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
47
#include "charset.h"
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
48

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
49
#include <gcrypt.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
50
#include <gnutls/gnutls.h>
51
#include <gnutls/x509.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
52

53
54
55
#define DH_BITS             1024
#define CACHE_EXPIRATION    3600
#define CACHE_SIZE          64
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
56
57
58
59
60
61
62
63
64

/*****************************************************************************
 * 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_( \
65
66
67
    "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
68

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

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

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

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

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

97
    add_bool( "tls-check-cert", VLC_TRUE, NULL, CHECK_CERT_TEXT,
98
              CHECK_CERT_LONGTEXT, VLC_FALSE );
99
    add_bool( "tls-check-hostname", VLC_TRUE, NULL, CHECK_HOSTNAME_TEXT,
100
101
              CHECK_HOSTNAME_LONGTEXT, VLC_FALSE );

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


111
112
113
114
115
116
117
118
119
120
121
122
#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
123
124
125

typedef struct tls_server_sys_t
{
126
127
128
129
130
131
132
    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;
133
134

    int                             (*pf_handshake2)( tls_session_t * );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
135
136
137
} tls_server_sys_t;


138
139
140
typedef struct tls_session_sys_t
{
    gnutls_session  session;
141
    char            *psz_hostname;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
142
    vlc_bool_t      b_handshaked;
143
144
145
} tls_session_sys_t;


146
147
typedef struct tls_client_sys_t
{
148
    struct tls_session_sys_t       session;
149
150
151
152
    gnutls_certificate_credentials x509_cred;
} tls_client_sys_t;


153
static int
154
_get_Int( vlc_object_t *p_this, const char *var )
155
156
157
158
159
160
161
162
163
164
165
166
{
    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;
}

167
168
169
170
171
172
173
174
175
176
177
178
179
180
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;
}

181
#define get_Int( a, b ) _get_Int( (vlc_object_t *)(a), (b) )
182
#define get_Bool( a, b ) _get_Bool( (vlc_object_t *)(a), (b) )
183
184


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

194
195
196
197
    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
198
199
200
201
    return val < 0 ? -1 : val;
}


202
/**
203
 * Receives data through a TLS session.
204
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
205
static int
206
gnutls_Recv( void *p_session, void *buf, int i_length )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
207
208
{
    int val;
209
210
211
    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
212

213
214
    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
215
216
217
218
219
    return val < 0 ? -1 : val;
}


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

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

235
     /* TODO: handle fatal error */
236
237
238
#ifdef WIN32
    WSASetLastError( 0 );
#endif
239
240
241
    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
242
243
244

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
254
    p_sys->b_handshaked = VLC_TRUE;
255
256
257
    return 0;
}

258
259
260
261
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
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;
}

304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326

typedef struct
{
    int flag;
    const char *msg;
} error_msg_t;

static const error_msg_t cert_errors[] =
{
    { GNUTLS_CERT_INVALID,
        "Certificate could not be verified" },
    { GNUTLS_CERT_REVOKED,
        "Certificate was revoked" },
    { GNUTLS_CERT_SIGNER_NOT_FOUND,
        "Certificate's signer was not found" },
    { GNUTLS_CERT_SIGNER_NOT_CA,
        "Certificate's signer is not a CA" },
    { GNUTLS_CERT_INSECURE_ALGORITHM,
        "Insecure certificate signature algorithm" },
    { 0, NULL }
};


327
static int
328
gnutls_HandshakeAndValidate( tls_session_t *session )
329
{
330
331
332
    int val = gnutls_ContinueHandshake( session );
    if( val )
        return val;
333

334
    tls_session_sys_t *p_sys = (tls_session_sys_t *)(session->p_sys);
335

336
337
338
339
340
341
342
343
344
345
    /* certificates chain verification */
    unsigned status;
    val = gnutls_certificate_verify_peers2( p_sys->session, &status );

    if( val )
    {
        msg_Err( session, "Certificate verification failed: %s",
                 gnutls_strerror( val ) );
        goto error;
    }
346

347
348
349
350
    if( status )
    {
        msg_Err( session, "TLS session: access denied" );
        for( const error_msg_t *e = cert_errors; e->flag; e++ )
351
        {
352
353
354
355
356
            if( status & e->flag )
            {
                msg_Err( session, e->msg );
                status &= ~e->flag;
            }
357
358
359
        }

        if( status )
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
            msg_Err( session,
                     "unknown certificate error (you found a bug in VLC)" );

        goto error;
    }

    /* certificate (host)name verification */
    const gnutls_datum *data = gnutls_certificate_get_peers( p_sys->session,
                                                             &(size_t){ 0 } );
    if( data == NULL )
    {
        msg_Err( session, "Peer certificate not available" );
        goto error;
    }

    gnutls_x509_crt cert;
    val = gnutls_x509_crt_init( &cert );
    if( val )
    {
        msg_Err( session, "x509 fatal error: %s", gnutls_strerror( val ) );
        goto error;
    }

    val = gnutls_x509_crt_import( cert, data, GNUTLS_X509_FMT_DER );
    if( val )
    {
        msg_Err( session, "Certificate import error: %s",
                 gnutls_strerror( val ) );
        gnutls_x509_crt_deinit( cert );
        goto crt_error;
    }
391

392
393
394
    if( p_sys->psz_hostname != NULL )
    {
        if ( !gnutls_x509_crt_check_hostname( cert, p_sys->psz_hostname ) )
395
        {
396
397
398
            msg_Err( session, "Certificate does not match \"%s\"",
                     p_sys->psz_hostname );
            goto crt_error;
399
        }
400
401
402
403
404
405
406
407
    }
    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;
408
409
    }

410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
    if( gnutls_x509_crt_get_activation_time( cert ) > time ( NULL ) )
    {
        msg_Err( session, "Certificate not yet valid" );
        goto crt_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 );
    return -1;
425
426
}

427
428
429
430
431
432
433
434
435
436
/**
 * 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.
 */
437
438
static int
gnutls_BeginHandshake( tls_session_t *p_session, int fd,
439
                       const char *psz_hostname )
440
441
442
443
444
{
    tls_session_sys_t *p_sys;

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

445
    gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)(intptr_t)fd);
446

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
447
    if( psz_hostname != NULL )
448
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
449
450
        gnutls_server_name_set( p_sys->session, GNUTLS_NAME_DNS, psz_hostname,
                                strlen( psz_hostname ) );
451
452
453
454
455
456
457
458
459
460
        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;
            }
        }
    }
461

462
    return p_session->pf_handshake2( p_session );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
463
464
}

465
/**
466
 * Terminates TLS session and releases session data.
467
468
 * You still have to close the socket yourself.
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
469
470
471
static void
gnutls_SessionClose( tls_session_t *p_session )
{
472
    tls_session_sys_t *p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
473

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
476
477
    if( p_sys->b_handshaked == VLC_TRUE )
        gnutls_bye( p_sys->session, GNUTLS_SHUT_WR );
478
    gnutls_deinit( p_sys->session );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
479

480
481
482
    if( p_sys->psz_hostname != NULL )
        free( p_sys->psz_hostname );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
483
484
485
    vlc_object_detach( p_session );
    vlc_object_destroy( p_session );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
486
    free( p_sys );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
487
488
489
490
491
492
}

static void
gnutls_ClientDelete( tls_session_t *p_session )
{
    /* On the client-side, credentials are re-allocated per session */
493
494
495
    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
496
    gnutls_SessionClose( p_session );
497
498
499

    /* 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
500
501
}

502
503
504
505
506
507

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

508
static int
509
510
511
gnutls_Addx509Directory( vlc_object_t *p_this,
                         gnutls_certificate_credentials cred,
                         const char *psz_dirname,
512
                         vlc_bool_t b_priv )
513
514
{
    DIR* dir;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
515
    const char *psz_dirent;
516
517
518
519

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
520
    dir = utf8_opendir( psz_dirname );
521
522
    if( dir == NULL )
    {
523
        msg_Warn( p_this, "cannot open directory (%s): %s", psz_dirname,
524
525
526
                  strerror( errno ) );
        return VLC_EGENERIC;
    }
527
528
529
530
531
532
533
534
535
536
537
#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
538
539
        if( ( fd == -1)
         || fstat( fd, &st1 ) || utf8_lstat( psz_dirname, &st2 )
540
541
542
543
544
545
546
         || S_ISLNK( st2.st_mode ) || ( st1.st_ino != st2.st_ino ) )
        {
            closedir( dir );
            return VLC_EGENERIC;
        }
    }
#endif
547

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
548
    while( ( psz_dirent = utf8_readdir( dir ) ) != NULL )
549
550
    {
        char *psz_filename;
551
552
553
554
555
556
557
        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
558
559
560
                              psz_dirent );
        LocaleFree( psz_dirent );
        if( check == -1 )
561
            continue;
562

563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
        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 ) )
582
        {
583
584
            char *psz_localname = ToLocale( psz_path );
            int i = b_priv
585
                    ? gnutls_certificate_set_x509_key_file( cred,
586
587
588
589
590
591
592
                    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 )
            {
593
                msg_Warn( p_this, "cannot add x509 credentials (%s): %s",
594
595
                          psz_path, gnutls_strerror( i ) );
                return VLC_EGENERIC;
596
            }
597
            else
598
            {
599
                msg_Dbg( p_this, "added x509 credentials (%s)",
600
601
                         psz_path );
                return VLC_SUCCESS;
602
603
            }
        }
604
605
606
        else if( S_ISDIR( st.st_mode ) )
        {
            msg_Dbg( p_this,
607
                     "looking recursively for x509 credentials in %s",
608
609
610
                     psz_path );
            return gnutls_Addx509Directory( p_this, cred, psz_path, b_priv);
        }
611
    }
612
    else
613
        msg_Warn( p_this, "cannot add x509 credentials (%s): %s",
614
                  psz_path, strerror( errno ) );
615
    return VLC_EGENERIC;
616
617
}

618

619
620
621
/**
 * Initializes a client-side TLS session.
 */
622
static tls_session_t *
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
623
gnutls_ClientCreate( tls_t *p_tls )
624
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
625
626
    tls_session_t *p_session = NULL;
    tls_client_sys_t *p_sys = NULL;
627
628
629
630
631
632
633
634
635
636
    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
637

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
638
639
640
641
642
643
644
645
646
647
648
    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;
649
    p_session->pf_handshake = gnutls_BeginHandshake;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
650
651
652
    p_session->pf_close = gnutls_ClientDelete;

    p_sys->session.b_handshaked = VLC_FALSE;
653
    p_sys->session.psz_hostname = NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
654
655

    vlc_object_attach( p_session, p_tls );
656
657
658
659

    i_val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
    if( i_val != 0 )
    {
660
        msg_Err( p_tls, "cannot allocate X509 credentials: %s",
661
                 gnutls_strerror( i_val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
662
        goto error;
663
664
    }

665
    if( get_Bool( p_tls, "tls-check-cert" ) )
666
    {
667
668
        char *psz_path;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
669
        if( asprintf( &psz_path, "%s/"CONFIG_DIR"/ssl/certs",
670
                      p_tls->p_libvlc->psz_homedir ) != -1 )
671
        {
672
673
674
            gnutls_Addx509Directory( (vlc_object_t *)p_session,
                                     p_sys->x509_cred, psz_path, VLC_FALSE );
            free( psz_path );
675
        }
676

677
678
679
680
681
682
683
        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 );
        }
684
        p_session->pf_handshake2 = gnutls_HandshakeAndValidate;
685
    }
686
687
688
    else
        p_session->pf_handshake2 = gnutls_ContinueHandshake;

689
690
691
    {
        char *psz_path;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
692
        if( asprintf( &psz_path, "%s/"CONFIG_DIR"/ssl/private",
693
                      p_tls->p_libvlc->psz_homedir ) == -1 )
694
695
696
697
698
699
700
701
702
703
704
        {
            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 );
    }

705
    i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT );
706
707
    if( i_val != 0 )
    {
708
        msg_Err( p_tls, "cannot initialize TLS session: %s",
709
710
                 gnutls_strerror( i_val ) );
        gnutls_certificate_free_credentials( p_sys->x509_cred );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
711
        goto error;
712
713
    }

714
    i_val = gnutls_set_default_priority( p_sys->session.session );
715
716
    if( i_val < 0 )
    {
717
        msg_Err( p_tls, "cannot set ciphers priorities: %s",
718
                 gnutls_strerror( i_val ) );
719
        gnutls_deinit( p_sys->session.session );
720
        gnutls_certificate_free_credentials( p_sys->x509_cred );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
721
        goto error;
722
723
    }

724
725
    i_val = gnutls_certificate_type_set_priority( p_sys->session.session,
                                                  cert_type_priority );
726
727
    if( i_val < 0 )
    {
728
        msg_Err( p_tls, "cannot set certificate type priorities: %s",
729
                 gnutls_strerror( i_val ) );
730
        gnutls_deinit( p_sys->session.session );
731
        gnutls_certificate_free_credentials( p_sys->x509_cred );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
732
        goto error;
733
734
    }

735
736
    i_val = gnutls_credentials_set( p_sys->session.session,
                                    GNUTLS_CRD_CERTIFICATE,
737
738
739
                                    p_sys->x509_cred );
    if( i_val < 0 )
    {
740
        msg_Err( p_tls, "cannot set TLS session credentials: %s",
741
                 gnutls_strerror( i_val ) );
742
        gnutls_deinit( p_sys->session.session );
743
        gnutls_certificate_free_credentials( p_sys->x509_cred );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
744
        goto error;
745
746
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
747
    return p_session;
748

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
749
750
751
752
error:
    vlc_object_detach( p_session );
    vlc_object_destroy( p_session );
    free( p_sys );
753

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
754
    return NULL;
755
756
757
}


758
759
760
/**
 * TLS session resumption callbacks (server-side)
 */
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
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
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;
}


856
857
858
/**
 * Initializes a server-side TLS session.
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
859
860
861
862
static tls_session_t *
gnutls_ServerSessionPrepare( tls_server_t *p_server )
{
    tls_session_t *p_session;
863
    tls_server_sys_t *p_server_sys;
864
    gnutls_session session;
865
    int i_val;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
866

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
867
868
869
    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
870

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
871
872
873
874
875
876
877
878
879
    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 );

880
    p_server_sys = (tls_server_sys_t *)p_server->p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
881
882
883
    p_session->sock.p_sys = p_session;
    p_session->sock.pf_send = gnutls_Send;
    p_session->sock.pf_recv = gnutls_Recv;
884
    p_session->pf_handshake = gnutls_BeginHandshake;
885
    p_session->pf_handshake2 = p_server_sys->pf_handshake2;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
886
887
888
    p_session->pf_close = gnutls_SessionClose;

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

891
    i_val = gnutls_init( &session, GNUTLS_SERVER );
892
    if( i_val != 0 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
893
    {
894
        msg_Err( p_server, "cannot initialize TLS session: %s",
895
                 gnutls_strerror( i_val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
896
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
897
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
898
899
900

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

901
    i_val = gnutls_set_default_priority( session );
902
    if( i_val < 0 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
903
    {
904
        msg_Err( p_server, "cannot set ciphers priorities: %s",
905
                 gnutls_strerror( i_val ) );
906
        gnutls_deinit( session );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
907
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
908
909
    }

910
    i_val = gnutls_credentials_set( session, GNUTLS_CRD_CERTIFICATE,
911
                                    p_server_sys->x509_cred );
912
    if( i_val < 0 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
913
    {
914
        msg_Err( p_server, "cannot set TLS session credentials: %s",
915
                 gnutls_strerror( i_val ) );
916
        gnutls_deinit( session );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
917
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
918
919
    }

920
921
    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
922

923
    gnutls_dh_set_prime_bits( session, get_Int( p_server, "gnutls-dh-bits" ) );
924

925
    /* Session resumption support */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
926
    gnutls_db_set_cache_expiration( session, get_Int( p_server,
927
                                    "gnutls-cache-expiration" ) );
928
929
930
931
    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
932
933

    return p_session;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
934
935
936
937
938
939

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
940
941
942
}


943
944
945
/**
 * Releases data allocated with tls_ServerCreate().
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
946
947
948
static void
gnutls_ServerDelete( tls_server_t *p_server )
{
949
950
951
952
    tls_server_sys_t *p_sys;
    p_sys = (tls_server_sys_t *)p_server->p_sys;

    vlc_mutex_destroy( &p_sys->cache_lock );
953
    free( p_sys->p_cache );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
954
955
956
957

    vlc_object_detach( p_server );
    vlc_object_destroy( p_server );

958
959
960
    /* 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 );
961
    free( p_sys );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
962
963
964
}


965
/**
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
966
 * Adds one or more certificate authorities.
967
968
969
970
 *
 * @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
971
972
973
974
 *****************************************************************************/
static int
gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
{
975
    tls_server_sys_t *p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
976
977
    char *psz_local_path;
    int val;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
978

979
980
    p_sys = (tls_server_sys_t *)(p_server->p_sys);

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
981
    psz_local_path = ToLocale( psz_ca_path );
982
    val = gnutls_certificate_set_x509_trust_file( p_sys->x509_cred,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
983
                                                  psz_local_path,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
984
                                                  GNUTLS_X509_FMT_PEM );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
985
    LocaleFree( psz_local_path );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
986
987
    if( val < 0 )
    {
988
        msg_Err( p_server, "cannot add trusted CA (%s): %s", psz_ca_path,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
989
                 gnutls_strerror( val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
990
991
        return VLC_EGENERIC;
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
992
    msg_Dbg( p_server, " %d trusted CA added (%s)", val, psz_ca_path );
993
994
995
996

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
997
998
999
1000
    return VLC_SUCCESS;
}


1001
/**
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1002
 * Adds a certificates revocation list to be sent to TLS clients.
1003
1004
1005
1006
1007
 *
 * @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
1008
1009
1010
1011
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
1012
    char *psz_local_path = ToLocale( psz_crl_path );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1013
1014
1015

    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
1016
                                                psz_local_path,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1017
                                                GNUTLS_X509_FMT_PEM );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1018
    LocaleFree( psz_crl_path );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1019
1020
    if( val < 0 )
    {
1021
        msg_Err( p_server, "cannot add CRL (%s): %s", psz_crl_path,
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
1024
        return VLC_EGENERIC;
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1025
    msg_Dbg( p_server, "%d CRL added (%s)", val, psz_crl_path );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1026
1027
    return VLC_SUCCESS;
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1028

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

1030
/**
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1031
 * Allocates a whole server's TLS credentials.
1032
1033
1034
 *
 * @return NULL on error.
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1035
static tls_server_t *
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1036
1037
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
1038
1039
{
    tls_server_t *p_server;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1040
    tls_server_sys_t *p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1041
    char *psz_local_key, *psz_local_cert;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1042
1043
    int val;

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1046
1047
    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
1048
1049
        return NULL;

1050
    p_sys->i_cache_size = get_Int( p_tls, "gnutls-cache-size" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
    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 )
1062
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1063
1064
        free( p_sys->p_cache );
        free( p_sys );
1065
1066
        return NULL;
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1067
1068
1069
1070
1071
1072
1073
1074
1075

    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;

1076
1077
1078
    /* No certificate validation by default */
    p_sys->pf_handshake2 = gnutls_ContinueHandshake;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1079
    vlc_mutex_init( p_server, &p_sys->cache_lock );
1080

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1081
    /* Sets server's credentials */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1082
    val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1083
1084
    if( val != 0 )
    {
1085
        msg_Err( p_server, "cannot allocate X509 credentials: %s",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1086
                 gnutls_strerror( val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1087
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1088
1089
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1090
1091
    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
1092
    val = gnutls_certificate_set_x509_key_file( p_sys->x509_cred,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1093
                                                psz_local_cert, psz_local_key,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1094
                                                GNUTLS_X509_FMT_PEM );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1095
1096
    LocaleFree( psz_cert_path );
    LocaleFree( psz_key_path );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1097
1098
    if( val < 0 )
    {
1099
        msg_Err( p_server, "cannot set certificate chain or private key: %s",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1100
                 gnutls_strerror( val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1101
1102
        gnutls_certificate_free_credentials( p_sys->x509_cred );
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1103
1104
    }

1105
1106
1107
1108
    /* FIXME:
     * - regenerate these regularly
     * - support other ciper suites
     */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1109
    val = gnutls_dh_params_init( &p_sys->dh_params );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1110
1111
    if( val >= 0 )
    {
1112
        msg_Dbg( p_server, "computing Diffie Hellman ciphers parameters" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1113
        val = gnutls_dh_params_generate2( p_sys->dh_params,
1114
                                          get_Int( p_tls, "gnutls-dh-bits" ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1115
1116
1117
    }
    if( val < 0 )
    {
1118
        msg_Err( p_server, "cannot initialize DH cipher suites: %s",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1119
                 gnutls_strerror( val ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1120
1121
        gnutls_certificate_free_credentials( p_sys->x509_cred );
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1122
    }
1123
    msg_Dbg( p_server, "ciphers parameters computed" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1124

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1125
    gnutls_certificate_set_dh_params( p_sys->x509_cred, p_sys->dh_params);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1126
1127

    return p_server;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1128
1129
1130
1131
1132
1133
1134

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
1135
1136
1137
}


1138
1139
1140
1141
#ifdef LIBVLC_USE_PTHREAD
GCRY_THREAD_OPTION_PTHREAD_IMPL;
# define gcry_threads_vlc gcry_threads_pthread
#else
1142
1143
1144
/**
 * gcrypt thread option VLC implementation
 */
1145
1146
1147

# define NEED_THREAD_CONTEXT 1
static vlc_object_t *__p_gcry_data;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1148

1149
static int gcry_vlc_mutex_init( void **p_sys )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1150
1151
{
    int i_val;
1152
    vlc_mutex_t *p_lock = (vlc_mutex_t