Need help on RTSP server and TLS including verify peer (ca cert) and (cert,key).

enthusiastic geek enthusiasticgeek at gmail.com
Mon Aug 1 21:59:43 UTC 2016


I have modified RTSP server example (test-video.c) as follows to include
cacert as a database file. However on the receiving end I am not sure how to
construct the rtspsrc pipeline with TLS (I can use pipeline without TLS but
I want TLS). If I remove 
  
gst_rtsp_auth_set_tls_authentication_mode(auth,
G_TLS_AUTHENTICATION_REQUIRED);
 from the server code then both my server and client code work, but it
defeats the purpose of x509 digital certificates. Can anyone who is
experienced in "tls-database" property on rtspsrc or "tls-interaction"
property on rtspsrc guide me on this?
RTSP Server


#include <gst/gst.h>
#include <gst/rtsp-server/rtsp-server.h>
#include <gio/gio.h>

/* define this if you want the resource to only be available when using
 * user/password as the password */
#undef WITH_AUTH
#define WITH_AUTH 1

/* define this if you want the server to use TLS (it will also need
WITH_AUTH
 * to be defined) */
#undef WITH_TLS
#define WITH_TLS 1

/* this timeout is periodically run to clean up the expired sessions from
the
 * pool. This needs to be run explicitly currently but might be done
 * automatically as part of the mainloop. */
static gboolean
timeout (GstRTSPServer * server)
{
  GstRTSPSessionPool *pool;

  pool = gst_rtsp_server_get_session_pool (server);
  gst_rtsp_session_pool_cleanup (pool);
  g_object_unref (pool);

  return TRUE;
}

int
main (int argc, char *argv[])
{
  GMainLoop *loop;
  GstRTSPServer *server;
  GstRTSPMountPoints *mounts;
  GstRTSPMediaFactory *factory;
#ifdef WITH_AUTH
  GstRTSPAuth *auth;
  GstRTSPToken *token;
  gchar *basic;
  GstRTSPPermissions *permissions;
#endif
#ifdef WITH_TLS
  GTlsCertificate *cert;
  GTlsCertificate *ca_cert;
  GError *error = NULL;
#endif

  gst_init (&argc, &argv);

  loop = g_main_loop_new (NULL, FALSE);

  /* create a server instance */
  server = gst_rtsp_server_new ();

#ifdef WITH_AUTH
  /* make a new authentication manager. it can be added to control access to
all
   * the factories on the server or on individual factories. */
  auth = gst_rtsp_auth_new ();
#ifdef WITH_TLS
  cert =
g_tls_certificate_new_from_files("/home/enthusiasticgeek/toyIntermediate.pem","/home/enthusiasticgeek/toyDecryptedIntermediate.key",&error);
/*  cert = g_tls_certificate_new_from_pem ("-----BEGIN CERTIFICATE-----"
      "MIICJjCCAY+gAwIBAgIBBzANBgkqhkiG9w0BAQUFADCBhjETMBEGCgmSJomT8ixk"
      "ARkWA0NPTTEXMBUGCgmSJomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRp"
      "ZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkq"
      "hkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tMB4XDTExMDExNzE5NDcxN1oXDTIxMDEx"
      "NDE5NDcxN1owSzETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmSJomT8ixkARkW"
      "B0VYQU1QTEUxGzAZBgNVBAMTEnNlcnZlci5leGFtcGxlLmNvbTBcMA0GCSqGSIb3"
      "DQEBAQUAA0sAMEgCQQDYScTxk55XBmbDM9zzwO+grVySE4rudWuzH2PpObIonqbf"
      "hRoAalKVluG9jvbHI81eXxCdSObv1KBP1sbN5RzpAgMBAAGjIjAgMAkGA1UdEwQC"
      "MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAYx6fMqT1"
      "Gvo0jq88E8mc+bmp4LfXD4wJ7KxYeadQxt75HFRpj4FhFO3DOpVRFgzHlOEo3Fwk"
      "PZOKjvkT0cbcoEq5whLH25dHoQxGoVQgFyAP5s+7Vp5AlHh8Y/vAoXeEVyy/RCIH"
      "QkhUlAflfDMcrrYjsmwoOPSjhx6Mm/AopX4="
      "-----END CERTIFICATE-----"
      "-----BEGIN PRIVATE KEY-----"
      "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEA2EnE8ZOeVwZmwzPc"
      "88DvoK1ckhOK7nVrsx9j6TmyKJ6m34UaAGpSlZbhvY72xyPNXl8QnUjm79SgT9bG"
      "zeUc6QIDAQABAkBRFJZ32VbqWMP9OVwDJLiwC01AlYLnka0mIQZbT/2xq9dUc9GW"
      "U3kiVw4lL8v/+sPjtTPCYYdzHHOyDen6znVhAiEA9qJT7BtQvRxCvGrAhr9MS022"
      "tTdPbW829BoUtIeH64cCIQDggG5i48v7HPacPBIH1RaSVhXl8qHCpQD3qrIw3FMw"
      "DwIga8PqH5Sf5sHedy2+CiK0V4MRfoU4c3zQ6kArI+bEgSkCIQCLA1vXBiE31B5s"
      "bdHoYa1BXebfZVd+1Hd95IfEM5mbRwIgSkDuQwV55BBlvWph3U8wVIMIb4GStaH8"
      "W535W8UBbEg=" "-----END PRIVATE KEY-----", -1, &error);
*/
  if (cert == NULL) {
    g_printerr ("failed to parse PEM: %s\n", error->message);
    return -1;
  }
  GTlsDatabase* database = g_tls_file_database_new
("/home/enthusiasticgeek/toyCA.pem", &error);
  gst_rtsp_auth_set_tls_database (auth, database);
  ca_cert =
g_tls_certificate_new_from_file("/home/enthusiasticgeek/toyCA.pem",&error);
  if (ca_cert == NULL) {
    g_printerr ("failed to parse CA PEM: %s\n", error->message);
    return -1;
  }
  gst_rtsp_auth_set_tls_authentication_mode(auth,
G_TLS_AUTHENTICATION_REQUIRED);
 GTlsCertificateFlags verification = g_tls_certificate_verify(cert, NULL,
ca_cert);
  g_print("verification code = %d\n", verification);
  gst_rtsp_auth_set_tls_certificate (auth, cert);
  g_object_unref (cert);
#endif

  /* make user token */
  token =
      gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
      "user", NULL);
  basic = gst_rtsp_auth_make_basic ("user", "password");
  gst_rtsp_auth_add_basic (auth, basic, token);
  g_free (basic);
  gst_rtsp_token_unref (token);

  /* configure in the server */
  gst_rtsp_server_set_auth (server, auth);
#endif

  /* get the mount points for this server, every server has a default object
   * that be used to map uri mount points to media factories */
  mounts = gst_rtsp_server_get_mount_points (server);

  /* make a media factory for a test stream. The default media factory can
use
   * gst-launch syntax to create pipelines.
   * any launch line works as long as it contains elements named pay%d. Each
   * element with pay%d names will be a stream */
  factory = gst_rtsp_media_factory_new ();
  gst_rtsp_media_factory_set_launch (factory, "( "
      "videotestsrc ! video/x-raw,width=352,height=288,framerate=15/1 ! "
      "x264enc ! rtph264pay name=pay0 pt=96 "
      "audiotestsrc ! audio/x-raw,rate=8000 ! "
      "alawenc ! rtppcmapay name=pay1 pt=97 " ")");
#ifdef WITH_AUTH
  /* add permissions for the user media role */
  permissions = gst_rtsp_permissions_new ();
  gst_rtsp_permissions_add_role (permissions, "user",
      GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
      GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
  gst_rtsp_media_factory_set_permissions (factory, permissions);
  gst_rtsp_permissions_unref (permissions);
#ifdef WITH_TLS
  gst_rtsp_media_factory_set_profiles (factory, GST_RTSP_PROFILE_SAVP);
#endif
#endif

  /* attach the test factory to the /test url */
  gst_rtsp_mount_points_add_factory (mounts, "/test", factory);

  /* don't need the ref to the mapper anymore */
  g_object_unref (mounts);

  /* attach the server to the default maincontext */
  if (gst_rtsp_server_attach (server, NULL) == 0)
    goto failed;

  /* add a timeout for the session cleanup */
  g_timeout_add_seconds (2, (GSourceFunc) timeout, server);

  /* start serving, this never stops */
#ifdef WITH_TLS
  g_print ("stream ready at rtsps://127.0.0.1:8554/test\n");
#else
  g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
#endif
  g_main_loop_run (loop);

  return 0;

  /* ERRORS */
failed:
  {
    g_print ("failed to attach the server\n");
    return -1;
  }
}



My client code is as follows
RTSP Client


//Display RTSP streaming of video
 //(c) 2011 enthusiasticgeek
 // This code 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.  

#include <string.h>
#include <math.h>
#include <gst/gst.h>
#include <glib.h>
#include <gio/gio.h>

static gboolean bus_call (GstBus *bus,GstMessage *msg, gpointer data){
  GMainLoop *loop = (GMainLoop *) data;

  switch (GST_MESSAGE_TYPE (msg)) {

    case GST_MESSAGE_EOS:
      g_print ("Stream Ends\n");
      g_main_loop_quit (loop);
      break;

    case GST_MESSAGE_ERROR: {
      gchar  *debug;
      GError *error;

      gst_message_parse_error (msg, &error, &debug);
      g_free (debug);

      g_printerr ("Error: %s\n", error->message);
      g_error_free (error);

      g_main_loop_quit (loop);
      break;
    }
    default:
      break;
  }

  return TRUE;
}

static void on_pad_added (GstElement *element, GstPad *pad, gpointer data){

  GstPad *sinkpad;
  GstElement *decoder = (GstElement *) data;

  /* We can now link this pad with the rtsp-decoder sink pad */
  g_print ("Dynamic pad created, linking source/demuxer\n");

  sinkpad = gst_element_get_static_pad (decoder, "sink");

  gst_pad_link (pad, sinkpad);

  gst_object_unref (sinkpad);
}

int main (int argc, char *argv[])
{
  GMainLoop *loop;
  GstBus *bus;
  GstElement *source;
  GstElement *decoder;
  GstElement *sink;
  GstElement *pipeline;
  GstElement *demux;
  GstElement *parse;
  GstElement *videoconvert;

  /* Initializing GStreamer */
  gst_init (&argc, &argv);
  loop = g_main_loop_new (NULL, FALSE);

 //gst-launch-0.10 rtspsrc location=rtsp://<ip> ! decodebin !
ffmpegvideoconvert ! autovideosink
 //gst-launch -v rtspsrc location="rtsp://<ip> ! rtpmp4vdepay !
mpeg4videoparse ! ffdec_mpeg4 ! ffmpegvideoconvert! autovideosink
 //gst-launch -v rtspsrc location="rtsp://<ip> ! rtpmp4vdepay ! ffdec_mpeg4
! ffmpegvideoconvert! autovideosink
  /* Create Pipe's Elements */
  pipeline = gst_pipeline_new ("video player");
  g_assert (pipeline);
  source   = gst_element_factory_make ("rtspsrc", "Source");
  g_assert (source);
  demux = gst_element_factory_make ("rtph264depay", "Depay");
  g_assert (demux);
  parse = gst_element_factory_make ("h264parse", "Parse");
  g_assert (parse);
  decoder = gst_element_factory_make ("avdec_h264", "Decoder");
  g_assert (decoder);
  videoconvert     = gst_element_factory_make ("videoconvert",
"VideoConvert");
  g_assert(videoconvert);
  sink     = gst_element_factory_make ("autovideosink", "Output");
  g_assert (sink);

  /*Make sure: Every elements was created ok*/
  if (!pipeline || !source || !demux || !parse || !decoder || !videoconvert
|| !sink) {
    g_printerr ("One of the elements wasn't create... Exiting\n");
    return -1;
  }

  g_print(" \nPipeline is Part(A) ->(dynamic/runtime link)  Part(B)[
Part(B-1) -> Part(B-2) -> Part(B-3) ]\n\n");
  g_print("
[source](dynamic)->(dynamic)[demux]->[parse]->[decoder]->[videoconvert]->[videosink]
\n\n");

  /* Set video Source */
  g_object_set (G_OBJECT (source), "location", argv[1], NULL);
  //g_object_set (G_OBJECT (source), "do-rtcp", TRUE, NULL);
  g_object_set (G_OBJECT (source), "latency", 0, NULL);
  g_object_set (G_OBJECT (source), "user-id", "user", NULL);
  g_object_set (G_OBJECT (source), "user-pw", "password", NULL);
  // generic error
  //g_object_set (G_OBJECT (source), "tls-validation-flags", 0x00000040,
NULL);
  //validate all
  g_object_set (G_OBJECT (source), "tls-validation-flags", 0x0000007f,
NULL);
  GTlsCertificate *cert;
  GTlsCertificate *ca_cert;
  GError *error=NULL;
  cert =
g_tls_certificate_new_from_files("/home/enthusiasticgeek/toyIntermediate.pem","/home/enthusiasticgeek/toyDecryptedIntermediate.key",&error);
 if (cert == NULL) {
    g_printerr ("failed to parse PEM: %s\n", error->message);
    return -1;
  }
  //GTlsDatabase* database = g_tls_file_database_new
("/home/enthusiasticgeek/toyCA.pem", &error);
  GTlsDatabase* database = g_tls_file_database_new
("/home/enthusiasticgeek/toyCombined.pem", &error);
  ca_cert =
g_tls_certificate_new_from_file("/home/enthusiasticgeek/toyCA.pem",&error);
  if (ca_cert == NULL) {
    g_printerr ("failed to parse CA PEM: %s\n", error->message);
    return -1;
  }
  g_object_set (G_OBJECT (source), "tls-database", database, NULL);

  /* Putting a Message handler */
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  gst_bus_add_watch (bus, bus_call, loop);
  gst_object_unref (bus);

  /* Add Elements to the Bin */
  gst_bin_add_many (GST_BIN (pipeline), source, demux, parse, decoder,
videoconvert, sink, NULL);

  /* Link confirmation */
  if (!gst_element_link_many (demux, parse, decoder, videoconvert, sink,
NULL)){
     g_warning ("Linking part (B) Fail...");
  }

  g_print("\nNote that the source will be linked to the demuxer(depayload)
dynamically.\n\
     The reason is that rtspsrc may contain various elements (for example\n\
     audio and video). The source pad(s) will be created at run time,\n\
     by the rtspsrc when it detects the amount and nature of elements.\n\
     Therefore we connect a callback function which will be executed\n\
     when the \"pad-added\" is emitted.\n");

  /* Dynamic Pad Creation */
  if(! g_signal_connect (source, "pad-added", G_CALLBACK
(on_pad_added),demux))
  {
    g_warning ("Linking part (A) with part (B) Fail...");
  }
  /* Run the pipeline */
  g_print ("Playing: %s\n", argv[1]);
  gst_element_set_state (pipeline, GST_STATE_PLAYING);
  g_main_loop_run (loop);

  /* Ending Playback */
  g_print ("End of the Streaming... ending the playback\n");
  gst_element_set_state (pipeline, GST_STATE_NULL);

  /* Eliminating Pipeline */
  g_print ("Eliminating Pipeline\n");
  gst_object_unref (GST_OBJECT (pipeline));

  return 0;
}                          




--
View this message in context: http://gstreamer-devel.966125.n4.nabble.com/Need-help-on-RTSP-server-and-TLS-including-verify-peer-ca-cert-and-cert-key-tp4678923.html
Sent from the GStreamer-devel mailing list archive at Nabble.com.


More information about the gstreamer-devel mailing list