Skip to content

Commit

Permalink
Add support for --enable-sasl-pwdb
Browse files Browse the repository at this point in the history
--enable-sasl-pwdb allows memcached to use it's own password file and
verify a plaintext password.

The file is specified with the environment variable
MEMCACHED_SASL_PWDB, and is a plain text file with the following
syntax:

username:password

Please note that you have to specify "mech_list: plain" in your sasl
config file for this to work.

Ex:
echo "mech_list: plain" > memcached.conf
echo "myname:mypass" > /tmp/memcached-sasl-db
export MEMCACHED_SASL_PWDB=/tmp/memcached-sasl-db
export SASL_CONF_PATH=`pwd`/memcached.conf
./memcached -S -v

and you should be able to use your favorite memcached client with sasl
support to connect to the server.

(Please note that not all SASL implementations support
SASL_CB_GETCONF, so you may have to install the sasl config
(memcached.conf) to the systemwide location)
  • Loading branch information
trondn authored and dustin committed Mar 6, 2010
1 parent d3094ed commit 7a221d9
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 4 deletions.
37 changes: 36 additions & 1 deletion configure.ac
Expand Up @@ -75,13 +75,48 @@ AC_PROG_INSTALL
AC_ARG_ENABLE(sasl,
[AS_HELP_STRING([--enable-sasl],[Enable SASL authentication])])

AC_ARG_ENABLE(sasl_pwdb,
[AS_HELP_STRING([--enable-sasl-pwdb],[Enable plaintext password db])])

AS_IF([test "x$enable_sasl_pwdb" = "xyes"],
[enable_sasl=yes ])


dnl **********************************************************************
dnl DETECT_SASL_CB_GETCONF
dnl
dnl check if we can use SASL_CB_GETCONF
dnl **********************************************************************
AC_DEFUN([AC_C_DETECT_SASL_CB_GETCONF],
[
AC_CACHE_CHECK([for SASL_CB_GETCONF],
[ac_cv_c_sasl_cb_getconf],
[AC_TRY_COMPILE(
[
#include <sasl/sasl.h>
], [
unsigned long val = SASL_CB_GETCONF;
],
[ ac_cv_c_sasl_cb_getconf=yes ],
[ ac_cv_c_sasl_cb_getconf=no ])
])
AS_IF([test "$ac_cv_c_sasl_cb_getconf" = "yes"],
[AC_DEFINE([HAVE_SASL_CB_GETCONF], 1,
[Set to nonzero if your SASL implementation supports SASL_CB_GETCONF])])
])

AC_CHECK_HEADERS([sasl/sasl.h])
if test "x$enable_sasl" = "xyes"; then
AC_C_DETECT_SASL_CB_GETCONF
AC_DEFINE([ENABLE_SASL],1,[Set to nonzero if you want to include SASL])
AC_SEARCH_LIBS([sasl_server_init], [sasl2], [],
AC_SEARCH_LIBS([sasl_server_init], [sasl2 sasl], [],
[
AC_MSG_ERROR([Failed to locate the library containing sasl_server_init])
])

AS_IF([test "x$enable_sasl_pwdb" = "xyes"],
[AC_DEFINE([ENABLE_SASL_PWDB], 1,
[Set to nonzero if you want to enable a SASL pwdb])])
fi

AC_ARG_ENABLE(dtrace,
Expand Down
161 changes: 158 additions & 3 deletions sasl_defs.c
@@ -1,14 +1,169 @@
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include "memcached.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static sasl_callback_t sasl_callbacks[] = {
{
SASL_CB_LIST_END, NULL, NULL
#ifdef HAVE_SASL_CB_GETCONF
/* The locations we may search for a SASL config file if the user didn't
* specify one in the environment variable SASL_CONF_PATH
*/
const char * const locations[] = {
"/etc/sasl/memcached.conf",
"/etc/sasl2/memcached.conf",
NULL
};
#endif

#ifdef ENABLE_SASL_PWDB
#define MAX_ENTRY_LEN 256

static const char *memcached_sasl_pwdb;

static int sasl_server_userdb_checkpass(sasl_conn_t *conn,
void *context,
const char *user,
const char *pass,
unsigned passlen,
struct propctx *propctx)
{
size_t unmlen = strlen(user);
if ((passlen + unmlen) > (MAX_ENTRY_LEN - 4)) {
fprintf(stderr,
"WARNING: Failed to authenticate <%s> due to too long password (%d)\n",
user, passlen);
return SASL_NOAUTHZ;
}

FILE *pwfile = fopen(memcached_sasl_pwdb, "r");
if (pwfile == NULL) {
if (settings.verbose) {
vperror("WARNING: Failed to open sasl database <%s>",
memcached_sasl_pwdb);
}
return SASL_NOAUTHZ;
}

char buffer[MAX_ENTRY_LEN];
bool ok = false;

while ((fgets(buffer, sizeof(buffer), pwfile)) != NULL) {
if (memcmp(user, buffer, unmlen) == 0 && buffer[unmlen] == ':') {
/* This is the correct user */
++unmlen;
if (memcmp(pass, buffer + unmlen, passlen) == 0 &&
(buffer[unmlen + passlen] == ':' || /* Additional tokens */
buffer[unmlen + passlen] == '\n' || /* end of line */
buffer[unmlen + passlen] == '\r'|| /* dos format? */
buffer[unmlen + passlen] == '\0')) { /* line truncated */
ok = true;
}

break;
}
}
(void)fclose(pwfile);
if (ok) {
return SASL_OK;
}

if (settings.verbose) {
fprintf(stderr, "INFO: User <%s> failed to authenticate\n", user);
}

return SASL_NOAUTHZ;
}
#endif

#ifdef HAVE_SASL_CB_GETCONF
static int sasl_getconf(void *context, const char **path)
{
*path = getenv("SASL_CONF_PATH");

if (*path == NULL) {
for (int i = 0; locations[i] != NULL; ++i) {
if (access(locations[i], F_OK) == 0) {
*path = locations[i];
break;
}
}
}

if (settings.verbose) {
if (*path != NULL) {
fprintf(stderr, "Reading configuration from: <%s>\n", *path);
} else {
fprintf(stderr, "Failed to locate a config path\n");
}

}

return (*path != NULL) ? SASL_OK : SASL_FAIL;
}
#endif

static int sasl_log(void *context, int level, const char *message)
{
bool log = true;

switch (level) {
case SASL_LOG_NONE:
log = false;
break;
case SASL_LOG_PASS:
case SASL_LOG_TRACE:
case SASL_LOG_DEBUG:
case SASL_LOG_NOTE:
if (settings.verbose < 2) {
log = false;
}
break;
case SASL_LOG_WARN:
case SASL_LOG_FAIL:
if (settings.verbose < 1) {
log = false;
}
break;
default:
/* This is an error */
;
}

if (log) {
fprintf(stderr, "SASL (severity %d): %s\n", level, message);
}

return SASL_OK;
}

static sasl_callback_t sasl_callbacks[] = {
#ifdef ENABLE_SASL_PWDB
{ SASL_CB_SERVER_USERDB_CHECKPASS, sasl_server_userdb_checkpass, NULL },
#endif

{ SASL_CB_LOG, sasl_log, NULL },

#ifdef HAVE_SASL_CB_GETCONF
{ SASL_CB_GETCONF, sasl_getconf, NULL },
#endif

{ SASL_CB_LIST_END, NULL, NULL }
};

void init_sasl(void) {
#ifdef ENABLE_SASL_PWDB
memcached_sasl_pwdb = getenv("MEMCACHED_SASL_PWDB");
if (memcached_sasl_pwdb == NULL) {
if (settings.verbose) {
fprintf(stderr,
"INFO: MEMCACHED_SASL_PWDB not specified. "
"Internal passwd database disabled\n");
}
sasl_callbacks[0].id = SASL_CB_LIST_END;
sasl_callbacks[0].proc = NULL;
}
#endif

if (sasl_server_init(sasl_callbacks, "memcached") != SASL_OK) {
fprintf(stderr, "Error initializing sasl.\n");
exit(EXIT_FAILURE);
Expand Down

0 comments on commit 7a221d9

Please sign in to comment.