0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 16:10:02 +01:00

Initial openssl support for net2

This commit is contained in:
Rhys Jones 2010-04-03 00:11:53 +01:00 committed by Ryan Dahl
parent fdae14070c
commit fb3a9cd0d8
5 changed files with 1232 additions and 16 deletions

View File

@ -9,7 +9,6 @@ var debugLevel = process.env['NODE_DEBUG'] ? 1 : 0;
function debug () {
if (debugLevel > 0) sys.error.apply(this, arguments);
}
var binding = process.binding('net');
// Note about Buffer interface:
@ -45,6 +44,14 @@ var EINPROGRESS = binding.EINPROGRESS;
var ENOENT = binding.ENOENT;
var END_OF_FILE = 42;
// Do we have openssl crypto?
try {
var SecureContext = process.binding('crypto').SecureContext;
var SecureStream = process.binding('crypto').SecureStream;
var crypto = true;
} catch (e) {
var crypto = false;
}
// IDLE TIMEOUTS
//
@ -246,6 +253,16 @@ function allocNewPool () {
pool.used = 0;
}
var securePool = null;
function allocNewSecurePool () {
securePool = new Buffer(40*1024);
}
var emptyBuffer = null;
function allocEmptyBuffer () {
emptyBuffer = new Buffer(1);
emptyBuffer.sent = 0;
emptyBuffer.length = 0;
}
function _doFlush () {
var socket = this.socket;
@ -271,20 +288,46 @@ function initStream (self) {
//debug('pool.used ' + pool.used);
var bytesRead;
var secureBytesRead;
try {
bytesRead = read(self.fd,
if (self.secure) {
if (!securePool) allocNewSecurePool();
secureBytesRead = read(self.fd, securePool, 0, securePool.length);
self.secureStream.readInject(securePool, 0, secureBytesRead);
bytesRead = self.secureStream.readExtract(pool, pool.used, pool.length - pool.used);
if(!self.secureEstablished) {
if (self.secureStream.isInitFinished()) {
self.secureEstablished = true;
if (self._events && self._events['secure']) self.emit('secure');
}
}
if (secureBytesRead === null && !self.server) {
// Client needs to write as part of handshake
this._writeWatcher.start();
}
} else {
bytesRead = read(self.fd,
pool,
pool.used,
pool.length - pool.used);
pool.length - pool.used);
}
} catch (e) {
self.forceClose(e);
if (this.forceClose) this.forceClose(e);
return;
}
//debug('bytesRead ' + bytesRead + '\n');
if (bytesRead === 0) {
if (self.secure && bytesRead == 0 && secureBytesRead >0){
// Deal with SSL handshake
if (self.server) {
self._checkForSecureHandshake();
} else {
if (self.secureEstablised) self.flush();
else self._checkForSecureHandshake();
}
} else if (bytesRead === 0) {
self.readable = false;
self._readWatcher.stop();
@ -341,10 +384,36 @@ function initStream (self) {
self.writable = false;
}
function Credentials(method) {
if (!crypto) {
throw new Error('node.js not compiled with openssl crypto support.');
}
this.context = new SecureContext();
if (method) this.context.init(method);
else this.context.init();
}
exports.createCredentials = function(cred) {
var c = new Credentials(cred.method);
if (cred.key) c.context.setKey(cred.key);
if (cred.cert) c.context.setCert(cred.cert);
if (cred.ca) {
if ( (typeof(cred.ca) == 'object') && cred.ca.length ) {
for(var i=0; i<cred.ca.length; i++)
c.context.addCACert(cred.ca[i]);
} else {
c.context.addCACert(cred.ca);
}
}
return c;
}
exports.Credentials = Credentials;
function Stream (fd) {
events.EventEmitter.call(this);
this.fd = null;
this.secure = false;
if (parseInt(fd) >= 0) {
this.open(fd);
@ -353,6 +422,55 @@ function Stream (fd) {
sys.inherits(Stream, events.EventEmitter);
exports.Stream = Stream;
Stream.prototype.setSecure = function(credentials) {
if (!crypto) {
throw new Error('node.js not compiled with openssl crypto support.');
}
this.secure = true;
this.secureEstablished = false;
// If no credentials given, create a new one for just this Stream
if (!credentials) {
this.credentials = new Credentials();
} else {
this.credentials = credentials;
}
this.secureStream = new SecureStream(this.credentials.context, this.server?1:0);
if (!this.server) {
// If client, trigger handshake
this._checkForSecureHandshake();
}
}
Stream.prototype.verifyPeer = function() {
if (!this.secure) {
throw new Error('Stream is not a secure stream.');
}
return this.secureStream.verifyPeer(this.credentials.context);
}
Stream.prototype._checkForSecureHandshake = function() {
// Do an empty write to see if we need to write out as part of handshake
if (!emptyBuffer) allocEmptyBuffer();
this.write(emptyBuffer);
}
Stream.prototype.getPeerCertificate = function(credentials) {
if (!this.secure) {
throw new Error('Stream is not a secure stream.');
}
return this.secureStream.getPeerCertificate();
}
Stream.prototype.getCipher = function() {
if (!this.secure) {
throw new Error('Stream is not a secure stream.');
}
return this.secureStream.getCurrentCipher();
}
Stream.prototype.open = function (fd) {
initStream(this);
@ -411,6 +529,15 @@ Stream.prototype.write = function (data, encoding) {
};
Stream.prototype._shutdownSecure = function () {
this.secureStream.shutdown();
if (!securePool) allocNewSecurePool();
var secureLen = this.secureStream.writeExtract(securePool, 0, securePool.length);
try {
var secureBytesWritten = write(this.fd, securePool, 0, secureLen);
} catch (e) {}
}
// Directly writes the data to socket.
//
// Steps:
@ -420,14 +547,15 @@ Stream.prototype.write = function (data, encoding) {
// 3. Slice out remaining
// 4. Unshift remaining onto _writeQueue. Return false.
Stream.prototype._writeOut = function (data, encoding) {
if (!this.writable) throw new Error('Stream is not writable');
if (!this.writable) {
if (this.secure) return false;
else throw new Error('Stream is not writable');
}
var buffer, off, len;
var bytesWritten, charsWritten;
var queuedData = false;
if (typeof data != 'string') {
// 'data' is a buffer, ignore 'encoding'
buffer = data;
@ -458,7 +586,7 @@ Stream.prototype._writeOut = function (data, encoding) {
assert(charsWritten <= data.length);
}
assert(bytesWritten > 0);
if (encoding) assert(bytesWritten > 0);
buffer = pool;
len = bytesWritten;
@ -478,11 +606,26 @@ Stream.prototype._writeOut = function (data, encoding) {
}
}
// Send the buffer.
try {
bytesWritten = write(this.fd, buffer, off, len);
if (this.secure) {
if (!buffer) return false;
bytesWritten = this.secureStream.writeInject(buffer, off, len);
if (!securePool) allocNewSecurePool();
var secureLen = this.secureStream.writeExtract(securePool, 0, securePool.length);
if (secureLen==-1) {
// Check our read again for secure handshake
this._readWatcher.callback();
secureBytesWritten = 0;
} else {
var secureBytesWritten = write(this.fd, securePool, 0, secureLen);
}
if(!this.secureEstablished && this.secureStream.isInitFinished()) {
this.secureEstablished = true;
if (this._events && this._events['secure']) this.emit('secure');
}
} else {
bytesWritten = write(this.fd, buffer, off, len);
}
} catch (e) {
this.forceClose(e);
return false;
@ -675,6 +818,10 @@ Stream.prototype.forceClose = function (exception) {
timeout.unenroll(this);
if (this.secure) {
this.secureStream.close();
}
// FIXME Bug when this.fd == 0
if (typeof this.fd == 'number') {
close(this.fd);
@ -691,6 +838,9 @@ Stream.prototype._shutdown = function () {
if (this.writable) {
this.writable = false;
if (this.secure) {
this._shutdownSecure();
}
try {
shutdown(this.fd, 'write')
} catch (e) {

View File

@ -32,6 +32,9 @@
#include <node_stdio.h>
#include <node_natives.h>
#include <node_version.h>
#ifdef HAVE_OPENSSL
#include <node_crypto.h>
#endif
#include <v8-debug.h>
@ -1179,7 +1182,16 @@ static Handle<Value> Binding(const Arguments& args) {
Buffer::Initialize(exports);
binding_cache->Set(module, exports);
}
#ifdef HAVE_OPENSSL
} else if (!strcmp(*module_v, "crypto")) {
if (binding_cache->Has(module)) {
exports = binding_cache->Get(module)->ToObject();
} else {
exports = Object::New();
InitCrypto(exports);
binding_cache->Set(module, exports);
}
#endif
} else if (!strcmp(*module_v, "natives")) {
if (binding_cache->Has(module)) {
exports = binding_cache->Get(module)->ToObject();

971
src/node_crypto.cc Normal file
View File

@ -0,0 +1,971 @@
#include <node_crypto.h>
#include <v8.h>
#include <node.h>
#include <node_buffer.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
namespace node {
using namespace v8;
static Persistent<String> errno_symbol;
static Persistent<String> syscall_symbol;
static Persistent<String> subject_symbol;
static Persistent<String> issuer_symbol;
static Persistent<String> valid_from_symbol;
static Persistent<String> valid_to_symbol;
static Persistent<String> name_symbol;
static Persistent<String> version_symbol;
static int x509_verify_error;
static inline const char *errno_string(int errorno) {
#define ERRNO_CASE(e) case e: return #e;
switch (errorno) {
#ifdef EACCES
ERRNO_CASE(EACCES);
#endif
#ifdef EADDRINUSE
ERRNO_CASE(EADDRINUSE);
#endif
#ifdef EADDRNOTAVAIL
ERRNO_CASE(EADDRNOTAVAIL);
#endif
#ifdef EAFNOSUPPORT
ERRNO_CASE(EAFNOSUPPORT);
#endif
#ifdef EAGAIN
ERRNO_CASE(EAGAIN);
#else
# ifdef EWOULDBLOCK
ERRNO_CASE(EWOULDBLOCK);
# endif
#endif
#ifdef EALREADY
ERRNO_CASE(EALREADY);
#endif
#ifdef EBADF
ERRNO_CASE(EBADF);
#endif
#ifdef EBADMSG
ERRNO_CASE(EBADMSG);
#endif
#ifdef EBUSY
ERRNO_CASE(EBUSY);
#endif
#ifdef ECANCELED
ERRNO_CASE(ECANCELED);
#endif
#ifdef ECHILD
ERRNO_CASE(ECHILD);
#endif
#ifdef ECONNABORTED
ERRNO_CASE(ECONNABORTED);
#endif
#ifdef ECONNREFUSED
ERRNO_CASE(ECONNREFUSED);
#endif
#ifdef ECONNRESET
ERRNO_CASE(ECONNRESET);
#endif
#ifdef EDEADLK
ERRNO_CASE(EDEADLK);
#endif
#ifdef EDESTADDRREQ
ERRNO_CASE(EDESTADDRREQ);
#endif
#ifdef EDOM
ERRNO_CASE(EDOM);
#endif
#ifdef EDQUOT
ERRNO_CASE(EDQUOT);
#endif
#ifdef EEXIST
ERRNO_CASE(EEXIST);
#endif
#ifdef EFAULT
ERRNO_CASE(EFAULT);
#endif
#ifdef EFBIG
ERRNO_CASE(EFBIG);
#endif
#ifdef EHOSTUNREACH
ERRNO_CASE(EHOSTUNREACH);
#endif
#ifdef EIDRM
ERRNO_CASE(EIDRM);
#endif
#ifdef EILSEQ
ERRNO_CASE(EILSEQ);
#endif
#ifdef EINPROGRESS
ERRNO_CASE(EINPROGRESS);
#endif
#ifdef EINTR
ERRNO_CASE(EINTR);
#endif
#ifdef EINVAL
ERRNO_CASE(EINVAL);
#endif
#ifdef EIO
ERRNO_CASE(EIO);
#endif
#ifdef EISCONN
ERRNO_CASE(EISCONN);
#endif
#ifdef EISDIR
ERRNO_CASE(EISDIR);
#endif
#ifdef ELOOP
ERRNO_CASE(ELOOP);
#endif
#ifdef EMFILE
ERRNO_CASE(EMFILE);
#endif
#ifdef EMLINK
ERRNO_CASE(EMLINK);
#endif
#ifdef EMSGSIZE
ERRNO_CASE(EMSGSIZE);
#endif
#ifdef EMULTIHOP
ERRNO_CASE(EMULTIHOP);
#endif
#ifdef ENAMETOOLONG
ERRNO_CASE(ENAMETOOLONG);
#endif
#ifdef ENETDOWN
ERRNO_CASE(ENETDOWN);
#endif
#ifdef ENETRESET
ERRNO_CASE(ENETRESET);
#endif
#ifdef ENETUNREACH
ERRNO_CASE(ENETUNREACH);
#endif
#ifdef ENFILE
ERRNO_CASE(ENFILE);
#endif
#ifdef ENOBUFS
ERRNO_CASE(ENOBUFS);
#endif
#ifdef ENODATA
ERRNO_CASE(ENODATA);
#endif
#ifdef ENODEV
ERRNO_CASE(ENODEV);
#endif
#ifdef ENOENT
ERRNO_CASE(ENOENT);
#endif
#ifdef ENOEXEC
ERRNO_CASE(ENOEXEC);
#endif
#ifdef ENOLCK
ERRNO_CASE(ENOLCK);
#endif
#ifdef ENOLINK
ERRNO_CASE(ENOLINK);
#endif
#ifdef ENOMEM
ERRNO_CASE(ENOMEM);
#endif
#ifdef ENOMSG
ERRNO_CASE(ENOMSG);
#endif
#ifdef ENOPROTOOPT
ERRNO_CASE(ENOPROTOOPT);
#endif
#ifdef ENOSPC
ERRNO_CASE(ENOSPC);
#endif
#ifdef ENOSR
ERRNO_CASE(ENOSR);
#endif
#ifdef ENOSTR
ERRNO_CASE(ENOSTR);
#endif
#ifdef ENOSYS
ERRNO_CASE(ENOSYS);
#endif
#ifdef ENOTCONN
ERRNO_CASE(ENOTCONN);
#endif
#ifdef ENOTDIR
ERRNO_CASE(ENOTDIR);
#endif
#ifdef ENOTEMPTY
ERRNO_CASE(ENOTEMPTY);
#endif
#ifdef ENOTSOCK
ERRNO_CASE(ENOTSOCK);
#endif
#ifdef ENOTSUP
ERRNO_CASE(ENOTSUP);
#else
# ifdef EOPNOTSUPP
ERRNO_CASE(EOPNOTSUPP);
# endif
#endif
#ifdef ENOTTY
ERRNO_CASE(ENOTTY);
#endif
#ifdef ENXIO
ERRNO_CASE(ENXIO);
#endif
#ifdef EOVERFLOW
ERRNO_CASE(EOVERFLOW);
#endif
#ifdef EPERM
ERRNO_CASE(EPERM);
#endif
#ifdef EPIPE
ERRNO_CASE(EPIPE);
#endif
#ifdef EPROTO
ERRNO_CASE(EPROTO);
#endif
#ifdef EPROTONOSUPPORT
ERRNO_CASE(EPROTONOSUPPORT);
#endif
#ifdef EPROTOTYPE
ERRNO_CASE(EPROTOTYPE);
#endif
#ifdef ERANGE
ERRNO_CASE(ERANGE);
#endif
#ifdef EROFS
ERRNO_CASE(EROFS);
#endif
#ifdef ESPIPE
ERRNO_CASE(ESPIPE);
#endif
#ifdef ESRCH
ERRNO_CASE(ESRCH);
#endif
#ifdef ESTALE
ERRNO_CASE(ESTALE);
#endif
#ifdef ETIME
ERRNO_CASE(ETIME);
#endif
#ifdef ETIMEDOUT
ERRNO_CASE(ETIMEDOUT);
#endif
#ifdef ETXTBSY
ERRNO_CASE(ETXTBSY);
#endif
#ifdef EXDEV
ERRNO_CASE(EXDEV);
#endif
default: return "";
}
}
static int verify_callback(int ok, X509_STORE_CTX *ctx) {
x509_verify_error = ctx->error;
return(ok);
}
static inline Local<Value> ErrnoException(int errorno,
const char *syscall,
const char *msg = "") {
Local<String> estring = String::NewSymbol(errno_string(errorno));
if (!msg[0]) msg = strerror(errorno);
Local<String> message = String::NewSymbol(msg);
Local<String> cons1 = String::Concat(estring, String::NewSymbol(", "));
Local<String> cons2 = String::Concat(cons1, message);
Local<Value> e = Exception::Error(cons2);
Local<Object> obj = e->ToObject();
obj->Set(errno_symbol, Integer::New(errorno));
obj->Set(syscall_symbol, String::NewSymbol(syscall));
return e;
}
void SecureContext::Initialize(Handle<Object> target) {
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New(SecureContext::New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(String::NewSymbol("SecureContext"));
NODE_SET_PROTOTYPE_METHOD(t, "init", SecureContext::Init);
NODE_SET_PROTOTYPE_METHOD(t, "setKey", SecureContext::SetKey);
NODE_SET_PROTOTYPE_METHOD(t, "setCert", SecureContext::SetCert);
NODE_SET_PROTOTYPE_METHOD(t, "addCACert", SecureContext::AddCACert);
NODE_SET_PROTOTYPE_METHOD(t, "setCiphers", SecureContext::SetCiphers);
NODE_SET_PROTOTYPE_METHOD(t, "close", SecureContext::Close);
target->Set(String::NewSymbol("SecureContext"), t->GetFunction());
}
Handle<Value> SecureContext::New(const Arguments& args) {
HandleScope scope;
SecureContext *p = new SecureContext();
p->Wrap(args.Holder());
return args.This();
}
Handle<Value> SecureContext::Init(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(args.Holder());
SSL_METHOD *method = SSLv23_method();
if (args.Length() == 1) {
if (!args[0]->IsString())
return ThrowException(Exception::TypeError(
String::New("Bad parameter")));
String::Utf8Value sslmethod(args[0]->ToString());
if (strcmp(*sslmethod, "SSLv2_method") == 0)
method = SSLv2_method();
if (strcmp(*sslmethod, "SSLv2_server_method") == 0)
method = SSLv2_server_method();
if (strcmp(*sslmethod, "SSLv2_client_method") == 0)
method = SSLv2_client_method();
if (strcmp(*sslmethod, "SSLv3_method") == 0)
method = SSLv3_method();
if (strcmp(*sslmethod, "SSLv3_server_method") == 0)
method = SSLv3_server_method();
if (strcmp(*sslmethod, "SSLv3_client_method") == 0)
method = SSLv3_client_method();
if (strcmp(*sslmethod, "SSLv23_method") == 0)
method = SSLv23_method();
if (strcmp(*sslmethod, "SSLv23_server_method") == 0)
method = SSLv23_server_method();
if (strcmp(*sslmethod, "SSLv23_client_method") == 0)
method = SSLv23_client_method();
if (strcmp(*sslmethod, "TLSv1_method") == 0)
method = TLSv1_method();
if (strcmp(*sslmethod, "TLSv1_server_method") == 0)
method = TLSv1_server_method();
if (strcmp(*sslmethod, "TLSv1_client_method") == 0)
method = TLSv1_client_method();
}
sc->pCtx = SSL_CTX_new(method);
// Enable session caching?
SSL_CTX_set_session_cache_mode(sc->pCtx, SSL_SESS_CACHE_SERVER);
// SSL_CTX_set_session_cache_mode(sc->pCtx,SSL_SESS_CACHE_OFF);
sc->caStore = X509_STORE_new();
return True();
}
Handle<Value> SecureContext::SetKey(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(args.Holder());
if (args.Length() != 1 ||
!args[0]->IsString()) {
return ThrowException(Exception::TypeError(
String::New("Bad parameter")));
}
String::Utf8Value keyPem(args[0]->ToString());
BIO *bp = NULL;
EVP_PKEY* pkey;
bp = BIO_new(BIO_s_mem());
if (!BIO_write(bp, *keyPem, strlen(*keyPem)))
return False();
pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
if (pkey == NULL)
return False();
SSL_CTX_use_PrivateKey(sc->pCtx, pkey);
BIO_free(bp);
return True();
}
Handle<Value> SecureContext::SetCert(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(args.Holder());
if (args.Length() != 1 ||
!args[0]->IsString()) {
return ThrowException(Exception::TypeError(
String::New("Bad parameter")));
}
String::Utf8Value certPem(args[0]->ToString());
BIO *bp = NULL;
X509 * x509;
bp = BIO_new(BIO_s_mem());
if (!BIO_write(bp, *certPem, strlen(*certPem)))
return False();
x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
if (x509 == NULL)
return False();
SSL_CTX_use_certificate(sc->pCtx, x509);
BIO_free(bp);
X509_free(x509);
return True();
}
Handle<Value> SecureContext::AddCACert(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(args.Holder());
if (args.Length() != 1 ||
!args[0]->IsString()) {
return ThrowException(Exception::TypeError(
String::New("Bad parameter")));
}
String::Utf8Value certPem(args[0]->ToString());
BIO *bp = NULL;
X509 *x509;
bp = BIO_new(BIO_s_mem());
if (!BIO_write(bp, *certPem, strlen(*certPem)))
return False();
x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
if (x509 == NULL)
return False();
X509_STORE_add_cert(sc->caStore, x509);
BIO_free(bp);
X509_free(x509);
return True();
}
Handle<Value> SecureContext::SetCiphers(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(args.Holder());
if (args.Length() != 1 ||
!args[0]->IsString()) {
return ThrowException(Exception::TypeError(
String::New("Bad parameter")));
}
String::Utf8Value ciphers(args[0]->ToString());
SSL_CTX_set_cipher_list(sc->pCtx, *ciphers);
return True();
}
Handle<Value> SecureContext::Close(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(args.Holder());
if (sc->pCtx != NULL) {
SSL_CTX_free(sc->pCtx);
return True();
}
return False();
}
void SecureStream::Initialize(Handle<Object> target) {
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New(SecureStream::New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(String::NewSymbol("SecureStream"));
NODE_SET_PROTOTYPE_METHOD(t, "readInject",
SecureStream::ReadInject);
NODE_SET_PROTOTYPE_METHOD(t, "readExtract",
SecureStream::ReadExtract);
NODE_SET_PROTOTYPE_METHOD(t, "writeInject",
SecureStream::WriteInject);
NODE_SET_PROTOTYPE_METHOD(t, "writeExtract",
SecureStream::WriteExtract);
NODE_SET_PROTOTYPE_METHOD(t, "writeCanExtract",
SecureStream::WriteCanExtract);
NODE_SET_PROTOTYPE_METHOD(t, "getPeerCertificate",
SecureStream::GetPeerCertificate);
NODE_SET_PROTOTYPE_METHOD(t, "isInitFinished",
SecureStream::IsInitFinished);
NODE_SET_PROTOTYPE_METHOD(t, "verifyPeer",
SecureStream::VerifyPeer);
NODE_SET_PROTOTYPE_METHOD(t, "getCurrentCipher",
SecureStream::GetCurrentCipher);
NODE_SET_PROTOTYPE_METHOD(t, "shutdown",
SecureStream::Shutdown);
NODE_SET_PROTOTYPE_METHOD(t, "close",
SecureStream::Close);
target->Set(String::NewSymbol("SecureStream"), t->GetFunction());
}
Handle<Value> SecureStream::New(const Arguments& args) {
HandleScope scope;
SecureStream *p = new SecureStream();
p->Wrap(args.Holder());
if (args.Length() != 2 ||
!args[0]->IsObject() ||
!args[1]->IsNumber()) {
return ThrowException(Exception::Error(String::New("Bad arguments.")));
}
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(args[0]->ToObject());
int isServer = args[1]->Int32Value();
p->pSSL = SSL_new(sc->pCtx);
p->pbioRead = BIO_new(BIO_s_mem());
p->pbioWrite = BIO_new(BIO_s_mem());
SSL_set_bio(p->pSSL, p->pbioRead, p->pbioWrite);
p->server = isServer>0;
if (p->server) {
SSL_set_accept_state(p->pSSL);
} else {
SSL_set_connect_state(p->pSSL);
}
return args.This();
}
Handle<Value> SecureStream::ReadInject(const Arguments& args) {
HandleScope scope;
SecureStream *ss = ObjectWrap::Unwrap<SecureStream>(args.Holder());
if (args.Length() < 3) {
return ThrowException(Exception::TypeError(
String::New("Takes 3 parameters")));
}
if (!Buffer::HasInstance(args[0])) {
return ThrowException(Exception::TypeError(
String::New("Second argument should be a buffer")));
}
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
size_t off = args[1]->Int32Value();
if (off >= buffer->length()) {
return ThrowException(Exception::Error(
String::New("Offset is out of bounds")));
}
size_t len = args[2]->Int32Value();
if (off + len > buffer->length()) {
return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer")));
}
int bytes_written = BIO_write(ss->pbioRead, (char*)buffer->data() + off, len);
if (bytes_written < 0) {
if (errno == EAGAIN || errno == EINTR) return Null();
return ThrowException(ErrnoException(errno, "read"));
}
return scope.Close(Integer::New(bytes_written));
}
Handle<Value> SecureStream::ReadExtract(const Arguments& args) {
HandleScope scope;
SecureStream *ss = ObjectWrap::Unwrap<SecureStream>(args.Holder());
if (args.Length() < 3) {
return ThrowException(Exception::TypeError(
String::New("Takes 3 parameters")));
}
if (!Buffer::HasInstance(args[0])) {
return ThrowException(Exception::TypeError(
String::New("Second argument should be a buffer")));
}
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
size_t off = args[1]->Int32Value();
if (off >= buffer->length()) {
return ThrowException(Exception::Error(
String::New("Offset is out of bounds")));
}
size_t len = args[2]->Int32Value();
if (off + len > buffer->length()) {
return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer")));
}
int bytes_read;
if (!SSL_is_init_finished(ss->pSSL)) {
if (ss->server) {
bytes_read = SSL_accept(ss->pSSL);
} else {
bytes_read = SSL_connect(ss->pSSL);
}
if (bytes_read < 0) {
int err;
if ((err = SSL_get_error(ss->pSSL, bytes_read)) == SSL_ERROR_WANT_READ) {
return scope.Close(Integer::New(0));
}
}
return scope.Close(Integer::New(0));
}
bytes_read = SSL_read(ss->pSSL, (char*)buffer->data() + off, len);
if (bytes_read < 0) {
int err = SSL_get_error(ss->pSSL, bytes_read);
if (err == SSL_ERROR_WANT_READ) {
return scope.Close(Integer::New(0));
}
// SSL read error
return scope.Close(Integer::New(-2));
}
if (bytes_read < 0) {
if (errno == EAGAIN || errno == EINTR) return Null();
return ThrowException(ErrnoException(errno, "read"));
}
return scope.Close(Integer::New(bytes_read));
}
Handle<Value> SecureStream::WriteCanExtract(const Arguments& args) {
HandleScope scope;
SecureStream *ss = ObjectWrap::Unwrap<SecureStream>(args.Holder());
int bytes_pending = BIO_pending(ss->pbioWrite);
return scope.Close(Integer::New(bytes_pending));
}
Handle<Value> SecureStream::WriteExtract(const Arguments& args) {
HandleScope scope;
SecureStream *ss = ObjectWrap::Unwrap<SecureStream>(args.Holder());
if (args.Length() < 3) {
return ThrowException(Exception::TypeError(
String::New("Takes 3 parameters")));
}
if (!Buffer::HasInstance(args[0])) {
return ThrowException(Exception::TypeError(
String::New("Second argument should be a buffer")));
}
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
size_t off = args[1]->Int32Value();
if (off >= buffer->length()) {
return ThrowException(Exception::Error(
String::New("Offset is out of bounds")));
}
size_t len = args[2]->Int32Value();
if (off + len > buffer->length()) {
return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer")));
}
int bytes_read = BIO_read(ss->pbioWrite, (char*)buffer->data() + off, len);
return scope.Close(Integer::New(bytes_read));
}
Handle<Value> SecureStream::WriteInject(const Arguments& args) {
HandleScope scope;
SecureStream *ss = ObjectWrap::Unwrap<SecureStream>(args.Holder());
if (args.Length() < 3) {
return ThrowException(Exception::TypeError(
String::New("Takes 3 parameters")));
}
if (!Buffer::HasInstance(args[0])) {
return ThrowException(Exception::TypeError(
String::New("Second argument should be a buffer")));
}
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
size_t off = args[1]->Int32Value();
if (off >= buffer->length()) {
return ThrowException(Exception::Error(
String::New("Offset is out of bounds")));
}
size_t len = args[2]->Int32Value();
if (off + len > buffer->length()) {
return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer")));
}
if (!SSL_is_init_finished(ss->pSSL)) {
int s;
if (ss->server) {
s = SSL_accept(ss->pSSL);
} else {
s = SSL_connect(ss->pSSL);
}
return scope.Close(Integer::New(0));
}
int bytes_written = SSL_write(ss->pSSL, (char*)buffer->data() + off, len);
return scope.Close(Integer::New(bytes_written));
}
Handle<Value> SecureStream::GetPeerCertificate(const Arguments& args) {
HandleScope scope;
SecureStream *ss = ObjectWrap::Unwrap<SecureStream>(args.Holder());
if (ss->pSSL == NULL) return Undefined();
Local<Object> info = Object::New();
X509* peer_cert = SSL_get_peer_certificate(ss->pSSL);
if (peer_cert != NULL) {
char* subject = X509_NAME_oneline(X509_get_subject_name(peer_cert), 0, 0);
if (subject != NULL) {
info->Set(subject_symbol, String::New(subject));
OPENSSL_free(subject);
}
char* issuer = X509_NAME_oneline(X509_get_issuer_name(peer_cert), 0, 0);
if (subject != NULL) {
info->Set(issuer_symbol, String::New(issuer));
OPENSSL_free(issuer);
}
char buf[256];
BIO* bio = BIO_new(BIO_s_mem());
ASN1_TIME_print(bio, X509_get_notBefore(peer_cert));
memset(buf, 0, sizeof(buf));
BIO_read(bio, buf, sizeof(buf) - 1);
info->Set(valid_from_symbol, String::New(buf));
ASN1_TIME_print(bio, X509_get_notAfter(peer_cert));
memset(buf, 0, sizeof(buf));
BIO_read(bio, buf, sizeof(buf) - 1);
BIO_free(bio);
info->Set(valid_to_symbol, String::New(buf));
X509_free(peer_cert);
}
return scope.Close(info);
}
Handle<Value> SecureStream::Shutdown(const Arguments& args) {
HandleScope scope;
SecureStream *ss = ObjectWrap::Unwrap<SecureStream>(args.Holder());
if (ss->pSSL == NULL) return False();
if (SSL_shutdown(ss->pSSL) == 1) {
return True();
}
return False();
}
Handle<Value> SecureStream::IsInitFinished(const Arguments& args) {
HandleScope scope;
SecureStream *ss = ObjectWrap::Unwrap<SecureStream>(args.Holder());
if (ss->pSSL == NULL) return False();
if (SSL_is_init_finished(ss->pSSL)) {
return True();
}
return False();
}
Handle<Value> SecureStream::VerifyPeer(const Arguments& args) {
HandleScope scope;
SecureStream *ss = ObjectWrap::Unwrap<SecureStream>(args.Holder());
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(args[0]->ToObject());
if (ss->pSSL == NULL) return False();
if (sc->caStore == NULL) return False();
X509 *cert = SSL_get_peer_certificate(ss->pSSL);
STACK_OF(X509) *certChain = SSL_get_peer_cert_chain(ss->pSSL);
X509_STORE_set_verify_cb_func(sc->caStore, verify_callback);
X509_STORE_CTX *storeCtx = X509_STORE_CTX_new();
X509_STORE_CTX_init(storeCtx, sc->caStore, cert, certChain);
x509_verify_error = 0;
// OS X Bug in openssl : x509_verify_cert is always true?
// This is why we have our global.
X509_verify_cert(storeCtx);
X509_STORE_CTX_free(storeCtx);
// Can also check for:
// X509_V_ERR_CERT_HAS_EXPIRED
// X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
// X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
// X509_V_ERR_INVALID_CA
// X509_V_ERR_PATH_LENGTH_EXCEEDED
// X509_V_ERR_INVALID_PURPOSE
// X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
// printf("%s\n", X509_verify_cert_error_string(x509_verify_error));
if (!x509_verify_error) return True();
return False();
}
Handle<Value> SecureStream::GetCurrentCipher(const Arguments& args) {
HandleScope scope;
SecureStream *ss = ObjectWrap::Unwrap<SecureStream>(args.Holder());
SSL_CIPHER *c;
if ( ss->pSSL == NULL ) return Undefined();
c = SSL_get_current_cipher(ss->pSSL);
if ( c == NULL ) return Undefined();
Local<Object> info = Object::New();
const char *cipher_name = SSL_CIPHER_get_name(c);
info->Set(name_symbol, String::New(cipher_name));
const char *cipher_version = SSL_CIPHER_get_version(c);
info->Set(version_symbol, String::New(cipher_version));
return scope.Close(info);
}
Handle<Value> SecureStream::Close(const Arguments& args) {
HandleScope scope;
SecureStream *ss = ObjectWrap::Unwrap<SecureStream>(args.Holder());
if (ss->pSSL != NULL) {
SSL_free(ss->pSSL);
ss->pSSL = NULL;
}
return True();
}
void InitCrypto(Handle<Object> target) {
HandleScope scope;
SSL_library_init();
OpenSSL_add_ssl_algorithms();
SSL_load_error_strings();
ERR_load_crypto_strings();
SecureContext::Initialize(target);
SecureStream::Initialize(target);
subject_symbol = NODE_PSYMBOL("subject");
issuer_symbol = NODE_PSYMBOL("issuer");
valid_from_symbol = NODE_PSYMBOL("valid_from");
valid_to_symbol = NODE_PSYMBOL("valid_to");
name_symbol = NODE_PSYMBOL("name");
version_symbol = NODE_PSYMBOL("version");
}
} // namespace node

73
src/node_crypto.h Normal file
View File

@ -0,0 +1,73 @@
#ifndef SRC_NODE_CRYPTO_H_
#define SRC_NODE_CRYPTO_H_
#include <node.h>
#include <node_object_wrap.h>
#include <v8.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
namespace node {
class SecureContext : ObjectWrap {
public:
static void Initialize(v8::Handle<v8::Object> target);
SSL_CTX *pCtx;
X509_STORE *caStore;
protected:
static v8::Handle<v8::Value> New(const v8::Arguments& args);
static v8::Handle<v8::Value> Init(const v8::Arguments& args);
static v8::Handle<v8::Value> SetKey(const v8::Arguments& args);
static v8::Handle<v8::Value> SetCert(const v8::Arguments& args);
static v8::Handle<v8::Value> AddCACert(const v8::Arguments& args);
static v8::Handle<v8::Value> SetCiphers(const v8::Arguments& args);
static v8::Handle<v8::Value> Close(const v8::Arguments& args);
SecureContext() : ObjectWrap() {
}
~SecureContext() {
// Free up
}
private:
};
class SecureStream : ObjectWrap {
public:
static void Initialize(v8::Handle<v8::Object> target);
protected:
static v8::Handle<v8::Value> New(const v8::Arguments& args);
static v8::Handle<v8::Value> ReadInject(const v8::Arguments& args);
static v8::Handle<v8::Value> ReadExtract(const v8::Arguments& args);
static v8::Handle<v8::Value> WriteCanExtract(const v8::Arguments& args);
static v8::Handle<v8::Value> WriteExtract(const v8::Arguments& args);
static v8::Handle<v8::Value> WriteInject(const v8::Arguments& args);
static v8::Handle<v8::Value> GetPeerCertificate(const v8::Arguments& args);
static v8::Handle<v8::Value> IsInitFinished(const v8::Arguments& args);
static v8::Handle<v8::Value> VerifyPeer(const v8::Arguments& args);
static v8::Handle<v8::Value> GetCurrentCipher(const v8::Arguments& args);
static v8::Handle<v8::Value> Shutdown(const v8::Arguments& args);
static v8::Handle<v8::Value> Close(const v8::Arguments& args);
SecureStream() : ObjectWrap() {
}
~SecureStream() {
}
private:
BIO *pbioRead;
BIO *pbioWrite;
SSL *pSSL;
bool server;
};
void InitCrypto(v8::Handle<v8::Object> target);
}
#endif // SRC_NODE_CRYPTO_H_

14
wscript
View File

@ -134,6 +134,13 @@ def configure(conf):
if sys.platform.startswith("freebsd"):
conf.fatal("Install the libexecinfo port from /usr/ports/devel/libexecinfo.")
if conf.check_cfg(package='openssl',
args='--cflags --libs',
#libpath=['/usr/lib', '/usr/local/lib'],
uselib_store='OPENSSL'):
conf.env["USE_OPENSSL"] = True
conf.env.append_value("CXXFLAGS", "-DHAVE_OPENSSL=1")
if conf.check_cfg(package='gnutls',
args='--cflags --libs',
atleast_version='2.5.0',
@ -415,6 +422,9 @@ def build(bld):
src/node_timer.cc
src/node_idle_watcher.cc
"""
if bld.env["USE_OPENSSL"]:
node.source += "src/node_crypto.cc"
if not bld.env["USE_SYSTEM"]:
node.includes = """
src/
@ -428,7 +438,7 @@ def build(bld):
"""
node.add_objects = 'ev eio evcom http_parser coupling'
node.uselib_local = ''
node.uselib = 'GNUTLS GPGERROR UDNS V8 EXECINFO DL KVM SOCKET NSL'
node.uselib = 'OPENSSL GNUTLS GPGERROR UDNS V8 EXECINFO DL KVM SOCKET NSL'
else:
node.includes = """
src/
@ -439,7 +449,7 @@ def build(bld):
"""
node.add_objects = 'eio evcom http_parser coupling'
node.uselib_local = 'eio'
node.uselib = 'EV GNUTLS GPGERROR UDNS V8 EXECINFO DL KVM SOCKET NSL'
node.uselib = 'EV OPENSSL GNUTLS GPGERROR UDNS V8 EXECINFO DL KVM SOCKET NSL'
node.install_path = '${PREFIX}/lib'
node.install_path = '${PREFIX}/bin'