1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/manager/boot/src/nsSecurityHeaderParser.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,254 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "nsSecurityHeaderParser.h" 1.9 +#include "prlog.h" 1.10 + 1.11 +// The character classes in this file are informed by [RFC2616], Section 2.2. 1.12 +// signed char is a signed data type one byte (8 bits) wide, so its value can 1.13 +// never be greater than 127. The following implicitly makes use of this. 1.14 + 1.15 +// A token is one or more CHAR except CTLs or separators. 1.16 +// A CHAR is any US-ASCII character (octets 0 - 127). 1.17 +// A CTL is any US-ASCII control character (octets 0 - 31) and DEL (127). 1.18 +// A separator is one of ()<>@,;:\"/[]?={} as well as space and 1.19 +// horizontal-tab (32 and 9, respectively). 1.20 +// So, this returns true if chr is any octet 33-126 except ()<>@,;:\"/[]?={} 1.21 +bool 1.22 +IsTokenSymbol(signed char chr) { 1.23 + if (chr < 33 || chr == 127 || 1.24 + chr == '(' || chr == ')' || chr == '<' || chr == '>' || 1.25 + chr == '@' || chr == ',' || chr == ';' || chr == ':' || 1.26 + chr == '"' || chr == '/' || chr == '[' || chr == ']' || 1.27 + chr == '?' || chr == '=' || chr == '{' || chr == '}' || chr == '\\') { 1.28 + return false; 1.29 + } 1.30 + return true; 1.31 +} 1.32 + 1.33 +// A quoted-string consists of a quote (") followed by any amount of 1.34 +// qdtext or quoted-pair, followed by a quote. 1.35 +// qdtext is any TEXT except a quote. 1.36 +// TEXT is any 8-bit octet except CTLs, but including LWS. 1.37 +// quoted-pair is a backslash (\) followed by a CHAR. 1.38 +// So, it turns out, \ can't really be a qdtext symbol for our purposes. 1.39 +// This returns true if chr is any octet 9,10,13,32-126 except <"> or "\" 1.40 +bool 1.41 +IsQuotedTextSymbol(signed char chr) { 1.42 + return ((chr >= 32 && chr != '"' && chr != '\\' && chr != 127) || 1.43 + chr == 0x9 || chr == 0xa || chr == 0xd); 1.44 +} 1.45 + 1.46 +// The octet following the "\" in a quoted pair can be anything 0-127. 1.47 +bool 1.48 +IsQuotedPairSymbol(signed char chr) { 1.49 + return (chr >= 0); 1.50 +} 1.51 + 1.52 +#if defined(PR_LOGGING) 1.53 +static PRLogModuleInfo * 1.54 +GetSHParserLog() 1.55 +{ 1.56 + static PRLogModuleInfo *sSHParserLog; 1.57 + if (!sSHParserLog) { 1.58 + sSHParserLog = PR_NewLogModule("nsSecurityHeaderParser"); 1.59 + } 1.60 + return sSHParserLog; 1.61 +} 1.62 +#endif 1.63 + 1.64 +#define SHPARSERLOG(args) PR_LOG(GetSHParserLog(), PR_LOG_DEBUG, args) 1.65 + 1.66 +nsSecurityHeaderParser::nsSecurityHeaderParser(const char *aHeader) 1.67 + : mCursor(aHeader) 1.68 + , mError(false) 1.69 +{ 1.70 +} 1.71 + 1.72 +nsSecurityHeaderParser::~nsSecurityHeaderParser() { 1.73 + nsSecurityHeaderDirective *directive; 1.74 + while ((directive = mDirectives.popFirst())) { 1.75 + delete directive; 1.76 + } 1.77 +} 1.78 + 1.79 +mozilla::LinkedList<nsSecurityHeaderDirective> * 1.80 +nsSecurityHeaderParser::GetDirectives() { 1.81 + return &mDirectives; 1.82 +} 1.83 + 1.84 +nsresult 1.85 +nsSecurityHeaderParser::Parse() { 1.86 + MOZ_ASSERT(mDirectives.isEmpty()); 1.87 + SHPARSERLOG(("trying to parse '%s'", mCursor)); 1.88 + 1.89 + Header(); 1.90 + 1.91 + // if we didn't consume the entire input, we were unable to parse it => error 1.92 + if (mError || *mCursor) { 1.93 + return NS_ERROR_FAILURE; 1.94 + } else { 1.95 + return NS_OK; 1.96 + } 1.97 +} 1.98 + 1.99 +bool 1.100 +nsSecurityHeaderParser::Accept(char aChr) 1.101 +{ 1.102 + if (*mCursor == aChr) { 1.103 + Advance(); 1.104 + return true; 1.105 + } 1.106 + 1.107 + return false; 1.108 +} 1.109 + 1.110 +bool 1.111 +nsSecurityHeaderParser::Accept(bool (*aClassifier) (signed char)) 1.112 +{ 1.113 + if (aClassifier(*mCursor)) { 1.114 + Advance(); 1.115 + return true; 1.116 + } 1.117 + 1.118 + return false; 1.119 +} 1.120 + 1.121 +void 1.122 +nsSecurityHeaderParser::Expect(char aChr) 1.123 +{ 1.124 + if (*mCursor != aChr) { 1.125 + mError = true; 1.126 + } else { 1.127 + Advance(); 1.128 + } 1.129 +} 1.130 + 1.131 +void 1.132 +nsSecurityHeaderParser::Advance() 1.133 +{ 1.134 + // Technically, 0 is valid in quoted-pair, but we were handed a 1.135 + // null-terminated const char *, so this doesn't handle that. 1.136 + if (*mCursor) { 1.137 + mOutput.Append(*mCursor); 1.138 + mCursor++; 1.139 + } else { 1.140 + mError = true; 1.141 + } 1.142 +} 1.143 + 1.144 +void 1.145 +nsSecurityHeaderParser::Header() 1.146 +{ 1.147 + Directive(); 1.148 + while (Accept(';')) { 1.149 + Directive(); 1.150 + } 1.151 +} 1.152 + 1.153 +void 1.154 +nsSecurityHeaderParser::Directive() 1.155 +{ 1.156 + mDirective = new nsSecurityHeaderDirective(); 1.157 + LWSMultiple(); 1.158 + DirectiveName(); 1.159 + LWSMultiple(); 1.160 + if (Accept('=')) { 1.161 + LWSMultiple(); 1.162 + DirectiveValue(); 1.163 + LWSMultiple(); 1.164 + } 1.165 + mDirectives.insertBack(mDirective); 1.166 + SHPARSERLOG(("read directive name '%s', value '%s'", 1.167 + mDirective->mName.Data(), mDirective->mValue.Data())); 1.168 +} 1.169 + 1.170 +void 1.171 +nsSecurityHeaderParser::DirectiveName() 1.172 +{ 1.173 + mOutput.Truncate(0); 1.174 + Token(); 1.175 + mDirective->mName.Assign(mOutput); 1.176 +} 1.177 + 1.178 +void 1.179 +nsSecurityHeaderParser::DirectiveValue() 1.180 +{ 1.181 + mOutput.Truncate(0); 1.182 + if (Accept(IsTokenSymbol)) { 1.183 + Token(); 1.184 + mDirective->mValue.Assign(mOutput); 1.185 + } else if (Accept('"')) { 1.186 + // Accept advances the cursor if successful, which appends a character to 1.187 + // mOutput. The " is not part of what we want to capture, so truncate 1.188 + // mOutput again. 1.189 + mOutput.Truncate(0); 1.190 + QuotedString(); 1.191 + mDirective->mValue.Assign(mOutput); 1.192 + Expect('"'); 1.193 + } 1.194 +} 1.195 + 1.196 +void 1.197 +nsSecurityHeaderParser::Token() 1.198 +{ 1.199 + while (Accept(IsTokenSymbol)); 1.200 +} 1.201 + 1.202 +void 1.203 +nsSecurityHeaderParser::QuotedString() 1.204 +{ 1.205 + while (true) { 1.206 + if (Accept(IsQuotedTextSymbol)) { 1.207 + QuotedText(); 1.208 + } else if (Accept('\\')) { 1.209 + QuotedPair(); 1.210 + } else { 1.211 + break; 1.212 + } 1.213 + } 1.214 +} 1.215 + 1.216 +void 1.217 +nsSecurityHeaderParser::QuotedText() 1.218 +{ 1.219 + while (Accept(IsQuotedTextSymbol)); 1.220 +} 1.221 + 1.222 +void 1.223 +nsSecurityHeaderParser::QuotedPair() 1.224 +{ 1.225 + Accept(IsQuotedPairSymbol); 1.226 +} 1.227 + 1.228 +void 1.229 +nsSecurityHeaderParser::LWSMultiple() 1.230 +{ 1.231 + while (true) { 1.232 + if (Accept('\r')) { 1.233 + LWSCRLF(); 1.234 + } else if (Accept(' ') || Accept('\t')) { 1.235 + LWS(); 1.236 + } else { 1.237 + break; 1.238 + } 1.239 + } 1.240 +} 1.241 + 1.242 +void 1.243 +nsSecurityHeaderParser::LWSCRLF() { 1.244 + Expect('\n'); 1.245 + if (!(Accept(' ') || Accept('\t'))) { 1.246 + mError = true; 1.247 + } 1.248 + LWS(); 1.249 +} 1.250 + 1.251 +void 1.252 +nsSecurityHeaderParser::LWS() 1.253 +{ 1.254 + // Note that becaue of how we're called, we don't have to check for 1.255 + // the mandatory presense of at least one of SP or HT. 1.256 + while (Accept(' ') || Accept('\t')); 1.257 +}