Skip to content

Commit

Permalink
switch to native SSL on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
jeroen committed Feb 17, 2017
1 parent 153506d commit 40d3912
Show file tree
Hide file tree
Showing 6 changed files with 825 additions and 74 deletions.
32 changes: 12 additions & 20 deletions src/Makevars.win
@@ -1,18 +1,12 @@
PKG_CPPFLAGS = \
-I../windows/openssl-1.0.2h/include \
-I../windows/libsasl-2.1.26/include \
-I. -Ijsonsl -Ibson -Imongoc \
-DLIBSASL_EXPORTS -DBSON_COMPILATION -DMONGOC_COMPILATION \
-DMONGOC_HAVE_SASL_CLIENT_DONE -D__USE_MINGW_ANSI_STDIO
-DLIBSASL_EXPORTS -DBSON_COMPILATION -DMONGOC_COMPILATION

PKG_CFLAGS = \
-Wno-unused-value -Wno-implicit-function-declaration

PKG_LIBS = \
-L. -L../windows/openssl-1.0.2h/lib${R_ARCH} \
-L../windows/libsasl-2.1.26/lib${R_ARCH} \
-lmongoc -lbson -lssl -lcrypto -lsasl2 \
-lcrypto -lgdi32 -lcrypt32 -lws2_32
-L. -lmongoc -lbson -lcrypt32 -lws2_32 -lbcrypt -lsecur32

LIBBSON=bson/bcon.o bson/bson-atomic.o bson/bson-clock.o bson/bson-context.o \
bson/bson-error.o bson/bson-iso8601.o bson/bson-iter.o bson/bson-json.o \
Expand All @@ -30,8 +24,8 @@ LIBMONGOC=mongoc/mongoc-array.o mongoc/mongoc-b64.o mongoc/mongoc-buffer.o \
mongoc/mongoc-gridfs-file.o mongoc/mongoc-gridfs.o mongoc/mongoc-index.o \
mongoc/mongoc-init.o mongoc/mongoc-list.o mongoc/mongoc-log.o \
mongoc/mongoc-matcher-op.o mongoc/mongoc-matcher.o mongoc/mongoc-queue.o \
mongoc/mongoc-rand-openssl.o mongoc/mongoc-read-prefs.o mongoc/mongoc-rpc.o \
mongoc/mongoc-sasl.o mongoc/mongoc-scram.o mongoc/mongoc-socket.o mongoc/mongoc-ssl.o \
mongoc/mongoc-rand-cng.o mongoc/mongoc-read-prefs.o mongoc/mongoc-rpc.o \
mongoc/mongoc-scram.o mongoc/mongoc-socket.o mongoc/mongoc-ssl.o \
mongoc/mongoc-stream-buffered.o mongoc/mongoc-stream-file.o \
mongoc/mongoc-stream-gridfs.o mongoc/mongoc-stream-socket.o mongoc/mongoc-stream-tls.o \
mongoc/mongoc-stream.o mongoc/mongoc-uri.o mongoc/mongoc-util.o \
Expand All @@ -41,29 +35,27 @@ LIBMONGOC=mongoc/mongoc-array.o mongoc/mongoc-b64.o mongoc/mongoc-buffer.o \
mongoc/mongoc-server-description.o mongoc/mongoc-topology-description.o \
mongoc/mongoc-topology.o mongoc/mongoc-topology-scanner.o mongoc/mongoc-version-functions.o \
mongoc/mongoc-memcmp.o mongoc/mongoc-server-stream.o mongoc/mongoc-find-and-modify.o \
mongoc/mongoc-read-concern.o mongoc/mongoc-openssl.o mongoc/mongoc-apm.o \
mongoc/mongoc-crypto.o mongoc/mongoc-crypto-openssl.o mongoc/mongoc-handshake.o \
mongoc/mongoc-stream-tls-openssl.o mongoc/mongoc-stream-tls-openssl-bio.o \
mongoc/mongoc-cluster-sasl.o
mongoc/mongoc-read-concern.o mongoc/mongoc-apm.o \
mongoc/mongoc-crypto.o mongoc/mongoc-crypto-cng.o mongoc/mongoc-handshake.o \
mongoc/mongoc-stream-tls-secure-channel.o mongoc/mongoc-stream-tls-secure-transport.o \
mongoc/mongoc-secure-channel.o mongoc/mongoc-cluster-sspi.o mongoc/mongoc-cluster-sasl.o \
mongoc/mongoc-sspi.o

# For development only
all: clean winlibs
all: clean

$(SHLIB): libbson.a libmongoc.a

libbson.a: $(LIBBSON)
$(AR) rcs libbson.a $(LIBBSON)

libmongoc.a: $(LIBMONGOC:.c=.o) win32/ipv6.o
libmongoc.a: $(LIBMONGOC) win32/ipv6.o
ifeq "$(WIN)" "64"
$(AR) rcs libmongoc.a mongoc/*.o
else
$(AR) rcs libmongoc.a mongoc/*.o win32/ipv6.o
endif

winlibs:
"${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" "../tools/winlibs.R"

clean:
rm -f $(LIBBSON) $(LIBMONGOC) $(SHLIB) $(OBJECTS)
rm -f $(SHLIB) $(LIBBSON) $(LIBMONGOC) $(OBJECTS)
rm -f libbson.a libmongoc.a win32/ipv6.o
287 changes: 287 additions & 0 deletions src/mongoc/mongoc-cluster-sspi.c
@@ -0,0 +1,287 @@
/*
* Copyright 2017 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "mongoc-config.h"

#ifdef MONGOC_ENABLE_SASL_SSPI

#include "mongoc-cluster-sspi-private.h"
#include "mongoc-cluster-sasl-private.h"
#include "mongoc-sspi-private.h"
#include "mongoc-error.h"
#include "mongoc-util-private.h"



/*
*--------------------------------------------------------------------------
*
* _mongoc_cluster_auth_node_sasl --
*
* Perform authentication for a cluster node using SASL. This only
* supports GSSAPI at the moment.
*
* Returns:
* true if successful; otherwise false and @error is set.
*
* Side effects:
* error may be set.
*
*--------------------------------------------------------------------------
*/

mongoc_sspi_client_state_t *
_mongoc_cluster_sspi_new (mongoc_uri_t *uri, const char *hostname)
{
WCHAR *service; /* L"serviceName@hostname@REALM" */
const char *service_name = "mongodb";
ULONG flags = ISC_REQ_MUTUAL_AUTH;
const char *service_realm = NULL;
char *service_ascii = NULL;
mongoc_sspi_client_state_t *state;
const char *tmp_creds;
int service_ascii_len;
const bson_t *options;
int tmp_creds_len;
bson_t properties;
bson_iter_t iter;
int service_len;
int user_len = 0;
int pass_len = 0;
WCHAR *pass = NULL;
WCHAR *user = NULL;
int res;

options = mongoc_uri_get_options (uri);

if (!mongoc_uri_get_mechanism_properties (uri, &properties)) {
bson_init (&properties);
}

if (bson_iter_init_find_case (&iter, options, "gssapiservicename") &&
BSON_ITER_HOLDS_UTF8 (&iter)) {
service_name = bson_iter_utf8 (&iter, NULL);
}
if (bson_iter_init_find_case (&iter, &properties, "SERVICE_NAME") &&
BSON_ITER_HOLDS_UTF8 (&iter)) {
service_name = bson_iter_utf8 (&iter, NULL);
}

if (bson_iter_init_find_case (&iter, &properties, "SERVICE_REALM") &&
BSON_ITER_HOLDS_UTF8 (&iter)) {
service_realm = bson_iter_utf8 (&iter, NULL);
service_ascii =
bson_strdup_printf ("%s@%s@%s", service_name, hostname, service_realm);
} else {
service_ascii = bson_strdup_printf ("%s@%s", service_name, hostname);
}
service_ascii_len = strlen (service_ascii);

/* this is donated to the sspi */
service = calloc (service_ascii_len + 1, sizeof (WCHAR));
service_len = MultiByteToWideChar (
CP_UTF8, 0, service_ascii, service_ascii_len, service, service_ascii_len);
service[service_len] = L'\0';
bson_free (service_ascii);


tmp_creds = mongoc_uri_get_password (uri);
if (tmp_creds) {
tmp_creds_len = strlen (tmp_creds);

/* this is donated to the sspi */
pass = calloc (tmp_creds_len + 1, sizeof (WCHAR));
pass_len = MultiByteToWideChar (
CP_UTF8, 0, tmp_creds, tmp_creds_len, pass, tmp_creds_len);
pass[pass_len] = L'\0';
}

tmp_creds = mongoc_uri_get_username (uri);
if (tmp_creds) {
tmp_creds_len = strlen (tmp_creds);

/* this is donated to the sspi */
user = calloc (tmp_creds_len + 1, sizeof (WCHAR));
user_len = MultiByteToWideChar (
CP_UTF8, 0, tmp_creds, tmp_creds_len, user, tmp_creds_len);
user[user_len] = L'\0';
}

state = (mongoc_sspi_client_state_t *) bson_malloc0 (sizeof *state);
res = _mongoc_sspi_auth_sspi_client_init (
service, flags, user, user_len, NULL, 0, pass, pass_len, state);


if (res != MONGOC_SSPI_AUTH_GSS_ERROR) {
return state;
}
bson_free (state);
return NULL;
}

bool
_mongoc_cluster_auth_node_sspi (mongoc_cluster_t *cluster,
mongoc_stream_t *stream,
const char *hostname,
bson_error_t *error)
{
mongoc_sspi_client_state_t *state;
uint8_t buf[4096] = {0};
bson_iter_t iter;
uint32_t buflen;
bson_t reply;
char *tmpstr;
int conv_id;
bson_t cmd;
int res = MONGOC_SSPI_AUTH_GSS_CONTINUE;
int step;
bool canonicalize = false;
const bson_t *options;
bson_t properties;
char real_name[BSON_HOST_NAME_MAX + 1];

options = mongoc_uri_get_options (cluster->uri);

if (bson_iter_init_find_case (&iter, options, "canonicalizeHostname") &&
BSON_ITER_HOLDS_UTF8 (&iter)) {
canonicalize = bson_iter_bool (&iter);
}

if (mongoc_uri_get_mechanism_properties (cluster->uri, &properties)) {
if (bson_iter_init_find_case (
&iter, &properties, "CANONICALIZE_HOST_NAME") &&
BSON_ITER_HOLDS_UTF8 (&iter)) {
canonicalize = !strcasecmp (bson_iter_utf8 (&iter, NULL), "true");
}
bson_destroy (&properties);
}

if (canonicalize &&
_mongoc_cluster_get_canonicalized_name (
cluster, stream, real_name, sizeof real_name, error)) {
state = _mongoc_cluster_sspi_new (cluster->uri, real_name);
} else {
state = _mongoc_cluster_sspi_new (cluster->uri, hostname);
}


if (!state) {
bson_set_error (error,
MONGOC_ERROR_CLIENT,
MONGOC_ERROR_CLIENT_AUTHENTICATE,
"Couldn't initialize SSPI service.");
goto failure;
}

for (step = 0;; step++) {
bson_init (&cmd);

if (res == MONGOC_SSPI_AUTH_GSS_CONTINUE) {
res = _mongoc_sspi_auth_sspi_client_step (state, buf);
} else if (res == MONGOC_SSPI_AUTH_GSS_COMPLETE) {
char *response;
const char *tmp_creds = mongoc_uri_get_username (cluster->uri);
int tmp_creds_len = strlen (tmp_creds);

res = _mongoc_sspi_auth_sspi_client_unwrap (state, buf);
response = bson_strdup (state->response);
_mongoc_sspi_auth_sspi_client_wrap (state, response, tmp_creds, tmp_creds_len, 0);
bson_free (response);
}

if (res == MONGOC_SSPI_AUTH_GSS_ERROR) {
bson_set_error (error,
MONGOC_ERROR_CLIENT,
MONGOC_ERROR_CLIENT_AUTHENTICATE,
"Received invalid SSPI data.");
break;
}

if (step == 0) {
_mongoc_cluster_build_sasl_start (
&cmd, "GSSAPI", state->response, strlen (state->response));
} else {
if (state->response) {
_mongoc_cluster_build_sasl_continue (
&cmd, conv_id, state->response, strlen (state->response));
} else {
_mongoc_cluster_build_sasl_continue (&cmd, conv_id, "", 0);
}
}

if (!mongoc_cluster_run_command (cluster,
stream,
0,
MONGOC_QUERY_SLAVE_OK,
"$external",
&cmd,
&reply,
error)) {
bson_destroy (&cmd);
bson_destroy (&reply);
break;
}

bson_destroy (&cmd);

if (bson_iter_init_find (&iter, &reply, "done") &&
bson_iter_as_bool (&iter)) {
bson_destroy (&reply);
break;
}

conv_id = _mongoc_cluster_get_conversation_id (&reply);

if (!bson_iter_init_find (&iter, &reply, "payload") ||
!BSON_ITER_HOLDS_UTF8 (&iter)) {
bson_destroy (&reply);
bson_set_error (error,
MONGOC_ERROR_CLIENT,
MONGOC_ERROR_CLIENT_AUTHENTICATE,
"Received invalid SASL reply from MongoDB server.");
break;
}


tmpstr = bson_iter_utf8 (&iter, &buflen);

if (buflen > sizeof buf) {
bson_set_error (error,
MONGOC_ERROR_CLIENT,
MONGOC_ERROR_CLIENT_AUTHENTICATE,
"SASL reply from MongoDB is too large.");

bson_destroy (&reply);
break;
}

memcpy (buf, tmpstr, buflen);

bson_destroy (&reply);
}

bson_free (state);

failure:

if (error->domain) {
return false;
}

return true;
}

#endif

0 comments on commit 40d3912

Please sign in to comment.