ikev2: Destroy IKE_SA when receiving invalid authenticated requests
authorTobias Brunner <tobias@strongswan.org>
Mon, 25 Nov 2019 13:43:36 +0000 (14:43 +0100)
committerTobias Brunner <tobias@strongswan.org>
Mon, 9 Dec 2019 11:26:54 +0000 (12:26 +0100)
RFC 7296, section 2.21.3:

   If a peer parsing a request notices that it is badly formatted (after
   it has passed the message authentication code checks and window
   checks) and it returns an INVALID_SYNTAX notification, then this
   error notification is considered fatal in both peers, meaning that
   the IKE SA is deleted without needing an explicit Delete payload.

src/libcharon/sa/ikev2/task_manager_v2.c

index ee3422c..0b223d3 100644 (file)
@@ -1325,16 +1325,35 @@ static void send_notify_response(private_task_manager_t *this,
 }
 
 /**
+ * Send an INVALID_SYNTAX notify and destroy the IKE_SA for authenticated
+ * messages.
+ */
+static status_t send_invalid_syntax(private_task_manager_t *this,
+                                                                       message_t *msg)
+{
+       send_notify_response(this, msg, INVALID_SYNTAX, chunk_empty);
+       incr_mid(this, FALSE);
+
+       /* IKE_SA_INIT is currently the only type the parser accepts unprotected,
+        * don't destroy the IKE_SA if such a message is invalid */
+       if (msg->get_exchange_type(msg) == IKE_SA_INIT)
+       {
+               return FAILED;
+       }
+       return DESTROY_ME;
+}
+
+/**
  * Parse the given message and verify that it is valid.
  */
 static status_t parse_message(private_task_manager_t *this, message_t *msg)
 {
-       status_t status;
+       status_t parse_status, status;
        uint8_t type = 0;
 
-       status = msg->parse_body(msg, this->ike_sa->get_keymat(this->ike_sa));
+       parse_status = msg->parse_body(msg, this->ike_sa->get_keymat(this->ike_sa));
 
-       if (status == SUCCESS)
+       if (parse_status == SUCCESS)
        {       /* check for unsupported critical payloads */
                enumerator_t *enumerator;
                unknown_payload_t *unknown;
@@ -1350,8 +1369,8 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg)
                                {
                                        type = unknown->get_type(unknown);
                                        DBG1(DBG_ENC, "payload type %N is not supported, "
-                                                "but its critical!", payload_type_names, type);
-                                       status = NOT_SUPPORTED;
+                                                "but payload is critical!", payload_type_names, type);
+                                       parse_status = NOT_SUPPORTED;
                                        break;
                                }
                        }
@@ -1359,11 +1378,13 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg)
                enumerator->destroy(enumerator);
        }
 
-       if (status != SUCCESS)
+       status = parse_status;
+
+       if (parse_status != SUCCESS)
        {
                bool is_request = msg->get_request(msg);
 
-               switch (status)
+               switch (parse_status)
                {
                        case NOT_SUPPORTED:
                                DBG1(DBG_IKE, "critical unknown payloads found");
@@ -1379,18 +1400,14 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg)
                                DBG1(DBG_IKE, "message parsing failed");
                                if (is_request)
                                {
-                                       send_notify_response(this, msg,
-                                                                                INVALID_SYNTAX, chunk_empty);
-                                       incr_mid(this, FALSE);
+                                       status = send_invalid_syntax(this, msg);
                                }
                                break;
                        case VERIFY_ERROR:
                                DBG1(DBG_IKE, "message verification failed");
                                if (is_request)
                                {
-                                       send_notify_response(this, msg,
-                                                                                INVALID_SYNTAX, chunk_empty);
-                                       incr_mid(this, FALSE);
+                                       status = send_invalid_syntax(this, msg);
                                }
                                break;
                        case FAILED:
@@ -1407,11 +1424,25 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg)
                         is_request ? "request" : "response",
                         msg->get_message_id(msg));
 
-               charon->bus->alert(charon->bus, ALERT_PARSE_ERROR_BODY, msg, status);
+               charon->bus->alert(charon->bus, ALERT_PARSE_ERROR_BODY, msg,
+                                                  parse_status);
 
-               if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED)
-               {       /* invalid initiation attempt, close SA */
-                       return DESTROY_ME;
+               switch (this->ike_sa->get_state(this->ike_sa))
+               {
+                       case IKE_CREATED:
+                               /* invalid initiation attempt, close SA */
+                               status = DESTROY_ME;
+                               break;
+                       case IKE_CONNECTING:
+                       case IKE_REKEYED:
+                               /* don't trigger updown event in these states */
+                               break;
+                       default:
+                               if (status == DESTROY_ME)
+                               {
+                                       charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                               }
+                               break;
                }
        }
        return status;