diff options
author | Rajith Muditha Attapattu <rajith@apache.org> | 2011-05-27 15:44:23 +0000 |
---|---|---|
committer | Rajith Muditha Attapattu <rajith@apache.org> | 2011-05-27 15:44:23 +0000 |
commit | 66765100f4257159622cefe57bed50125a5ad017 (patch) | |
tree | a88ee23bb194eb91f0ebb2d9b23ff423e3ea8e37 /qpid/ruby/ext/sasl/sasl.c | |
parent | 1aeaa7b16e5ce54f10c901d75c4d40f9f88b9db6 (diff) | |
parent | 88b98b2f4152ef59a671fad55a0d08338b6b78ca (diff) | |
download | qpid-python-rajith_jms_client.tar.gz |
Creating a branch for experimenting with some ideas for JMS client.rajith_jms_client
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/rajith_jms_client@1128369 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/ruby/ext/sasl/sasl.c')
-rw-r--r-- | qpid/ruby/ext/sasl/sasl.c | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/qpid/ruby/ext/sasl/sasl.c b/qpid/ruby/ext/sasl/sasl.c new file mode 100644 index 0000000000..2d4e40d30e --- /dev/null +++ b/qpid/ruby/ext/sasl/sasl.c @@ -0,0 +1,472 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <stdio.h> +#include <unistd.h> +#include <malloc.h> +#include <string.h> +#include <sasl/sasl.h> +#include <ruby.h> + +static VALUE mSasl; + +#define INPUT_SIZE 512 +#define MECH_SIZE 32 + +typedef void* sasl_context_t; + +#define QSASL_OK 0 +#define QSASL_CONTINUE 1 +#define QSASL_FAILED 2 + +typedef struct { + char magic[8]; + sasl_conn_t* conn; + sasl_callback_t callbacks[8]; + char* userName; + char* password; + char* operUserName; + unsigned int minSsf; + unsigned int maxSsf; + char mechanism[MECH_SIZE]; + char input[INPUT_SIZE]; +} context_t; + +// +// Resolve forward references +// +static VALUE qsasl_free(int, VALUE*, VALUE); + +// +// Validate an input string to ensure that it is either NULL or of reasonable size. +// +static int qsasl_valid(char* str) +{ + int idx; + + if (str == 0) + return 1; + + for (idx = 0; idx < INPUT_SIZE; idx++) { + if (str[idx] == '\0') + return 1; + } + + return 0; +} + +// +// SASL callback for identity and authentication identity. +// +static int qsasl_cb_user(void* _context, int id, const char **result, unsigned *len) +{ + context_t* context = (context_t*) _context; + + if (context->userName) + *result = context->userName; + + return SASL_OK; +} + +// +// SASL callback for passwords. +// +static int qsasl_cb_password(sasl_conn_t* conn, void* _context, int id, sasl_secret_t **psecret) +{ + context_t* context = (context_t*) _context; + sasl_secret_t* secret; + size_t length; + + if (context->password) + length = strlen(context->password); + else + length = 0; + + secret = (sasl_secret_t*) malloc(sizeof(sasl_secret_t) + length); + secret->len = length; + if (length) + memcpy(secret->data, context->password, length); + *psecret = secret; + + return SASL_OK; +} + +// +// Interactively prompt the user for authentication data. +// +static void qsasl_prompt(sasl_context_t _context, sasl_interact_t* interact) +{ + context_t* context = (context_t*) _context; + char *pass; + char *input; + char passwdPrompt[100]; + + if (interact->id == SASL_CB_PASS) { + strncpy(passwdPrompt, interact->prompt, 95); + strcat(passwdPrompt, ": "); + pass = getpass(passwdPrompt); + strncpy(context->input, pass, INPUT_SIZE - 1); + context->input[INPUT_SIZE - 1] = '\0'; + } else { + printf(interact->prompt); + if (interact->defresult) { + printf(" (%s)", interact->defresult); + } + printf(": "); + input = fgets(context->input, INPUT_SIZE, stdin); + if (input != context->input) { + rb_raise(rb_eRuntimeError, "Unexpected EOF on interactive prompt"); + } + } + + interact->result = context->input; + interact->len = strlen(context->input); +} + +// +// Initialize the SASL client library. +// +static VALUE qsasl_client_init() +{ + int result; + + result = sasl_client_init(0); + if (result != SASL_OK) + rb_raise(rb_eRuntimeError, + "sasl_client_init failed: %d - %s", + result, sasl_errstring(result, -0, 0)); + return Qnil; +} + +// +// Allocate a new SASL client context. +// +static VALUE qsasl_client_new(int argc, VALUE *argv, VALUE obj) +{ + char* mechanism = 0; + char* serviceName = 0; + char* hostName = 0; + char* userName = 0; + char* password = 0; + unsigned int minSsf = 0; + unsigned int maxSsf = 65535; + + int result; + int i = 0; + context_t *context; + sasl_security_properties_t secprops; + + if (argc != 7) + rb_raise(rb_eRuntimeError, "Wrong number of arguments"); + + if (!NIL_P(argv[0])) + mechanism = StringValuePtr(argv[0]); + if (!NIL_P(argv[1])) + serviceName = StringValuePtr(argv[1]); + if (!NIL_P(argv[2])) + hostName = StringValuePtr(argv[2]); + if (!NIL_P(argv[3])) + userName = StringValuePtr(argv[3]); + if (!NIL_P(argv[4])) + password = StringValuePtr(argv[4]); + minSsf = FIX2INT(argv[5]); + maxSsf = FIX2INT(argv[6]); + + if (!qsasl_valid(mechanism) || !qsasl_valid(serviceName) || + !qsasl_valid(hostName) || !qsasl_valid(userName) || + !qsasl_valid(password)) { + rb_raise(rb_eRuntimeError, "Invalid string argument"); + } + + context = (context_t*) malloc(sizeof(context_t)); + memset(context, 0, sizeof(context_t)); + strcpy(context->magic, "QSASL01"); + + context->minSsf = minSsf; + context->maxSsf = maxSsf; + if (mechanism != 0) { + strncpy(context->mechanism, mechanism, MECH_SIZE - 1); + context->mechanism[MECH_SIZE - 1] = '\0'; + } + + context->callbacks[i].id = SASL_CB_GETREALM; + context->callbacks[i].proc = 0; + context->callbacks[i++].context = 0; + + if (userName != 0 && userName[0] != '\0') { + context->userName = (char*) malloc(strlen(userName) + 1); + strcpy(context->userName, userName); + + context->callbacks[i].id = SASL_CB_USER; + context->callbacks[i].proc = qsasl_cb_user; + context->callbacks[i++].context = context; + + context->callbacks[i].id = SASL_CB_AUTHNAME; + context->callbacks[i].proc = qsasl_cb_user; + context->callbacks[i++].context = context; + } + + context->callbacks[i].id = SASL_CB_PASS; + if (password != 0 && password[0] != '\0') { + context->password = (char*) malloc(strlen(password) + 1); + strcpy(context->password, password); + + context->callbacks[i].proc = qsasl_cb_password; + } else + context->callbacks[i].proc = 0; + context->callbacks[i++].context = context; + + context->callbacks[i].id = SASL_CB_LIST_END; + context->callbacks[i].proc = 0; + context->callbacks[i++].context = 0; + + result = sasl_client_new(serviceName, hostName, 0, 0, + context->callbacks, 0, &context->conn); + + if (result != SASL_OK) { + context->conn = 0; + qsasl_free(1, (VALUE*) &context, Qnil); + rb_raise(rb_eRuntimeError, "sasl_client_new failed: %d - %s", + result, sasl_errstring(result, 0, 0)); + } + + secprops.min_ssf = minSsf; + secprops.max_ssf = maxSsf; + secprops.maxbufsize = 65535; + secprops.property_names = 0; + secprops.property_values = 0; + secprops.security_flags = 0;//TODO: provide means for application to configure these + + result = sasl_setprop(context->conn, SASL_SEC_PROPS, &secprops); + if (result != SASL_OK) { + qsasl_free(1, (VALUE*) &context, Qnil); + rb_raise(rb_eRuntimeError, "sasl_setprop failed: %d - %s", + result, sasl_errdetail(context->conn)); + } + + return (VALUE) context; +} + +// +// Free a SASL client context. +// +static VALUE qsasl_free(int argc, VALUE *argv, VALUE obj) +{ + context_t* context; + + if (argc == 1) + context = (context_t*) argv[0]; + else + rb_raise(rb_eRuntimeError, "Wrong Number of Arguments"); + + if (context->conn) + sasl_dispose(&context->conn); + if (context->userName) + free(context->userName); + if (context->password) + free(context->password); + if (context->operUserName) + free(context->operUserName); + free(context); + + return Qnil; +} + +// +// Start the SASL exchange from the client's point of view. +// +static VALUE qsasl_client_start(int argc, VALUE *argv, VALUE obj) +{ + context_t* context; + char* mechList; + char* mechToUse; + int result; + int propResult; + const char* response; + unsigned int len; + sasl_interact_t* interact = 0; + const char* chosen; + const char* operName; + + if (argc == 2) { + context = (context_t*) argv[0]; + mechList = StringValuePtr(argv[1]); + } else + rb_raise(rb_eRuntimeError, "Wrong Number of Arguments"); + + if (strlen(context->mechanism) == 0) + mechToUse = mechList; + else + mechToUse = context->mechanism; + + do { + result = sasl_client_start(context->conn, mechToUse, &interact, + &response, &len, &chosen); + if (result == SASL_INTERACT) { + qsasl_prompt(context, interact); + } + } while (result == SASL_INTERACT); + + if (result != SASL_OK && result != SASL_CONTINUE) + rb_raise(rb_eRuntimeError, "sasl_client_start failed: %d - %s", + result, sasl_errdetail(context->conn)); + + if (result == SASL_OK) { + propResult = sasl_getprop(context->conn, SASL_USERNAME, (const void**) &operName); + if (propResult == SASL_OK) { + context->operUserName = (char*) malloc(strlen(operName) + 1); + strcpy(context->operUserName, operName); + } + } + + return rb_ary_new3(3, INT2NUM(result), rb_str_new(response, len), rb_str_new2(chosen)); +} + +// +// Take a step in the SASL exchange (only needed for multi-challenge mechanisms). +// +static VALUE qsasl_client_step(int argc, VALUE *argv, VALUE obj) +{ + context_t* context; + VALUE challenge; + int result; + int propResult; + const char* response; + const char* operName; + unsigned int len; + sasl_interact_t* interact = 0; + + if (argc == 2) { + context = (context_t*) argv[0]; + challenge = argv[1]; + } + else + rb_raise(rb_eRuntimeError, "Wrong Number of Arguments"); + + do { + result = sasl_client_step(context->conn, + RSTRING(challenge)->ptr, RSTRING(challenge)->len, + &interact, &response, &len); + if (result == SASL_INTERACT) { + qsasl_prompt(context, interact); + } + } while (result == SASL_INTERACT); + + if (result != SASL_OK && result != SASL_CONTINUE) + return QSASL_FAILED; + + if (result == SASL_OK) { + propResult = sasl_getprop(context->conn, SASL_USERNAME, (const void**) &operName); + if (propResult == SASL_OK) { + context->operUserName = (char*) malloc(strlen(operName) + 1); + strcpy(context->operUserName, operName); + } + } + + return rb_ary_new3(2, INT2NUM(result), rb_str_new(response, len)); +} + +static VALUE qsasl_user_id(int argc, VALUE *argv, VALUE obj) +{ + context_t* context; + + if (argc == 1) { + context = (context_t*) argv[0]; + } else { + rb_raise(rb_eRuntimeError, "Wrong Number of Arguments"); + } + + if (context->operUserName) + return rb_str_new2(context->operUserName); + + return Qnil; +} + +// +// Encode transport data for the security layer. +// +static VALUE qsasl_encode(int argc, VALUE *argv, VALUE obj) +{ + context_t* context; + VALUE clearText; + const char* outBuffer; + unsigned int outSize; + int result; + + if (argc == 2) { + context = (context_t*) argv[0]; + clearText = argv[1]; + } + else + rb_raise(rb_eRuntimeError, "Wrong Number of Arguments"); + + result = sasl_encode(context->conn, + RSTRING(clearText)->ptr, RSTRING(clearText)->len, + &outBuffer, &outSize); + if (result != SASL_OK) + rb_raise(rb_eRuntimeError, "sasl_encode failed: %d - %s", + result, sasl_errdetail(context->conn)); + + return rb_str_new(outBuffer, outSize); +} + +// +// Decode transport data for the security layer. +// +static VALUE qsasl_decode(int argc, VALUE *argv, VALUE obj) +{ + context_t* context; + VALUE cipherText; + const char* outBuffer; + unsigned int outSize; + int result; + + if (argc == 2) { + context = (context_t*) argv[0]; + cipherText = argv[1]; + } + else + rb_raise(rb_eRuntimeError, "Wrong Number of Arguments"); + + result = sasl_decode(context->conn, + RSTRING(cipherText)->ptr, RSTRING(cipherText)->len, + &outBuffer, &outSize); + if (result != SASL_OK) + rb_raise(rb_eRuntimeError, "sasl_decode failed: %d - %s", + result, sasl_errdetail(context->conn)); + + return rb_str_new(outBuffer, outSize); +} + +// +// Initialize the Sasl module. +// +void Init_sasl() +{ + mSasl = rb_define_module("Sasl"); + + rb_define_module_function(mSasl, "client_init", qsasl_client_init, -1); + rb_define_module_function(mSasl, "client_new", qsasl_client_new, -1); + rb_define_module_function(mSasl, "free", qsasl_free, -1); + rb_define_module_function(mSasl, "client_start", qsasl_client_start, -1); + rb_define_module_function(mSasl, "client_step", qsasl_client_step, -1); + rb_define_module_function(mSasl, "user_id", qsasl_user_id, -1); + rb_define_module_function(mSasl, "encode", qsasl_encode, -1); + rb_define_module_function(mSasl, "decode", qsasl_decode, -1); +} |