1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include "mozilla/net/HttpAuthUtils.h"
6 #include "mozilla/Tokenizer.h"
7 #include "nsIPrefService.h"
8 #include "nsIURI.h"
9 #include "nsNetUtil.h"
10 #include "nsUnicharUtils.h"
11 
12 namespace mozilla {
13 namespace net {
14 namespace auth {
15 
16 namespace detail {
17 
MatchesBaseURI(const nsACString & matchScheme,const nsACString & matchHost,int32_t matchPort,nsDependentCSubstring const & url)18 bool MatchesBaseURI(const nsACString& matchScheme, const nsACString& matchHost,
19                     int32_t matchPort, nsDependentCSubstring const& url) {
20   // check if scheme://host:port matches baseURI
21 
22   // parse the base URI
23   mozilla::Tokenizer t(url);
24   mozilla::Tokenizer::Token token;
25 
26   t.SkipWhites();
27 
28   // We don't know if the url to check against starts with scheme
29   // or a host name.  Start recording here.
30   t.Record();
31 
32   mozilla::Unused << t.Next(token);
33 
34   // The ipv6 literals MUST be enclosed with [] in the preference.
35   bool ipv6 = false;
36   if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
37     nsDependentCSubstring ipv6BareLiteral;
38     if (!t.ReadUntil(mozilla::Tokenizer::Token::Char(']'), ipv6BareLiteral)) {
39       // Broken ipv6 literal
40       return false;
41     }
42 
43     nsDependentCSubstring ipv6Literal;
44     t.Claim(ipv6Literal, mozilla::Tokenizer::INCLUDE_LAST);
45     if (!matchHost.Equals(ipv6Literal,
46                           nsCaseInsensitiveUTF8StringComparator()) &&
47         !matchHost.Equals(ipv6BareLiteral,
48                           nsCaseInsensitiveUTF8StringComparator())) {
49       return false;
50     }
51 
52     ipv6 = true;
53   } else if (t.CheckChar(':') && t.CheckChar('/') && t.CheckChar('/')) {
54     if (!matchScheme.Equals(token.Fragment())) {
55       return false;
56     }
57     // Re-start recording the hostname from the point after scheme://.
58     t.Record();
59   }
60 
61   while (t.Next(token)) {
62     bool eof = token.Equals(mozilla::Tokenizer::Token::EndOfFile());
63     bool port = token.Equals(mozilla::Tokenizer::Token::Char(':'));
64 
65     if (eof || port) {
66       if (!ipv6) {  // Match already performed above.
67         nsDependentCSubstring hostName;
68         t.Claim(hostName);
69 
70         // An empty hostname means to accept everything for the schema
71         if (!hostName.IsEmpty()) {
72           /*
73            host:      bar.com   foo.bar.com   foobar.com  foo.bar.com    bar.com
74            pref:      bar.com       bar.com      bar.com     .bar.com   .bar.com
75            result:     accept        accept       reject       accept     reject
76           */
77           if (!StringEndsWith(matchHost, hostName,
78                               nsCaseInsensitiveUTF8StringComparator())) {
79             return false;
80           }
81           if (matchHost.Length() > hostName.Length() &&
82               matchHost[matchHost.Length() - hostName.Length() - 1] != '.' &&
83               hostName[0] != '.') {
84             return false;
85           }
86         }
87       }
88 
89       if (port) {
90         uint16_t portNumber;
91         if (!t.ReadInteger(&portNumber)) {
92           // Missing port number
93           return false;
94         }
95         if (matchPort != portNumber) {
96           return false;
97         }
98         if (!t.CheckEOF()) {
99           return false;
100         }
101       }
102     } else if (ipv6) {
103       // After an ipv6 literal there can only be EOF or :port.  Everything else
104       // must be treated as non-match/broken input.
105       return false;
106     }
107   }
108 
109   // All negative checks has passed positively.
110   return true;
111 }
112 
113 }  // namespace detail
114 
URIMatchesPrefPattern(nsIURI * uri,const char * pref)115 bool URIMatchesPrefPattern(nsIURI* uri, const char* pref) {
116   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
117   if (!prefs) {
118     return false;
119   }
120 
121   nsAutoCString scheme, host;
122   int32_t port;
123 
124   if (NS_FAILED(uri->GetScheme(scheme))) {
125     return false;
126   }
127   if (NS_FAILED(uri->GetAsciiHost(host))) {
128     return false;
129   }
130 
131   port = NS_GetRealPort(uri);
132   if (port == -1) {
133     return false;
134   }
135 
136   nsAutoCString hostList;
137   if (NS_FAILED(prefs->GetCharPref(pref, hostList))) {
138     return false;
139   }
140 
141   // pseudo-BNF
142   // ----------
143   //
144   // url-list       base-url ( base-url "," LWS )*
145   // base-url       ( scheme-part | host-part | scheme-part host-part )
146   // scheme-part    scheme "://"
147   // host-part      host [":" port]
148   //
149   // for example:
150   //   "https://, http://office.foo.com"
151   //
152 
153   mozilla::Tokenizer t(hostList);
154   while (!t.CheckEOF()) {
155     t.SkipWhites();
156     nsDependentCSubstring url;
157     mozilla::Unused << t.ReadUntil(mozilla::Tokenizer::Token::Char(','), url);
158     if (url.IsEmpty()) {
159       continue;
160     }
161     if (detail::MatchesBaseURI(scheme, host, port, url)) {
162       return true;
163     }
164   }
165 
166   return false;
167 }
168 
169 }  // namespace auth
170 }  // namespace net
171 }  // namespace mozilla
172