diff options
Diffstat (limited to 'app/src')
3 files changed, 58 insertions, 8 deletions
| diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Introducer.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Introducer.java index 32eabadf..e3175010 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/models/Introducer.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Introducer.java @@ -5,18 +5,18 @@ import android.os.Parcel;  import android.os.Parcelable;  import java.io.UnsupportedEncodingException; +import java.net.IDN;  import java.net.URISyntaxException;  import java.net.URLEncoder;  import java.util.Locale;  public class Introducer implements Parcelable { -    private String type; -    private String address; -    private String certificate; -    private String fullyQualifiedDomainName; -    private boolean kcpEnabled; - -    private String auth; +    private final String type; +    private final String address; +    private final String certificate; +    private final String fullyQualifiedDomainName; +    private final boolean kcpEnabled; +    private final String auth;      public Introducer(String type, String address, String certificate, String fullyQualifiedDomainName, boolean kcpEnabled, String auth) {          this.type = type; @@ -94,6 +94,10 @@ public class Introducer implements Parcelable {              throw new IllegalArgumentException("FQDN not found in the introducer URL");          } +        if (!isAscii(fqdn)) { +            throw new IllegalArgumentException("FQDN is not ASCII: " + fqdn); +        } +          boolean kcp = "1".equals(uri.getQueryParameter( "kcp"));          String cert = uri.getQueryParameter( "cert"); @@ -112,6 +116,15 @@ public class Introducer implements Parcelable {          return auth;      } +    private static boolean isAscii(String fqdn) { +        try { +            String asciiFQDN = IDN.toASCII(fqdn, IDN.USE_STD3_ASCII_RULES); +            return fqdn.equals(asciiFQDN); +        } catch (IllegalArgumentException e) { +            return false; +        } +    } +      public String toUrl() throws UnsupportedEncodingException {          return String.format(Locale.US, "%s://%s?fqdn=%s&kcp=%d&cert=%s&auth=%s", type, address, URLEncoder.encode(fullyQualifiedDomainName, "UTF-8"), kcpEnabled ? 1 : 0, URLEncoder.encode(certificate, "UTF-8"),  URLEncoder.encode(auth, "UTF-8"));      } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java index 76795616..b4ec23e6 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java @@ -983,7 +983,7 @@ public final class Provider implements Parcelable {          return introducer;      } -    public void setIntroducer(String introducerUrl) throws URISyntaxException { +    public void setIntroducer(String introducerUrl) throws URISyntaxException, IllegalArgumentException {         this.introducer = Introducer.fromUrl(introducerUrl);      } diff --git a/app/src/test/java/se/leap/bitmaskclient/base/models/IntroducerTest.java b/app/src/test/java/se/leap/bitmaskclient/base/models/IntroducerTest.java index 01989cf9..b53f06fe 100644 --- a/app/src/test/java/se/leap/bitmaskclient/base/models/IntroducerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/base/models/IntroducerTest.java @@ -1,6 +1,7 @@  package se.leap.bitmaskclient.base.models;  import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows;  import android.net.Uri;  import android.os.Build; @@ -43,4 +44,40 @@ public class IntroducerTest {              throw new RuntimeException(e);          }      } + +    @Test +    public void testFromUrl_homograph_attack() { +        String code = "obfsvpnintro://37.2.240.90:443?fqdn=ft1.bitmasк.net&kcp=0&cert=XXXXXXX&auth=solitech_w4gOlm%2BseC5spDL8E1Q6dg"; +        assertThrows(IllegalArgumentException.class, () -> Introducer.fromUrl(code)); +    } + +    @Test +    public void testFromUrl_invalid_fqdn() { +        String code = "obfsvpnintro://37.2.240.90:443?fqdn=file://var/wwww&kcp=0&cert=XXXXXXX&auth=solitech_w4gOlm%2BseC5spDL8E1Q6dg"; +        assertThrows(IllegalArgumentException.class, () -> Introducer.fromUrl(code)); +    } + +    @Test +    public void testFromUrl_missing_fqdn() { +        String code = "obfsvpnintro://37.2.240.90:443?fqdn=&kcp=0&cert=XXXXXXX&auth=solitech_w4gOlm%2BseC5spDL8E1Q6dg"; +        assertThrows(IllegalArgumentException.class, () -> Introducer.fromUrl(code)); + +        String code2 = "obfsvpnintro://37.2.240.90:443?kcp=0&cert=XXXXXXX&auth=solitech_w4gOlm%2BseC5spDL8E1Q6dg"; +        assertThrows(IllegalArgumentException.class, () -> Introducer.fromUrl(code2)); +    } + +    @Test +    public void testFromUrl_missing_cert() { +        String code = "obfsvpnintro://37.2.240.90:443?fqdn=ft1.bitmask.net&kcp=0&cert=&auth=solitech_w4gOlm%2BseC5spDL8E1Q6dg"; +        assertThrows(IllegalArgumentException.class, () -> Introducer.fromUrl(code)); +    } + +    @Test +    public void testFromUrl_missing_auth() { +        String code = "obfsvpnintro://37.2.240.90:443?fqdn=ft1.bitmask.net&kcp=0&cert=XXXXXXX&auth="; +        assertThrows(IllegalArgumentException.class, () -> Introducer.fromUrl(code)); + +        String code2 = "obfsvpnintro://37.2.240.90:443?fqdn=ft1.bitmask.net&kcp=0&cert=XXXXXXX"; +        assertThrows(IllegalArgumentException.class, () -> Introducer.fromUrl(code2)); +    }  } | 
