Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6147 serge 1
/*
2
 * TLS/SSL Protocol
3
 * Copyright (c) 2011 Martin Storsjo
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
 
22
#include 
23
 
24
#include 
25
#include 
26
 
27
#include "avformat.h"
28
#include "internal.h"
29
#include "network.h"
30
#include "os_support.h"
31
#include "url.h"
32
#include "tls.h"
33
#include "libavcodec/internal.h"
34
#include "libavutil/avstring.h"
35
#include "libavutil/opt.h"
36
#include "libavutil/parseutils.h"
37
 
38
#if HAVE_THREADS && GNUTLS_VERSION_NUMBER <= 0x020b00
39
#include 
40
#include "libavutil/thread.h"
41
GCRY_THREAD_OPTION_PTHREAD_IMPL;
42
#endif
43
 
44
typedef struct TLSContext {
45
    const AVClass *class;
46
    TLSShared tls_shared;
47
    gnutls_session_t session;
48
    gnutls_certificate_credentials_t cred;
49
    int need_shutdown;
50
} TLSContext;
51
 
52
void ff_gnutls_init(void)
53
{
54
    avpriv_lock_avformat();
55
#if HAVE_THREADS && GNUTLS_VERSION_NUMBER < 0x020b00
56
    if (gcry_control(GCRYCTL_ANY_INITIALIZATION_P) == 0)
57
        gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
58
#endif
59
    gnutls_global_init();
60
    avpriv_unlock_avformat();
61
}
62
 
63
void ff_gnutls_deinit(void)
64
{
65
    avpriv_lock_avformat();
66
    gnutls_global_deinit();
67
    avpriv_unlock_avformat();
68
}
69
 
70
static int print_tls_error(URLContext *h, int ret)
71
{
72
    switch (ret) {
73
    case GNUTLS_E_AGAIN:
74
    case GNUTLS_E_INTERRUPTED:
75
        break;
76
    case GNUTLS_E_WARNING_ALERT_RECEIVED:
77
        av_log(h, AV_LOG_WARNING, "%s\n", gnutls_strerror(ret));
78
        break;
79
    default:
80
        av_log(h, AV_LOG_ERROR, "%s\n", gnutls_strerror(ret));
81
        break;
82
    }
83
    return AVERROR(EIO);
84
}
85
 
86
static int tls_close(URLContext *h)
87
{
88
    TLSContext *c = h->priv_data;
89
    if (c->need_shutdown)
90
        gnutls_bye(c->session, GNUTLS_SHUT_WR);
91
    if (c->session)
92
        gnutls_deinit(c->session);
93
    if (c->cred)
94
        gnutls_certificate_free_credentials(c->cred);
95
    if (c->tls_shared.tcp)
96
        ffurl_close(c->tls_shared.tcp);
97
    ff_gnutls_deinit();
98
    return 0;
99
}
100
 
101
static ssize_t gnutls_url_pull(gnutls_transport_ptr_t transport,
102
                               void *buf, size_t len)
103
{
104
    URLContext *h = (URLContext*) transport;
105
    int ret = ffurl_read(h, buf, len);
106
    if (ret >= 0)
107
        return ret;
108
    if (ret == AVERROR_EXIT)
109
        return 0;
110
    errno = EIO;
111
    return -1;
112
}
113
 
114
static ssize_t gnutls_url_push(gnutls_transport_ptr_t transport,
115
                               const void *buf, size_t len)
116
{
117
    URLContext *h = (URLContext*) transport;
118
    int ret = ffurl_write(h, buf, len);
119
    if (ret >= 0)
120
        return ret;
121
    if (ret == AVERROR_EXIT)
122
        return 0;
123
    errno = EIO;
124
    return -1;
125
}
126
 
127
static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
128
{
129
    TLSContext *p = h->priv_data;
130
    TLSShared *c = &p->tls_shared;
131
    int ret;
132
 
133
    ff_gnutls_init();
134
 
135
    if ((ret = ff_tls_open_underlying(c, h, uri, options)) < 0)
136
        goto fail;
137
 
138
    gnutls_init(&p->session, c->listen ? GNUTLS_SERVER : GNUTLS_CLIENT);
139
    if (!c->listen && !c->numerichost)
140
        gnutls_server_name_set(p->session, GNUTLS_NAME_DNS, c->host, strlen(c->host));
141
    gnutls_certificate_allocate_credentials(&p->cred);
142
    if (c->ca_file) {
143
        ret = gnutls_certificate_set_x509_trust_file(p->cred, c->ca_file, GNUTLS_X509_FMT_PEM);
144
        if (ret < 0)
145
            av_log(h, AV_LOG_ERROR, "%s\n", gnutls_strerror(ret));
146
    }
147
#if GNUTLS_VERSION_NUMBER >= 0x030020
148
    else
149
        gnutls_certificate_set_x509_system_trust(p->cred);
150
#endif
151
    gnutls_certificate_set_verify_flags(p->cred, c->verify ?
152
                                        GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT : 0);
153
    if (c->cert_file && c->key_file) {
154
        ret = gnutls_certificate_set_x509_key_file(p->cred,
155
                                                   c->cert_file, c->key_file,
156
                                                   GNUTLS_X509_FMT_PEM);
157
        if (ret < 0) {
158
            av_log(h, AV_LOG_ERROR,
159
                   "Unable to set cert/key files %s and %s: %s\n",
160
                   c->cert_file, c->key_file, gnutls_strerror(ret));
161
            ret = AVERROR(EIO);
162
            goto fail;
163
        }
164
    } else if (c->cert_file || c->key_file)
165
        av_log(h, AV_LOG_ERROR, "cert and key required\n");
166
    gnutls_credentials_set(p->session, GNUTLS_CRD_CERTIFICATE, p->cred);
167
    gnutls_transport_set_pull_function(p->session, gnutls_url_pull);
168
    gnutls_transport_set_push_function(p->session, gnutls_url_push);
169
    gnutls_transport_set_ptr(p->session, c->tcp);
170
    gnutls_priority_set_direct(p->session, "NORMAL", NULL);
171
    ret = gnutls_handshake(p->session);
172
    if (ret) {
173
        ret = print_tls_error(h, ret);
174
        goto fail;
175
    }
176
    p->need_shutdown = 1;
177
    if (c->verify) {
178
        unsigned int status, cert_list_size;
179
        gnutls_x509_crt_t cert;
180
        const gnutls_datum_t *cert_list;
181
        if ((ret = gnutls_certificate_verify_peers2(p->session, &status)) < 0) {
182
            av_log(h, AV_LOG_ERROR, "Unable to verify peer certificate: %s\n",
183
                                    gnutls_strerror(ret));
184
            ret = AVERROR(EIO);
185
            goto fail;
186
        }
187
        if (status & GNUTLS_CERT_INVALID) {
188
            av_log(h, AV_LOG_ERROR, "Peer certificate failed verification\n");
189
            ret = AVERROR(EIO);
190
            goto fail;
191
        }
192
        if (gnutls_certificate_type_get(p->session) != GNUTLS_CRT_X509) {
193
            av_log(h, AV_LOG_ERROR, "Unsupported certificate type\n");
194
            ret = AVERROR(EIO);
195
            goto fail;
196
        }
197
        gnutls_x509_crt_init(&cert);
198
        cert_list = gnutls_certificate_get_peers(p->session, &cert_list_size);
199
        gnutls_x509_crt_import(cert, cert_list, GNUTLS_X509_FMT_DER);
200
        ret = gnutls_x509_crt_check_hostname(cert, c->host);
201
        gnutls_x509_crt_deinit(cert);
202
        if (!ret) {
203
            av_log(h, AV_LOG_ERROR,
204
                   "The certificate's owner does not match hostname %s\n", c->host);
205
            ret = AVERROR(EIO);
206
            goto fail;
207
        }
208
    }
209
 
210
    return 0;
211
fail:
212
    tls_close(h);
213
    return ret;
214
}
215
 
216
static int tls_read(URLContext *h, uint8_t *buf, int size)
217
{
218
    TLSContext *c = h->priv_data;
219
    int ret = gnutls_record_recv(c->session, buf, size);
220
    if (ret > 0)
221
        return ret;
222
    if (ret == 0)
223
        return AVERROR_EOF;
224
    return print_tls_error(h, ret);
225
}
226
 
227
static int tls_write(URLContext *h, const uint8_t *buf, int size)
228
{
229
    TLSContext *c = h->priv_data;
230
    int ret = gnutls_record_send(c->session, buf, size);
231
    if (ret > 0)
232
        return ret;
233
    if (ret == 0)
234
        return AVERROR_EOF;
235
    return print_tls_error(h, ret);
236
}
237
 
238
static const AVOption options[] = {
239
    TLS_COMMON_OPTIONS(TLSContext, tls_shared),
240
    { NULL }
241
};
242
 
243
static const AVClass tls_class = {
244
    .class_name = "tls",
245
    .item_name  = av_default_item_name,
246
    .option     = options,
247
    .version    = LIBAVUTIL_VERSION_INT,
248
};
249
 
250
URLProtocol ff_tls_gnutls_protocol = {
251
    .name           = "tls",
252
    .url_open2      = tls_open,
253
    .url_read       = tls_read,
254
    .url_write      = tls_write,
255
    .url_close      = tls_close,
256
    .priv_data_size = sizeof(TLSContext),
257
    .flags          = URL_PROTOCOL_FLAG_NETWORK,
258
    .priv_data_class = &tls_class,
259
};