]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
rtp: Enable srtp replay protection
authorAlexander Traud <pabstraud@compuserve.com>
Tue, 26 Jan 2021 17:09:53 +0000 (10:09 -0700)
committerGeorge Joseph <gjoseph@digium.com>
Thu, 18 Feb 2021 16:36:30 +0000 (10:36 -0600)
Add option "srtpreplayprotection" rtp.conf to enable srtp
replay protection.

ASTERISK-29260
Reported by: Alexander Traud

Change-Id: I5cd346e3c6b6812039d1901aa4b7be688173b458

configs/samples/rtp.conf.sample
doc/CHANGES-staging/srtp_replay_protection.txt [new file with mode: 0644]
doc/UPGRADE-staging/srtp_replay_protection.txt [new file with mode: 0644]
res/res_rtp_asterisk.c
res/res_srtp.c

index f002449da1c72297a4fb1c70a3927a476ec128ee..7ab81b3bc4b631f56350d49ff0ad888881eaa021 100644 (file)
@@ -45,6 +45,18 @@ rtpend=20000
 ; connected. This option is set to 4 by default.
 ; probation=8
 ;
+; Enable sRTP replay protection. Buggy SIP user agents (UAs) reset the
+; sequence number (RTP-SEQ) on a re-INVITE, for example, with Session Timers
+; or on Call Hold/Resume, but keep the synchronization source (RTP-SSRC). If
+; the new RTP-SEQ is higher than the previous one, the call continues if the
+; roll-over counter (sRTP-ROC) is zero (the call lasted less than 22 minutes).
+; In all other cases, the call faces one-way audio or even no audio at all.
+; "replay check failed (index too old)" gets printed continuously. This is a
+; software bug. You have to report this to the creator of that UA. Until it is
+; fixed, you could disable sRTP replay protection (see RFC 3711 section 3.3.2).
+; This option is enabled by default.
+; srtpreplayprotection=yes
+;
 ; Whether to enable or disable ICE support. This option is enabled by default.
 ; icesupport=false
 ;
diff --git a/doc/CHANGES-staging/srtp_replay_protection.txt b/doc/CHANGES-staging/srtp_replay_protection.txt
new file mode 100644 (file)
index 0000000..945ddb5
--- /dev/null
@@ -0,0 +1,9 @@
+Subject: res_srtp
+
+SRTP replay protection has been added to res_srtp and
+a new configuration option "srtpreplayprotection" has
+been added to the rtp.conf config file.  For security
+reasons, the default setting is "yes".  Buggy clients
+may not handle this correctly which could result in
+no, or one way, audio and Asterisk error messages like
+"replay check failed".
diff --git a/doc/UPGRADE-staging/srtp_replay_protection.txt b/doc/UPGRADE-staging/srtp_replay_protection.txt
new file mode 100644 (file)
index 0000000..945ddb5
--- /dev/null
@@ -0,0 +1,9 @@
+Subject: res_srtp
+
+SRTP replay protection has been added to res_srtp and
+a new configuration option "srtpreplayprotection" has
+been added to the rtp.conf config file.  For security
+reasons, the default setting is "yes".  Buggy clients
+may not handle this correctly which could result in
+no, or one way, audio and Asterisk error messages like
+"replay check failed".
index 2ed18318f0a3cfdec8ea67868c7ea2028488a5ce..5a41d9c37af76ec1a142b093e05f5ebab282526c 100644 (file)
@@ -179,6 +179,7 @@ enum strict_rtp_mode {
 #define STRICT_RTP_LEARN_TIMEOUT       5000
 
 #define DEFAULT_STRICT_RTP STRICT_RTP_YES      /*!< Enabled by default */
+#define DEFAULT_SRTP_REPLAY_PROTECTION 1
 #define DEFAULT_ICESUPPORT 1
 #define DEFAULT_DTLS_MTU 1200
 
@@ -203,6 +204,7 @@ static int nochecksums;
 static int strictrtp = DEFAULT_STRICT_RTP; /*!< Only accept RTP frames from a defined source. If we receive an indication of a changing source, enter learning mode. */
 static int learning_min_sequential = DEFAULT_LEARNING_MIN_SEQUENTIAL; /*!< Number of sequential RTP frames needed from a single source during learning mode to accept new source. */
 static int learning_min_duration = DEFAULT_LEARNING_MIN_DURATION; /*!< Lowest acceptable timeout between the first and the last sequential RTP frame. */
+static int srtp_replay_protection = DEFAULT_SRTP_REPLAY_PROTECTION;
 #if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
 static int dtls_mtu = DEFAULT_DTLS_MTU;
 #endif
@@ -6022,7 +6024,7 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, s
 
        /* If this is encrypted then decrypt the payload */
        if ((*rtcpheader & 0xC0) && res_srtp && srtp && res_srtp->unprotect(
-                   srtp, rtcpheader, &len, 1) < 0) {
+                   srtp, rtcpheader, &len, 1 | (srtp_replay_protection << 1)) < 0) {
           return &ast_null_frame;
        }
 
@@ -7109,7 +7111,7 @@ static struct ast_frame *ast_rtp_interpret(struct ast_rtp_instance *instance, st
 
        /* If this payload is encrypted then decrypt it using the given SRTP instance */
        if ((*read_area & 0xC0) && res_srtp && srtp && res_srtp->unprotect(
-                   srtp, read_area, &res, 0) < 0) {
+                   srtp, read_area, &res, 0 | (srtp_replay_protection << 1)) < 0) {
                return &ast_null_frame;
        }
 
@@ -8897,6 +8899,8 @@ static char *handle_cli_rtp_settings(struct ast_cli_entry *e, int cmd, struct as
        if (strictrtp) {
                ast_cli(a->fd, "  Probation:       %d frames\n", learning_min_sequential);
        }
+
+       ast_cli(a->fd, "  Replay Protect:  %s\n", AST_CLI_YESNO(srtp_replay_protection));
 #ifdef HAVE_PJPROJECT
        ast_cli(a->fd, "  ICE support:     %s\n", AST_CLI_YESNO(icesupport));
 #endif
@@ -8999,6 +9003,7 @@ static int rtp_reload(int reload, int by_external_config)
        strictrtp = DEFAULT_STRICT_RTP;
        learning_min_sequential = DEFAULT_LEARNING_MIN_SEQUENTIAL;
        learning_min_duration = DEFAULT_LEARNING_MIN_DURATION;
+       srtp_replay_protection = DEFAULT_SRTP_REPLAY_PROTECTION;
 
        /** This resource is not "reloaded" so much as unloaded and loaded again.
         * In the case of the TURN related variables, the memory referenced by a
@@ -9078,6 +9083,9 @@ static int rtp_reload(int reload, int by_external_config)
                }
                learning_min_duration = CALC_LEARNING_MIN_DURATION(learning_min_sequential);
        }
+       if ((s = ast_variable_retrieve(cfg, "general", "srtpreplayprotection"))) {
+               srtp_replay_protection = ast_true(s);
+       }
 #ifdef HAVE_PJPROJECT
        if ((s = ast_variable_retrieve(cfg, "general", "icesupport"))) {
                icesupport = ast_true(s);
index 78742160798867efb9da18326e365cdcf6dd7b55..3519def7ed267a073536e1ae00a6e98e1fdbaf8e 100644 (file)
@@ -364,11 +364,12 @@ static void ast_srtp_set_cb(struct ast_srtp *srtp, const struct ast_srtp_cb *cb,
 }
 
 /* Vtable functions */
-static int ast_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len, int rtcp)
+static int ast_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len, int flags)
 {
        int res = 0;
        int i;
-       int retry = 0;
+       int rtcp  = (flags & 0x01) >> 0;
+       int retry = (flags & 0x02) >> 1;
        struct ast_rtp_instance_stats stats = {0,};
 
 tryagain: