tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsSecurityHeaderParser.cpp (5791B)


      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 IsTokenSymbol(signed char chr) {
     19  if (chr < 33 || chr == 127 || chr == '(' || chr == ')' || chr == '<' ||
     20      chr == '>' || chr == '@' || chr == ',' || chr == ';' || chr == ':' ||
     21      chr == '"' || chr == '/' || chr == '[' || chr == ']' || chr == '?' ||
     22      chr == '=' || chr == '{' || chr == '}' || chr == '\\') {
     23    return false;
     24  }
     25  return true;
     26 }
     27 
     28 // A quoted-string consists of a quote (") followed by any amount of
     29 // qdtext or quoted-pair, followed by a quote.
     30 // qdtext is any TEXT except a quote.
     31 // TEXT is any 8-bit octet except CTLs, but including LWS.
     32 // quoted-pair is a backslash (\) followed by a CHAR.
     33 // So, it turns out, \ can't really be a qdtext symbol for our purposes.
     34 // This returns true if chr is any octet 9,10,13,32-126 except <"> or "\"
     35 bool IsQuotedTextSymbol(signed char chr) {
     36  return ((chr >= 32 && chr != '"' && chr != '\\' && chr != 127) ||
     37          chr == 0x9 || chr == 0xa || chr == 0xd);
     38 }
     39 
     40 // The octet following the "\" in a quoted pair can be anything 0-127.
     41 bool IsQuotedPairSymbol(signed char chr) { return (chr >= 0); }
     42 
     43 static mozilla::LazyLogModule sSHParserLog("nsSecurityHeaderParser");
     44 
     45 #define SHPARSERLOG(args) MOZ_LOG(sSHParserLog, mozilla::LogLevel::Debug, args)
     46 
     47 nsSecurityHeaderParser::nsSecurityHeaderParser(const nsCString& aHeader)
     48    : mCursor(aHeader.get()), mDirective(nullptr), mError(false) {}
     49 
     50 nsSecurityHeaderParser::~nsSecurityHeaderParser() {
     51  nsSecurityHeaderDirective* directive;
     52  while ((directive = mDirectives.popFirst())) {
     53    delete directive;
     54  }
     55 }
     56 
     57 mozilla::LinkedList<nsSecurityHeaderDirective>*
     58 nsSecurityHeaderParser::GetDirectives() {
     59  return &mDirectives;
     60 }
     61 
     62 nsresult nsSecurityHeaderParser::Parse() {
     63  MOZ_ASSERT(mDirectives.isEmpty());
     64  SHPARSERLOG(("trying to parse '%s'", mCursor));
     65 
     66  Header();
     67 
     68  // if we didn't consume the entire input, we were unable to parse it => error
     69  if (mError || *mCursor) {
     70    return NS_ERROR_FAILURE;
     71  } else {
     72    return NS_OK;
     73  }
     74 }
     75 
     76 bool nsSecurityHeaderParser::Accept(char aChr) {
     77  if (*mCursor == aChr) {
     78    Advance();
     79    return true;
     80  }
     81 
     82  return false;
     83 }
     84 
     85 bool nsSecurityHeaderParser::Accept(bool (*aClassifier)(signed char)) {
     86  if (aClassifier(*mCursor)) {
     87    Advance();
     88    return true;
     89  }
     90 
     91  return false;
     92 }
     93 
     94 void nsSecurityHeaderParser::Expect(char aChr) {
     95  if (*mCursor != aChr) {
     96    mError = true;
     97  } else {
     98    Advance();
     99  }
    100 }
    101 
    102 void nsSecurityHeaderParser::Advance() {
    103  // Technically, 0 is valid in quoted-pair, but we were handed a
    104  // null-terminated const char *, so this doesn't handle that.
    105  if (*mCursor) {
    106    mOutput.Append(*mCursor);
    107    mCursor++;
    108  } else {
    109    mError = true;
    110  }
    111 }
    112 
    113 void nsSecurityHeaderParser::Header() {
    114  Directive();
    115  while (Accept(';')) {
    116    Directive();
    117  }
    118 }
    119 
    120 void nsSecurityHeaderParser::Directive() {
    121  mDirective = new nsSecurityHeaderDirective();
    122  LWSMultiple();
    123  DirectiveName();
    124  LWSMultiple();
    125  if (Accept('=')) {
    126    LWSMultiple();
    127    DirectiveValue();
    128    LWSMultiple();
    129  }
    130  mDirectives.insertBack(mDirective);
    131  if (mDirective->mValue.isSome()) {
    132    SHPARSERLOG(("read directive name '%s', value '%s'",
    133                 mDirective->mName.Data(), mDirective->mValue->Data()));
    134  } else {
    135    SHPARSERLOG(
    136        ("read valueless directive name '%s'", mDirective->mName.Data()));
    137  }
    138 }
    139 
    140 void nsSecurityHeaderParser::DirectiveName() {
    141  mOutput.Truncate(0);
    142  Token();
    143  mDirective->mName.Assign(mOutput);
    144 }
    145 
    146 void nsSecurityHeaderParser::DirectiveValue() {
    147  mOutput.Truncate(0);
    148  mDirective->mValue.emplace();
    149  if (Accept(IsTokenSymbol)) {
    150    Token();
    151    mDirective->mValue->Assign(mOutput);
    152  } else if (Accept('"')) {
    153    // Accept advances the cursor if successful, which appends a character to
    154    // mOutput. The " is not part of what we want to capture, so truncate
    155    // mOutput again.
    156    mOutput.Truncate(0);
    157    QuotedString();
    158    mDirective->mValue->Assign(mOutput);
    159    Expect('"');
    160  }
    161 }
    162 
    163 void nsSecurityHeaderParser::Token() { while (Accept(IsTokenSymbol)); }
    164 
    165 void nsSecurityHeaderParser::QuotedString() {
    166  while (true) {
    167    if (Accept(IsQuotedTextSymbol)) {
    168      QuotedText();
    169    } else if (Accept('\\')) {
    170      QuotedPair();
    171    } else {
    172      break;
    173    }
    174  }
    175 }
    176 
    177 void nsSecurityHeaderParser::QuotedText() {
    178  while (Accept(IsQuotedTextSymbol));
    179 }
    180 
    181 void nsSecurityHeaderParser::QuotedPair() { Accept(IsQuotedPairSymbol); }
    182 
    183 void nsSecurityHeaderParser::LWSMultiple() {
    184  while (true) {
    185    if (Accept('\r')) {
    186      LWSCRLF();
    187    } else if (Accept(' ') || Accept('\t')) {
    188      LWS();
    189    } else {
    190      break;
    191    }
    192  }
    193 }
    194 
    195 void nsSecurityHeaderParser::LWSCRLF() {
    196  Expect('\n');
    197  if (!(Accept(' ') || Accept('\t'))) {
    198    mError = true;
    199  }
    200  LWS();
    201 }
    202 
    203 void nsSecurityHeaderParser::LWS() {
    204  // Note that becaue of how we're called, we don't have to check for
    205  // the mandatory presense of at least one of SP or HT.
    206  while (Accept(' ') || Accept('\t'));
    207 }