diff options
-rw-r--r-- | net/wireless/wext.c | 258 |
1 files changed, 134 insertions, 124 deletions
diff --git a/net/wireless/wext.c b/net/wireless/wext.c index cd2cf9fec10f..d17c0f45accb 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c | |||
@@ -694,6 +694,138 @@ void wext_proc_exit(struct net *net) | |||
694 | */ | 694 | */ |
695 | 695 | ||
696 | /* ---------------------------------------------------------------- */ | 696 | /* ---------------------------------------------------------------- */ |
697 | static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, | ||
698 | const struct iw_ioctl_description *descr, | ||
699 | iw_handler handler, struct net_device *dev, | ||
700 | struct iw_request_info *info) | ||
701 | { | ||
702 | int err, extra_size, user_length = 0, essid_compat = 0; | ||
703 | char *extra; | ||
704 | |||
705 | /* Calculate space needed by arguments. Always allocate | ||
706 | * for max space. | ||
707 | */ | ||
708 | extra_size = descr->max_tokens * descr->token_size; | ||
709 | |||
710 | /* Check need for ESSID compatibility for WE < 21 */ | ||
711 | switch (cmd) { | ||
712 | case SIOCSIWESSID: | ||
713 | case SIOCGIWESSID: | ||
714 | case SIOCSIWNICKN: | ||
715 | case SIOCGIWNICKN: | ||
716 | if (iwp->length == descr->max_tokens + 1) | ||
717 | essid_compat = 1; | ||
718 | else if (IW_IS_SET(cmd) && (iwp->length != 0)) { | ||
719 | char essid[IW_ESSID_MAX_SIZE + 1]; | ||
720 | |||
721 | err = copy_from_user(essid, iwp->pointer, | ||
722 | iwp->length * | ||
723 | descr->token_size); | ||
724 | if (err) | ||
725 | return -EFAULT; | ||
726 | |||
727 | if (essid[iwp->length - 1] == '\0') | ||
728 | essid_compat = 1; | ||
729 | } | ||
730 | break; | ||
731 | default: | ||
732 | break; | ||
733 | } | ||
734 | |||
735 | iwp->length -= essid_compat; | ||
736 | |||
737 | /* Check what user space is giving us */ | ||
738 | if (IW_IS_SET(cmd)) { | ||
739 | /* Check NULL pointer */ | ||
740 | if (!iwp->pointer && iwp->length != 0) | ||
741 | return -EFAULT; | ||
742 | /* Check if number of token fits within bounds */ | ||
743 | if (iwp->length > descr->max_tokens) | ||
744 | return -E2BIG; | ||
745 | if (iwp->length < descr->min_tokens) | ||
746 | return -EINVAL; | ||
747 | } else { | ||
748 | /* Check NULL pointer */ | ||
749 | if (!iwp->pointer) | ||
750 | return -EFAULT; | ||
751 | /* Save user space buffer size for checking */ | ||
752 | user_length = iwp->length; | ||
753 | |||
754 | /* Don't check if user_length > max to allow forward | ||
755 | * compatibility. The test user_length < min is | ||
756 | * implied by the test at the end. | ||
757 | */ | ||
758 | |||
759 | /* Support for very large requests */ | ||
760 | if ((descr->flags & IW_DESCR_FLAG_NOMAX) && | ||
761 | (user_length > descr->max_tokens)) { | ||
762 | /* Allow userspace to GET more than max so | ||
763 | * we can support any size GET requests. | ||
764 | * There is still a limit : -ENOMEM. | ||
765 | */ | ||
766 | extra_size = user_length * descr->token_size; | ||
767 | |||
768 | /* Note : user_length is originally a __u16, | ||
769 | * and token_size is controlled by us, | ||
770 | * so extra_size won't get negative and | ||
771 | * won't overflow... | ||
772 | */ | ||
773 | } | ||
774 | } | ||
775 | |||
776 | /* kzalloc() ensures NULL-termination for essid_compat. */ | ||
777 | extra = kzalloc(extra_size, GFP_KERNEL); | ||
778 | if (!extra) | ||
779 | return -ENOMEM; | ||
780 | |||
781 | /* If it is a SET, get all the extra data in here */ | ||
782 | if (IW_IS_SET(cmd) && (iwp->length != 0)) { | ||
783 | if (copy_from_user(extra, iwp->pointer, | ||
784 | iwp->length * | ||
785 | descr->token_size)) { | ||
786 | err = -EFAULT; | ||
787 | goto out; | ||
788 | } | ||
789 | } | ||
790 | |||
791 | err = handler(dev, info, (union iwreq_data *) iwp, extra); | ||
792 | |||
793 | iwp->length += essid_compat; | ||
794 | |||
795 | /* If we have something to return to the user */ | ||
796 | if (!err && IW_IS_GET(cmd)) { | ||
797 | /* Check if there is enough buffer up there */ | ||
798 | if (user_length < iwp->length) { | ||
799 | err = -E2BIG; | ||
800 | goto out; | ||
801 | } | ||
802 | |||
803 | if (copy_to_user(iwp->pointer, extra, | ||
804 | iwp->length * | ||
805 | descr->token_size)) { | ||
806 | err = -EFAULT; | ||
807 | goto out; | ||
808 | } | ||
809 | } | ||
810 | |||
811 | /* Generate an event to notify listeners of the change */ | ||
812 | if ((descr->flags & IW_DESCR_FLAG_EVENT) && err == -EIWCOMMIT) { | ||
813 | union iwreq_data *data = (union iwreq_data *) iwp; | ||
814 | |||
815 | if (descr->flags & IW_DESCR_FLAG_RESTRICT) | ||
816 | /* If the event is restricted, don't | ||
817 | * export the payload. | ||
818 | */ | ||
819 | wireless_send_event(dev, cmd, data, NULL); | ||
820 | else | ||
821 | wireless_send_event(dev, cmd, data, extra); | ||
822 | } | ||
823 | |||
824 | out: | ||
825 | kfree(extra); | ||
826 | return err; | ||
827 | } | ||
828 | |||
697 | /* | 829 | /* |
698 | * Wrapper to call a standard Wireless Extension handler. | 830 | * Wrapper to call a standard Wireless Extension handler. |
699 | * We do various checks and also take care of moving data between | 831 | * We do various checks and also take care of moving data between |
@@ -729,130 +861,8 @@ static int ioctl_standard_call(struct net_device * dev, | |||
729 | ((ret == 0) || (ret == -EIWCOMMIT))) | 861 | ((ret == 0) || (ret == -EIWCOMMIT))) |
730 | wireless_send_event(dev, cmd, &(iwr->u), NULL); | 862 | wireless_send_event(dev, cmd, &(iwr->u), NULL); |
731 | } else { | 863 | } else { |
732 | char * extra; | 864 | ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr, |
733 | int extra_size; | 865 | handler, dev, &info); |
734 | int user_length = 0; | ||
735 | int err; | ||
736 | int essid_compat = 0; | ||
737 | |||
738 | /* Calculate space needed by arguments. Always allocate | ||
739 | * for max space. Easier, and won't last long... */ | ||
740 | extra_size = descr->max_tokens * descr->token_size; | ||
741 | |||
742 | /* Check need for ESSID compatibility for WE < 21 */ | ||
743 | switch (cmd) { | ||
744 | case SIOCSIWESSID: | ||
745 | case SIOCGIWESSID: | ||
746 | case SIOCSIWNICKN: | ||
747 | case SIOCGIWNICKN: | ||
748 | if (iwr->u.data.length == descr->max_tokens + 1) | ||
749 | essid_compat = 1; | ||
750 | else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { | ||
751 | char essid[IW_ESSID_MAX_SIZE + 1]; | ||
752 | |||
753 | err = copy_from_user(essid, iwr->u.data.pointer, | ||
754 | iwr->u.data.length * | ||
755 | descr->token_size); | ||
756 | if (err) | ||
757 | return -EFAULT; | ||
758 | |||
759 | if (essid[iwr->u.data.length - 1] == '\0') | ||
760 | essid_compat = 1; | ||
761 | } | ||
762 | break; | ||
763 | default: | ||
764 | break; | ||
765 | } | ||
766 | |||
767 | iwr->u.data.length -= essid_compat; | ||
768 | |||
769 | /* Check what user space is giving us */ | ||
770 | if (IW_IS_SET(cmd)) { | ||
771 | /* Check NULL pointer */ | ||
772 | if ((iwr->u.data.pointer == NULL) && | ||
773 | (iwr->u.data.length != 0)) | ||
774 | return -EFAULT; | ||
775 | /* Check if number of token fits within bounds */ | ||
776 | if (iwr->u.data.length > descr->max_tokens) | ||
777 | return -E2BIG; | ||
778 | if (iwr->u.data.length < descr->min_tokens) | ||
779 | return -EINVAL; | ||
780 | } else { | ||
781 | /* Check NULL pointer */ | ||
782 | if (iwr->u.data.pointer == NULL) | ||
783 | return -EFAULT; | ||
784 | /* Save user space buffer size for checking */ | ||
785 | user_length = iwr->u.data.length; | ||
786 | |||
787 | /* Don't check if user_length > max to allow forward | ||
788 | * compatibility. The test user_length < min is | ||
789 | * implied by the test at the end. */ | ||
790 | |||
791 | /* Support for very large requests */ | ||
792 | if ((descr->flags & IW_DESCR_FLAG_NOMAX) && | ||
793 | (user_length > descr->max_tokens)) { | ||
794 | /* Allow userspace to GET more than max so | ||
795 | * we can support any size GET requests. | ||
796 | * There is still a limit : -ENOMEM. */ | ||
797 | extra_size = user_length * descr->token_size; | ||
798 | /* Note : user_length is originally a __u16, | ||
799 | * and token_size is controlled by us, | ||
800 | * so extra_size won't get negative and | ||
801 | * won't overflow... */ | ||
802 | } | ||
803 | } | ||
804 | |||
805 | /* Create the kernel buffer */ | ||
806 | /* kzalloc ensures NULL-termination for essid_compat */ | ||
807 | extra = kzalloc(extra_size, GFP_KERNEL); | ||
808 | if (extra == NULL) | ||
809 | return -ENOMEM; | ||
810 | |||
811 | /* If it is a SET, get all the extra data in here */ | ||
812 | if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { | ||
813 | err = copy_from_user(extra, iwr->u.data.pointer, | ||
814 | iwr->u.data.length * | ||
815 | descr->token_size); | ||
816 | if (err) { | ||
817 | kfree(extra); | ||
818 | return -EFAULT; | ||
819 | } | ||
820 | } | ||
821 | |||
822 | /* Call the handler */ | ||
823 | ret = handler(dev, &info, &(iwr->u), extra); | ||
824 | |||
825 | iwr->u.data.length += essid_compat; | ||
826 | |||
827 | /* If we have something to return to the user */ | ||
828 | if (!ret && IW_IS_GET(cmd)) { | ||
829 | /* Check if there is enough buffer up there */ | ||
830 | if (user_length < iwr->u.data.length) { | ||
831 | kfree(extra); | ||
832 | return -E2BIG; | ||
833 | } | ||
834 | |||
835 | err = copy_to_user(iwr->u.data.pointer, extra, | ||
836 | iwr->u.data.length * | ||
837 | descr->token_size); | ||
838 | if (err) | ||
839 | ret = -EFAULT; | ||
840 | } | ||
841 | |||
842 | /* Generate an event to notify listeners of the change */ | ||
843 | if ((descr->flags & IW_DESCR_FLAG_EVENT) && | ||
844 | ((ret == 0) || (ret == -EIWCOMMIT))) { | ||
845 | if (descr->flags & IW_DESCR_FLAG_RESTRICT) | ||
846 | /* If the event is restricted, don't | ||
847 | * export the payload */ | ||
848 | wireless_send_event(dev, cmd, &(iwr->u), NULL); | ||
849 | else | ||
850 | wireless_send_event(dev, cmd, &(iwr->u), | ||
851 | extra); | ||
852 | } | ||
853 | |||
854 | /* Cleanup - I told you it wasn't that long ;-) */ | ||
855 | kfree(extra); | ||
856 | } | 866 | } |
857 | 867 | ||
858 | /* Call commit handler if needed and defined */ | 868 | /* Call commit handler if needed and defined */ |