Index: src/wp-includes/formatting.php
===================================================================
--- src/wp-includes/formatting.php	(revision 25504)
+++ src/wp-includes/formatting.php	(working copy)
@@ -2935,7 +2935,7 @@
 			$value = array();
 
 			foreach ( $domains as $domain ) {
-				if ( ! preg_match( '/(--|\.\.)/', $domain ) && preg_match( '|^([a-zA-Z0-9-\.])+$|', $domain ) )
+				if ( ! preg_match( '/(--|\.\.)/', $domain ) && preg_match( '|^([a-zA-Z0-9-\.\*])+$|', $domain ) )
 					$value[] = $domain;
 			}
 			if ( ! $value )
Index: src/wp-includes/ms-functions.php
===================================================================
--- src/wp-includes/ms-functions.php	(revision 25504)
+++ src/wp-includes/ms-functions.php	(working copy)
@@ -373,35 +373,110 @@
  */
 function is_email_address_unsafe( $user_email ) {
 	$banned_names = get_site_option( 'banned_email_domains' );
-	if ( $banned_names && ! is_array( $banned_names ) )
-		$banned_names = explode( "\n", $banned_names );
 
-	$is_email_address_unsafe = false;
+	$is_email_domain_in_list = is_email_domain_in_list( $user_email, $banned_names );
+	/**
+	 * Filter a check for whether an email's domain is banned.
+	 *
+	 * @since 3.5.0
+	 *
+	 * @param bool $is_email_domain_in_list True if the email domain is banned.
+	 * @param string $user_email The email provided by the user at registration.
+	 */
+	return apply_filters( 'is_email_address_unsafe', $is_email_domain_in_list, $user_email );
+}
 
-	if ( $banned_names && is_array( $banned_names ) ) {
-		$banned_names = array_map( 'strtolower', $banned_names );
-		$normalized_email = strtolower( $user_email );
+/**
+ * Checks an email address against a whitelist of allowed domains.
+ *
+ * This function checks against the Limited Email Domains list
+ * at wp-admin/network/settings.php. The check is only run on
+ * self-registrations; user creation at wp-admin/network/users.php
+ * bypasses this check.
+ *
+ * @since 3.7.0
+ *
+ * @param string $user_email The email provided by the user at registration.
+ * @return bool Returns true when the email address is allowed.
+ */
+function is_email_address_allowed( $user_email ) {
+	$allowed_names = get_site_option( 'limited_email_domains' );
 
+	// Any address is allowed when no whitelist is present
+	if ( empty( $allowed_names ) ) {
+		$is_email_address_allowed = true;
+	} else {
+		$is_email_address_allowed = is_email_domain_in_list( $user_email, $allowed_names );
+	}
+
+	/**
+	 * Filter a check for whether an email's domain is allowed via whitelist.
+	 *
+	 * @since 3.7.0
+	 *
+	 * @param bool $is_email_address_allowed True when the email address is allowed
+	 * @param string $user_email The email provided by the user at registration.
+	 */
+	return apply_filters( 'is_email_address_allowed', $is_email_address_allowed, $user_email );
+}
+
+/**
+ * Checks whether an email is on a whitelist/blacklist
+ *
+ * Used by is_email_address_unsafe() and is_email_address_allowed() to do
+ * a wildcard-safe check of an email against an array of allowed/banned
+ * domains.
+ *
+ * Any complete section of a URL (between the dots) can be represented by
+ * a wildcard. Eg, 'test@foo.bar.com' will count as a match for '*.bar.com'.
+ *
+ * @since 3.7.0
+ *
+ * @param string $email The email address being checked
+ * @param array|string $domain_list Domains to check against
+ * @return bool Returns true when the email matches one of the domains on
+ *   the list
+ */
+function is_email_domain_in_list( $email, $domain_list ) {
+	if ( ! is_array( $domain_list ) ) {
+		$domain_list = explode( "\n", $domain_list );
+	}
+
+	$is_in_list = false;
+
+	if ( $domain_list && is_array( $domain_list ) ) {
+		$domain_list = array_map( 'strtolower', $domain_list );
+		$normalized_email = strtolower( $email );
 		list( $email_local_part, $email_domain ) = explode( '@', $normalized_email );
 
-		foreach ( $banned_names as $banned_domain ) {
-			if ( ! $banned_domain )
+		foreach ( $domain_list as $domain ) {
+			if ( ! $domain ) {
 				continue;
+			}
 
-			if ( $email_domain == $banned_domain ) {
-				$is_email_address_unsafe = true;
+			if ( $email_domain == $domain ) {
+				$is_in_list = true;
 				break;
 			}
 
-			$dotted_domain = ".$banned_domain";
-			if ( $dotted_domain === substr( $normalized_email, -strlen( $dotted_domain ) ) ) {
-				$is_email_address_unsafe = true;
+			$dotted_domain = ".$domain";
+			if ( $dotted_domain === substr( $email, -strlen( $dotted_domain ) ) ) {
+				$is_in_list = true;
 				break;
 			}
+
+			if ( false !== strpos( $domain, '*' ) ) {
+				$domain_pattern = '|' . str_replace( '\*', '[a-zA-Z0-9-]+', preg_quote( $domain ) ) . '|';
+				preg_match( $domain_pattern, $email_domain, $matches );
+				if ( isset( $matches[0] ) && $matches[0] == $email_domain ) {
+					$is_in_list = true;
+					break;
+ 				}
+ 			}
 		}
-	}
+  	}
 
-	return apply_filters( 'is_email_address_unsafe', $is_email_address_unsafe, $user_email );
+ 	return $is_in_list;
 }
 
 /**
@@ -470,12 +545,8 @@
 	if ( !is_email( $user_email ) )
 		$errors->add('user_email', __( 'Please enter a valid email address.' ) );
 
-	$limited_email_domains = get_site_option( 'limited_email_domains' );
-	if ( is_array( $limited_email_domains ) && empty( $limited_email_domains ) == false ) {
-		$emaildomain = substr( $user_email, 1 + strpos( $user_email, '@' ) );
-		if ( in_array( $emaildomain, $limited_email_domains ) == false )
-			$errors->add('user_email', __('Sorry, that email address is not allowed!'));
-	}
+	if ( ! is_email_address_allowed( $user_email ) )
+		$errors->add('user_email', __('Sorry, that email address is not allowed!'));
 
 	// Check if the username has been used already.
 	if ( username_exists($user_name) )
Index: tests/phpunit/data/images/a2-small-100x75.jpg
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: tests/phpunit/tests/ms.php
===================================================================
--- tests/phpunit/tests/ms.php	(revision 25504)
+++ tests/phpunit/tests/ms.php	(working copy)
@@ -839,13 +839,13 @@
 	 * @ticket 21570
 	 */
 	function test_aggressiveness_of_is_email_address_unsafe() {
-		update_site_option( 'banned_email_domains', array( 'bar.com', 'foo.co' ) );
+		update_site_option( 'banned_email_domains', array( 'bar.com', 'foo.co', '*.foo.org', 'foo.*.gov' ) );
 
-		foreach ( array( 'test@bar.com', 'test@foo.bar.com', 'test@foo.co', 'test@subdomain.foo.co' ) as $email_address ) {
+		foreach ( array( 'test@bar.com', 'test@foo.bar.com', 'test@foo.co', 'test@subdomain.foo.co', 'test@bar.foo.org', 'test@foo.bar.gov' ) as $email_address ) {
 			$this->assertTrue( is_email_address_unsafe( $email_address ), "$email_address should be UNSAFE" );
 		}
 
-		foreach ( array( 'test@foobar.com', 'test@foo-bar.com', 'test@foo.com', 'test@subdomain.foo.com' ) as $email_address ) {
+		foreach ( array( 'test@foobar.com', 'test@foo-bar.com', 'test@foo.com', 'test@subdomain.foo.com', 'test@bar.baz.foo.org', 'test@foo.bar.baz.gov' ) as $email_address ) {
 			$this->assertFalse( is_email_address_unsafe( $email_address ), "$email_address should be SAFE" );
 		}
 	}
@@ -854,17 +854,36 @@
 	 * @ticket 25046
 	 */
 	function test_case_sensitivity_of_is_email_address_unsafe() {
-		update_site_option( 'banned_email_domains', array( 'baR.com', 'Foo.co', 'barfoo.COM', 'BAZ.com' ) );
+		update_site_option( 'banned_email_domains', array( 'baR.com', 'Foo.co', 'barfoo.COM', 'BAZ.com', '*.fOo.org', 'foo.*.Gov' ) );
 
-		foreach ( array( 'test@Bar.com', 'tEst@bar.com', 'test@barFoo.com', 'tEst@foo.bar.com', 'test@baz.Com' ) as $email_address ) {
+		foreach ( array( 'test@Bar.com', 'tEst@bar.com', 'test@barFoo.com', 'tEst@foo.bar.com', 'test@baz.Com', 'test@bAR.foo.org', 'test@fOO.bar.gov' ) as $email_address ) {
 			$this->assertTrue( is_email_address_unsafe( $email_address ), "$email_address should be UNSAFE" );
 		}
 
-		foreach ( array( 'test@Foobar.com', 'test@Foo-bar.com', 'tEst@foobar.com', 'test@Subdomain.Foo.com', 'test@fooBAz.com' ) as $email_address ) {
+		foreach ( array( 'test@Foobar.com', 'test@Foo-bar.com', 'tEst@foobar.com', 'test@Subdomain.Foo.com', 'test@fooBAz.com', 'test@bar.bAZ.foo.org', 'test@foo.BAr.baz.gov' ) as $email_address ) {
 			$this->assertFalse( is_email_address_unsafe( $email_address ), "$email_address should be SAFE" );
 		}
 
 	}
+
+	function test_is_email_address_allowed() {
+		update_site_option( 'limited_email_domains', array( 'bar.com', 'foo.co', '*.foo.org', 'foo.*.gov' ) );
+
+		foreach ( array( 'test@bar.com', 'test@foo.bar.com', 'test@foo.co', 'test@subdomain.foo.co', 'test@bar.foo.org', 'test@foo.bar.gov' ) as $email_address ) {
+			$this->assertTrue( is_email_address_allowed( $email_address ), "$email_address should be UNSAFE" );
+ 		}
+
+ 		foreach ( array( 'test@foobar.com', 'test@foo-bar.com', 'test@foo.com', 'test@subdomain.foo.com', 'test@bar.baz.foo.org', 'test@foo.bar.baz.gov' ) as $email_address ) {
+			$this->assertFalse( is_email_address_allowed( $email_address ), "$email_address should be SAFE" );
+		}
+
+		update_site_option( 'limited_email_domains', '' );
+
+		foreach ( array( 'test@foobar.com', 'test@foo-bar.com', 'test@foo.com', 'test@subdomain.foo.com', 'test@bar.baz.foo.org', 'test@foo.bar.baz.gov' ) as $email_address ) {
+			$this->assertTrue( is_email_address_allowed( $email_address ), "$email_address should be SAFE" );
+		}
+	}
+
 	/**
 	 * @ticket 21552
 	 * @ticket 23418
