P2PS: Add validation for P2PS PD Request
authorIlan Peer <ilan.peer@intel.com>
Thu, 24 Sep 2015 17:38:00 +0000 (20:38 +0300)
committerJouni Malinen <j@w1.fi>
Wed, 7 Oct 2015 14:07:22 +0000 (17:07 +0300)
Validate that all the required attributes appear in a P2PS PD Request,
and in addition, in the case of follow-on PD Request, check that the
given values match those of the original PD Request.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
src/p2p/p2p_pd.c

index dadc0b5..6363e36 100644 (file)
@@ -427,6 +427,117 @@ static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask)
 }
 
 
+/* Check if the message contains a valid P2PS PD Request */
+static int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg,
+                               const u8 *addr)
+{
+       u8 group_id = 0;
+       u8 intended_addr = 0;
+       u8 operating_channel = 0;
+       u8 channel_list = 0;
+       u8 config_timeout = 0;
+       u8 listen_channel = 0;
+
+#define P2PS_PD_REQ_CHECK(_val, _attr) \
+do { \
+       if ((_val) && !msg->_attr) { \
+               p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \
+               return -1; \
+       } \
+} while (0)
+
+       P2PS_PD_REQ_CHECK(1, adv_id);
+       P2PS_PD_REQ_CHECK(1, session_id);
+       P2PS_PD_REQ_CHECK(1, session_mac);
+       P2PS_PD_REQ_CHECK(1, adv_mac);
+       P2PS_PD_REQ_CHECK(1, capability);
+       P2PS_PD_REQ_CHECK(1, p2p_device_info);
+       P2PS_PD_REQ_CHECK(1, feature_cap);
+
+       /*
+        * We don't need to check Connection Capability, Persistent Group,
+        * and related attributes for follow-on PD Request with a status
+        * other than SUCCESS_DEFERRED.
+        */
+       if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED)
+               return 0;
+
+       P2PS_PD_REQ_CHECK(1, conn_cap);
+
+       /*
+        * Note 1: A feature capability attribute structure can be changed
+        * in the future. The assumption is that such modifications are
+        * backward compatible, therefore we allow processing of msg.feature_cap
+        * exceeding the size of the p2ps_feature_capab structure.
+        * Note 2: Verification of msg.feature_cap_len below has to be changed
+        * to allow 2 byte feature capability processing if
+        * struct p2ps_feature_capab is extended to include additional fields
+        * and it affects the structure size.
+        */
+       if (msg->feature_cap_len < sizeof(struct p2ps_feature_capab)) {
+               p2p_dbg(p2p, "P2PS: Invalid feature capability len");
+               return -1;
+       }
+
+       switch (*msg->conn_cap) {
+       case P2PS_SETUP_NEW:
+               group_id = 1;
+               intended_addr = 1;
+               operating_channel = 1;
+               channel_list = 1;
+               config_timeout = 1;
+               listen_channel = 1;
+               break;
+       case P2PS_SETUP_CLIENT:
+               channel_list = 1;
+               listen_channel = 1;
+               break;
+       case P2PS_SETUP_GROUP_OWNER:
+               group_id = 1;
+               intended_addr = 1;
+               operating_channel = 1;
+               break;
+       case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER:
+               group_id = 1;
+               operating_channel = 1;
+               intended_addr = 1;
+               channel_list = 1;
+               config_timeout = 1;
+               break;
+       case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER:
+               group_id = 1;
+               intended_addr = 1;
+               operating_channel = 1;
+               channel_list = 1;
+               config_timeout = 1;
+               break;
+       default:
+               p2p_dbg(p2p, "Invalid P2PS PD connection capability");
+               return -1;
+       }
+
+       if (msg->persistent_dev) {
+               channel_list = 1;
+               config_timeout = 1;
+               if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) {
+                       intended_addr = 1;
+                       operating_channel = 1;
+               }
+       }
+
+       P2PS_PD_REQ_CHECK(group_id, group_id);
+       P2PS_PD_REQ_CHECK(intended_addr, intended_addr);
+       P2PS_PD_REQ_CHECK(operating_channel, operating_channel);
+       P2PS_PD_REQ_CHECK(channel_list, channel_list);
+       P2PS_PD_REQ_CHECK(config_timeout, config_timeout);
+       P2PS_PD_REQ_CHECK(listen_channel, listen_channel);
+
+#undef P2PS_PD_REQ_CHECK
+
+       return 0;
+}
+
+
 void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
                               const u8 *data, size_t len, int rx_freq)
 {
@@ -538,21 +649,23 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
        os_memset(session_mac, 0, ETH_ALEN);
        os_memset(adv_mac, 0, ETH_ALEN);
 
-       /* Note 1: A feature capability attribute structure can be changed
-        * in the future. The assumption is that such modifications are
-        * backwards compatible, therefore we allow processing of
-        * msg.feature_cap exceeding the size of the p2ps_feature_capab
-        * structure.
-        * Note 2: Vverification of msg.feature_cap_len below has to be changed
-        * to allow 2 byte feature capability processing if struct
-        * p2ps_feature_capab is extended to include additional fields and it
-        * affects the structure size.
+       /*
+        * Validate a P2PS PD Request and skip P2PS processing if not a valid
+        * P2PS PD.
         */
-       if (!msg.adv_id || !msg.session_id || !msg.session_mac ||
-           !msg.adv_mac || !msg.feature_cap ||
-           msg.feature_cap_len < sizeof(*req_fcap) ||
-           !(msg.status || msg.conn_cap))
+       if (msg.adv_id) {
+               /*
+                * Set adv_id here, so that in case of an error, a P2PS PD
+                * Response will be sent.
+                */
+               adv_id = WPA_GET_LE32(msg.adv_id);
+               if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) {
+                       reject = P2P_SC_FAIL_INVALID_PARAMS;
+                       goto out;
+               }
+       } else {
                goto out;
+       }
 
        req_fcap = (struct p2ps_feature_capab *) msg.feature_cap;
 
@@ -560,17 +673,28 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
        os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
 
        session_id = WPA_GET_LE32(msg.session_id);
-       adv_id = WPA_GET_LE32(msg.adv_id);
-
-       if (!msg.status)
-               p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
-
-       p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv);
 
        if (msg.conn_cap)
                conncap = *msg.conn_cap;
        remote_conncap = conncap;
 
+       if (!msg.status) {
+               if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) {
+                       p2p_dbg(p2p,
+                               "P2PS PD adv mac does not match the local one");
+                       reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+                       goto out;
+               }
+
+               p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
+               if (!p2ps_adv) {
+                       p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id);
+                       reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+                       goto out;
+               }
+               p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv);
+       }
+
        if (p2ps_adv) {
                auto_accept = p2ps_adv->auto_accept;
                conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
@@ -613,9 +737,6 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
                if (auto_accept || reject != P2P_SC_SUCCESS) {
                        struct p2ps_provision *tmp;
 
-                       if (reject == P2P_SC_SUCCESS && !conncap)
-                               reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
-
                        if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
                                                 msg.wps_config_methods,
                                                 session_mac, adv_mac) < 0) {
@@ -635,9 +756,6 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
                        if (reject != P2P_SC_SUCCESS)
                                goto out;
                }
-       } else if (!msg.status) {
-               reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
-               goto out;
        }
 
        if (!msg.status && !auto_accept &&
@@ -672,6 +790,19 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
        if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov)
                goto out;
 
+       if (p2p->p2ps_prov->adv_id != adv_id ||
+           os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) {
+               p2p_dbg(p2p,
+                       "P2PS Follow-on PD with mismatch Advertisement ID/MAC");
+               goto out;
+       }
+
+       if (p2p->p2ps_prov->session_id != session_id ||
+           os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) {
+               p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC");
+               goto out;
+       }
+
        method = p2p->p2ps_prov->method;
 
        conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,