From 05779686924ef5c69046ecd18d706efae0abeb42 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Thu, 24 Jul 2014 17:30:16 -0400 Subject: [PATCH 01/24] Basic structure for Sasl GSSAPI c ext --- ext/csasl/csasl.c | 0 ext/csasl/extconf.rb | 4 +++ lib/mongo.rb | 9 ++++++ lib/mongo/functional/authentication.rb | 3 -- lib/mongo/functional/sasl_c.rb | 45 ++++++++++++++++++++++++++ mongo.gemspec | 4 +++ tasks/compile.rake | 5 +++ 7 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 ext/csasl/csasl.c create mode 100644 ext/csasl/extconf.rb create mode 100644 lib/mongo/functional/sasl_c.rb diff --git a/ext/csasl/csasl.c b/ext/csasl/csasl.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ext/csasl/extconf.rb b/ext/csasl/extconf.rb new file mode 100644 index 0000000000..d7293da3d3 --- /dev/null +++ b/ext/csasl/extconf.rb @@ -0,0 +1,4 @@ +require 'mkmf' +find_header('sasl/sasl.h') +have_library('sasl2', 'sasl_version') +create_makefile('csasl') diff --git a/lib/mongo.rb b/lib/mongo.rb index bd68f93ad3..f1b35259fc 100644 --- a/lib/mongo.rb +++ b/lib/mongo.rb @@ -95,3 +95,12 @@ module ErrorCode # MongoDB Core Server src/mongo/base/error_codes.err require 'mongo/mongo_replica_set_client' require 'mongo/mongo_sharded_client' require 'mongo/legacy' + +# Load the Sasl c extension if not on JRuby +unless RUBY_PLATFORM =~ /java/ + begin + require "csasl" + rescue LoadError + $stderr.puts("Sasl GSSAPI authentication is not available.") + end +end diff --git a/lib/mongo/functional/authentication.rb b/lib/mongo/functional/authentication.rb index faaa15dfb5..c8d19b8b9e 100644 --- a/lib/mongo/functional/authentication.rb +++ b/lib/mongo/functional/authentication.rb @@ -284,9 +284,6 @@ def issue_plain(auth, opts={}) # # @private def issue_gssapi(auth, opts={}) - raise NotImplementedError, - "The #{auth[:mechanism]} authentication mechanism is only supported " + - "for JRuby." unless RUBY_PLATFORM =~ /java/ Mongo::Sasl::GSSAPI.authenticate(auth[:username], self, opts[:socket], auth[:extra] || {}) end diff --git a/lib/mongo/functional/sasl_c.rb b/lib/mongo/functional/sasl_c.rb new file mode 100644 index 0000000000..cc2620d34f --- /dev/null +++ b/lib/mongo/functional/sasl_c.rb @@ -0,0 +1,45 @@ +# Copyright (C) 2009-2013 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. + +module Mongo + module Sasl + + module GSSAPI + + def self.authenticate(username, client, socket, opts={}) + raise LoadError, + "The Sasl GSSAPI authentication mechanism cannot be used because " + + "its extension did not load properly" unless defined?(Mongo::Sasl::GSSAPIAuthenticator) + + db = client.db('$external') + hostname = socket.pool.host + servicename = opts[:gssapi_service_name] || 'mongodb' + #canonicalize = opts[:canonicalize_host_name] ? opts[:canonicalize_host_name] : false + + authenticator = Mongo::Sasl::GSSAPIAuthenticator.new(username, hostname, servicename, canonicalize) + token = BSON::Binary.new(authenticator.initialize_challenge) + cmd = BSON::OrderedHash['saslStart', 1, 'mechanism', 'GSSAPI', 'payload', token, 'autoAuthorize', 1] + response = db.command(cmd, :check_response => false, :socket => socket) + + until response['done'] do + token = BSON::Binary.new(authenticator.evaluate_challenge(response['payload'].to_s)) + cmd = BSON::OrderedHash['saslContinue', 1, 'conversationId', response['conversationId'], 'payload', token] + response = db.command(cmd, :check_response => false, :socket => socket) + end + response + end + end + + end +end diff --git a/mongo.gemspec b/mongo.gemspec index 4effc8a7c1..a09ef3df0c 100644 --- a/mongo.gemspec +++ b/mongo.gemspec @@ -25,6 +25,10 @@ Gem::Specification.new do |s| if RUBY_PLATFORM =~ /java/ s.platform = 'java' s.files << 'ext/jsasl/target/jsasl.jar' + else + s.platform = Gem::Platform::RUBY + s.files += Dir.glob('ext/**/*.{c,h,rb}') + s.extensions = ['ext/csasl/extconf.rb'] end s.test_files = Dir['test/**/*.rb'] - Dir['test/bson/*'] diff --git a/tasks/compile.rake b/tasks/compile.rake index 5f562aed99..e8ab6b0613 100644 --- a/tasks/compile.rake +++ b/tasks/compile.rake @@ -31,6 +31,11 @@ else ext.lib_dir = "lib/bson_ext" Rake::Task['clean'].invoke end + Rake::ExtensionsTask.new('csasl') do |ext| + ext.name = "csasl" + ext.ext_dir = "ext/csasl" + ext.lib_dir = "lib" + end end desc "Run the default compile task" From 31bd049265e51eb7f600358210cb63463ad15846 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Thu, 24 Jul 2014 17:30:29 -0400 Subject: [PATCH 02/24] csasl C code --- ext/csasl/csasl.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/ext/csasl/csasl.c b/ext/csasl/csasl.c index e69de29bb2..c2b9456726 100644 --- a/ext/csasl/csasl.c +++ b/ext/csasl/csasl.c @@ -0,0 +1,138 @@ +#include + +#include +#include + + + +static VALUE a_init(VALUE self, VALUE username, VALUE hostname, VALUE servicename, VALUE canonicalizehostname) +{ + rb_iv_set(self, "@username", username); + rb_iv_set(self, "@hostname", hostname); + rb_iv_set(self, "@servicename", servicename); + //rb_iv_set(self, "@canonicalizehostname", canonicalizehostname); + + return self; +} + +// auxiliary functions +int is_sasl_failure(int result, char **error_message) +{ + if (result < 0) { + *error_message = malloc(256); + snprintf(*error_message, 256, "Authentication error: %s", sasl_errstring(result, NULL, NULL)); + return 1; + } + + return 0; +} + +char *rb_mongo_saslstart(sasl_conn_t *conn, char **out_payload, int *out_payload_len, int32_t *conversation_id, char **error_message) +{ + const char *raw_payload; + char encoded_payload[4096]; + unsigned int raw_payload_len, encoded_payload_len; + int result; + char *mechanism_list = "GSSAPI"; + const char *mechanism_selected; + sasl_interact_t *client_interact=NULL; + + result = sasl_client_start(conn, mechanism_list, &client_interact, &raw_payload, &raw_payload_len, &mechanism_selected); + if (is_sasl_failure(result, error_message)) { + return NULL; + } + + if (result != SASL_CONTINUE) { + *error_message = strdup("Could not negotiate SASL mechanism"); + return NULL; + } + + mechanism_selected = "GSSAPI"; + + + result = sasl_encode64(raw_payload, raw_payload_len, encoded_payload, sizeof(encoded_payload), &encoded_payload_len); + if (is_sasl_failure(result, error_message)) { + return NULL; + } + + return encoded_payload; +} + +static VALUE initialize_challenge(VALUE self) { + int result; + char *initpayload; + int initpayload_len; + char **error_message; + sasl_conn_t *conn; + int32_t conversation_id; + //sasl_callback_t client_interact=NULL; + char *payload; + + const char *servicename = RSTRING_PTR(rb_iv_get(self, "@servicename")); + const char *hostname = RSTRING_PTR(rb_iv_get(self, "@hostname")); + + result = sasl_client_new(servicename, hostname, NULL, NULL, NULL, 0, &conn); + + if (result != SASL_OK) { + sasl_dispose(&conn); + *error_message = strdup("Could not initialize a client exchange (SASL) to MongoDB"); + return 0; + } + + payload = rb_mongo_saslstart(conn, &initpayload, &initpayload_len, &conversation_id, error_message); + if (!conn) { + return 0; + } + + rb_iv_set(self, "@context", conn); + + return rb_str_new2(payload); +} + + +static VALUE evaluate_challenge(VALUE self, VALUE rb_payload) { + + sasl_interact_t *client_interact=NULL; + + char step_payload[4096], base_payload[4096], payload[4096]; + unsigned int step_payload_len, base_payload_len, payload_len; + const char *out; + unsigned int outlen; + unsigned char done = 0; + char **error_message; + int result; + + step_payload = RSTRING_PTR(rb_payload); + step_payload_len = RSTRING_LEN(rb_payload); + + step_payload_len--; /* Remove the \0 from the string */ + result = sasl_decode64(RSTRING_PTR(rb_payload), step_payload_len, base_payload, sizeof(base_payload), &base_payload_len); + if (is_sasl_failure(result, error_message)) { + return 0; + } + + sasl_conn_t *conn = rb_iv_get(self, "@context"); + + result = sasl_client_step(conn, (const char *)base_payload, base_payload_len, &client_interact, &out, &outlen); + if (is_sasl_failure(result, error_message)) { + return 0; + } + + result = sasl_encode64(out, outlen, payload, sizeof(base_payload), &payload_len); + if (is_sasl_failure(result, error_message)) { + return 0; + } + + return rb_str_new2(payload); +} + + +// define the class +VALUE c_GSSAPI_authenticator; + +void Init_GSSAPIAuthenticator() { + c_GSSAPI_authenticator = rb_define_class("Mongo::SASL::GSSAPIAuthenticator", rb_cObject); + rb_define_method(c_GSSAPI_authenticator, "initialize", a_init, 4); + rb_define_method(c_GSSAPI_authenticator, "initialize_challenge", initialize_challenge, 0); + rb_define_method(c_GSSAPI_authenticator, "evaluate_challenge", evaluate_challenge, 0); +} \ No newline at end of file From 1db171a554cc74e4af5db11c25ee2bb3f3ac0a2d Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Fri, 25 Jul 2014 14:49:36 -0400 Subject: [PATCH 03/24] fix typo --- tasks/compile.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/compile.rake b/tasks/compile.rake index e8ab6b0613..69054155ba 100644 --- a/tasks/compile.rake +++ b/tasks/compile.rake @@ -31,7 +31,7 @@ else ext.lib_dir = "lib/bson_ext" Rake::Task['clean'].invoke end - Rake::ExtensionsTask.new('csasl') do |ext| + Rake::ExtensionTask.new('csasl') do |ext| ext.name = "csasl" ext.ext_dir = "ext/csasl" ext.lib_dir = "lib" From af23133202471c5d6eb28ec4858d103a2754e74d Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Fri, 25 Jul 2014 17:10:56 -0400 Subject: [PATCH 04/24] c code fixes --- ext/csasl/csasl.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ext/csasl/csasl.c b/ext/csasl/csasl.c index c2b9456726..9109e02da4 100644 --- a/ext/csasl/csasl.c +++ b/ext/csasl/csasl.c @@ -3,6 +3,7 @@ #include #include +static sasl_conn_t *conn; static VALUE a_init(VALUE self, VALUE username, VALUE hostname, VALUE servicename, VALUE canonicalizehostname) @@ -30,7 +31,7 @@ int is_sasl_failure(int result, char **error_message) char *rb_mongo_saslstart(sasl_conn_t *conn, char **out_payload, int *out_payload_len, int32_t *conversation_id, char **error_message) { const char *raw_payload; - char encoded_payload[4096]; + static char encoded_payload[4096]; unsigned int raw_payload_len, encoded_payload_len; int result; char *mechanism_list = "GSSAPI"; @@ -63,7 +64,6 @@ static VALUE initialize_challenge(VALUE self) { char *initpayload; int initpayload_len; char **error_message; - sasl_conn_t *conn; int32_t conversation_id; //sasl_callback_t client_interact=NULL; char *payload; @@ -84,8 +84,6 @@ static VALUE initialize_challenge(VALUE self) { return 0; } - rb_iv_set(self, "@context", conn); - return rb_str_new2(payload); } @@ -94,7 +92,8 @@ static VALUE evaluate_challenge(VALUE self, VALUE rb_payload) { sasl_interact_t *client_interact=NULL; - char step_payload[4096], base_payload[4096], payload[4096]; + char *step_payload; + char base_payload[4096], payload[4096]; unsigned int step_payload_len, base_payload_len, payload_len; const char *out; unsigned int outlen; @@ -111,8 +110,6 @@ static VALUE evaluate_challenge(VALUE self, VALUE rb_payload) { return 0; } - sasl_conn_t *conn = rb_iv_get(self, "@context"); - result = sasl_client_step(conn, (const char *)base_payload, base_payload_len, &client_interact, &out, &outlen); if (is_sasl_failure(result, error_message)) { return 0; From 71c97b25b9ef6f8bdd7fba732318f04d78be9005 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Fri, 25 Jul 2014 17:11:22 -0400 Subject: [PATCH 05/24] Put csasl bundle in its own directory, then clean it up --- tasks/compile.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/compile.rake b/tasks/compile.rake index 69054155ba..b7484f082f 100644 --- a/tasks/compile.rake +++ b/tasks/compile.rake @@ -34,7 +34,7 @@ else Rake::ExtensionTask.new('csasl') do |ext| ext.name = "csasl" ext.ext_dir = "ext/csasl" - ext.lib_dir = "lib" + ext.lib_dir = "lib/csasl" end end From c86d810e94cd4187e7efb5da9c7888c713eb9568 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Tue, 29 Jul 2014 05:33:43 -0400 Subject: [PATCH 06/24] Compile csasl extension if not on jruby --- tasks/compile.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/compile.rake b/tasks/compile.rake index b7484f082f..29ab4c78c7 100644 --- a/tasks/compile.rake +++ b/tasks/compile.rake @@ -39,4 +39,4 @@ else end desc "Run the default compile task" -task :compile => RUBY_PLATFORM =~ /java/ ? ['compile:jbson', 'compile:jsasl'] : 'compile:cbson' \ No newline at end of file +task :compile => RUBY_PLATFORM =~ /java/ ? ['compile:jbson', 'compile:jsasl'] : ['compile:cbson', 'compile:csasl'] \ No newline at end of file From d2167aac042257fb4e21e79797dca10dca7c6e2d Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Tue, 29 Jul 2014 11:04:53 -0400 Subject: [PATCH 07/24] RUBY-530 Kerberos support for MRI c ext and Ruby code --- ext/csasl/csasl.c | 190 ++++++++++++++++++--------------- ext/csasl/extconf.rb | 1 + lib/mongo/functional.rb | 6 +- lib/mongo/functional/sasl_c.rb | 7 +- tasks/compile.rake | 2 +- 5 files changed, 116 insertions(+), 90 deletions(-) diff --git a/ext/csasl/csasl.c b/ext/csasl/csasl.c index 9109e02da4..9e9051cbd6 100644 --- a/ext/csasl/csasl.c +++ b/ext/csasl/csasl.c @@ -3,133 +3,153 @@ #include #include -static sasl_conn_t *conn; +static void mongo_sasl_conn_free(void* data) { + sasl_conn_t *conn = (sasl_conn_t*) data; + if(conn) sasl_dispose(&conn); +} +static sasl_conn_t* mongo_sasl_context(VALUE self) { + sasl_conn_t* conn; + VALUE context = rb_iv_get(self, "@context"); + Data_Get_Struct(context, sasl_conn_t, conn); + return conn; +} -static VALUE a_init(VALUE self, VALUE username, VALUE hostname, VALUE servicename, VALUE canonicalizehostname) +static VALUE a_init(VALUE self, VALUE user_name, VALUE host_name, VALUE service_name, VALUE canonicalize_host_name) { - rb_iv_set(self, "@username", username); - rb_iv_set(self, "@hostname", hostname); - rb_iv_set(self, "@servicename", servicename); - //rb_iv_set(self, "@canonicalizehostname", canonicalizehostname); + if (sasl_client_init(NULL) != SASL_OK) { + rb_iv_set(self, "@valid", Qfalse); + return 0; + } + else { + rb_iv_set(self, "@valid", Qtrue); + rb_iv_set(self, "@user_name", user_name); + rb_iv_set(self, "@host_name", host_name); + rb_iv_set(self, "@service_name", service_name); + //rb_iv_set(self, "@canonicalize_host_name", canonicalize_host_name); + } return self; } -// auxiliary functions +static VALUE valid(VALUE self) { + return rb_iv_get(self, "@valid"); +} + int is_sasl_failure(int result, char **error_message) { if (result < 0) { - *error_message = malloc(256); - snprintf(*error_message, 256, "Authentication error: %s", sasl_errstring(result, NULL, NULL)); return 1; } return 0; } -char *rb_mongo_saslstart(sasl_conn_t *conn, char **out_payload, int *out_payload_len, int32_t *conversation_id, char **error_message) -{ - const char *raw_payload; - static char encoded_payload[4096]; - unsigned int raw_payload_len, encoded_payload_len; - int result; - char *mechanism_list = "GSSAPI"; - const char *mechanism_selected; - sasl_interact_t *client_interact=NULL; - - result = sasl_client_start(conn, mechanism_list, &client_interact, &raw_payload, &raw_payload_len, &mechanism_selected); - if (is_sasl_failure(result, error_message)) { - return NULL; - } - - if (result != SASL_CONTINUE) { - *error_message = strdup("Could not negotiate SASL mechanism"); - return NULL; - } - - mechanism_selected = "GSSAPI"; - - - result = sasl_encode64(raw_payload, raw_payload_len, encoded_payload, sizeof(encoded_payload), &encoded_payload_len); - if (is_sasl_failure(result, error_message)) { - return NULL; - } +static int sasl_interact(VALUE self, int id, const char **result, unsigned *len) { + switch (id) { + case SASL_CB_AUTHNAME: + case SASL_CB_USER: + { + VALUE user_name = rb_iv_get(self, "@user_name"); + *result = RSTRING_PTR(user_name); + if (len) { + *len = RSTRING_LEN(user_name); + } + return SASL_OK; + } + } - return encoded_payload; + return SASL_FAIL; } static VALUE initialize_challenge(VALUE self) { - int result; - char *initpayload; - int initpayload_len; + int result; char **error_message; - int32_t conversation_id; - //sasl_callback_t client_interact=NULL; - char *payload; + char encoded_payload[4096]; + const char *raw_payload; + unsigned int raw_payload_len, encoded_payload_len; + char *mechanism_list = "GSSAPI"; + const char *mechanism_selected = "GSSAPI"; + sasl_callback_t client_interact [] = { + { SASL_CB_AUTHNAME, sasl_interact, (void*)self }, + { SASL_CB_USER, sasl_interact, (void*)self }, + //{ SASL_CB_PASS, sasl_interact, self }, + { SASL_CB_LIST_END, NULL, NULL } + }; + sasl_conn_t *conn; - const char *servicename = RSTRING_PTR(rb_iv_get(self, "@servicename")); - const char *hostname = RSTRING_PTR(rb_iv_get(self, "@hostname")); + const char *servicename = RSTRING_PTR(rb_iv_get(self, "@service_name")); + const char *hostname = RSTRING_PTR(rb_iv_get(self, "@host_name")); - result = sasl_client_new(servicename, hostname, NULL, NULL, NULL, 0, &conn); + result = sasl_client_new(servicename, hostname, NULL, NULL, client_interact, 0, &conn); if (result != SASL_OK) { sasl_dispose(&conn); - *error_message = strdup("Could not initialize a client exchange (SASL) to MongoDB"); - return 0; + // *error_message = strdup("Could not initialize a client exchange (SASL) to MongoDB"); + return Qfalse; } - payload = rb_mongo_saslstart(conn, &initpayload, &initpayload_len, &conversation_id, error_message); - if (!conn) { - return 0; - } + VALUE context = Data_Wrap_Struct(rb_cObject, NULL, mongo_sasl_conn_free, conn); + rb_iv_set(self, "@context", context); + - return rb_str_new2(payload); -} + result = sasl_client_start(conn, mechanism_list, NULL, &raw_payload, &raw_payload_len, &mechanism_selected); + if (is_sasl_failure(result, error_message)) { + // *error_message = strdup("Could not start sasl client"); + return Qfalse; + } + if (result != SASL_CONTINUE) { + // *error_message = strdup("Could not negotiate SASL mechanism"); + return Qfalse; + } -static VALUE evaluate_challenge(VALUE self, VALUE rb_payload) { + result = sasl_encode64(raw_payload, raw_payload_len, encoded_payload, sizeof(encoded_payload), &encoded_payload_len); - sasl_interact_t *client_interact=NULL; + encoded_payload[encoded_payload_len] = 0; + return rb_str_new(encoded_payload, encoded_payload_len); +} - char *step_payload; - char base_payload[4096], payload[4096]; - unsigned int step_payload_len, base_payload_len, payload_len; - const char *out; - unsigned int outlen; - unsigned char done = 0; - char **error_message; - int result; +static VALUE evaluate_challenge(VALUE self, VALUE rb_payload) { - step_payload = RSTRING_PTR(rb_payload); - step_payload_len = RSTRING_LEN(rb_payload); + char base_payload[4096], payload[4096]; + const char *step_payload, *out; + unsigned int step_payload_len, payload_len, base_payload_len, outlen; + char **error_message; + int result; + sasl_conn_t *conn = mongo_sasl_context(self); - step_payload_len--; /* Remove the \0 from the string */ - result = sasl_decode64(RSTRING_PTR(rb_payload), step_payload_len, base_payload, sizeof(base_payload), &base_payload_len); - if (is_sasl_failure(result, error_message)) { - return 0; - } + StringValue(rb_payload); + step_payload = RSTRING_PTR(rb_payload); + step_payload_len = RSTRING_LEN(rb_payload); - result = sasl_client_step(conn, (const char *)base_payload, base_payload_len, &client_interact, &out, &outlen); - if (is_sasl_failure(result, error_message)) { - return 0; - } + result = sasl_decode64(step_payload, step_payload_len, base_payload, sizeof(base_payload), &base_payload_len); + if (is_sasl_failure(result, error_message)) { + return Qfalse; + } - result = sasl_encode64(out, outlen, payload, sizeof(base_payload), &payload_len); - if (is_sasl_failure(result, error_message)) { - return 0; - } + result = sasl_client_step(conn, base_payload, base_payload_len, NULL, &out, &outlen); + if (is_sasl_failure(result, error_message)) { + return Qfalse; + } - return rb_str_new2(payload); -} + result = sasl_encode64(out, outlen, payload, sizeof(payload), &payload_len); + if (is_sasl_failure(result, error_message)) { + return Qfalse; + } + return rb_str_new(payload, payload_len); +} // define the class VALUE c_GSSAPI_authenticator; -void Init_GSSAPIAuthenticator() { - c_GSSAPI_authenticator = rb_define_class("Mongo::SASL::GSSAPIAuthenticator", rb_cObject); +void Init_csasl() { + VALUE mongo = rb_const_get(rb_cObject, rb_intern("Mongo")); + VALUE sasl = rb_const_get(mongo, rb_intern("Sasl")); + c_GSSAPI_authenticator = rb_define_class_under(sasl, "GSSAPIAuthenticator", rb_cObject); rb_define_method(c_GSSAPI_authenticator, "initialize", a_init, 4); rb_define_method(c_GSSAPI_authenticator, "initialize_challenge", initialize_challenge, 0); - rb_define_method(c_GSSAPI_authenticator, "evaluate_challenge", evaluate_challenge, 0); -} \ No newline at end of file + rb_define_method(c_GSSAPI_authenticator, "evaluate_challenge", evaluate_challenge, 1); + rb_define_method(rb_cObject, "valid?", valid, 0); +} diff --git a/ext/csasl/extconf.rb b/ext/csasl/extconf.rb index d7293da3d3..50b3ae87da 100644 --- a/ext/csasl/extconf.rb +++ b/ext/csasl/extconf.rb @@ -1,4 +1,5 @@ require 'mkmf' find_header('sasl/sasl.h') have_library('sasl2', 'sasl_version') + create_makefile('csasl') diff --git a/lib/mongo/functional.rb b/lib/mongo/functional.rb index 9949555139..bec4a61afa 100644 --- a/lib/mongo/functional.rb +++ b/lib/mongo/functional.rb @@ -18,4 +18,8 @@ require 'mongo/functional/write_concern' require 'mongo/functional/uri_parser' -require 'mongo/functional/sasl_java' if RUBY_PLATFORM =~ /java/ +if RUBY_PLATFORM =~ /java/ + require 'mongo/functional/sasl_java' +else + require 'mongo/functional/sasl_c' +end diff --git a/lib/mongo/functional/sasl_c.rb b/lib/mongo/functional/sasl_c.rb index cc2620d34f..46ae5b6b78 100644 --- a/lib/mongo/functional/sasl_c.rb +++ b/lib/mongo/functional/sasl_c.rb @@ -25,15 +25,16 @@ def self.authenticate(username, client, socket, opts={}) db = client.db('$external') hostname = socket.pool.host servicename = opts[:gssapi_service_name] || 'mongodb' - #canonicalize = opts[:canonicalize_host_name] ? opts[:canonicalize_host_name] : false + canonicalize = opts[:canonicalize_host_name] ? opts[:canonicalize_host_name] : false authenticator = Mongo::Sasl::GSSAPIAuthenticator.new(username, hostname, servicename, canonicalize) - token = BSON::Binary.new(authenticator.initialize_challenge) + token = authenticator.initialize_challenge cmd = BSON::OrderedHash['saslStart', 1, 'mechanism', 'GSSAPI', 'payload', token, 'autoAuthorize', 1] response = db.command(cmd, :check_response => false, :socket => socket) + # if authentication failed, raise error until response['done'] do - token = BSON::Binary.new(authenticator.evaluate_challenge(response['payload'].to_s)) + token = authenticator.evaluate_challenge(response['payload']) cmd = BSON::OrderedHash['saslContinue', 1, 'conversationId', response['conversationId'], 'payload', token] response = db.command(cmd, :check_response => false, :socket => socket) end diff --git a/tasks/compile.rake b/tasks/compile.rake index 29ab4c78c7..d5249005e3 100644 --- a/tasks/compile.rake +++ b/tasks/compile.rake @@ -34,7 +34,7 @@ else Rake::ExtensionTask.new('csasl') do |ext| ext.name = "csasl" ext.ext_dir = "ext/csasl" - ext.lib_dir = "lib/csasl" + ext.lib_dir = "lib" end end From ab9b6ec32695d0884b8566d895cd71f59c37dbc1 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Mon, 4 Aug 2014 12:15:34 +0200 Subject: [PATCH 08/24] RUBY-530 GSSAPI tests should test both MRI and JRuby --- lib/mongo/functional/sasl_c.rb | 3 +- lib/mongo/functional/sasl_java.rb | 1 + test/shared/authentication/gssapi_shared.rb | 37 +++++++++++++++------ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/lib/mongo/functional/sasl_c.rb b/lib/mongo/functional/sasl_c.rb index 46ae5b6b78..816acbe37e 100644 --- a/lib/mongo/functional/sasl_c.rb +++ b/lib/mongo/functional/sasl_c.rb @@ -32,8 +32,8 @@ def self.authenticate(username, client, socket, opts={}) cmd = BSON::OrderedHash['saslStart', 1, 'mechanism', 'GSSAPI', 'payload', token, 'autoAuthorize', 1] response = db.command(cmd, :check_response => false, :socket => socket) - # if authentication failed, raise error until response['done'] do + break unless Support.ok?(response) token = authenticator.evaluate_challenge(response['payload']) cmd = BSON::OrderedHash['saslContinue', 1, 'conversationId', response['conversationId'], 'payload', token] response = db.command(cmd, :check_response => false, :socket => socket) @@ -41,6 +41,5 @@ def self.authenticate(username, client, socket, opts={}) response end end - end end diff --git a/lib/mongo/functional/sasl_java.rb b/lib/mongo/functional/sasl_java.rb index a54144a9f0..4dfba2ed81 100644 --- a/lib/mongo/functional/sasl_java.rb +++ b/lib/mongo/functional/sasl_java.rb @@ -36,6 +36,7 @@ def self.authenticate(username, client, socket, opts={}) response = db.command(cmd, :check_response => false, :socket => socket) until response['done'] do + break unless Support.ok?(response) token = BSON::Binary.new(authenticator.evaluate_challenge(response['payload'].to_s)) cmd = BSON::OrderedHash['saslContinue', 1, 'conversationId', response['conversationId'], 'payload', token] response = db.command(cmd, :check_response => false, :socket => socket) diff --git a/test/shared/authentication/gssapi_shared.rb b/test/shared/authentication/gssapi_shared.rb index 7d0f5b82a2..ddabe60b93 100644 --- a/test/shared/authentication/gssapi_shared.rb +++ b/test/shared/authentication/gssapi_shared.rb @@ -28,7 +28,8 @@ module GSSAPITests # export MONGODB_GSSAPI_REALM='applicationuser@example.com' # export MONGODB_GSSAPI_KDC='SERVER.DOMAIN.COM' # - # You must either use kinit or provide a config file that references a keytab file: + # You must use kinit when on MRI. + # You have the option of providing a config file that references a keytab file on JRuby: # # export JAAS_LOGIN_CONFIG_FILE='file:///path/to/config/file' # @@ -37,10 +38,10 @@ module GSSAPITests MONGODB_GSSAPI_REALM = ENV['MONGODB_GSSAPI_REALM'] MONGODB_GSSAPI_KDC = ENV['MONGODB_GSSAPI_KDC'] MONGODB_GSSAPI_PORT = ENV['MONGODB_GSSAPI_PORT'] || '27017' - JAAS_LOGIN_CONFIG_FILE = ENV['JAAS_LOGIN_CONFIG_FILE'] + JAAS_LOGIN_CONFIG_FILE = ENV['JAAS_LOGIN_CONFIG_FILE'] # only JRuby if ENV.key?('MONGODB_GSSAPI_HOST') && ENV.key?('MONGODB_GSSAPI_USER') && - ENV.key?('MONGODB_GSSAPI_REALM') && ENV.key?('MONGODB_GSSAPI_KDC') && RUBY_PLATFORM =~ /java/ + ENV.key?('MONGODB_GSSAPI_REALM') && ENV.key?('MONGODB_GSSAPI_KDC') def test_gssapi_authenticate client = Mongo::MongoClient.new(MONGODB_GSSAPI_HOST, MONGODB_GSSAPI_PORT) if client['admin'].command(:isMaster => 1)['setName'] @@ -79,8 +80,14 @@ def test_wrong_service_name_fails end set_system_properties - assert_raise_error Java::OrgMongodbSasl::MongoSecurityException do - client['kerberos'].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) + if RUBY_PLATFORM =~ /java/ + assert_raise_error Java::OrgMongodbSasl::MongoSecurityException do + client['kerberos'].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) + end + else + assert_raise_error Mongo::AuthenticationError do + client['kerberos'].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) + end end end @@ -92,8 +99,14 @@ def test_wrong_service_name_fails_uri uri = "mongodb://#{username}@#{ENV['MONGODB_GSSAPI_HOST']}:#{ENV['MONGODB_GSSAPI_PORT']}/?" + "authMechanism=GSSAPI&gssapiServiceName=example" client = @client.class.from_uri(uri) - assert_raise_error Java::OrgMongodbSasl::MongoSecurityException do - client['kerberos'].command(:dbstats => 1) + if RUBY_PLATFORM =~ /java/ + assert_raise_error Java::OrgMongodbSasl::MongoSecurityException do + client['kerberos'].command(:dbstats => 1) + end + else + assert_raise_error Mongo::AuthenticationError do + client['kerberos'].command(:dbstats => 1) + end end end @@ -154,10 +167,12 @@ def test_invalid_extra_options private def set_system_properties - java.lang.System.set_property 'javax.security.auth.useSubjectCredsOnly', 'false' - java.lang.System.set_property "java.security.krb5.realm", MONGODB_GSSAPI_REALM - java.lang.System.set_property "java.security.krb5.kdc", MONGODB_GSSAPI_KDC - java.lang.System.set_property "java.security.auth.login.config", JAAS_LOGIN_CONFIG_FILE if JAAS_LOGIN_CONFIG_FILE + if RUBY_PLATFORM =~ /java/ + java.lang.System.set_property 'javax.security.auth.useSubjectCredsOnly', 'false' + java.lang.System.set_property "java.security.krb5.realm", MONGODB_GSSAPI_REALM + java.lang.System.set_property "java.security.krb5.kdc", MONGODB_GSSAPI_KDC + java.lang.System.set_property "java.security.auth.login.config", JAAS_LOGIN_CONFIG_FILE if JAAS_LOGIN_CONFIG_FILE + end end end From 1f4d91dea1ed1570433184d43092fad794820d78 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Mon, 4 Aug 2014 18:06:52 +0200 Subject: [PATCH 09/24] RUBY-530 Fix spacing and clean up C code --- ext/csasl/csasl.c | 106 ++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 59 deletions(-) diff --git a/ext/csasl/csasl.c b/ext/csasl/csasl.c index 9e9051cbd6..e20ce1c0f9 100644 --- a/ext/csasl/csasl.c +++ b/ext/csasl/csasl.c @@ -1,5 +1,4 @@ #include - #include #include @@ -19,30 +18,29 @@ static VALUE a_init(VALUE self, VALUE user_name, VALUE host_name, VALUE service_ { if (sasl_client_init(NULL) != SASL_OK) { rb_iv_set(self, "@valid", Qfalse); - return 0; } else { rb_iv_set(self, "@valid", Qtrue); rb_iv_set(self, "@user_name", user_name); rb_iv_set(self, "@host_name", host_name); rb_iv_set(self, "@service_name", service_name); - //rb_iv_set(self, "@canonicalize_host_name", canonicalize_host_name); + rb_iv_set(self, "@canonicalize_host_name", canonicalize_host_name); } return self; } static VALUE valid(VALUE self) { - return rb_iv_get(self, "@valid"); + return rb_iv_get(self, "@valid"); } -int is_sasl_failure(int result, char **error_message) +int is_sasl_failure(int result) { - if (result < 0) { - return 1; - } + if (result < 0) { + return 1; + } - return 0; + return 0; } static int sasl_interact(VALUE self, int id, const char **result, unsigned *len) { @@ -64,84 +62,74 @@ static int sasl_interact(VALUE self, int id, const char **result, unsigned *len) static VALUE initialize_challenge(VALUE self) { int result; - char **error_message; char encoded_payload[4096]; - const char *raw_payload; - unsigned int raw_payload_len, encoded_payload_len; - char *mechanism_list = "GSSAPI"; - const char *mechanism_selected = "GSSAPI"; - sasl_callback_t client_interact [] = { + const char *raw_payload; + unsigned int raw_payload_len, encoded_payload_len; + char *mechanism_list = "GSSAPI"; + const char *mechanism_selected = "GSSAPI"; + sasl_conn_t *conn; + sasl_callback_t client_interact [] = { { SASL_CB_AUTHNAME, sasl_interact, (void*)self }, { SASL_CB_USER, sasl_interact, (void*)self }, - //{ SASL_CB_PASS, sasl_interact, self }, { SASL_CB_LIST_END, NULL, NULL } }; - sasl_conn_t *conn; - const char *servicename = RSTRING_PTR(rb_iv_get(self, "@service_name")); - const char *hostname = RSTRING_PTR(rb_iv_get(self, "@host_name")); + const char *servicename = RSTRING_PTR(rb_iv_get(self, "@service_name")); + const char *hostname = RSTRING_PTR(rb_iv_get(self, "@host_name")); - result = sasl_client_new(servicename, hostname, NULL, NULL, client_interact, 0, &conn); + result = sasl_client_new(servicename, hostname, NULL, NULL, client_interact, 0, &conn); - if (result != SASL_OK) { - sasl_dispose(&conn); - // *error_message = strdup("Could not initialize a client exchange (SASL) to MongoDB"); - return Qfalse; - } + if (result != SASL_OK) { + sasl_dispose(&conn); + return Qfalse; + } VALUE context = Data_Wrap_Struct(rb_cObject, NULL, mongo_sasl_conn_free, conn); rb_iv_set(self, "@context", context); - result = sasl_client_start(conn, mechanism_list, NULL, &raw_payload, &raw_payload_len, &mechanism_selected); - if (is_sasl_failure(result, error_message)) { - // *error_message = strdup("Could not start sasl client"); - return Qfalse; - } + if (is_sasl_failure(result)) { + return Qfalse; + } - if (result != SASL_CONTINUE) { - // *error_message = strdup("Could not negotiate SASL mechanism"); - return Qfalse; - } + if (result != SASL_CONTINUE) { + return Qfalse; + } result = sasl_encode64(raw_payload, raw_payload_len, encoded_payload, sizeof(encoded_payload), &encoded_payload_len); - encoded_payload[encoded_payload_len] = 0; return rb_str_new(encoded_payload, encoded_payload_len); } static VALUE evaluate_challenge(VALUE self, VALUE rb_payload) { + char base_payload[4096], payload[4096]; + const char *step_payload, *out; + unsigned int step_payload_len, payload_len, base_payload_len, outlen; + int result; + sasl_conn_t *conn = mongo_sasl_context(self); - char base_payload[4096], payload[4096]; - const char *step_payload, *out; - unsigned int step_payload_len, payload_len, base_payload_len, outlen; - char **error_message; - int result; - sasl_conn_t *conn = mongo_sasl_context(self); - - StringValue(rb_payload); - step_payload = RSTRING_PTR(rb_payload); - step_payload_len = RSTRING_LEN(rb_payload); + StringValue(rb_payload); + step_payload = RSTRING_PTR(rb_payload); + step_payload_len = RSTRING_LEN(rb_payload); - result = sasl_decode64(step_payload, step_payload_len, base_payload, sizeof(base_payload), &base_payload_len); - if (is_sasl_failure(result, error_message)) { - return Qfalse; - } + result = sasl_decode64(step_payload, step_payload_len, base_payload, sizeof(base_payload), &base_payload_len); + if (is_sasl_failure(result)) { + return Qfalse; + } - result = sasl_client_step(conn, base_payload, base_payload_len, NULL, &out, &outlen); - if (is_sasl_failure(result, error_message)) { - return Qfalse; - } + result = sasl_client_step(conn, base_payload, base_payload_len, NULL, &out, &outlen); + if (is_sasl_failure(result)) { + return Qfalse; + } - result = sasl_encode64(out, outlen, payload, sizeof(payload), &payload_len); - if (is_sasl_failure(result, error_message)) { - return Qfalse; - } + result = sasl_encode64(out, outlen, payload, sizeof(payload), &payload_len); + if (is_sasl_failure(result)) { + return Qfalse; + } - return rb_str_new(payload, payload_len); + return rb_str_new(payload, payload_len); } -// define the class VALUE c_GSSAPI_authenticator; void Init_csasl() { From 0e67ff809d326b7005be055835bf1fa5cf612e6e Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Mon, 4 Aug 2014 18:07:15 +0200 Subject: [PATCH 10/24] RUBY-530 Check to make sure the GSSAPIAuthentication initialized properly --- lib/mongo/functional/sasl_c.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/mongo/functional/sasl_c.rb b/lib/mongo/functional/sasl_c.rb index 816acbe37e..83ed09c89f 100644 --- a/lib/mongo/functional/sasl_c.rb +++ b/lib/mongo/functional/sasl_c.rb @@ -22,15 +22,17 @@ def self.authenticate(username, client, socket, opts={}) "The Sasl GSSAPI authentication mechanism cannot be used because " + "its extension did not load properly" unless defined?(Mongo::Sasl::GSSAPIAuthenticator) - db = client.db('$external') - hostname = socket.pool.host - servicename = opts[:gssapi_service_name] || 'mongodb' - canonicalize = opts[:canonicalize_host_name] ? opts[:canonicalize_host_name] : false - + db = client.db('$external') + hostname = socket.pool.host + servicename = opts[:gssapi_service_name] || 'mongodb' + canonicalize = opts[:canonicalize_host_name] ? opts[:canonicalize_host_name] : false authenticator = Mongo::Sasl::GSSAPIAuthenticator.new(username, hostname, servicename, canonicalize) - token = authenticator.initialize_challenge - cmd = BSON::OrderedHash['saslStart', 1, 'mechanism', 'GSSAPI', 'payload', token, 'autoAuthorize', 1] - response = db.command(cmd, :check_response => false, :socket => socket) + + return {} unless authenticator.valid? + + token = authenticator.initialize_challenge + cmd = BSON::OrderedHash['saslStart', 1, 'mechanism', 'GSSAPI', 'payload', token, 'autoAuthorize', 1] + response = db.command(cmd, :check_response => false, :socket => socket) until response['done'] do break unless Support.ok?(response) From 7a59f76c97ce00bb1da05a188d99d3f730e3581e Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Mon, 4 Aug 2014 18:18:05 +0200 Subject: [PATCH 11/24] RUBY-530 make db to authenticate to an ENV variable --- test/shared/authentication/gssapi_shared.rb | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/test/shared/authentication/gssapi_shared.rb b/test/shared/authentication/gssapi_shared.rb index ddabe60b93..b3dba55cf1 100644 --- a/test/shared/authentication/gssapi_shared.rb +++ b/test/shared/authentication/gssapi_shared.rb @@ -38,6 +38,7 @@ module GSSAPITests MONGODB_GSSAPI_REALM = ENV['MONGODB_GSSAPI_REALM'] MONGODB_GSSAPI_KDC = ENV['MONGODB_GSSAPI_KDC'] MONGODB_GSSAPI_PORT = ENV['MONGODB_GSSAPI_PORT'] || '27017' + MONGODB_GSSAPI_DB = ENV['MONGODB_GSSAPI_DB'] || TEST_DB JAAS_LOGIN_CONFIG_FILE = ENV['JAAS_LOGIN_CONFIG_FILE'] # only JRuby if ENV.key?('MONGODB_GSSAPI_HOST') && ENV.key?('MONGODB_GSSAPI_USER') && @@ -49,7 +50,7 @@ def test_gssapi_authenticate end set_system_properties - db = client['kerberos'] + db = client[MONGODB_GSSAPI_DB] db.authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI') assert db.command(:dbstats => 1) @@ -69,7 +70,7 @@ def test_gssapi_authenticate_uri uri = "mongodb://#{username}@#{ENV['MONGODB_GSSAPI_HOST']}:#{ENV['MONGODB_GSSAPI_PORT']}/?" + "authMechanism=GSSAPI" client = @client.class.from_uri(uri) - assert client['kerberos'].command(:dbstats => 1) + assert client[MONGODB_GSSAPI_DB].command(:dbstats => 1) end def test_wrong_service_name_fails @@ -82,11 +83,11 @@ def test_wrong_service_name_fails set_system_properties if RUBY_PLATFORM =~ /java/ assert_raise_error Java::OrgMongodbSasl::MongoSecurityException do - client['kerberos'].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) + client[MONGODB_GSSAPI_DB].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) end else assert_raise_error Mongo::AuthenticationError do - client['kerberos'].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) + client[MONGODB_GSSAPI_DB].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) end end end @@ -101,11 +102,11 @@ def test_wrong_service_name_fails_uri client = @client.class.from_uri(uri) if RUBY_PLATFORM =~ /java/ assert_raise_error Java::OrgMongodbSasl::MongoSecurityException do - client['kerberos'].command(:dbstats => 1) + client[MONGODB_GSSAPI_DB].command(:dbstats => 1) end else assert_raise_error Mongo::AuthenticationError do - client['kerberos'].command(:dbstats => 1) + client[MONGODB_GSSAPI_DB].command(:dbstats => 1) end end end @@ -119,7 +120,7 @@ def test_extra_opts opts[:gssapi_service_name] == extra_opts[:gssapi_service_name] opts[:canonicalize_host_name] == extra_opts[:canonicalize_host_name] end.returns('ok' => true ) - client['kerberos'].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) + client[MONGODB_GSSAPI_DB].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) end def test_extra_opts_uri @@ -137,7 +138,7 @@ def test_extra_opts_uri "authMechanism=GSSAPI&gssapiServiceName=example&canonicalizeHostName=true" client = @client.class.from_uri(uri) client.expects(:receive_message).returns([[{ 'ok' => 1 }], 1, 1]) - client['kerberos'].command(:dbstats => 1) + client[MONGODB_GSSAPI_DB].command(:dbstats => 1) end # In order to run this test, you must set the following environment variable: @@ -150,7 +151,7 @@ def test_canonicalize_host_name set_system_properties client = Mongo::MongoClient.new(ENV['MONGODB_GSSAPI_HOST_IP'], MONGODB_GSSAPI_PORT) - db = client['kerberos'] + db = client[MONGODB_GSSAPI_DB] db.authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) assert db.command(:dbstats => 1) end @@ -161,7 +162,7 @@ def test_invalid_extra_options client = Mongo::MongoClient.new(MONGODB_GSSAPI_HOST) assert_raise Mongo::MongoArgumentError do - client['kerberos'].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) + client[MONGODB_GSSAPI_DB].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) end end From 10d647de277c114d3f0b6eb7a682ef330021ea83 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Mon, 4 Aug 2014 18:30:40 +0200 Subject: [PATCH 12/24] RUBY-530 Make java ext and c ext throw the same auth error from GSSAPI failure --- lib/mongo/functional/sasl_java.rb | 26 ++++++++++++--------- test/shared/authentication/gssapi_shared.rb | 20 ++++------------ 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/lib/mongo/functional/sasl_java.rb b/lib/mongo/functional/sasl_java.rb index 4dfba2ed81..0fcf35081e 100644 --- a/lib/mongo/functional/sasl_java.rb +++ b/lib/mongo/functional/sasl_java.rb @@ -30,18 +30,22 @@ def self.authenticate(username, client, socket, opts={}) servicename = opts[:gssapi_service_name] || 'mongodb' canonicalize = opts[:canonicalize_host_name] ? opts[:canonicalize_host_name] : false - authenticator = org.mongodb.sasl.GSSAPIAuthenticator.new(JRuby.runtime, username, hostname, servicename, canonicalize) - token = BSON::Binary.new(authenticator.initialize_challenge) - cmd = BSON::OrderedHash['saslStart', 1, 'mechanism', 'GSSAPI', 'payload', token, 'autoAuthorize', 1] - response = db.command(cmd, :check_response => false, :socket => socket) - - until response['done'] do - break unless Support.ok?(response) - token = BSON::Binary.new(authenticator.evaluate_challenge(response['payload'].to_s)) - cmd = BSON::OrderedHash['saslContinue', 1, 'conversationId', response['conversationId'], 'payload', token] - response = db.command(cmd, :check_response => false, :socket => socket) + begin + authenticator = org.mongodb.sasl.GSSAPIAuthenticator.new(JRuby.runtime, username, hostname, servicename, canonicalize) + token = BSON::Binary.new(authenticator.initialize_challenge) + cmd = BSON::OrderedHash['saslStart', 1, 'mechanism', 'GSSAPI', 'payload', token, 'autoAuthorize', 1] + response = db.command(cmd, :check_response => false, :socket => socket) + + until response['done'] do + break unless Support.ok?(response) + token = BSON::Binary.new(authenticator.evaluate_challenge(response['payload'].to_s)) + cmd = BSON::OrderedHash['saslContinue', 1, 'conversationId', response['conversationId'], 'payload', token] + response = db.command(cmd, :check_response => false, :socket => socket) + end + response + rescue Java::OrgMongodbSasl::MongoSecurityException + return {} end - response end end diff --git a/test/shared/authentication/gssapi_shared.rb b/test/shared/authentication/gssapi_shared.rb index b3dba55cf1..2c33646acc 100644 --- a/test/shared/authentication/gssapi_shared.rb +++ b/test/shared/authentication/gssapi_shared.rb @@ -81,14 +81,8 @@ def test_wrong_service_name_fails end set_system_properties - if RUBY_PLATFORM =~ /java/ - assert_raise_error Java::OrgMongodbSasl::MongoSecurityException do - client[MONGODB_GSSAPI_DB].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) - end - else - assert_raise_error Mongo::AuthenticationError do - client[MONGODB_GSSAPI_DB].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) - end + assert_raise_error Mongo::AuthenticationError do + client[MONGODB_GSSAPI_DB].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) end end @@ -100,14 +94,8 @@ def test_wrong_service_name_fails_uri uri = "mongodb://#{username}@#{ENV['MONGODB_GSSAPI_HOST']}:#{ENV['MONGODB_GSSAPI_PORT']}/?" + "authMechanism=GSSAPI&gssapiServiceName=example" client = @client.class.from_uri(uri) - if RUBY_PLATFORM =~ /java/ - assert_raise_error Java::OrgMongodbSasl::MongoSecurityException do - client[MONGODB_GSSAPI_DB].command(:dbstats => 1) - end - else - assert_raise_error Mongo::AuthenticationError do - client[MONGODB_GSSAPI_DB].command(:dbstats => 1) - end + assert_raise_error Mongo::AuthenticationError do + client[MONGODB_GSSAPI_DB].command(:dbstats => 1) end end From ce2c5d62d6879bd64f6130615c19b4c4a84fd30c Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Tue, 5 Aug 2014 12:38:54 +0200 Subject: [PATCH 13/24] RUBY-530 Compile csasl c extension into its own directory --- ext/csasl/extconf.rb | 2 +- lib/mongo.rb | 9 --------- lib/mongo/functional.rb | 7 +++++++ lib/mongo/functional/sasl_c.rb | 2 +- lib/mongo/functional/sasl_java.rb | 3 +++ tasks/compile.rake | 2 +- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ext/csasl/extconf.rb b/ext/csasl/extconf.rb index 50b3ae87da..a28355760e 100644 --- a/ext/csasl/extconf.rb +++ b/ext/csasl/extconf.rb @@ -2,4 +2,4 @@ find_header('sasl/sasl.h') have_library('sasl2', 'sasl_version') -create_makefile('csasl') +create_makefile('csasl/csasl') diff --git a/lib/mongo.rb b/lib/mongo.rb index f1b35259fc..bd68f93ad3 100644 --- a/lib/mongo.rb +++ b/lib/mongo.rb @@ -95,12 +95,3 @@ module ErrorCode # MongoDB Core Server src/mongo/base/error_codes.err require 'mongo/mongo_replica_set_client' require 'mongo/mongo_sharded_client' require 'mongo/legacy' - -# Load the Sasl c extension if not on JRuby -unless RUBY_PLATFORM =~ /java/ - begin - require "csasl" - rescue LoadError - $stderr.puts("Sasl GSSAPI authentication is not available.") - end -end diff --git a/lib/mongo/functional.rb b/lib/mongo/functional.rb index bec4a61afa..3af70b98eb 100644 --- a/lib/mongo/functional.rb +++ b/lib/mongo/functional.rb @@ -22,4 +22,11 @@ require 'mongo/functional/sasl_java' else require 'mongo/functional/sasl_c' + begin + require "csasl/csasl" + Mongo::HAS_SASL = true + rescue LoadError + Mongo::HAS_SASL = false + end end + diff --git a/lib/mongo/functional/sasl_c.rb b/lib/mongo/functional/sasl_c.rb index 83ed09c89f..41651198d8 100644 --- a/lib/mongo/functional/sasl_c.rb +++ b/lib/mongo/functional/sasl_c.rb @@ -20,7 +20,7 @@ module GSSAPI def self.authenticate(username, client, socket, opts={}) raise LoadError, "The Sasl GSSAPI authentication mechanism cannot be used because " + - "its extension did not load properly" unless defined?(Mongo::Sasl::GSSAPIAuthenticator) + "its extension did not load properly" unless Mongo::HAS_SASL db = client.db('$external') hostname = socket.pool.host diff --git a/lib/mongo/functional/sasl_java.rb b/lib/mongo/functional/sasl_java.rb index 0fcf35081e..8fdbb1fb31 100644 --- a/lib/mongo/functional/sasl_java.rb +++ b/lib/mongo/functional/sasl_java.rb @@ -25,6 +25,9 @@ module Sasl module GSSAPI def self.authenticate(username, client, socket, opts={}) + raise LoadError, + "The Sasl GSSAPI authentication mechanism cannot be used because " + + "its extension did not load properly" unless Mongo::HAS_SASL db = client.db('$external') hostname = socket.pool.host servicename = opts[:gssapi_service_name] || 'mongodb' diff --git a/tasks/compile.rake b/tasks/compile.rake index d5249005e3..7df68ee3a6 100644 --- a/tasks/compile.rake +++ b/tasks/compile.rake @@ -34,7 +34,7 @@ else Rake::ExtensionTask.new('csasl') do |ext| ext.name = "csasl" ext.ext_dir = "ext/csasl" - ext.lib_dir = "lib" + ext.lib_dir = "lib/mongo/csasl" end end From ec40832c4d010ed5b2c7a48cea08ce01bc2acb1a Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Wed, 6 Aug 2014 12:22:54 +0200 Subject: [PATCH 14/24] Use HAS_SASL constant --- lib/mongo/functional.rb | 19 +++++++++---------- lib/mongo/functional/sasl_c.rb | 4 ++-- lib/mongo/functional/sasl_java.rb | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/mongo/functional.rb b/lib/mongo/functional.rb index 3af70b98eb..54908a2e16 100644 --- a/lib/mongo/functional.rb +++ b/lib/mongo/functional.rb @@ -18,15 +18,14 @@ require 'mongo/functional/write_concern' require 'mongo/functional/uri_parser' -if RUBY_PLATFORM =~ /java/ - require 'mongo/functional/sasl_java' -else - require 'mongo/functional/sasl_c' - begin +begin + if RUBY_PLATFORM =~ /java/ + require 'mongo/functional/sasl_java' + else + require 'mongo/functional/sasl_c' require "csasl/csasl" - Mongo::HAS_SASL = true - rescue LoadError - Mongo::HAS_SASL = false end -end - + Mongo::HAS_SASL = true +rescue LoadError + Mongo::HAS_SASL = false +end \ No newline at end of file diff --git a/lib/mongo/functional/sasl_c.rb b/lib/mongo/functional/sasl_c.rb index 41651198d8..b22aad471f 100644 --- a/lib/mongo/functional/sasl_c.rb +++ b/lib/mongo/functional/sasl_c.rb @@ -1,4 +1,4 @@ -# Copyright (C) 2009-2013 MongoDB, Inc. +# Copyright (C) 2009-2014 MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ def self.authenticate(username, client, socket, opts={}) canonicalize = opts[:canonicalize_host_name] ? opts[:canonicalize_host_name] : false authenticator = Mongo::Sasl::GSSAPIAuthenticator.new(username, hostname, servicename, canonicalize) - return {} unless authenticator.valid? + return { } unless authenticator.valid? token = authenticator.initialize_challenge cmd = BSON::OrderedHash['saslStart', 1, 'mechanism', 'GSSAPI', 'payload', token, 'autoAuthorize', 1] diff --git a/lib/mongo/functional/sasl_java.rb b/lib/mongo/functional/sasl_java.rb index 8fdbb1fb31..0bcc8b703f 100644 --- a/lib/mongo/functional/sasl_java.rb +++ b/lib/mongo/functional/sasl_java.rb @@ -1,4 +1,4 @@ -# Copyright (C) 2009-2013 MongoDB, Inc. +# Copyright (C) 2009-2014 MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ def self.authenticate(username, client, socket, opts={}) end response rescue Java::OrgMongodbSasl::MongoSecurityException - return {} + return { } end end end From 13bcbaa0ef68e5b63334416b8c3ea7671d01fd66 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Wed, 6 Aug 2014 12:26:10 +0200 Subject: [PATCH 15/24] Remove uneeded platform definition --- mongo.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/mongo.gemspec b/mongo.gemspec index a09ef3df0c..59c4d1354c 100644 --- a/mongo.gemspec +++ b/mongo.gemspec @@ -26,7 +26,6 @@ Gem::Specification.new do |s| s.platform = 'java' s.files << 'ext/jsasl/target/jsasl.jar' else - s.platform = Gem::Platform::RUBY s.files += Dir.glob('ext/**/*.{c,h,rb}') s.extensions = ['ext/csasl/extconf.rb'] end From 247f9279fcbc4fdd7c779c863d1b8d95ccafb321 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Wed, 6 Aug 2014 12:26:39 +0200 Subject: [PATCH 16/24] Cleanup after code review --- ext/csasl/csasl.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/ext/csasl/csasl.c b/ext/csasl/csasl.c index e20ce1c0f9..0703494cda 100644 --- a/ext/csasl/csasl.c +++ b/ext/csasl/csasl.c @@ -1,3 +1,17 @@ +// Copyright (C) 2009-2014 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 #include #include @@ -16,10 +30,7 @@ static sasl_conn_t* mongo_sasl_context(VALUE self) { static VALUE a_init(VALUE self, VALUE user_name, VALUE host_name, VALUE service_name, VALUE canonicalize_host_name) { - if (sasl_client_init(NULL) != SASL_OK) { - rb_iv_set(self, "@valid", Qfalse); - } - else { + if (sasl_client_init(NULL) == SASL_OK) { rb_iv_set(self, "@valid", Qtrue); rb_iv_set(self, "@user_name", user_name); rb_iv_set(self, "@host_name", host_name); @@ -27,6 +38,10 @@ static VALUE a_init(VALUE self, VALUE user_name, VALUE host_name, VALUE service_ rb_iv_set(self, "@canonicalize_host_name", canonicalize_host_name); } + else { + rb_iv_set(self, "@valid", Qfalse); + } + return self; } @@ -69,8 +84,8 @@ static VALUE initialize_challenge(VALUE self) { const char *mechanism_selected = "GSSAPI"; sasl_conn_t *conn; sasl_callback_t client_interact [] = { - { SASL_CB_AUTHNAME, sasl_interact, (void*)self }, - { SASL_CB_USER, sasl_interact, (void*)self }, + { SASL_CB_AUTHNAME, (int (*)(void))sasl_interact, (void*)self }, + { SASL_CB_USER, (int (*)(void))sasl_interact, (void*)self }, { SASL_CB_LIST_END, NULL, NULL } }; @@ -97,6 +112,10 @@ static VALUE initialize_challenge(VALUE self) { } result = sasl_encode64(raw_payload, raw_payload_len, encoded_payload, sizeof(encoded_payload), &encoded_payload_len); + if (is_sasl_failure(result)) { + return Qfalse; + } + encoded_payload[encoded_payload_len] = 0; return rb_str_new(encoded_payload, encoded_payload_len); } From 448654846f2bd0a1d63623b794acba5ab81a6e8c Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Thu, 7 Aug 2014 17:27:23 +0200 Subject: [PATCH 17/24] RUBY-530 Use sasl_done and change copyright --- ext/csasl/csasl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/csasl/csasl.c b/ext/csasl/csasl.c index 0703494cda..8ce2f5c0a7 100644 --- a/ext/csasl/csasl.c +++ b/ext/csasl/csasl.c @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2014 MongoDB, Inc. +// Copyright (C) 2014 MongoDB, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ static void mongo_sasl_conn_free(void* data) { sasl_conn_t *conn = (sasl_conn_t*) data; - if(conn) sasl_dispose(&conn); + if(conn) sasl_done(); } static sasl_conn_t* mongo_sasl_context(VALUE self) { From 8296363c6ad6026efd17c234062928e7cd395eab Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Wed, 3 Sep 2014 14:13:41 +0200 Subject: [PATCH 18/24] RUBY-530 Adding rake cleanup task back in --- tasks/testing.rake | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tasks/testing.rake b/tasks/testing.rake index e6428a3d78..57907e16eb 100644 --- a/tasks/testing.rake +++ b/tasks/testing.rake @@ -107,4 +107,15 @@ namespace :test do t.libs << 'test' end end + + task :cleanup do |t| + %w(data tmp coverage lib/bson_ext lib/mongo/csasl).each do |dir| + if File.directory?(dir) + puts "[CLEAN-UP] Removing '#{dir}'..." + FileUtils.rm_rf(dir) + end + end + t.reenable + end + Rake.application.top_level_tasks << 'test:cleanup' end From 02ec836443ae1bd405eb74b056ccbbf9c3dd5a18 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Wed, 3 Sep 2014 14:14:12 +0200 Subject: [PATCH 19/24] RUBY-530 Require gssapi test db in env for test --- test/shared/authentication/gssapi_shared.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/shared/authentication/gssapi_shared.rb b/test/shared/authentication/gssapi_shared.rb index 2c33646acc..707dfb8b0b 100644 --- a/test/shared/authentication/gssapi_shared.rb +++ b/test/shared/authentication/gssapi_shared.rb @@ -38,11 +38,12 @@ module GSSAPITests MONGODB_GSSAPI_REALM = ENV['MONGODB_GSSAPI_REALM'] MONGODB_GSSAPI_KDC = ENV['MONGODB_GSSAPI_KDC'] MONGODB_GSSAPI_PORT = ENV['MONGODB_GSSAPI_PORT'] || '27017' - MONGODB_GSSAPI_DB = ENV['MONGODB_GSSAPI_DB'] || TEST_DB + MONGODB_GSSAPI_DB = ENV['MONGODB_GSSAPI_DB'] JAAS_LOGIN_CONFIG_FILE = ENV['JAAS_LOGIN_CONFIG_FILE'] # only JRuby if ENV.key?('MONGODB_GSSAPI_HOST') && ENV.key?('MONGODB_GSSAPI_USER') && - ENV.key?('MONGODB_GSSAPI_REALM') && ENV.key?('MONGODB_GSSAPI_KDC') + ENV.key?('MONGODB_GSSAPI_REALM') && ENV.key?('MONGODB_GSSAPI_KDC') && + ENV.key?('MONGODB_GSSAPI_DB') def test_gssapi_authenticate client = Mongo::MongoClient.new(MONGODB_GSSAPI_HOST, MONGODB_GSSAPI_PORT) if client['admin'].command(:isMaster => 1)['setName'] From 90c027ecca77f11ee5516c274fd340cda60f9253 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Wed, 3 Sep 2014 15:05:43 +0200 Subject: [PATCH 20/24] RUBY-530 Note that we'd ideally use sasl_client_done for future reference --- ext/csasl/csasl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/csasl/csasl.c b/ext/csasl/csasl.c index 8ce2f5c0a7..177a98c5e4 100644 --- a/ext/csasl/csasl.c +++ b/ext/csasl/csasl.c @@ -18,6 +18,7 @@ static void mongo_sasl_conn_free(void* data) { sasl_conn_t *conn = (sasl_conn_t*) data; + // Ideally we would use sasl_client_done() but that's only available as of cyrus sasl 2.1.25 if(conn) sasl_done(); } From ac72abab6bdbf4da72fb6fdbbbb195b2e92f5de6 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Fri, 5 Sep 2014 14:03:09 +0200 Subject: [PATCH 21/24] RUBY-530 Use lib/csasl directory --- tasks/compile.rake | 2 +- tasks/testing.rake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/compile.rake b/tasks/compile.rake index 7df68ee3a6..29ab4c78c7 100644 --- a/tasks/compile.rake +++ b/tasks/compile.rake @@ -34,7 +34,7 @@ else Rake::ExtensionTask.new('csasl') do |ext| ext.name = "csasl" ext.ext_dir = "ext/csasl" - ext.lib_dir = "lib/mongo/csasl" + ext.lib_dir = "lib/csasl" end end diff --git a/tasks/testing.rake b/tasks/testing.rake index 57907e16eb..b09882d72e 100644 --- a/tasks/testing.rake +++ b/tasks/testing.rake @@ -109,7 +109,7 @@ namespace :test do end task :cleanup do |t| - %w(data tmp coverage lib/bson_ext lib/mongo/csasl).each do |dir| + %w(data tmp coverage lib/bson_ext lib/csasl).each do |dir| if File.directory?(dir) puts "[CLEAN-UP] Removing '#{dir}'..." FileUtils.rm_rf(dir) From 6c83ce834ffa16e39918d016017cdac48df18f8a Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Fri, 5 Sep 2014 14:04:08 +0200 Subject: [PATCH 22/24] RUBY-530 Compile csasl for certain test suites --- tasks/testing.rake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tasks/testing.rake b/tasks/testing.rake index b09882d72e..5187ee6697 100644 --- a/tasks/testing.rake +++ b/tasks/testing.rake @@ -67,6 +67,12 @@ namespace :test do end task :commit => :default + # Both the functional and replica_set tests will use the kerberos C ext + # when testing GSSAPI. So we must compile when on MRI. + task :default => 'compile:csasl' unless RUBY_PLATFORM =~ /java/ + task :functional => 'compile:csasl' unless RUBY_PLATFORM =~ /java/ + task :replica_set => 'compile:csasl' unless RUBY_PLATFORM =~ /java/ + desc 'Outputs diagnostic information for troubleshooting test failures.' task :diagnostic do puts <<-MSG From 058ff278a283292f8f79b4c2a492a603d5f5c7bd Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Fri, 5 Sep 2014 14:04:54 +0200 Subject: [PATCH 23/24] RUBY-530 Test args to gssapi authenticate correctly --- test/shared/authentication/gssapi_shared.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/shared/authentication/gssapi_shared.rb b/test/shared/authentication/gssapi_shared.rb index 707dfb8b0b..7400c7f0b2 100644 --- a/test/shared/authentication/gssapi_shared.rb +++ b/test/shared/authentication/gssapi_shared.rb @@ -106,8 +106,9 @@ def test_extra_opts set_system_properties Mongo::Sasl::GSSAPI.expects(:authenticate).with do |username, client, socket, opts| - opts[:gssapi_service_name] == extra_opts[:gssapi_service_name] - opts[:canonicalize_host_name] == extra_opts[:canonicalize_host_name] + assert_equal opts[:gssapi_service_name], extra_opts[:gssapi_service_name] + assert_equal opts[:canonicalize_host_name], extra_opts[:canonicalize_host_name] + [ username, client, socket, opts ] end.returns('ok' => true ) client[MONGODB_GSSAPI_DB].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts) end @@ -117,8 +118,9 @@ def test_extra_opts_uri set_system_properties Mongo::Sasl::GSSAPI.expects(:authenticate).with do |username, client, socket, opts| - opts[:gssapi_service_name] == extra_opts[:gssapi_service_name] - opts[:canonicalize_host_name] == extra_opts[:canonicalize_host_name] + assert_equal opts[:gssapi_service_name], extra_opts[:gssapi_service_name] + assert_equal opts[:canonicalize_host_name], extra_opts[:canonicalize_host_name] + [ username, client, socket, opts ] end.returns('ok' => true) require 'cgi' From 9bf6131a0bcf865175bc084d74884dec06c46eb3 Mon Sep 17 00:00:00 2001 From: Emily Stolfo Date: Fri, 5 Sep 2014 14:49:29 +0200 Subject: [PATCH 24/24] RUBY-530 Fix compile warnings on C90 --- ext/csasl/csasl.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ext/csasl/csasl.c b/ext/csasl/csasl.c index 177a98c5e4..50564ca48e 100644 --- a/ext/csasl/csasl.c +++ b/ext/csasl/csasl.c @@ -24,7 +24,8 @@ static void mongo_sasl_conn_free(void* data) { static sasl_conn_t* mongo_sasl_context(VALUE self) { sasl_conn_t* conn; - VALUE context = rb_iv_get(self, "@context"); + VALUE context; + context = rb_iv_get(self, "@context"); Data_Get_Struct(context, sasl_conn_t, conn); return conn; } @@ -64,7 +65,8 @@ static int sasl_interact(VALUE self, int id, const char **result, unsigned *len) case SASL_CB_AUTHNAME: case SASL_CB_USER: { - VALUE user_name = rb_iv_get(self, "@user_name"); + VALUE user_name; + user_name = rb_iv_get(self, "@user_name"); *result = RSTRING_PTR(user_name); if (len) { *len = RSTRING_LEN(user_name); @@ -81,8 +83,9 @@ static VALUE initialize_challenge(VALUE self) { char encoded_payload[4096]; const char *raw_payload; unsigned int raw_payload_len, encoded_payload_len; - char *mechanism_list = "GSSAPI"; + const char *mechanism_list = "GSSAPI"; const char *mechanism_selected = "GSSAPI"; + VALUE context; sasl_conn_t *conn; sasl_callback_t client_interact [] = { { SASL_CB_AUTHNAME, (int (*)(void))sasl_interact, (void*)self }, @@ -100,7 +103,7 @@ static VALUE initialize_challenge(VALUE self) { return Qfalse; } - VALUE context = Data_Wrap_Struct(rb_cObject, NULL, mongo_sasl_conn_free, conn); + context = Data_Wrap_Struct(rb_cObject, NULL, mongo_sasl_conn_free, conn); rb_iv_set(self, "@context", context); result = sasl_client_start(conn, mechanism_list, NULL, &raw_payload, &raw_payload_len, &mechanism_selected); @@ -153,8 +156,9 @@ static VALUE evaluate_challenge(VALUE self, VALUE rb_payload) { VALUE c_GSSAPI_authenticator; void Init_csasl() { - VALUE mongo = rb_const_get(rb_cObject, rb_intern("Mongo")); - VALUE sasl = rb_const_get(mongo, rb_intern("Sasl")); + VALUE mongo, sasl; + mongo = rb_const_get(rb_cObject, rb_intern("Mongo")); + sasl = rb_const_get(mongo, rb_intern("Sasl")); c_GSSAPI_authenticator = rb_define_class_under(sasl, "GSSAPIAuthenticator", rb_cObject); rb_define_method(c_GSSAPI_authenticator, "initialize", a_init, 4); rb_define_method(c_GSSAPI_authenticator, "initialize_challenge", initialize_challenge, 0);