Line data Source code
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 "OCSPRequestor.h"
8 :
9 : #include <limits>
10 :
11 : #include "ScopedNSSTypes.h"
12 : #include "mozilla/Base64.h"
13 : #include "mozilla/Casting.h"
14 : #include "nsIURLParser.h"
15 : #include "nsNSSCallbacks.h"
16 : #include "nsNetCID.h"
17 : #include "nsServiceManagerUtils.h"
18 : #include "secerr.h"
19 :
20 : extern mozilla::LazyLogModule gCertVerifierLog;
21 :
22 : namespace mozilla {
23 :
24 : void
25 0 : ReleaseHttpServerSession(nsNSSHttpServerSession* httpServerSession)
26 : {
27 0 : delete httpServerSession;
28 0 : }
29 :
30 : void
31 0 : ReleaseHttpRequestSession(nsNSSHttpRequestSession* httpRequestSession)
32 : {
33 0 : httpRequestSession->Release();
34 0 : }
35 :
36 0 : MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueHTTPServerSession,
37 : nsNSSHttpServerSession,
38 : ReleaseHttpServerSession)
39 :
40 0 : MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueHTTPRequestSession,
41 : nsNSSHttpRequestSession,
42 : ReleaseHttpRequestSession)
43 :
44 : } // namespace mozilla
45 :
46 : namespace mozilla { namespace psm {
47 :
48 : static nsresult
49 0 : AppendEscapedBase64Item(const SECItem* encodedRequest, nsACString& path)
50 : {
51 : nsresult rv;
52 : nsDependentCSubstring requestAsSubstring(
53 0 : BitwiseCast<char*, unsigned char*>(encodedRequest->data),
54 0 : encodedRequest->len);
55 0 : nsCString base64Request;
56 0 : rv = Base64Encode(requestAsSubstring, base64Request);
57 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
58 0 : return rv;
59 : }
60 :
61 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
62 : ("Setting up OCSP GET path, pre path =%s\n",
63 : PromiseFlatCString(path).get()));
64 :
65 : // The path transformation is not a direct url encoding. Three characters
66 : // need change '+' -> "%2B", '/' -> "%2F", and '=' -> '%3D'.
67 : // http://tools.ietf.org/html/rfc5019#section-5
68 0 : base64Request.ReplaceSubstring("+", "%2B");
69 0 : base64Request.ReplaceSubstring("/", "%2F");
70 0 : base64Request.ReplaceSubstring("=", "%3D");
71 0 : path.Append(base64Request);
72 0 : return NS_OK;
73 : }
74 :
75 : Result
76 0 : DoOCSPRequest(const UniquePLArenaPool& arena, const char* url,
77 : const OriginAttributes& originAttributes,
78 : const SECItem* encodedRequest, TimeDuration timeout,
79 : bool useGET,
80 : /*out*/ SECItem*& encodedResponse)
81 : {
82 0 : MOZ_ASSERT(arena.get());
83 0 : MOZ_ASSERT(url);
84 0 : MOZ_ASSERT(encodedRequest);
85 0 : MOZ_ASSERT(encodedRequest->data);
86 0 : if (!arena.get() || !url || !encodedRequest || !encodedRequest->data) {
87 0 : return Result::FATAL_ERROR_INVALID_ARGS;
88 : }
89 0 : uint32_t urlLen = strlen(url);
90 0 : if (urlLen > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
91 0 : return Result::FATAL_ERROR_INVALID_ARGS;
92 : }
93 :
94 0 : nsCOMPtr<nsIURLParser> urlParser = do_GetService(NS_STDURLPARSER_CONTRACTID);
95 0 : if (!urlParser) {
96 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
97 : }
98 :
99 : uint32_t schemePos;
100 : int32_t schemeLen;
101 : uint32_t authorityPos;
102 : int32_t authorityLen;
103 : uint32_t pathPos;
104 : int32_t pathLen;
105 0 : nsresult nsrv = urlParser->ParseURL(url, static_cast<int32_t>(urlLen),
106 : &schemePos, &schemeLen,
107 : &authorityPos, &authorityLen,
108 0 : &pathPos, &pathLen);
109 0 : if (NS_FAILED(nsrv)) {
110 0 : return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
111 : }
112 0 : if (schemeLen < 0 || authorityLen < 0) {
113 0 : return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
114 : }
115 : nsAutoCString scheme(url + schemePos,
116 0 : static_cast<nsAutoCString::size_type>(schemeLen));
117 0 : if (!scheme.LowerCaseEqualsLiteral("http")) {
118 : // We don't support HTTPS to avoid loops. See Bug 92923.
119 : // We also in general only support HTTP.
120 0 : return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
121 : }
122 :
123 : uint32_t hostnamePos;
124 : int32_t hostnameLen;
125 : int32_t port;
126 : // We ignore user:password sections: if one is present, we send an OCSP
127 : // request to the URL as normal without sending the username or password.
128 0 : nsrv = urlParser->ParseAuthority(url + authorityPos, authorityLen,
129 : nullptr, nullptr, nullptr, nullptr,
130 0 : &hostnamePos, &hostnameLen, &port);
131 0 : if (NS_FAILED(nsrv)) {
132 0 : return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
133 : }
134 0 : if (hostnameLen < 0) {
135 0 : return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
136 : }
137 0 : if (port == -1) {
138 0 : port = 80;
139 0 : } else if (port < 0 || port > 0xffff) {
140 0 : return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
141 : }
142 : nsAutoCString
143 0 : hostname(url + authorityPos + hostnamePos,
144 0 : static_cast<nsACString::size_type>(hostnameLen));
145 :
146 0 : nsNSSHttpServerSession* serverSessionPtr = nullptr;
147 0 : Result rv = nsNSSHttpInterface::createSessionFcn(
148 0 : hostname.BeginReading(), static_cast<uint16_t>(port), &serverSessionPtr);
149 0 : if (rv != Success) {
150 0 : return rv;
151 : }
152 0 : UniqueHTTPServerSession serverSession(serverSessionPtr);
153 :
154 0 : nsAutoCString path;
155 0 : if (pathLen > 0) {
156 0 : path.Assign(url + pathPos, static_cast<nsAutoCString::size_type>(pathLen));
157 : } else {
158 0 : path.Assign("/");
159 : }
160 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
161 : ("Setting up OCSP request: pre all path =%s pathlen=%d\n", path.get(),
162 : pathLen));
163 0 : nsAutoCString method("POST");
164 0 : if (useGET) {
165 0 : method.Assign("GET");
166 0 : if (!StringEndsWith(path, NS_LITERAL_CSTRING("/"))) {
167 0 : path.Append("/");
168 : }
169 0 : nsrv = AppendEscapedBase64Item(encodedRequest, path);
170 0 : if (NS_WARN_IF(NS_FAILED(nsrv))) {
171 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
172 : }
173 : }
174 :
175 : nsNSSHttpRequestSession* requestSessionPtr;
176 0 : rv = nsNSSHttpInterface::createFcn(serverSession.get(), "http", path.get(),
177 : method.get(), originAttributes, timeout,
178 0 : &requestSessionPtr);
179 0 : if (rv != Success) {
180 0 : return rv;
181 : }
182 :
183 0 : UniqueHTTPRequestSession requestSession(requestSessionPtr);
184 :
185 0 : if (!useGET) {
186 0 : rv = nsNSSHttpInterface::setPostDataFcn(
187 : requestSession.get(),
188 0 : BitwiseCast<char*, unsigned char*>(encodedRequest->data),
189 0 : encodedRequest->len, "application/ocsp-request");
190 0 : if (rv != Success) {
191 0 : return rv;
192 : }
193 : }
194 :
195 : uint16_t httpResponseCode;
196 : const char* httpResponseData;
197 0 : uint32_t httpResponseDataLen = 0; // 0 means any response size is acceptable
198 0 : rv = nsNSSHttpInterface::trySendAndReceiveFcn(requestSession.get(), nullptr,
199 : &httpResponseCode,
200 : nullptr, &httpResponseData,
201 0 : &httpResponseDataLen);
202 0 : if (rv != Success) {
203 0 : return rv;
204 : }
205 :
206 0 : if (httpResponseCode != 200) {
207 0 : return Result::ERROR_OCSP_SERVER_ERROR;
208 : }
209 :
210 0 : encodedResponse = SECITEM_AllocItem(arena.get(), nullptr, httpResponseDataLen);
211 0 : if (!encodedResponse) {
212 0 : return Result::FATAL_ERROR_NO_MEMORY;
213 : }
214 :
215 0 : memcpy(encodedResponse->data, httpResponseData, httpResponseDataLen);
216 0 : return Success;
217 : }
218 :
219 : } } // namespace mozilla::psm
|