Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/manager/ssl/nsSecurityHeaderParser.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsSecurityHeaderParser.h"
6
#include "mozilla/Logging.h"
7
8
// The character classes in this file are informed by [RFC2616], Section 2.2.
9
// signed char is a signed data type one byte (8 bits) wide, so its value can
10
// never be greater than 127. The following implicitly makes use of this.
11
12
// A token is one or more CHAR except CTLs or separators.
13
// A CHAR is any US-ASCII character (octets 0 - 127).
14
// A CTL is any US-ASCII control character (octets 0 - 31) and DEL (127).
15
// A separator is one of ()<>@,;:\"/[]?={} as well as space and
16
// horizontal-tab (32 and 9, respectively).
17
// So, this returns true if chr is any octet 33-126 except ()<>@,;:\"/[]?={}
18
bool
19
0
IsTokenSymbol(signed char chr) {
20
0
  if (chr < 33 || chr == 127 ||
21
0
      chr == '(' || chr == ')' || chr == '<' || chr == '>' ||
22
0
      chr == '@' || chr == ',' || chr == ';' || chr == ':' ||
23
0
      chr == '"' || chr == '/' || chr == '[' || chr == ']' ||
24
0
      chr == '?' || chr == '=' || chr == '{' || chr == '}' || chr == '\\') {
25
0
    return false;
26
0
  }
27
0
  return true;
28
0
}
29
30
// A quoted-string consists of a quote (") followed by any amount of
31
// qdtext or quoted-pair, followed by a quote.
32
// qdtext is any TEXT except a quote.
33
// TEXT is any 8-bit octet except CTLs, but including LWS.
34
// quoted-pair is a backslash (\) followed by a CHAR.
35
// So, it turns out, \ can't really be a qdtext symbol for our purposes.
36
// This returns true if chr is any octet 9,10,13,32-126 except <"> or "\"
37
bool
38
0
IsQuotedTextSymbol(signed char chr) {
39
0
  return ((chr >= 32 && chr != '"' && chr != '\\' && chr != 127) ||
40
0
          chr == 0x9 || chr == 0xa || chr == 0xd);
41
0
}
42
43
// The octet following the "\" in a quoted pair can be anything 0-127.
44
bool
45
0
IsQuotedPairSymbol(signed char chr) {
46
0
  return (chr >= 0);
47
0
}
48
49
static mozilla::LazyLogModule sSHParserLog("nsSecurityHeaderParser");
50
51
0
#define SHPARSERLOG(args) MOZ_LOG(sSHParserLog, mozilla::LogLevel::Debug, args)
52
53
nsSecurityHeaderParser::nsSecurityHeaderParser(const nsCString& aHeader)
54
  : mCursor(aHeader.get())
55
  , mDirective(nullptr)
56
  , mError(false)
57
0
{
58
0
}
59
60
0
nsSecurityHeaderParser::~nsSecurityHeaderParser() {
61
0
  nsSecurityHeaderDirective *directive;
62
0
  while ((directive = mDirectives.popFirst())) {
63
0
    delete directive;
64
0
  }
65
0
}
66
67
mozilla::LinkedList<nsSecurityHeaderDirective> *
68
0
nsSecurityHeaderParser::GetDirectives() {
69
0
  return &mDirectives;
70
0
}
71
72
nsresult
73
0
nsSecurityHeaderParser::Parse() {
74
0
  MOZ_ASSERT(mDirectives.isEmpty());
75
0
  SHPARSERLOG(("trying to parse '%s'", mCursor));
76
0
77
0
  Header();
78
0
79
0
  // if we didn't consume the entire input, we were unable to parse it => error
80
0
  if (mError || *mCursor) {
81
0
    return NS_ERROR_FAILURE;
82
0
  } else {
83
0
    return NS_OK;
84
0
  }
85
0
}
86
87
bool
88
nsSecurityHeaderParser::Accept(char aChr)
89
0
{
90
0
  if (*mCursor == aChr) {
91
0
    Advance();
92
0
    return true;
93
0
  }
94
0
95
0
  return false;
96
0
}
97
98
bool
99
nsSecurityHeaderParser::Accept(bool (*aClassifier) (signed char))
100
0
{
101
0
  if (aClassifier(*mCursor)) {
102
0
    Advance();
103
0
    return true;
104
0
  }
105
0
106
0
  return false;
107
0
}
108
109
void
110
nsSecurityHeaderParser::Expect(char aChr)
111
0
{
112
0
  if (*mCursor != aChr) {
113
0
    mError = true;
114
0
  } else {
115
0
    Advance();
116
0
  }
117
0
}
118
119
void
120
nsSecurityHeaderParser::Advance()
121
0
{
122
0
  // Technically, 0 is valid in quoted-pair, but we were handed a
123
0
  // null-terminated const char *, so this doesn't handle that.
124
0
  if (*mCursor) {
125
0
    mOutput.Append(*mCursor);
126
0
    mCursor++;
127
0
  } else {
128
0
    mError = true;
129
0
  }
130
0
}
131
132
void
133
nsSecurityHeaderParser::Header()
134
0
{
135
0
  Directive();
136
0
  while (Accept(';')) {
137
0
    Directive();
138
0
  }
139
0
}
140
141
void
142
nsSecurityHeaderParser::Directive()
143
0
{
144
0
  mDirective = new nsSecurityHeaderDirective();
145
0
  LWSMultiple();
146
0
  DirectiveName();
147
0
  LWSMultiple();
148
0
  if (Accept('=')) {
149
0
    LWSMultiple();
150
0
    DirectiveValue();
151
0
    LWSMultiple();
152
0
  }
153
0
  mDirectives.insertBack(mDirective);
154
0
  SHPARSERLOG(("read directive name '%s', value '%s'",
155
0
               mDirective->mName.Data(), mDirective->mValue.Data()));
156
0
}
157
158
void
159
nsSecurityHeaderParser::DirectiveName()
160
0
{
161
0
  mOutput.Truncate(0);
162
0
  Token();
163
0
  mDirective->mName.Assign(mOutput);
164
0
}
165
166
void
167
nsSecurityHeaderParser::DirectiveValue()
168
0
{
169
0
  mOutput.Truncate(0);
170
0
  if (Accept(IsTokenSymbol)) {
171
0
    Token();
172
0
    mDirective->mValue.Assign(mOutput);
173
0
  } else if (Accept('"')) {
174
0
    // Accept advances the cursor if successful, which appends a character to
175
0
    // mOutput. The " is not part of what we want to capture, so truncate
176
0
    // mOutput again.
177
0
    mOutput.Truncate(0);
178
0
    QuotedString();
179
0
    mDirective->mValue.Assign(mOutput);
180
0
    Expect('"');
181
0
  }
182
0
}
183
184
void
185
nsSecurityHeaderParser::Token()
186
0
{
187
0
  while (Accept(IsTokenSymbol));
188
0
}
189
190
void
191
nsSecurityHeaderParser::QuotedString()
192
0
{
193
0
  while (true) {
194
0
    if (Accept(IsQuotedTextSymbol)) {
195
0
      QuotedText();
196
0
    } else if (Accept('\\')) {
197
0
      QuotedPair();
198
0
    } else {
199
0
      break;
200
0
    }
201
0
  }
202
0
}
203
204
void
205
nsSecurityHeaderParser::QuotedText()
206
0
{
207
0
  while (Accept(IsQuotedTextSymbol));
208
0
}
209
210
void
211
nsSecurityHeaderParser::QuotedPair()
212
0
{
213
0
  Accept(IsQuotedPairSymbol);
214
0
}
215
216
void
217
nsSecurityHeaderParser::LWSMultiple()
218
0
{
219
0
  while (true) {
220
0
    if (Accept('\r')) {
221
0
      LWSCRLF();
222
0
    } else if (Accept(' ') || Accept('\t')) {
223
0
      LWS();
224
0
    } else {
225
0
      break;
226
0
    }
227
0
  }
228
0
}
229
230
void
231
0
nsSecurityHeaderParser::LWSCRLF() {
232
0
  Expect('\n');
233
0
  if (!(Accept(' ') || Accept('\t'))) {
234
0
    mError = true;
235
0
  }
236
0
  LWS();
237
0
}
238
239
void
240
nsSecurityHeaderParser::LWS()
241
0
{
242
0
  // Note that becaue of how we're called, we don't have to check for
243
0
  // the mandatory presense of at least one of SP or HT.
244
0
  while (Accept(' ') || Accept('\t'));
245
0
}