summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDustin Sallings <dustin@spy.net>2009-05-10 00:50:47 -0700
committerTrond Norbye <Trond.Norbye@sun.com>2009-10-25 21:19:14 +0100
commitf1307c4d9cadb94076a99cc2f88a00f7e0b4161f (patch)
tree2f7f1096abce3f8c986854b3681770dc8f6d7da4
parentda3d85a5d0fa9aaf927371ed70dce2366956a173 (diff)
downloadmemcached-f1307c4d9cadb94076a99cc2f88a00f7e0b4161f.tar.gz
SASL auth support.
-rw-r--r--Makefile.am4
-rw-r--r--memcached.c229
-rw-r--r--memcached.h9
-rw-r--r--protocol_binary.h6
-rw-r--r--sasl_defs.c20
-rw-r--r--sasl_defs.h26
-rw-r--r--testapp.c6
7 files changed, 298 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am
index 5989414..de8213d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -27,6 +27,10 @@ if BUILD_SOLARIS_PRIVS
memcached_SOURCES += solaris_priv.c
endif
+if ENABLE_SASL
+memcached_SOURCES += sasl_defs.c
+endif
+
memcached_debug_SOURCES = $(memcached_SOURCES)
memcached_CPPFLAGS = -DNDEBUG
memcached_debug_LDADD = @PROFILER_LDFLAGS@
diff --git a/memcached.c b/memcached.c
index da622f0..3732dbc 100644
--- a/memcached.c
+++ b/memcached.c
@@ -94,6 +94,7 @@ static int ensure_iov_space(conn *c);
static int add_iov(conn *c, const void *buf, int len);
static int add_msghdr(conn *c);
+
/* time handling */
static void set_current_time(void); /* update the global variable holding
global 32-bit seconds-since-start time
@@ -463,6 +464,12 @@ static void conn_cleanup(conn *c) {
free(c->write_and_free);
c->write_and_free = 0;
}
+
+ if (c->sasl_conn) {
+ assert(settings.sasl);
+ sasl_dispose(&c->sasl_conn);
+ c->sasl_conn = NULL;
+ }
}
/*
@@ -937,6 +944,9 @@ static void write_bin_error(conn *c, protocol_binary_response_status err, int sw
case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
errstr = "Not stored.";
break;
+ case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
+ errstr = "Auth failure.";
+ break;
default:
assert(false);
errstr = "UNHANDLED ERROR";
@@ -1454,6 +1464,182 @@ static void handle_binary_protocol_error(conn *c) {
c->write_and_go = conn_closing;
}
+static void init_sasl_conn(conn *c) {
+ assert(c);
+ /* should something else be returned? */
+ if (!settings.sasl)
+ return;
+
+ if (!c->sasl_conn) {
+ int result=sasl_server_new("memcached",
+ NULL, NULL, NULL, NULL,
+ NULL, 0, &c->sasl_conn);
+ if (result != SASL_OK) {
+ if (settings.verbose) {
+ fprintf(stderr, "Failed to initialize SASL conn.\n");
+ }
+ c->sasl_conn = NULL;
+ }
+ }
+}
+
+static void bin_list_sasl_mechs(conn *c) {
+ // Guard against a disabled SASL.
+ if (!settings.sasl) {
+ write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND,
+ c->binary_header.request.bodylen
+ - c->binary_header.request.keylen);
+ return;
+ }
+
+ init_sasl_conn(c);
+ const char *result_string = NULL;
+ unsigned int string_length = 0;
+ int result=sasl_listmech(c->sasl_conn, NULL,
+ "", /* What to prepend the string with */
+ " ", /* What to separate mechanisms with */
+ "", /* What to append to the string */
+ &result_string, &string_length,
+ NULL);
+ if (result != SASL_OK) {
+ /* Perhaps there's a better error for this... */
+ if (settings.verbose) {
+ fprintf(stderr, "Failed to list SASL mechanisms.\n");
+ }
+ write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, 0);
+ return;
+ }
+ write_bin_response(c, (char*)result_string, 0, 0, string_length);
+}
+
+static void process_bin_sasl_auth(conn *c) {
+ // Guard for handling disabled SASL on the server.
+ if (!settings.sasl) {
+ write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND,
+ c->binary_header.request.bodylen
+ - c->binary_header.request.keylen);
+ return;
+ }
+
+ assert(c->binary_header.request.extlen == 0);
+
+ int nkey = c->binary_header.request.keylen;
+ int vlen = c->binary_header.request.bodylen - nkey;
+
+ char *key = binary_get_key(c);
+ assert(key);
+
+ item *it = item_alloc(key, nkey, 0, 0, vlen);
+
+ if (it == 0) {
+ write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, vlen);
+ c->write_and_go = conn_swallow;
+ return;
+ }
+
+ c->item = it;
+ c->ritem = ITEM_data(it);
+ c->rlbytes = vlen;
+ conn_set_state(c, conn_nread);
+ c->substate = bin_reading_sasl_auth_data;
+}
+
+static void process_bin_complete_sasl_auth(conn *c) {
+ assert(settings.sasl);
+ const char *out = NULL;
+ unsigned int outlen = 0;
+
+ assert(c->item);
+ init_sasl_conn(c);
+
+ int nkey = c->binary_header.request.keylen;
+ int vlen = c->binary_header.request.bodylen - nkey;
+
+ char mech[nkey+1];
+ memcpy(mech, ITEM_key((item*)c->item), nkey);
+ mech[nkey] = 0x00;
+
+ if (settings.verbose)
+ fprintf(stderr, "mech: ``%s'' with %d bytes of data\n", mech, vlen);
+
+ const char *challenge = vlen == 0 ? NULL : ITEM_data((item*) c->item);
+
+ int result=-1;
+
+ switch (c->cmd) {
+ case PROTOCOL_BINARY_CMD_SASL_AUTH:
+ result = sasl_server_start(c->sasl_conn, mech,
+ challenge, vlen,
+ &out, &outlen);
+ break;
+ case PROTOCOL_BINARY_CMD_SASL_STEP:
+ result = sasl_server_step(c->sasl_conn,
+ challenge, vlen,
+ &out, &outlen);
+ break;
+ default:
+ assert(false); /* CMD should be one of the above */
+ /* This code is pretty much impossible, but makes the compiler
+ happier */
+ if (settings.verbose) {
+ fprintf(stderr, "Unhandled command %d with challenge %s\n",
+ c->cmd, challenge);
+ }
+ break;
+ }
+
+ item_unlink(c->item);
+
+ if (settings.verbose) {
+ fprintf(stderr, "sasl result code: %d\n", result);
+ }
+
+ switch(result) {
+ case SASL_OK:
+ write_bin_response(c, "Authenticated", 0, 0, strlen("Authenticated"));
+ break;
+ case SASL_CONTINUE:
+ add_bin_header(c, PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE, 0, 0, outlen);
+ if(outlen > 0) {
+ add_iov(c, out, outlen);
+ }
+ conn_set_state(c, conn_mwrite);
+ c->write_and_go = conn_new_cmd;
+ break;
+ default:
+ if (settings.verbose)
+ fprintf(stderr, "Unknown sasl response: %d\n", result);
+ write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, 0);
+ }
+}
+
+static bool authenticated(conn *c) {
+ assert(settings.sasl);
+ bool rv = false;
+
+ switch (c->cmd) {
+ case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: /* FALLTHROUGH */
+ case PROTOCOL_BINARY_CMD_SASL_AUTH: /* FALLTHROUGH */
+ case PROTOCOL_BINARY_CMD_SASL_STEP: /* FALLTHROUGH */
+ case PROTOCOL_BINARY_CMD_VERSION: /* FALLTHROUGH */
+ rv = true;
+ break;
+ default:
+ if (c->sasl_conn) {
+ const void *uname = NULL;
+ sasl_getprop(c->sasl_conn, SASL_USERNAME, &uname);
+ rv = uname != NULL;
+ }
+ }
+
+ if (settings.verbose > 1) {
+ fprintf(stderr, "authenticated() in cmd 0x%02x is %s\n",
+ c->cmd, rv ? "true" : "false");
+ }
+
+ return rv;
+}
+
static void dispatch_bin_command(conn *c) {
int protocol_error = 0;
@@ -1461,6 +1647,12 @@ static void dispatch_bin_command(conn *c) {
int keylen = c->binary_header.request.keylen;
uint32_t bodylen = c->binary_header.request.bodylen;
+ if (settings.sasl && !authenticated(c)) {
+ write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, 0);
+ c->write_and_go = conn_closing;
+ return;
+ }
+
MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes);
c->noreply = true;
@@ -1593,6 +1785,21 @@ static void dispatch_bin_command(conn *c) {
protocol_error = 1;
}
break;
+ case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
+ if (extlen == 0 && keylen == 0 && bodylen == 0) {
+ bin_list_sasl_mechs(c);
+ } else {
+ protocol_error = 1;
+ }
+ break;
+ case PROTOCOL_BINARY_CMD_SASL_AUTH:
+ case PROTOCOL_BINARY_CMD_SASL_STEP:
+ if (extlen == 0 && keylen != 0) {
+ bin_read_key(c, bin_reading_sasl_auth, 0);
+ } else {
+ protocol_error = 1;
+ }
+ break;
default:
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, bodylen);
}
@@ -1834,6 +2041,12 @@ static void complete_nread_binary(conn *c) {
case bin_read_flush_exptime:
process_bin_flush(c);
break;
+ case bin_reading_sasl_auth:
+ process_bin_sasl_auth(c);
+ break;
+ case bin_reading_sasl_auth_data:
+ process_bin_complete_sasl_auth(c);
+ break;
default:
fprintf(stderr, "Not handling substate %d\n", c->substate);
assert(0);
@@ -3814,6 +4027,9 @@ static void usage(void) {
printf("-B Binding protocol - one of ascii, binary, or auto (default)\n");
printf("-I Override the size of each slab page. Adjusts max item size\n"
" (default: 1mb, min: 1k, max: 128m)\n");
+#ifdef ENABLE_SASL
+ printf("-S Turn on Sasl authentication\n");
+#endif
return;
}
@@ -4029,6 +4245,7 @@ int main (int argc, char **argv) {
"b:" /* backlog queue limit */
"B:" /* Binding protocol */
"I:" /* Max item size */
+ "S" /* Sasl ON */
))) {
switch (c) {
case 'a':
@@ -4181,6 +4398,13 @@ int main (int argc, char **argv) {
);
}
break;
+ case 'S': /* set Sasl authentication to true. Default is false */
+#ifndef ENABLE_SASL
+ fprintf(stderr, "This server is not built with SASL support.\n");
+ exit(EX_USAGE);
+#endif
+ settings.sasl = true;
+ break;
default:
fprintf(stderr, "Illegal argument \"%c\"\n", c);
return 1;
@@ -4249,6 +4473,11 @@ int main (int argc, char **argv) {
}
}
+ /* Initialize Sasl if -S was specified */
+ if (settings.sasl) {
+ init_sasl();
+ }
+
/* daemonize if requested */
/* if we want to ensure our ability to dump core, don't chdir to / */
if (do_daemonize) {
diff --git a/memcached.h b/memcached.h
index a52e0bd..9b49012 100644
--- a/memcached.h
+++ b/memcached.h
@@ -18,10 +18,13 @@
#include <stdbool.h>
#include <stdint.h>
#include <pthread.h>
+#include <unistd.h>
#include "protocol_binary.h"
#include "cache.h"
+#include "sasl_defs.h"
+
/** Maximum length of a key. */
#define KEY_MAX_LENGTH 250
@@ -155,7 +158,9 @@ enum bin_substates {
bin_reading_stat,
bin_reading_del_header,
bin_reading_incr_header,
- bin_read_flush_exptime
+ bin_read_flush_exptime,
+ bin_reading_sasl_auth,
+ bin_reading_sasl_auth_data
};
enum protocol {
@@ -268,6 +273,7 @@ struct settings {
enum protocol binding_protocol;
int backlog;
int item_size_max; /* Maximum item size, and upper end for slabs */
+ bool sasl; /* SASL on/off */
};
extern struct stats stats;
@@ -324,6 +330,7 @@ typedef struct {
typedef struct conn conn;
struct conn {
int sfd;
+ sasl_conn_t *sasl_conn;
enum conn_states state;
enum bin_substates substate;
struct event event;
diff --git a/protocol_binary.h b/protocol_binary.h
index cb81331..e132f00 100644
--- a/protocol_binary.h
+++ b/protocol_binary.h
@@ -69,6 +69,8 @@ extern "C"
PROTOCOL_BINARY_RESPONSE_EINVAL = 0x04,
PROTOCOL_BINARY_RESPONSE_NOT_STORED = 0x05,
PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL = 0x06,
+ PROTOCOL_BINARY_RESPONSE_AUTH_ERROR = 0x20,
+ PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE = 0x21,
PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND = 0x81,
PROTOCOL_BINARY_RESPONSE_ENOMEM = 0x82
} protocol_binary_response_status;
@@ -106,6 +108,10 @@ extern "C"
PROTOCOL_BINARY_CMD_APPENDQ = 0x19,
PROTOCOL_BINARY_CMD_PREPENDQ = 0x1a,
+ PROTOCOL_BINARY_CMD_SASL_LIST_MECHS = 0x20,
+ PROTOCOL_BINARY_CMD_SASL_AUTH = 0x21,
+ PROTOCOL_BINARY_CMD_SASL_STEP = 0x22,
+
/* These commands are used for range operations and exist within
* this header for use in other projects. Range operations are
* not expected to be implemented in the memcached server itself.
diff --git a/sasl_defs.c b/sasl_defs.c
new file mode 100644
index 0000000..7e803d4
--- /dev/null
+++ b/sasl_defs.c
@@ -0,0 +1,20 @@
+#include "memcached.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+static sasl_callback_t sasl_callbacks[] = {
+ {
+ SASL_CB_LIST_END, NULL, NULL
+ }
+};
+
+void init_sasl(void) {
+ if (sasl_server_init(sasl_callbacks, "memcached") != SASL_OK) {
+ fprintf(stderr, "Error initializing sasl.\n");
+ exit(EXIT_FAILURE);
+ } else {
+ if (settings.verbose) {
+ fprintf(stderr, "Initialized SASL.\n");
+ }
+ }
+}
diff --git a/sasl_defs.h b/sasl_defs.h
new file mode 100644
index 0000000..39c3cbf
--- /dev/null
+++ b/sasl_defs.h
@@ -0,0 +1,26 @@
+#ifndef SASL_DEFS_H
+#define SASL_DEFS_H 1
+
+#if defined(HAVE_SASL_SASL_H) && defined(ENABLE_SASL)
+
+#include <sasl/sasl.h>
+void init_sasl(void);
+
+#else /* End of SASL support */
+
+typedef void* sasl_conn_t;
+
+#define init_sasl() {}
+#define sasl_dispose(x) {}
+#define sasl_server_new(a, b, c, d, e, f, g, h) 1
+#define sasl_listmech(a, b, c, d, e, f, g, h) 1
+#define sasl_server_start(a, b, c, d, e, f) 1
+#define sasl_server_step(a, b, c, d, e) 1
+#define sasl_getprop(a, b, c) {}
+
+#define SASL_OK 0
+#define SASL_CONTINUE -1
+
+#endif /* sasl compat */
+
+#endif /* SASL_DEFS_H */
diff --git a/testapp.c b/testapp.c
index 4a9dd10..a1bf42e 100644
--- a/testapp.c
+++ b/testapp.c
@@ -1475,7 +1475,7 @@ static enum test_return test_binary_stat(void) {
}
static enum test_return test_binary_illegal(void) {
- uint8_t cmd = 0x1b;
+ uint8_t cmd = 0x23;
while (cmd != 0x00) {
union {
protocol_binary_request_no_extras request;
@@ -1585,6 +1585,10 @@ static enum test_return test_binary_pipeline_hickup_chunk(void *buffer, size_t b
NULL, 0, NULL, 0);
break;
+ case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
+ case PROTOCOL_BINARY_CMD_SASL_AUTH:
+ case PROTOCOL_BINARY_CMD_SASL_STEP:
+ /* Ignoring SASL */
case PROTOCOL_BINARY_CMD_QUITQ:
case PROTOCOL_BINARY_CMD_QUIT:
/* I don't want to pass on the quit commands ;-) */