1 /* GStreamer
2 * Copyright (C) 2011 Axis Communications <dev-gstreamer@axis.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 /**
21 * SECTION:element-curltlssink
22 * @title: curltlssink
23 * @short_description: sink that uploads data to a server using libcurl
24 * @see_also:
25 *
26 * This is a network sink that uses libcurl.
27 *
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <curl/curl.h>
35 #include <string.h>
36 #include <stdio.h>
37
38 #if HAVE_SYS_SOCKET_H
39 #include <sys/socket.h>
40 #endif
41 #include <sys/types.h>
42 #if HAVE_NETINET_IN_H
43 #include <netinet/in.h>
44 #endif
45 #include <unistd.h>
46 #if HAVE_NETINET_IP_H
47 #include <netinet/ip.h>
48 #endif
49 #if HAVE_NETINET_TCP_H
50 #include <netinet/tcp.h>
51 #endif
52 #include <sys/stat.h>
53 #include <fcntl.h>
54
55 #include "gstcurlbasesink.h"
56 #include "gstcurltlssink.h"
57
58 /* Default values */
59 #define GST_CAT_DEFAULT gst_curl_tls_sink_debug
60 #define DEFAULT_INSECURE TRUE
61
62
63 /* Plugin specific settings */
64
65 GST_DEBUG_CATEGORY_STATIC (gst_curl_tls_sink_debug);
66
67 enum
68 {
69 PROP_0,
70 PROP_CA_CERT,
71 PROP_CA_PATH,
72 PROP_CRYPTO_ENGINE,
73 PROP_INSECURE
74 };
75
76
77 /* Object class function declarations */
78
79 static void gst_curl_tls_sink_set_property (GObject * object, guint prop_id,
80 const GValue * value, GParamSpec * pspec);
81 static void gst_curl_tls_sink_get_property (GObject * object, guint prop_id,
82 GValue * value, GParamSpec * pspec);
83 static void gst_curl_tls_sink_finalize (GObject * gobject);
84 static gboolean gst_curl_tls_sink_set_options_unlocked
85 (GstCurlBaseSink * bcsink);
86
87 #define gst_curl_tls_sink_parent_class parent_class
88 G_DEFINE_TYPE (GstCurlTlsSink, gst_curl_tls_sink, GST_TYPE_CURL_BASE_SINK);
89
90 /* private functions */
91
92 static void
gst_curl_tls_sink_class_init(GstCurlTlsSinkClass * klass)93 gst_curl_tls_sink_class_init (GstCurlTlsSinkClass * klass)
94 {
95 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
96 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
97
98 GST_DEBUG_CATEGORY_INIT (gst_curl_tls_sink_debug, "curltlssink", 0,
99 "curl tls sink element");
100 GST_DEBUG_OBJECT (klass, "class_init");
101
102 gst_element_class_set_static_metadata (element_class,
103 "Curl tls sink",
104 "Sink/Network",
105 "Upload data over TLS protocol using libcurl",
106 "Patricia Muscalu <patricia@axis.com>");
107
108 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_curl_tls_sink_finalize);
109
110 gobject_class->set_property = gst_curl_tls_sink_set_property;
111 gobject_class->get_property = gst_curl_tls_sink_get_property;
112
113 klass->set_options_unlocked = gst_curl_tls_sink_set_options_unlocked;
114
115 g_object_class_install_property (gobject_class, PROP_CA_CERT,
116 g_param_spec_string ("ca-cert",
117 "CA certificate",
118 "CA certificate to use in order to verify the peer",
119 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
120
121 g_object_class_install_property (gobject_class, PROP_CA_PATH,
122 g_param_spec_string ("ca-path",
123 "CA path",
124 "CA directory path to use in order to verify the peer",
125 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
126 g_object_class_install_property (gobject_class, PROP_CRYPTO_ENGINE,
127 g_param_spec_string ("crypto-engine",
128 "OpenSSL crypto engine",
129 "OpenSSL crypto engine to use for cipher operations",
130 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
131 g_object_class_install_property (gobject_class, PROP_INSECURE,
132 g_param_spec_boolean ("insecure",
133 "Perform insecure SSL connections",
134 "Allow curl to perform insecure SSL connections",
135 DEFAULT_INSECURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
136 }
137
138 static void
gst_curl_tls_sink_init(GstCurlTlsSink * sink)139 gst_curl_tls_sink_init (GstCurlTlsSink * sink)
140 {
141 sink->ca_cert = NULL;
142 sink->ca_path = NULL;
143 sink->crypto_engine = NULL;
144 sink->insecure = DEFAULT_INSECURE;
145 }
146
147 static void
gst_curl_tls_sink_finalize(GObject * gobject)148 gst_curl_tls_sink_finalize (GObject * gobject)
149 {
150 GstCurlTlsSink *this = GST_CURL_TLS_SINK (gobject);
151
152 GST_DEBUG ("finalizing curltlssink");
153
154 g_free (this->ca_cert);
155 g_free (this->ca_path);
156 g_free (this->crypto_engine);
157
158 G_OBJECT_CLASS (parent_class)->finalize (gobject);
159 }
160
161 static void
gst_curl_tls_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)162 gst_curl_tls_sink_set_property (GObject * object, guint prop_id,
163 const GValue * value, GParamSpec * pspec)
164 {
165 GstCurlTlsSink *sink;
166 GstState cur_state;
167
168 g_return_if_fail (GST_IS_CURL_TLS_SINK (object));
169 sink = GST_CURL_TLS_SINK (object);
170
171 gst_element_get_state (GST_ELEMENT (sink), &cur_state, NULL, 0);
172 if (cur_state != GST_STATE_PLAYING && cur_state != GST_STATE_PAUSED) {
173 GST_OBJECT_LOCK (sink);
174
175 switch (prop_id) {
176 case PROP_CA_CERT:
177 g_free (sink->ca_cert);
178 sink->ca_cert = g_value_dup_string (value);
179 sink->insecure = FALSE;
180 GST_DEBUG_OBJECT (sink, "ca_cert set to %s", sink->ca_cert);
181 break;
182 case PROP_CA_PATH:
183 g_free (sink->ca_path);
184 sink->ca_path = g_value_dup_string (value);
185 sink->insecure = FALSE;
186 GST_DEBUG_OBJECT (sink, "ca_path set to %s", sink->ca_path);
187 break;
188 case PROP_CRYPTO_ENGINE:
189 g_free (sink->crypto_engine);
190 sink->crypto_engine = g_value_dup_string (value);
191 GST_DEBUG_OBJECT (sink, "crypto_engine set to %s", sink->crypto_engine);
192 break;
193 case PROP_INSECURE:
194 sink->insecure = g_value_get_boolean (value);
195 GST_DEBUG_OBJECT (sink, "insecure set to %d", sink->insecure);
196 break;
197 }
198
199 GST_OBJECT_UNLOCK (sink);
200
201 return;
202 }
203
204 GST_OBJECT_UNLOCK (sink);
205 }
206
207 static void
gst_curl_tls_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)208 gst_curl_tls_sink_get_property (GObject * object, guint prop_id,
209 GValue * value, GParamSpec * pspec)
210 {
211 GstCurlTlsSink *sink;
212
213 g_return_if_fail (GST_IS_CURL_TLS_SINK (object));
214 sink = GST_CURL_TLS_SINK (object);
215
216 switch (prop_id) {
217 case PROP_CA_CERT:
218 g_value_set_string (value, sink->ca_cert);
219 break;
220 case PROP_CA_PATH:
221 g_value_set_string (value, sink->ca_path);
222 break;
223 case PROP_CRYPTO_ENGINE:
224 g_value_set_string (value, sink->crypto_engine);
225 break;
226 case PROP_INSECURE:
227 g_value_set_boolean (value, sink->insecure);
228 break;
229 default:
230 GST_DEBUG_OBJECT (sink, "invalid property id");
231 break;
232 }
233 }
234
235 static gboolean
gst_curl_tls_sink_set_options_unlocked(GstCurlBaseSink * bcsink)236 gst_curl_tls_sink_set_options_unlocked (GstCurlBaseSink * bcsink)
237 {
238 GstCurlTlsSink *sink = GST_CURL_TLS_SINK (bcsink);
239 CURLcode res;
240
241 if (!g_str_has_prefix (bcsink->url, "http")) {
242 res = curl_easy_setopt (bcsink->curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
243 if (res != CURLE_OK) {
244 bcsink->error = g_strdup_printf ("failed to set SSL level: %s",
245 curl_easy_strerror (res));
246 return FALSE;
247 }
248 }
249
250 /* crypto engine */
251 if ((sink->crypto_engine == NULL) ||
252 (strcmp (sink->crypto_engine, "auto") == 0)) {
253 res = curl_easy_setopt (bcsink->curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
254 if (res != CURLE_OK) {
255 bcsink->error =
256 g_strdup_printf ("failed to set default crypto engine: %s",
257 curl_easy_strerror (res));
258 return FALSE;
259 }
260 } else {
261 res = curl_easy_setopt (bcsink->curl, CURLOPT_SSLENGINE,
262 sink->crypto_engine);
263 if (res != CURLE_OK) {
264 bcsink->error = g_strdup_printf ("failed to set crypto engine: %s",
265 curl_easy_strerror (res));
266 return FALSE;
267 }
268 }
269
270 /* note that, using ca-path can allow libcurl to make SSL-connections much
271 * more efficiently than using ca-cert if the ca-cert file contains many CA
272 * certificates. */
273 if (sink->ca_cert != NULL && strlen (sink->ca_cert)) {
274 GST_DEBUG ("setting ca cert");
275 res = curl_easy_setopt (bcsink->curl, CURLOPT_CAINFO, sink->ca_cert);
276 if (res != CURLE_OK) {
277 bcsink->error = g_strdup_printf ("failed to set certificate: %s",
278 curl_easy_strerror (res));
279 return FALSE;
280 }
281 }
282
283 if (sink->ca_path != NULL && strlen (sink->ca_path)) {
284 GST_DEBUG ("setting ca path");
285 res = curl_easy_setopt (bcsink->curl, CURLOPT_CAPATH, sink->ca_path);
286 if (res != CURLE_OK) {
287 bcsink->error = g_strdup_printf ("failed to set certificate path: %s",
288 curl_easy_strerror (res));
289 return FALSE;
290 }
291 }
292
293 if (!sink->insecure) {
294 /* identify authenticity of the peer's certificate */
295 res = curl_easy_setopt (bcsink->curl, CURLOPT_SSL_VERIFYPEER, 1L);
296 if (res != CURLE_OK) {
297 bcsink->error = g_strdup_printf ("failed to set verification of peer: %s",
298 curl_easy_strerror (res));
299 return FALSE;
300 }
301 /* when CURLOPT_SSL_VERIFYHOST is 2, the commonName or subjectAltName
302 * fields are verified */
303 res = curl_easy_setopt (bcsink->curl, CURLOPT_SSL_VERIFYHOST, 2L);
304 if (res != CURLE_OK) {
305 bcsink->error =
306 g_strdup_printf
307 ("failed to set verification of server certificate: %s",
308 curl_easy_strerror (res));
309 return FALSE;
310 }
311 } else {
312 /* allow "insecure" SSL connections and transfers */
313 if (sink->insecure) {
314 res = curl_easy_setopt (bcsink->curl, CURLOPT_SSL_VERIFYPEER, 0L);
315 if (res != CURLE_OK) {
316 bcsink->error =
317 g_strdup_printf ("failed to set verification of peer: %s",
318 curl_easy_strerror (res));
319 return FALSE;
320 }
321
322 res = curl_easy_setopt (bcsink->curl, CURLOPT_SSL_VERIFYHOST, 0L);
323 if (res != CURLE_OK) {
324 bcsink->error =
325 g_strdup_printf
326 ("failed to set verification of server certificate: %s",
327 curl_easy_strerror (res));
328 return FALSE;
329 }
330 }
331 }
332
333 return TRUE;
334 }
335