Libertas: fix buffer overflow in lbs_get_essid()
[pandora-kernel.git] / drivers / net / wireless / libertas / wext.c
index be837a0..4b1aab5 100644 (file)
@@ -45,6 +45,63 @@ static inline void lbs_cancel_association_work(struct lbs_private *priv)
        priv->pending_assoc_req = NULL;
 }
 
+void lbs_send_disconnect_notification(struct lbs_private *priv)
+{
+       union iwreq_data wrqu;
+
+       memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN);
+       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+       wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+static void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str)
+{
+       union iwreq_data iwrq;
+       u8 buf[50];
+
+       lbs_deb_enter(LBS_DEB_WEXT);
+
+       memset(&iwrq, 0, sizeof(union iwreq_data));
+       memset(buf, 0, sizeof(buf));
+
+       snprintf(buf, sizeof(buf) - 1, "%s", str);
+
+       iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN;
+
+       /* Send Event to upper layer */
+       lbs_deb_wext("event indication string %s\n", (char *)buf);
+       lbs_deb_wext("event indication length %d\n", iwrq.data.length);
+       lbs_deb_wext("sending wireless event IWEVCUSTOM for %s\n", str);
+
+       wireless_send_event(priv->dev, IWEVCUSTOM, &iwrq, buf);
+
+       lbs_deb_leave(LBS_DEB_WEXT);
+}
+
+/**
+ *  @brief This function handles MIC failure event.
+ *
+ *  @param priv    A pointer to struct lbs_private structure
+ *  @para  event   the event id
+ *  @return       n/a
+ */
+void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event)
+{
+       char buf[50];
+
+       lbs_deb_enter(LBS_DEB_CMD);
+       memset(buf, 0, sizeof(buf));
+
+       sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication ");
+
+       if (event == MACREG_INT_CODE_MIC_ERR_UNICAST)
+               strcat(buf, "unicast ");
+       else
+               strcat(buf, "multicast ");
+
+       lbs_send_iwevcustom_event(priv, buf);
+       lbs_deb_leave(LBS_DEB_CMD);
+}
 
 /**
  *  @brief Find the channel frequency power info with specific channel
@@ -66,8 +123,6 @@ struct chan_freq_power *lbs_find_cfp_by_band_and_channel(
        for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
                rc = &priv->region_channel[j];
 
-               if (priv->enable11d)
-                       rc = &priv->universal_channel[j];
                if (!rc->valid || !rc->CFP)
                        continue;
                if (rc->band != band)
@@ -107,8 +162,6 @@ static struct chan_freq_power *find_cfp_by_band_and_freq(
        for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
                rc = &priv->region_channel[j];
 
-               if (priv->enable11d)
-                       rc = &priv->universal_channel[j];
                if (!rc->valid || !rc->CFP)
                        continue;
                if (rc->band != band)
@@ -169,12 +222,12 @@ static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
        lbs_deb_enter(LBS_DEB_WEXT);
 
        cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
-                                          priv->curbssparams.channel);
+                                          priv->channel);
 
        if (!cfp) {
-               if (priv->curbssparams.channel)
+               if (priv->channel)
                        lbs_deb_wext("invalid channel %d\n",
-                              priv->curbssparams.channel);
+                              priv->channel);
                return -EINVAL;
        }
 
@@ -547,8 +600,6 @@ static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
        struct chan_freq_power *cfp;
        u8 rates[MAX_RATES + 1];
 
-       u8 flag = 0;
-
        lbs_deb_enter(LBS_DEB_WEXT);
 
        dwrq->length = sizeof(struct iw_range);
@@ -570,52 +621,21 @@ static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
 
        range->scan_capa = IW_SCAN_CAPA_ESSID;
 
-       if (priv->enable11d &&
-           (priv->connect_status == LBS_CONNECTED ||
-           priv->mesh_connect_status == LBS_CONNECTED)) {
-               u8 chan_no;
-               u8 band;
-
-               struct parsed_region_chan_11d *parsed_region_chan =
-                   &priv->parsed_region_chan;
-
-               if (parsed_region_chan == NULL) {
-                       lbs_deb_wext("11d: parsed_region_chan is NULL\n");
-                       goto out;
-               }
-               band = parsed_region_chan->band;
-               lbs_deb_wext("band %d, nr_char %d\n", band,
-                      parsed_region_chan->nr_chan);
-
+       for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
+            && (j < ARRAY_SIZE(priv->region_channel)); j++) {
+               cfp = priv->region_channel[j].CFP;
                for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
-                    && (i < parsed_region_chan->nr_chan); i++) {
-                       chan_no = parsed_region_chan->chanpwr[i].chan;
-                       lbs_deb_wext("chan_no %d\n", chan_no);
-                       range->freq[range->num_frequency].i = (long)chan_no;
+                    && priv->region_channel[j].valid
+                    && cfp
+                    && (i < priv->region_channel[j].nrcfp); i++) {
+                       range->freq[range->num_frequency].i =
+                           (long)cfp->channel;
                        range->freq[range->num_frequency].m =
-                           (long)lbs_chan_2_freq(chan_no) * 100000;
+                           (long)cfp->freq * 100000;
                        range->freq[range->num_frequency].e = 1;
+                       cfp++;
                        range->num_frequency++;
                }
-               flag = 1;
-       }
-       if (!flag) {
-               for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
-                    && (j < ARRAY_SIZE(priv->region_channel)); j++) {
-                       cfp = priv->region_channel[j].CFP;
-                       for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
-                            && priv->region_channel[j].valid
-                            && cfp
-                            && (i < priv->region_channel[j].nrcfp); i++) {
-                               range->freq[range->num_frequency].i =
-                                   (long)cfp->channel;
-                               range->freq[range->num_frequency].m =
-                                   (long)cfp->freq * 100000;
-                               range->freq[range->num_frequency].e = 1;
-                               cfp++;
-                               range->num_frequency++;
-                       }
-               }
        }
 
        lbs_deb_wext("IW_MAX_FREQUENCIES %d, num_frequency %d\n",
@@ -700,7 +720,6 @@ static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
                                  | IW_ENC_CAPA_CIPHER_CCMP;
        }
 
-out:
        lbs_deb_leave(LBS_DEB_WEXT);
        return 0;
 }
@@ -709,6 +728,7 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
                          struct iw_param *vwrq, char *extra)
 {
        struct lbs_private *priv = dev->ml_priv;
+       int ret = 0;
 
        lbs_deb_enter(LBS_DEB_WEXT);
 
@@ -737,8 +757,54 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
                       "setting power timeout is not supported\n");
                return -EINVAL;
        } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
-               lbs_deb_wext("setting power period not supported\n");
-               return -EINVAL;
+               vwrq->value = vwrq->value / 1000;
+               if (!priv->enter_deep_sleep) {
+                       lbs_pr_err("deep sleep feature is not implemented "
+                                       "for this interface driver\n");
+                       return -EINVAL;
+               }
+
+               if (priv->connect_status == LBS_CONNECTED) {
+                       if ((priv->is_auto_deep_sleep_enabled) &&
+                                               (vwrq->value == -1000)) {
+                               lbs_exit_auto_deep_sleep(priv);
+                               return 0;
+                       } else {
+                               lbs_pr_err("can't use deep sleep cmd in "
+                                               "connected state\n");
+                               return -EINVAL;
+                       }
+               }
+
+               if ((vwrq->value < 0) && (vwrq->value != -1000)) {
+                       lbs_pr_err("unknown option\n");
+                       return -EINVAL;
+               }
+
+               if (vwrq->value > 0) {
+                       if (!priv->is_auto_deep_sleep_enabled) {
+                               priv->is_activity_detected = 0;
+                               priv->auto_deep_sleep_timeout = vwrq->value;
+                               lbs_enter_auto_deep_sleep(priv);
+                       } else {
+                               priv->auto_deep_sleep_timeout = vwrq->value;
+                               lbs_deb_debugfs("auto deep sleep: "
+                                               "already enabled\n");
+                       }
+                       return 0;
+               } else {
+                       if (priv->is_auto_deep_sleep_enabled) {
+                               lbs_exit_auto_deep_sleep(priv);
+                               /* Try to exit deep sleep if auto */
+                               /*deep sleep disabled */
+                               ret = lbs_set_deep_sleep(priv, 0);
+                       }
+                       if (vwrq->value == 0)
+                               ret = lbs_set_deep_sleep(priv, 1);
+                       else if (vwrq->value == -1000)
+                               ret = lbs_set_deep_sleep(priv, 0);
+                       return ret;
+               }
        }
 
        if (priv->psmode != LBS802_11POWERMODECAM) {
@@ -752,6 +818,7 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
        }
 
        lbs_deb_leave(LBS_DEB_WEXT);
+
        return 0;
 }
 
@@ -785,7 +852,7 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
        u32 rssi_qual;
        u32 tx_qual;
        u32 quality = 0;
-       int stats_valid = 0;
+       int ret, stats_valid = 0;
        u8 rssi;
        u32 tx_retries;
        struct cmd_ds_802_11_get_log log;
@@ -834,7 +901,9 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
 
        memset(&log, 0, sizeof(log));
        log.hdr.size = cpu_to_le16(sizeof(log));
-       lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log);
+       ret = lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log);
+       if (ret)
+               goto out;
 
        tx_retries = le32_to_cpu(log.retry);
 
@@ -862,8 +931,10 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
        stats_valid = 1;
 
        /* update stats asynchronously for future calls */
-       lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
+       ret = lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
                                        0, 0, NULL);
+       if (ret)
+               lbs_pr_err("RSSI command failed\n");
 out:
        if (!stats_valid) {
                priv->wstats.miss.beacon = 0;
@@ -973,7 +1044,7 @@ static int lbs_mesh_set_freq(struct net_device *dev,
                goto out;
        }
 
-       if (fwrq->m != priv->curbssparams.channel) {
+       if (fwrq->m != priv->channel) {
                lbs_deb_wext("mesh channel change forces eth disconnect\n");
                if (priv->mode == IW_MODE_INFRA)
                        lbs_cmd_80211_deauthenticate(priv,
@@ -1000,6 +1071,7 @@ static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
        u8 rates[MAX_RATES + 1];
 
        lbs_deb_enter(LBS_DEB_WEXT);
+
        lbs_deb_wext("vwrq->value %d\n", vwrq->value);
        lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);
 
@@ -1953,10 +2025,8 @@ static int lbs_get_essid(struct net_device *dev, struct iw_request_info *info,
        if (priv->connect_status == LBS_CONNECTED) {
                memcpy(extra, priv->curbssparams.ssid,
                       priv->curbssparams.ssid_len);
-               extra[priv->curbssparams.ssid_len] = '\0';
        } else {
                memset(extra, 0, 32);
-               extra[priv->curbssparams.ssid_len] = '\0';
        }
        /*
         * If none, we may want to get the one that was set
@@ -1975,7 +2045,7 @@ static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
 {
        struct lbs_private *priv = dev->ml_priv;
        int ret = 0;
-       u8 ssid[IW_ESSID_MAX_SIZE];
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
        u8 ssid_len = 0;
        struct assoc_request * assoc_req;
        int in_ssid_len = dwrq->length;
@@ -1989,7 +2059,7 @@ static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
        }
 
        /* Check the size of the string */
-       if (in_ssid_len > IW_ESSID_MAX_SIZE) {
+       if (in_ssid_len > IEEE80211_MAX_SSID_LEN) {
                ret = -E2BIG;
                goto out;
        }
@@ -2020,7 +2090,7 @@ out:
                        ret = -ENOMEM;
                } else {
                        /* Copy the SSID to the association request */
-                       memcpy(&assoc_req->ssid, &ssid, IW_ESSID_MAX_SIZE);
+                       memcpy(&assoc_req->ssid, &ssid, IEEE80211_MAX_SSID_LEN);
                        assoc_req->ssid_len = ssid_len;
                        set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
                        lbs_postpone_association_work(priv);
@@ -2071,7 +2141,7 @@ static int lbs_mesh_set_essid(struct net_device *dev,
        }
 
        /* Check the size of the string */
-       if (dwrq->length > IW_ESSID_MAX_SIZE) {
+       if (dwrq->length > IEEE80211_MAX_SSID_LEN) {
                ret = -E2BIG;
                goto out;
        }
@@ -2086,7 +2156,7 @@ static int lbs_mesh_set_essid(struct net_device *dev,
        }
 
        lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
-                       priv->curbssparams.channel);
+                       priv->channel);
  out:
        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
        return ret;