Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/io/FilePreferences.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "FilePreferences.h"
8
9
#include "mozilla/ClearOnShutdown.h"
10
#include "mozilla/Preferences.h"
11
#include "mozilla/StaticPtr.h"
12
#include "mozilla/Tokenizer.h"
13
#include "nsAppDirectoryServiceDefs.h"
14
#include "nsDirectoryServiceDefs.h"
15
#include "nsDirectoryServiceUtils.h"
16
#include "nsString.h"
17
18
namespace mozilla {
19
namespace FilePreferences {
20
21
static bool sBlockUNCPaths = false;
22
typedef nsTArray<nsString> WinPaths;
23
24
static WinPaths& PathWhitelist()
25
0
{
26
0
  static WinPaths sPaths;
27
0
  return sPaths;
28
0
}
29
30
#ifdef XP_WIN
31
typedef char16_t char_path_t;
32
#else
33
typedef char char_path_t;
34
#endif
35
36
typedef nsTArray<nsTString<char_path_t>> Paths;
37
static StaticAutoPtr<Paths> sBlacklist;
38
39
static Paths& PathBlacklist()
40
0
{
41
0
  if (!sBlacklist) {
42
0
    sBlacklist = new nsTArray<nsTString<char_path_t>>();
43
0
    ClearOnShutdown(&sBlacklist);
44
0
  }
45
0
  return *sBlacklist;
46
0
}
47
48
static void AllowUNCDirectory(char const* directory)
49
0
{
50
0
  nsCOMPtr<nsIFile> file;
51
0
  NS_GetSpecialDirectory(directory, getter_AddRefs(file));
52
0
  if (!file) {
53
0
    return;
54
0
  }
55
0
56
0
  nsString path;
57
0
  if (NS_FAILED(file->GetTarget(path))) {
58
0
    return;
59
0
  }
60
0
61
0
  // The whitelist makes sense only for UNC paths, because this code is used
62
0
  // to block only UNC paths, hence, no need to add non-UNC directories here
63
0
  // as those would never pass the check.
64
0
  if (!StringBeginsWith(path, NS_LITERAL_STRING("\\\\"))) {
65
0
    return;
66
0
  }
67
0
68
0
  if (!PathWhitelist().Contains(path)) {
69
0
    PathWhitelist().AppendElement(path);
70
0
  }
71
0
}
72
73
void InitPrefs()
74
0
{
75
0
  sBlockUNCPaths = Preferences::GetBool("network.file.disable_unc_paths", false);
76
0
77
0
  PathBlacklist().Clear();
78
0
  nsTAutoString<char_path_t> blacklist;
79
#ifdef XP_WIN
80
  Preferences::GetString("network.file.path_blacklist", blacklist);
81
#else
82
  Preferences::GetCString("network.file.path_blacklist", blacklist);
83
0
#endif
84
0
85
0
  TTokenizer<char_path_t> p(blacklist);
86
0
  while (!p.CheckEOF()) {
87
0
    nsTString<char_path_t> path;
88
0
    Unused << p.ReadUntil(TTokenizer<char_path_t>::Token::Char(','), path);
89
0
    path.Trim(" ");
90
0
    if (!path.IsEmpty()) {
91
0
      PathBlacklist().AppendElement(path);
92
0
    }
93
0
    Unused << p.CheckChar(',');
94
0
  }
95
0
}
96
97
void InitDirectoriesWhitelist()
98
0
{
99
0
  // NS_GRE_DIR is the installation path where the binary resides.
100
0
  AllowUNCDirectory(NS_GRE_DIR);
101
0
  // NS_APP_USER_PROFILE_50_DIR and NS_APP_USER_PROFILE_LOCAL_50_DIR are the two
102
0
  // parts of the profile we store permanent and local-specific data.
103
0
  AllowUNCDirectory(NS_APP_USER_PROFILE_50_DIR);
104
0
  AllowUNCDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR);
105
0
}
106
107
namespace { // anon
108
109
template <typename TChar>
110
class TNormalizer
111
  : public TTokenizer<TChar>
112
{
113
  typedef TTokenizer<TChar> base;
114
public:
115
  typedef typename base::Token Token;
116
117
  TNormalizer(const nsTSubstring<TChar>& aFilePath, const Token& aSeparator)
118
    : TTokenizer<TChar>(aFilePath)
119
    , mSeparator(aSeparator)
120
0
  {
121
0
  }
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:mozilla::FilePreferences::(anonymous namespace)::TNormalizer<char16_t>::TNormalizer(nsTSubstring<char16_t> const&, mozilla::TokenizerBase<char16_t>::Token const&)
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:mozilla::FilePreferences::(anonymous namespace)::TNormalizer<char>::TNormalizer(nsTSubstring<char> const&, mozilla::TokenizerBase<char>::Token const&)
122
123
  bool Get(nsTSubstring<TChar>& aNormalizedFilePath)
124
0
  {
125
0
    aNormalizedFilePath.Truncate();
126
0
127
0
    // Windows UNC paths begin with double separator (\\)
128
0
    // Linux paths begin with just one separator (/)
129
0
    // If we want to use the normalizer for regular windows paths this code
130
0
    // will need to be updated.
131
#ifdef XP_WIN
132
    if (base::Check(mSeparator)) {
133
      aNormalizedFilePath.Append(mSeparator.AsChar());
134
    }
135
#endif
136
137
0
    if (base::Check(mSeparator)) {
138
0
      aNormalizedFilePath.Append(mSeparator.AsChar());
139
0
    }
140
0
141
0
    while (base::HasInput()) {
142
0
      if (!ConsumeName()) {
143
0
        return false;
144
0
      }
145
0
    }
146
0
147
0
    for (auto const& name : mStack) {
148
0
      aNormalizedFilePath.Append(name);
149
0
    }
150
0
151
0
    return true;
152
0
  }
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:mozilla::FilePreferences::(anonymous namespace)::TNormalizer<char16_t>::Get(nsTSubstring<char16_t>&)
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:mozilla::FilePreferences::(anonymous namespace)::TNormalizer<char>::Get(nsTSubstring<char>&)
153
154
155
private:
156
  bool ConsumeName()
157
0
  {
158
0
    if (base::CheckEOF()) {
159
0
      return true;
160
0
    }
161
0
162
0
    if (CheckCurrentDir()) {
163
0
      return true;
164
0
    }
165
0
166
0
    if (CheckParentDir()) {
167
0
      if (!mStack.Length()) {
168
0
        // This means there are more \.. than valid names
169
0
        return false;
170
0
      }
171
0
172
0
      mStack.RemoveLastElement();
173
0
      return true;
174
0
    }
175
0
176
0
    nsTDependentSubstring<TChar> name;
177
0
    if (base::ReadUntil(mSeparator, name, base::INCLUDE_LAST) && name.Length() == 1) {
178
0
      // this means and empty name (a lone slash), which is illegal
179
0
      return false;
180
0
    }
181
0
    mStack.AppendElement(name);
182
0
183
0
    return true;
184
0
  }
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:mozilla::FilePreferences::(anonymous namespace)::TNormalizer<char16_t>::ConsumeName()
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:mozilla::FilePreferences::(anonymous namespace)::TNormalizer<char>::ConsumeName()
185
186
  bool CheckParentDir()
187
0
  {
188
0
    typename nsTString<TChar>::const_char_iterator cursor = base::mCursor;
189
0
    if (base::CheckChar('.') && base::CheckChar('.') && CheckSeparator()) {
190
0
      return true;
191
0
    }
192
0
193
0
    base::mCursor = cursor;
194
0
    return false;
195
0
  }
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:mozilla::FilePreferences::(anonymous namespace)::TNormalizer<char16_t>::CheckParentDir()
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:mozilla::FilePreferences::(anonymous namespace)::TNormalizer<char>::CheckParentDir()
196
197
  bool CheckCurrentDir()
198
0
  {
199
0
    typename nsTString<TChar>::const_char_iterator cursor = base::mCursor;
200
0
    if (base::CheckChar('.') && CheckSeparator()) {
201
0
      return true;
202
0
    }
203
0
204
0
    base::mCursor = cursor;
205
0
    return false;
206
0
  }
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:mozilla::FilePreferences::(anonymous namespace)::TNormalizer<char16_t>::CheckCurrentDir()
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:mozilla::FilePreferences::(anonymous namespace)::TNormalizer<char>::CheckCurrentDir()
207
208
  bool CheckSeparator()
209
0
  {
210
0
    return base::Check(mSeparator) || base::CheckEOF();
211
0
  }
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:mozilla::FilePreferences::(anonymous namespace)::TNormalizer<char16_t>::CheckSeparator()
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:mozilla::FilePreferences::(anonymous namespace)::TNormalizer<char>::CheckSeparator()
212
213
  Token const mSeparator;
214
  nsTArray<nsTDependentSubstring<TChar>> mStack;
215
};
216
217
} // anon
218
219
bool IsBlockedUNCPath(const nsAString& aFilePath)
220
0
{
221
0
  typedef TNormalizer<char16_t> Normalizer;
222
0
  if (!sBlockUNCPaths) {
223
0
    return false;
224
0
  }
225
0
226
0
  if (!StringBeginsWith(aFilePath, NS_LITERAL_STRING("\\\\"))) {
227
0
    return false;
228
0
  }
229
0
230
0
  nsAutoString normalized;
231
0
  if (!Normalizer(aFilePath, Normalizer::Token::Char('\\')).Get(normalized)) {
232
0
    // Broken paths are considered invalid and thus inaccessible
233
0
    return true;
234
0
  }
235
0
236
0
  for (const auto& allowedPrefix : PathWhitelist()) {
237
0
    if (StringBeginsWith(normalized, allowedPrefix)) {
238
0
      if (normalized.Length() == allowedPrefix.Length()) {
239
0
        return false;
240
0
      }
241
0
      if (normalized[allowedPrefix.Length()] == L'\\') {
242
0
        return false;
243
0
      }
244
0
245
0
      // When we are here, the path has a form "\\path\prefixevil"
246
0
      // while we have an allowed prefix of "\\path\prefix".
247
0
      // Note that we don't want to add a slash to the end of a prefix
248
0
      // so that opening the directory (no slash at the end) still works.
249
0
      break;
250
0
    }
251
0
  }
252
0
253
0
  return true;
254
0
}
255
256
#ifdef XP_WIN
257
const char kPathSeparator = '\\';
258
#else
259
const char kPathSeparator = '/';
260
#endif
261
262
bool IsAllowedPath(const nsTSubstring<char_path_t>& aFilePath)
263
177
{
264
177
  typedef TNormalizer<char_path_t> Normalizer;
265
177
  // If sBlacklist has been cleared at shutdown, we must avoid calling
266
177
  // PathBlacklist() again, as that will recreate the array and we will leak.
267
177
  if (!sBlacklist) {
268
177
    return true;
269
177
  }
270
0
271
0
  if (PathBlacklist().Length() == 0) {
272
0
    return true;
273
0
  }
274
0
275
0
  nsTAutoString<char_path_t> normalized;
276
0
  if (!Normalizer(aFilePath, Normalizer::Token::Char(kPathSeparator)).Get(normalized)) {
277
0
    // Broken paths are considered invalid and thus inaccessible
278
0
    return false;
279
0
  }
280
0
281
0
  for (const auto& prefix : PathBlacklist()) {
282
0
    if (StringBeginsWith(normalized, prefix)) {
283
0
      if (normalized.Length() > prefix.Length() &&
284
0
          normalized[prefix.Length()] != kPathSeparator) {
285
0
        continue;
286
0
      }
287
0
      return false;
288
0
    }
289
0
  }
290
0
291
0
  return true;
292
0
}
293
294
void testing::SetBlockUNCPaths(bool aBlock)
295
0
{
296
0
  sBlockUNCPaths = aBlock;
297
0
}
298
299
void testing::AddDirectoryToWhitelist(nsAString const & aPath)
300
0
{
301
0
  PathWhitelist().AppendElement(aPath);
302
0
}
303
304
bool testing::NormalizePath(nsAString const & aPath, nsAString & aNormalized)
305
0
{
306
0
  typedef TNormalizer<char16_t> Normalizer;
307
0
  Normalizer normalizer(aPath, Normalizer::Token::Char('\\'));
308
0
  return normalizer.Get(aNormalized);
309
0
}
310
311
} // ::FilePreferences
312
} // ::mozilla