• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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