Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(403)

Side by Side Diff: net/base/ssl_client_socket_nss.cc

Issue 4049: Port SSLClientSocket to Linux (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/base/ssl_client_socket_nss.h ('k') | net/base/ssl_client_socket_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/base/ssl_client_socket_nss.h"
6
7 #include <nspr.h>
8 #include <nss.h>
9 // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424
10 // until NSS 3.12.2 comes out and we update to it.
11 #define Lock FOO_NSS_Lock
12 #include <ssl.h>
13 #include <pk11pub.h>
14 #undef Lock
15
16 #include "base/logging.h"
17 #include "base/nss_init.h"
18 #include "base/string_util.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/ssl_info.h"
21
22 static const int kRecvBufferSize = 4096;
23
24 /*
25 * nss calls this if an incoming certificate is invalid.
26 * TODO(port): expose to app via GetSSLInfo so it can put up
27 * the appropriate GUI and retry with override if desired
28 */
29 static SECStatus
30 ownBadCertHandler(void * arg, PRFileDesc * socket)
31 {
32 PRErrorCode err = PR_GetError();
33 LOG(ERROR) << "server certificate is invalid; NSS error code " << err;
34 // Return SECSuccess to override the problem, SECFailure to let the original function fail
35 return SECSuccess; /* override, say it's OK. */
36 }
37
38
39 namespace net {
40
41 bool SSLClientSocketNSS::nss_options_initialized_ = false;
42
43 SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket,
44 const std::string& hostname,
45 const SSLConfig& ssl_config)
46 :
47 buffer_send_callback_(this, &SSLClientSocketNSS::BufferSendComplete),
48 buffer_recv_callback_(this, &SSLClientSocketNSS::BufferRecvComplete),
49 transport_send_busy_(false),
50 transport_recv_busy_(false),
51 io_callback_(this, &SSLClientSocketNSS::OnIOComplete),
52 transport_(transport_socket),
53 hostname_(hostname),
54 ssl_config_(ssl_config),
55 user_callback_(NULL),
56 user_buf_(NULL),
57 user_buf_len_(0),
58 completed_handshake_(false),
59 next_state_(STATE_NONE),
60 nss_fd_(NULL),
61 nss_bufs_(NULL) {
62 }
63
64 SSLClientSocketNSS::~SSLClientSocketNSS() {
65 Disconnect();
66 }
67
68 int SSLClientSocketNSS::Init() {
69 // Call NSS_NoDB_Init() in a threadsafe way.
70 base::EnsureNSSInit();
71
72 return OK;
73 }
74
75 int SSLClientSocketNSS::Connect(CompletionCallback* callback) {
76 DCHECK(transport_.get());
77 DCHECK(next_state_ == STATE_NONE);
78 DCHECK(!user_callback_);
79
80 next_state_ = STATE_CONNECT;
81 int rv = DoLoop(OK);
82 if (rv == ERR_IO_PENDING)
83 user_callback_ = callback;
84 return rv;
85 }
86
87 int SSLClientSocketNSS::ReconnectIgnoringLastError(CompletionCallback* callback) {
88 // TODO(darin): implement me!
89 return ERR_FAILED;
90 }
91
92 void SSLClientSocketNSS::Disconnect() {
93 // TODO(wtc): Send SSL close_notify alert.
94 if (nss_fd_ != NULL) {
95 PR_Close(nss_fd_);
96 nss_fd_ = NULL;
97 }
98 completed_handshake_ = false;
99 transport_->Disconnect();
100 }
101
102 bool SSLClientSocketNSS::IsConnected() const {
103 return completed_handshake_ && transport_->IsConnected();
104 }
105
106 int SSLClientSocketNSS::Read(char* buf, int buf_len,
107 CompletionCallback* callback) {
108 DCHECK(completed_handshake_);
109 DCHECK(next_state_ == STATE_NONE);
110 DCHECK(!user_callback_);
111
112 user_buf_ = buf;
113 user_buf_len_ = buf_len;
114
115 next_state_ = STATE_PAYLOAD_READ;
116 int rv = DoLoop(OK);
117 if (rv == ERR_IO_PENDING)
118 user_callback_ = callback;
119 return rv;
120 }
121
122 int SSLClientSocketNSS::Write(const char* buf, int buf_len,
123 CompletionCallback* callback) {
124 DCHECK(completed_handshake_);
125 DCHECK(next_state_ == STATE_NONE);
126 DCHECK(!user_callback_);
127
128 user_buf_ = const_cast<char*>(buf);
129 user_buf_len_ = buf_len;
130
131 next_state_ = STATE_PAYLOAD_WRITE;
132 int rv = DoLoop(OK);
133 if (rv == ERR_IO_PENDING)
134 user_callback_ = callback;
135 return rv;
136 }
137
138 void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
139 // TODO(port): implement!
140 ssl_info->Reset();
141 }
142
143 void SSLClientSocketNSS::DoCallback(int rv) {
144 DCHECK(rv != ERR_IO_PENDING);
145 DCHECK(user_callback_);
146
147 // since Run may result in Read being called, clear user_callback_ up front.
148 CompletionCallback* c = user_callback_;
149 user_callback_ = NULL;
150 c->Run(rv);
151 }
152
153 void SSLClientSocketNSS::OnIOComplete(int result) {
154 int rv = DoLoop(result);
155 if (rv != ERR_IO_PENDING)
156 DoCallback(rv);
157 }
158
159 // Map a Chromium net error code to an NSS error code
160 // See _MD_unix_map_default_error in the NSS source
161 // tree for inspiration.
162 static PRErrorCode MapErrorToNSS(int result) {
163 if (result >=0)
164 return result;
165 // TODO(port): add real table
166 LOG(ERROR) << "MapErrorToNSS " << result;
167 return PR_UNKNOWN_ERROR;
168 }
169
170 /*
171 * Do network I/O between the given buffer and the given socket.
172 * Return 0 for EOF,
173 * > 0 for bytes transferred immediately,
174 * < 0 for error (or the non-error ERR_IO_PENDING).
175 */
176 int SSLClientSocketNSS::BufferSend(void) {
177 if (transport_send_busy_) return ERR_IO_PENDING;
178
179 const char *buf;
180 int nb = memio_GetWriteParams(nss_bufs_, &buf);
181
182 int rv;
183 if (!nb) {
184 rv = OK;
185 } else {
186 rv = transport_->Write(buf, nb, &buffer_send_callback_);
187 if (rv == ERR_IO_PENDING)
188 transport_send_busy_ = true;
189 else
190 memio_PutWriteResult(nss_bufs_, MapErrorToNSS(rv));
191 }
192
193 return rv;
194 }
195
196 void SSLClientSocketNSS::BufferSendComplete(int result) {
197 memio_PutWriteResult(nss_bufs_, result);
198 transport_send_busy_ = false;
199 OnIOComplete(result);
200 }
201
202
203 int SSLClientSocketNSS::BufferRecv(void) {
204
205 if (transport_recv_busy_) return ERR_IO_PENDING;
206
207 char *buf;
208 int nb = memio_GetReadParams(nss_bufs_, &buf);
209 int rv;
210 if (!nb) {
211 // buffer too full to read into, so no I/O possible at moment
212 rv = ERR_IO_PENDING;
213 } else {
214 rv = transport_->Read(buf, nb, &buffer_recv_callback_);
215 if (rv == ERR_IO_PENDING)
216 transport_recv_busy_ = true;
217 else
218 memio_PutReadResult(nss_bufs_, MapErrorToNSS(rv));
219 }
220
221 return rv;
222 }
223
224 void SSLClientSocketNSS::BufferRecvComplete(int result) {
225 memio_PutReadResult(nss_bufs_, result);
226 transport_recv_busy_ = false;
227 OnIOComplete(result);
228 }
229
230
231 int SSLClientSocketNSS::DoLoop(int last_io_result) {
232 DCHECK(next_state_ != STATE_NONE);
233 bool network_moved;
234 int rv = last_io_result;
235 do {
236 network_moved = false;
237 State state = next_state_;
238 //DLOG(INFO) << "DoLoop state " << state;
239 next_state_ = STATE_NONE;
240 switch (state) {
241 case STATE_CONNECT:
242 rv = DoConnect();
243 break;
244 case STATE_CONNECT_COMPLETE:
245 rv = DoConnectComplete(rv);
246 break;
247 case STATE_HANDSHAKE_READ:
248 rv = DoHandshakeRead();
249 break;
250 case STATE_PAYLOAD_READ:
251 rv = DoPayloadRead();
252 break;
253 case STATE_PAYLOAD_WRITE:
254 rv = DoPayloadWrite();
255 break;
256 default:
257 rv = ERR_UNEXPECTED;
258 NOTREACHED() << "unexpected state";
259 break;
260 }
261
262 // Do the actual network I/O
263 if (nss_bufs_ != NULL) {
264 int nsent = BufferSend();
265 int nreceived = BufferRecv();
266 network_moved = (nsent > 0 || nreceived >= 0);
267 }
268 } while ((rv != ERR_IO_PENDING || network_moved) && next_state_ != STATE_NONE) ;
269 return rv;
270 }
271
272 int SSLClientSocketNSS::DoConnect() {
273 next_state_ = STATE_CONNECT_COMPLETE;
274 return transport_->Connect(&io_callback_);
275 }
276
277 int SSLClientSocketNSS::DoConnectComplete(int result) {
278 if (result < 0)
279 return result;
280
281 if (Init() != OK) {
282 NOTREACHED() << "Couldn't initialize nss";
283 }
284
285 // Transport connected, now hook it up to nss
286 // TODO(port): specify rx and tx buffer sizes separately
287 nss_fd_ = memio_CreateIOLayer(kRecvBufferSize);
288 if (nss_fd_ == NULL) {
289 return 9999; // TODO(port): real error
290 }
291
292 // Tell NSS who we're connected to
293 PRNetAddr peername;
294 socklen_t len = sizeof(PRNetAddr);
295 int err = transport_->GetPeerName((struct sockaddr *)&peername, &len);
296 if (err) {
297 DLOG(ERROR) << "GetPeerName failed";
298 return 9999; // TODO(port): real error
299 }
300 memio_SetPeerName(nss_fd_, &peername);
301
302 // Grab pointer to buffers
303 nss_bufs_ = memio_GetSecret(nss_fd_);
304
305 /* Create SSL state machine */
306 /* Push SSL onto our fake I/O socket */
307 nss_fd_ = SSL_ImportFD(NULL, nss_fd_);
308 if (nss_fd_ == NULL) {
309 return ERR_SSL_PROTOCOL_ERROR; // TODO(port): real error
310 }
311 // TODO(port): set more ssl options! Check errors!
312
313 int rv;
314
315 rv = SSL_OptionSet(nss_fd_, SSL_SECURITY, PR_TRUE);
316 if (rv != SECSuccess)
317 return ERR_UNEXPECTED;
318
319 rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL2, ssl_config_.ssl2_enabled);
320 if (rv != SECSuccess)
321 return ERR_UNEXPECTED;
322
323 rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.ssl3_enabled);
324 if (rv != SECSuccess)
325 return ERR_UNEXPECTED;
326
327 rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.tls1_enabled);
328 if (rv != SECSuccess)
329 return ERR_UNEXPECTED;
330
331 rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
332 if (rv != SECSuccess)
333 return ERR_UNEXPECTED;
334
335 rv = SSL_BadCertHook(nss_fd_, ownBadCertHandler, NULL);
336 if (rv != SECSuccess)
337 return ERR_UNEXPECTED;
338
339 // Tell SSL the hostname we're trying to connect to.
340 SSL_SetURL(nss_fd_, hostname_.c_str());
341
342 // Tell SSL we're a client; needed if not letting NSPR do socket I/O
343 SSL_ResetHandshake(nss_fd_, 0);
344 next_state_ = STATE_HANDSHAKE_READ;
345 // Return OK so DoLoop tries handshaking
346 return OK;
347 }
348
349 int SSLClientSocketNSS::DoHandshakeRead() {
350 int rv = SSL_ForceHandshake(nss_fd_);
351 if (rv == SECSuccess) {
352 // there's a callback for this, too
353 completed_handshake_ = true;
354 // Indicate we're ready to handle I/O. Badly named?
355 next_state_ = STATE_NONE;
356 return OK;
357 }
358 PRErrorCode prerr = PR_GetError();
359 if (prerr == PR_WOULD_BLOCK_ERROR) {
360 // at this point, it should have tried to send some bytes
361 next_state_ = STATE_HANDSHAKE_READ;
362 return ERR_IO_PENDING;
363 }
364 // TODO: map rv to net error code properly
365 return ERR_SSL_PROTOCOL_ERROR;
366 }
367
368 int SSLClientSocketNSS::DoPayloadRead() {
369 int rv = PR_Read(nss_fd_, user_buf_, user_buf_len_);
370 if (rv >= 0)
371 return rv;
372 PRErrorCode prerr = PR_GetError();
373 if (prerr == PR_WOULD_BLOCK_ERROR) {
374 next_state_ = STATE_PAYLOAD_READ;
375 return ERR_IO_PENDING;
376 }
377 // TODO: map rv to net error code properly
378 return ERR_SSL_PROTOCOL_ERROR;
379 }
380
381 int SSLClientSocketNSS::DoPayloadWrite() {
382 int rv = PR_Write(nss_fd_, user_buf_, user_buf_len_);
383 if (rv >= 0)
384 return rv;
385 PRErrorCode prerr = PR_GetError();
386 if (prerr == PR_WOULD_BLOCK_ERROR) {
387 next_state_ = STATE_PAYLOAD_WRITE;
388 return ERR_IO_PENDING;
389 }
390 // TODO: map rv to net error code properly
391 return ERR_SSL_PROTOCOL_ERROR;
392 }
393
394 } // namespace net
395
OLDNEW
« no previous file with comments | « net/base/ssl_client_socket_nss.h ('k') | net/base/ssl_client_socket_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698