• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2014, Ericsson AB. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification,
5  * are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * 2. Redistributions in binary form must reproduce the above copyright notice, this
11  * list of conditions and the following disclaimer in the documentation and/or other
12  * materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
18  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
23  * OF SUCH DAMAGE.
24  */
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 
30 #include "gstdtlssrtpdec.h"
31 
32 #include "gstdtlsconnection.h"
33 
34 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
35     GST_PAD_SINK,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS_ANY);
38 
39 static GstStaticPadTemplate rtp_src_template =
40 GST_STATIC_PAD_TEMPLATE ("rtp_src",
41     GST_PAD_SRC,
42     GST_PAD_ALWAYS,
43     GST_STATIC_CAPS ("application/x-rtp")
44     );
45 
46 static GstStaticPadTemplate rtcp_src_template =
47 GST_STATIC_PAD_TEMPLATE ("rtcp_src",
48     GST_PAD_SRC,
49     GST_PAD_ALWAYS,
50     GST_STATIC_CAPS ("application/x-rtcp")
51     );
52 
53 static GstStaticPadTemplate data_src_template =
54 GST_STATIC_PAD_TEMPLATE ("data_src",
55     GST_PAD_SRC,
56     GST_PAD_REQUEST,
57     GST_STATIC_CAPS_ANY);
58 
59 GST_DEBUG_CATEGORY_STATIC (gst_dtls_srtp_dec_debug);
60 #define GST_CAT_DEFAULT gst_dtls_srtp_dec_debug
61 
62 #define gst_dtls_srtp_dec_parent_class parent_class
63 G_DEFINE_TYPE_WITH_CODE (GstDtlsSrtpDec, gst_dtls_srtp_dec,
64     GST_TYPE_DTLS_SRTP_BIN, GST_DEBUG_CATEGORY_INIT (gst_dtls_srtp_dec_debug,
65         "dtlssrtpdec", 0, "DTLS Decoder"));
66 
67 enum
68 {
69   PROP_0,
70   PROP_PEM,
71   PROP_PEER_PEM,
72   NUM_PROPERTIES
73 };
74 
75 static GParamSpec *properties[NUM_PROPERTIES];
76 
77 #define DEFAULT_PEM NULL
78 #define DEFAULT_PEER_PEM NULL
79 
80 static void gst_dtls_srtp_dec_set_property (GObject *, guint prop_id,
81     const GValue *, GParamSpec *);
82 static void gst_dtls_srtp_dec_get_property (GObject *, guint prop_id,
83     GValue *, GParamSpec *);
84 
85 static GstPad *gst_dtls_srtp_dec_request_new_pad (GstElement *,
86     GstPadTemplate *, const gchar * name, const GstCaps *);
87 static void gst_dtls_srtp_dec_release_pad (GstElement *, GstPad *);
88 
89 static GstCaps *on_decoder_request_key (GstElement * srtp_decoder, guint ssrc,
90     GstDtlsSrtpBin *);
91 static void on_peer_pem (GstElement * srtp_decoder, GParamSpec * pspec,
92     GstDtlsSrtpDec * self);
93 
94 static void gst_dtls_srtp_dec_remove_dtls_element (GstDtlsSrtpBin *);
95 static GstPadProbeReturn remove_dtls_decoder_probe_callback (GstPad *,
96     GstPadProbeInfo *, GstElement *);
97 
98 static void
gst_dtls_srtp_dec_class_init(GstDtlsSrtpDecClass * klass)99 gst_dtls_srtp_dec_class_init (GstDtlsSrtpDecClass * klass)
100 {
101   GObjectClass *gobject_class;
102   GstElementClass *element_class;
103   GstDtlsSrtpBinClass *dtls_srtp_bin_class;
104 
105   gobject_class = (GObjectClass *) klass;
106   element_class = (GstElementClass *) klass;
107   dtls_srtp_bin_class = (GstDtlsSrtpBinClass *) klass;
108 
109   gobject_class->set_property =
110       GST_DEBUG_FUNCPTR (gst_dtls_srtp_dec_set_property);
111   gobject_class->get_property =
112       GST_DEBUG_FUNCPTR (gst_dtls_srtp_dec_get_property);
113 
114   element_class->request_new_pad =
115       GST_DEBUG_FUNCPTR (gst_dtls_srtp_dec_request_new_pad);
116   element_class->release_pad =
117       GST_DEBUG_FUNCPTR (gst_dtls_srtp_dec_release_pad);
118 
119   dtls_srtp_bin_class->remove_dtls_element =
120       GST_DEBUG_FUNCPTR (gst_dtls_srtp_dec_remove_dtls_element);
121 
122   properties[PROP_PEM] =
123       g_param_spec_string ("pem",
124       "PEM string",
125       "A string containing a X509 certificate and RSA private key in PEM format",
126       DEFAULT_PEM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
127 
128   properties[PROP_PEER_PEM] =
129       g_param_spec_string ("peer-pem",
130       "Peer PEM string",
131       "The X509 certificate received in the DTLS handshake, in PEM format",
132       DEFAULT_PEER_PEM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
133 
134   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
135 
136   gst_element_class_add_static_pad_template (element_class, &sink_template);
137   gst_element_class_add_static_pad_template (element_class, &rtp_src_template);
138   gst_element_class_add_static_pad_template (element_class, &rtcp_src_template);
139   gst_element_class_add_static_pad_template (element_class, &data_src_template);
140 
141   gst_element_class_set_static_metadata (element_class,
142       "DTLS-SRTP Decoder",
143       "Decoder/Network/DTLS/SRTP",
144       "Decodes SRTP packets with a key received from DTLS",
145       "Patrik Oldsberg patrik.oldsberg@ericsson.com");
146 }
147 
148 static void
gst_dtls_srtp_dec_init(GstDtlsSrtpDec * self)149 gst_dtls_srtp_dec_init (GstDtlsSrtpDec * self)
150 {
151   GstElementClass *klass = GST_ELEMENT_GET_CLASS (GST_ELEMENT (self));
152   GstPadTemplate *templ;
153   GstPad *target_pad, *ghost_pad;
154   gboolean ret;
155 
156 /*
157                                  +-----------+
158             +--------------+  .-o|  dtlsdec  |o-R----data
159             |          dtls|o-'  +-----------+
160     sink---o|  dtlsdemux   |
161             |       srt(c)p|o-.  +-----------+
162             +--------------+  '-o|srtp    rtp|o------rtp
163                                  |  srtpdec  |
164                                 o|srtcp  rtcp|o------rtcp
165                                  +-----------+
166 */
167 
168   self->srtp_dec = gst_element_factory_make ("srtpdec", NULL);
169   if (!self->srtp_dec) {
170     GST_ERROR_OBJECT (self,
171         "failed to create srtp_dec, is the srtp plugin registered?");
172     return;
173   }
174   self->dtls_srtp_demux = gst_element_factory_make ("dtlssrtpdemux", NULL);
175   if (!self->dtls_srtp_demux) {
176     GST_ERROR_OBJECT (self, "failed to create dtls_srtp_demux");
177     return;
178   }
179   self->bin.dtls_element = gst_element_factory_make ("dtlsdec", NULL);
180   if (!self->bin.dtls_element) {
181     GST_ERROR_OBJECT (self, "failed to create dtls_dec");
182     return;
183   }
184 
185   gst_bin_add_many (GST_BIN (self),
186       self->dtls_srtp_demux, self->bin.dtls_element, self->srtp_dec, NULL);
187 
188   ret =
189       gst_element_link_pads (self->dtls_srtp_demux, "dtls_src",
190       self->bin.dtls_element, NULL);
191   g_return_if_fail (ret);
192   ret =
193       gst_element_link_pads (self->dtls_srtp_demux, "rtp_src", self->srtp_dec,
194       "rtp_sink");
195   g_return_if_fail (ret);
196 
197   templ = gst_element_class_get_pad_template (klass, "rtp_src");
198   target_pad = gst_element_get_static_pad (self->srtp_dec, "rtp_src");
199   ghost_pad = gst_ghost_pad_new_from_template ("rtp_src", target_pad, templ);
200   gst_object_unref (target_pad);
201   g_return_if_fail (ghost_pad);
202 
203   ret = gst_element_add_pad (GST_ELEMENT (self), ghost_pad);
204   g_return_if_fail (ret);
205 
206   templ = gst_element_class_get_pad_template (klass, "rtcp_src");
207   target_pad = gst_element_get_static_pad (self->srtp_dec, "rtcp_src");
208   ghost_pad = gst_ghost_pad_new_from_template ("rtcp_src", target_pad, templ);
209   gst_object_unref (target_pad);
210   g_return_if_fail (ghost_pad);
211 
212   ret = gst_element_add_pad (GST_ELEMENT (self), ghost_pad);
213   g_return_if_fail (ret);
214 
215   templ = gst_element_class_get_pad_template (klass, "sink");
216   target_pad = gst_element_get_static_pad (self->dtls_srtp_demux, "sink");
217   ghost_pad = gst_ghost_pad_new_from_template ("sink", target_pad, templ);
218   gst_object_unref (target_pad);
219   g_return_if_fail (ghost_pad);
220 
221   ret = gst_element_add_pad (GST_ELEMENT (self), ghost_pad);
222   g_return_if_fail (ret);
223 
224   g_signal_connect (self->srtp_dec, "request-key",
225       G_CALLBACK (on_decoder_request_key), self);
226   g_signal_connect (self->bin.dtls_element, "notify::peer-pem",
227       G_CALLBACK (on_peer_pem), self);
228 }
229 
230 static void
gst_dtls_srtp_dec_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)231 gst_dtls_srtp_dec_set_property (GObject * object,
232     guint prop_id, const GValue * value, GParamSpec * pspec)
233 {
234   GstDtlsSrtpDec *self = GST_DTLS_SRTP_DEC (object);
235 
236   switch (prop_id) {
237     case PROP_PEM:
238       if (self->bin.dtls_element) {
239         g_object_set_property (G_OBJECT (self->bin.dtls_element), "pem", value);
240       } else {
241         GST_WARNING_OBJECT (self, "tried to set pem after disabling DTLS");
242       }
243       break;
244     default:
245       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
246   }
247 }
248 
249 static void
gst_dtls_srtp_dec_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)250 gst_dtls_srtp_dec_get_property (GObject * object,
251     guint prop_id, GValue * value, GParamSpec * pspec)
252 {
253   GstDtlsSrtpDec *self = GST_DTLS_SRTP_DEC (object);
254 
255   switch (prop_id) {
256     case PROP_PEM:
257       if (self->bin.dtls_element) {
258         g_object_get_property (G_OBJECT (self->bin.dtls_element), "pem", value);
259       } else {
260         GST_WARNING_OBJECT (self, "tried to get pem after disabling DTLS");
261       }
262       break;
263     case PROP_PEER_PEM:
264       if (self->bin.dtls_element) {
265         g_object_get_property (G_OBJECT (self->bin.dtls_element), "peer-pem",
266             value);
267       } else {
268         GST_WARNING_OBJECT (self, "tried to get peer-pem after disabling DTLS");
269       }
270       break;
271     default:
272       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
273   }
274 }
275 
276 static GstPad *
gst_dtls_srtp_dec_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * name,const GstCaps * caps)277 gst_dtls_srtp_dec_request_new_pad (GstElement * element,
278     GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
279 {
280   GstDtlsSrtpDec *self = GST_DTLS_SRTP_DEC (element);
281   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
282   GstPad *ghost_pad = NULL;
283   gboolean ret;
284 
285   GST_DEBUG_OBJECT (element, "pad requested");
286 
287   g_return_val_if_fail (self->bin.dtls_element, NULL);
288   g_return_val_if_fail (!self->bin.key_is_set, NULL);
289 
290   if (templ == gst_element_class_get_pad_template (klass, "data_src")) {
291     GstPad *target_pad;
292 
293     target_pad = gst_element_get_request_pad (self->bin.dtls_element, "src");
294 
295     ghost_pad = gst_ghost_pad_new_from_template (name, target_pad, templ);
296     gst_object_unref (target_pad);
297     g_return_val_if_fail (ghost_pad, NULL);
298 
299     ret = gst_pad_set_active (ghost_pad, TRUE);
300     g_return_val_if_fail (ret, NULL);
301     ret = gst_element_add_pad (element, ghost_pad);
302     g_return_val_if_fail (ret, NULL);
303 
304     GST_LOG_OBJECT (self, "added data src pad");
305 
306     if (caps) {
307       g_object_set (ghost_pad, "caps", caps, NULL);
308     }
309 
310     return ghost_pad;
311   }
312 
313   g_return_val_if_reached (NULL);
314 }
315 
316 static void
gst_dtls_srtp_dec_release_pad(GstElement * element,GstPad * pad)317 gst_dtls_srtp_dec_release_pad (GstElement * element, GstPad * pad)
318 {
319   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
320   GstDtlsSrtpDec *self = GST_DTLS_SRTP_DEC (element);
321 
322   if (GST_PAD_PAD_TEMPLATE (pad) ==
323       gst_element_class_get_pad_template (klass, "data_src")) {
324     GstGhostPad *ghost_pad;
325     GstPad *target_pad;
326 
327     ghost_pad = GST_GHOST_PAD (pad);
328     target_pad = gst_ghost_pad_get_target (ghost_pad);
329 
330     if (target_pad != NULL) {
331       gst_element_release_request_pad (self->bin.dtls_element, target_pad);
332 
333       gst_object_unref (target_pad);
334       gst_ghost_pad_set_target (GST_GHOST_PAD (pad), NULL);
335     }
336   }
337 
338   gst_element_remove_pad (element, pad);
339 }
340 
341 static GstCaps *
on_decoder_request_key(GstElement * srtp_decoder,guint ssrc,GstDtlsSrtpBin * bin)342 on_decoder_request_key (GstElement * srtp_decoder,
343     guint ssrc, GstDtlsSrtpBin * bin)
344 {
345   GstCaps *key_caps;
346   GstBuffer *key_buffer = NULL;
347   guint cipher;
348   guint auth;
349 
350   if (bin->key_is_set) {
351     if (bin->key) {
352       if (bin->srtp_cipher && bin->srtp_auth && bin->srtcp_cipher
353           && bin->srtcp_auth) {
354         GST_DEBUG_OBJECT (bin, "setting srtp key");
355         return gst_caps_new_simple ("application/x-srtp",
356             "srtp-key", GST_TYPE_BUFFER, gst_buffer_copy (bin->key),
357             "srtp-auth", G_TYPE_STRING, bin->srtp_auth,
358             "srtcp-auth", G_TYPE_STRING, bin->srtcp_auth,
359             "srtp-cipher", G_TYPE_STRING, bin->srtp_cipher,
360             "srtcp-cipher", G_TYPE_STRING, bin->srtcp_cipher, NULL);
361       } else {
362         GST_WARNING_OBJECT (bin,
363             "srtp key is set but not all ciphers and auths");
364         return NULL;
365       }
366     }
367 
368     GST_DEBUG_OBJECT (bin, "setting srtp key to null");
369     return gst_caps_new_simple ("application/x-srtp",
370         "srtp-key", GST_TYPE_BUFFER, NULL,
371         "srtp-auth", G_TYPE_STRING, "null",
372         "srtcp-auth", G_TYPE_STRING, "null",
373         "srtp-cipher", G_TYPE_STRING, "null",
374         "srtcp-cipher", G_TYPE_STRING, "null", NULL);
375   }
376 
377   if (bin->dtls_element) {
378     g_object_get (bin->dtls_element, "decoder-key", &key_buffer, NULL);
379   }
380 
381   if (key_buffer) {
382     g_object_get (bin->dtls_element,
383         "srtp-cipher", &cipher, "srtp-auth", &auth, NULL);
384 
385     g_return_val_if_fail (cipher == GST_DTLS_SRTP_CIPHER_AES_128_ICM, NULL);
386 
387     key_caps = gst_caps_new_simple ("application/x-srtp",
388         "srtp-key", GST_TYPE_BUFFER, key_buffer,
389         "srtp-cipher", G_TYPE_STRING, "aes-128-icm",
390         "srtcp-cipher", G_TYPE_STRING, "aes-128-icm", NULL);
391 
392     switch (auth) {
393       case GST_DTLS_SRTP_AUTH_HMAC_SHA1_32:
394         gst_caps_set_simple (key_caps,
395             "srtp-auth", G_TYPE_STRING, "hmac-sha1-32",
396             "srtcp-auth", G_TYPE_STRING, "hmac-sha1-32", NULL);
397         break;
398       case GST_DTLS_SRTP_AUTH_HMAC_SHA1_80:
399         gst_caps_set_simple (key_caps,
400             "srtp-auth", G_TYPE_STRING, "hmac-sha1-80",
401             "srtcp-auth", G_TYPE_STRING, "hmac-sha1-80", NULL);
402         break;
403       default:
404         g_return_val_if_reached (NULL);
405         break;
406     }
407 
408     gst_buffer_unref (key_buffer);
409 
410     return key_caps;
411   } else {
412     GST_WARNING_OBJECT (bin, "no srtp key available yet");
413   }
414 
415   return NULL;
416 }
417 
418 static void
on_peer_pem(GstElement * srtp_decoder,GParamSpec * pspec,GstDtlsSrtpDec * self)419 on_peer_pem (GstElement * srtp_decoder, GParamSpec * pspec,
420     GstDtlsSrtpDec * self)
421 {
422   g_return_if_fail (self);
423   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PEER_PEM]);
424 }
425 
426 static void
gst_dtls_srtp_dec_remove_dtls_element(GstDtlsSrtpBin * bin)427 gst_dtls_srtp_dec_remove_dtls_element (GstDtlsSrtpBin * bin)
428 {
429   GstDtlsSrtpDec *self = GST_DTLS_SRTP_DEC (bin);
430   GstPad *demux_pad;
431   gulong id;
432 
433   if (!bin->dtls_element) {
434     return;
435   }
436 
437   demux_pad = gst_element_get_static_pad (self->dtls_srtp_demux, "dtls_src");
438 
439   id = gst_pad_add_probe (demux_pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
440       (GstPadProbeCallback) remove_dtls_decoder_probe_callback,
441       bin->dtls_element, NULL);
442   g_return_if_fail (id);
443   bin->dtls_element = NULL;
444 
445   gst_pad_push_event (demux_pad,
446       gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
447           gst_structure_new_empty ("dummy")));
448 
449   gst_object_unref (demux_pad);
450 }
451 
452 static GstPadProbeReturn
remove_dtls_decoder_probe_callback(GstPad * pad,GstPadProbeInfo * info,GstElement * element)453 remove_dtls_decoder_probe_callback (GstPad * pad,
454     GstPadProbeInfo * info, GstElement * element)
455 {
456   gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));
457 
458   gst_element_set_state (GST_ELEMENT (element), GST_STATE_NULL);
459   gst_bin_remove (GST_BIN (GST_ELEMENT_PARENT (element)), element);
460 
461   return GST_PAD_PROBE_OK;
462 }
463