|
| 1 | +// Copyright (C) 2014 MongoDB, Inc. |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +#include <ruby.h> |
| 16 | +#include <sasl/sasl.h> |
| 17 | +#include <sasl/saslutil.h> |
| 18 | + |
| 19 | +static void mongo_sasl_conn_free(void* data) { |
| 20 | + sasl_conn_t *conn = (sasl_conn_t*) data; |
| 21 | + // Ideally we would use sasl_client_done() but that's only available as of cyrus sasl 2.1.25 |
| 22 | + if(conn) sasl_done(); |
| 23 | +} |
| 24 | + |
| 25 | +static sasl_conn_t* mongo_sasl_context(VALUE self) { |
| 26 | + sasl_conn_t* conn; |
| 27 | + VALUE context; |
| 28 | + context = rb_iv_get(self, "@context"); |
| 29 | + Data_Get_Struct(context, sasl_conn_t, conn); |
| 30 | + return conn; |
| 31 | +} |
| 32 | + |
| 33 | +static VALUE a_init(VALUE self, VALUE user_name, VALUE host_name, VALUE service_name, VALUE canonicalize_host_name) |
| 34 | +{ |
| 35 | + if (sasl_client_init(NULL) == SASL_OK) { |
| 36 | + rb_iv_set(self, "@valid", Qtrue); |
| 37 | + rb_iv_set(self, "@user_name", user_name); |
| 38 | + rb_iv_set(self, "@host_name", host_name); |
| 39 | + rb_iv_set(self, "@service_name", service_name); |
| 40 | + rb_iv_set(self, "@canonicalize_host_name", canonicalize_host_name); |
| 41 | + } |
| 42 | + |
| 43 | + else { |
| 44 | + rb_iv_set(self, "@valid", Qfalse); |
| 45 | + } |
| 46 | + |
| 47 | + return self; |
| 48 | +} |
| 49 | + |
| 50 | +static VALUE valid(VALUE self) { |
| 51 | + return rb_iv_get(self, "@valid"); |
| 52 | +} |
| 53 | + |
| 54 | +int is_sasl_failure(int result) |
| 55 | +{ |
| 56 | + if (result < 0) { |
| 57 | + return 1; |
| 58 | + } |
| 59 | + |
| 60 | + return 0; |
| 61 | +} |
| 62 | + |
| 63 | +static int sasl_interact(VALUE self, int id, const char **result, unsigned *len) { |
| 64 | + switch (id) { |
| 65 | + case SASL_CB_AUTHNAME: |
| 66 | + case SASL_CB_USER: |
| 67 | + { |
| 68 | + VALUE user_name; |
| 69 | + user_name = rb_iv_get(self, "@user_name"); |
| 70 | + *result = RSTRING_PTR(user_name); |
| 71 | + if (len) { |
| 72 | + *len = RSTRING_LEN(user_name); |
| 73 | + } |
| 74 | + return SASL_OK; |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + return SASL_FAIL; |
| 79 | +} |
| 80 | + |
| 81 | +static VALUE initialize_challenge(VALUE self) { |
| 82 | + int result; |
| 83 | + char encoded_payload[4096]; |
| 84 | + const char *raw_payload; |
| 85 | + unsigned int raw_payload_len, encoded_payload_len; |
| 86 | + const char *mechanism_list = "GSSAPI"; |
| 87 | + const char *mechanism_selected = "GSSAPI"; |
| 88 | + VALUE context; |
| 89 | + sasl_conn_t *conn; |
| 90 | + sasl_callback_t client_interact [] = { |
| 91 | + { SASL_CB_AUTHNAME, (int (*)(void))sasl_interact, (void*)self }, |
| 92 | + { SASL_CB_USER, (int (*)(void))sasl_interact, (void*)self }, |
| 93 | + { SASL_CB_LIST_END, NULL, NULL } |
| 94 | + }; |
| 95 | + |
| 96 | + const char *servicename = RSTRING_PTR(rb_iv_get(self, "@service_name")); |
| 97 | + const char *hostname = RSTRING_PTR(rb_iv_get(self, "@host_name")); |
| 98 | + |
| 99 | + result = sasl_client_new(servicename, hostname, NULL, NULL, client_interact, 0, &conn); |
| 100 | + |
| 101 | + if (result != SASL_OK) { |
| 102 | + sasl_dispose(&conn); |
| 103 | + return Qfalse; |
| 104 | + } |
| 105 | + |
| 106 | + context = Data_Wrap_Struct(rb_cObject, NULL, mongo_sasl_conn_free, conn); |
| 107 | + rb_iv_set(self, "@context", context); |
| 108 | + |
| 109 | + result = sasl_client_start(conn, mechanism_list, NULL, &raw_payload, &raw_payload_len, &mechanism_selected); |
| 110 | + if (is_sasl_failure(result)) { |
| 111 | + return Qfalse; |
| 112 | + } |
| 113 | + |
| 114 | + if (result != SASL_CONTINUE) { |
| 115 | + return Qfalse; |
| 116 | + } |
| 117 | + |
| 118 | + result = sasl_encode64(raw_payload, raw_payload_len, encoded_payload, sizeof(encoded_payload), &encoded_payload_len); |
| 119 | + if (is_sasl_failure(result)) { |
| 120 | + return Qfalse; |
| 121 | + } |
| 122 | + |
| 123 | + encoded_payload[encoded_payload_len] = 0; |
| 124 | + return rb_str_new(encoded_payload, encoded_payload_len); |
| 125 | +} |
| 126 | + |
| 127 | +static VALUE evaluate_challenge(VALUE self, VALUE rb_payload) { |
| 128 | + char base_payload[4096], payload[4096]; |
| 129 | + const char *step_payload, *out; |
| 130 | + unsigned int step_payload_len, payload_len, base_payload_len, outlen; |
| 131 | + int result; |
| 132 | + sasl_conn_t *conn = mongo_sasl_context(self); |
| 133 | + |
| 134 | + StringValue(rb_payload); |
| 135 | + step_payload = RSTRING_PTR(rb_payload); |
| 136 | + step_payload_len = RSTRING_LEN(rb_payload); |
| 137 | + |
| 138 | + result = sasl_decode64(step_payload, step_payload_len, base_payload, sizeof(base_payload), &base_payload_len); |
| 139 | + if (is_sasl_failure(result)) { |
| 140 | + return Qfalse; |
| 141 | + } |
| 142 | + |
| 143 | + result = sasl_client_step(conn, base_payload, base_payload_len, NULL, &out, &outlen); |
| 144 | + if (is_sasl_failure(result)) { |
| 145 | + return Qfalse; |
| 146 | + } |
| 147 | + |
| 148 | + result = sasl_encode64(out, outlen, payload, sizeof(payload), &payload_len); |
| 149 | + if (is_sasl_failure(result)) { |
| 150 | + return Qfalse; |
| 151 | + } |
| 152 | + |
| 153 | + return rb_str_new(payload, payload_len); |
| 154 | +} |
| 155 | + |
| 156 | +VALUE c_GSSAPI_authenticator; |
| 157 | + |
| 158 | +void Init_csasl() { |
| 159 | + VALUE mongo, sasl; |
| 160 | + mongo = rb_const_get(rb_cObject, rb_intern("Mongo")); |
| 161 | + sasl = rb_const_get(mongo, rb_intern("Sasl")); |
| 162 | + c_GSSAPI_authenticator = rb_define_class_under(sasl, "GSSAPIAuthenticator", rb_cObject); |
| 163 | + rb_define_method(c_GSSAPI_authenticator, "initialize", a_init, 4); |
| 164 | + rb_define_method(c_GSSAPI_authenticator, "initialize_challenge", initialize_challenge, 0); |
| 165 | + rb_define_method(c_GSSAPI_authenticator, "evaluate_challenge", evaluate_challenge, 1); |
| 166 | + rb_define_method(rb_cObject, "valid?", valid, 0); |
| 167 | +} |
0 commit comments