<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>sasl_defs.c</filename>
    </added>
    <added>
      <filename>sasl_defs.h</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -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>
      <filename>Makefile.am</filename>
    </modified>
    <modified>
      <diff>@@ -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-&gt;write_and_free);
         c-&gt;write_and_free = 0;
     }
+
+    if (c-&gt;sasl_conn) {
+        assert(settings.sasl);
+        sasl_dispose(&amp;c-&gt;sasl_conn);
+        c-&gt;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 = &quot;Not stored.&quot;;
         break;
+    case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
+        errstr = &quot;Auth failure.&quot;;
+        break;
     default:
         assert(false);
         errstr = &quot;UNHANDLED ERROR&quot;;
@@ -1454,6 +1464,182 @@ static void handle_binary_protocol_error(conn *c) {
     c-&gt;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-&gt;sasl_conn) {
+        int result=sasl_server_new(&quot;memcached&quot;,
+                                   NULL, NULL, NULL, NULL,
+                                   NULL, 0, &amp;c-&gt;sasl_conn);
+        if (result != SASL_OK) {
+            if (settings.verbose) {
+                fprintf(stderr, &quot;Failed to initialize SASL conn.\n&quot;);
+            }
+            c-&gt;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-&gt;binary_header.request.bodylen
+                        - c-&gt;binary_header.request.keylen);
+        return;
+    }
+
+    init_sasl_conn(c);
+    const char *result_string = NULL;
+    unsigned int string_length = 0;
+    int result=sasl_listmech(c-&gt;sasl_conn, NULL,
+                             &quot;&quot;,   /* What to prepend the string with */
+                             &quot; &quot;,  /* What to separate mechanisms with */
+                             &quot;&quot;,   /* What to append to the string */
+                             &amp;result_string, &amp;string_length,
+                             NULL);
+    if (result != SASL_OK) {
+        /* Perhaps there's a better error for this... */
+        if (settings.verbose) {
+            fprintf(stderr, &quot;Failed to list SASL mechanisms.\n&quot;);
+        }
+        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-&gt;binary_header.request.bodylen
+                        - c-&gt;binary_header.request.keylen);
+        return;
+    }
+
+    assert(c-&gt;binary_header.request.extlen == 0);
+
+    int nkey = c-&gt;binary_header.request.keylen;
+    int vlen = c-&gt;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-&gt;write_and_go = conn_swallow;
+        return;
+    }
+
+    c-&gt;item = it;
+    c-&gt;ritem = ITEM_data(it);
+    c-&gt;rlbytes = vlen;
+    conn_set_state(c, conn_nread);
+    c-&gt;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-&gt;item);
+    init_sasl_conn(c);
+
+    int nkey = c-&gt;binary_header.request.keylen;
+    int vlen = c-&gt;binary_header.request.bodylen - nkey;
+
+    char mech[nkey+1];
+    memcpy(mech, ITEM_key((item*)c-&gt;item), nkey);
+    mech[nkey] = 0x00;
+
+    if (settings.verbose)
+        fprintf(stderr, &quot;mech:  ``%s'' with %d bytes of data\n&quot;, mech, vlen);
+
+    const char *challenge = vlen == 0 ? NULL : ITEM_data((item*) c-&gt;item);
+
+    int result=-1;
+
+    switch (c-&gt;cmd) {
+    case PROTOCOL_BINARY_CMD_SASL_AUTH:
+        result = sasl_server_start(c-&gt;sasl_conn, mech,
+                                   challenge, vlen,
+                                   &amp;out, &amp;outlen);
+        break;
+    case PROTOCOL_BINARY_CMD_SASL_STEP:
+        result = sasl_server_step(c-&gt;sasl_conn,
+                                  challenge, vlen,
+                                  &amp;out, &amp;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, &quot;Unhandled command %d with challenge %s\n&quot;,
+                    c-&gt;cmd, challenge);
+        }
+        break;
+    }
+
+    item_unlink(c-&gt;item);
+
+    if (settings.verbose) {
+        fprintf(stderr, &quot;sasl result code:  %d\n&quot;, result);
+    }
+
+    switch(result) {
+    case SASL_OK:
+        write_bin_response(c, &quot;Authenticated&quot;, 0, 0, strlen(&quot;Authenticated&quot;));
+        break;
+    case SASL_CONTINUE:
+        add_bin_header(c, PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE, 0, 0, outlen);
+        if(outlen &gt; 0) {
+            add_iov(c, out, outlen);
+        }
+        conn_set_state(c, conn_mwrite);
+        c-&gt;write_and_go = conn_new_cmd;
+        break;
+    default:
+        if (settings.verbose)
+            fprintf(stderr, &quot;Unknown sasl response:  %d\n&quot;, result);
+        write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, 0);
+    }
+}
+
+static bool authenticated(conn *c) {
+    assert(settings.sasl);
+    bool rv = false;
+
+    switch (c-&gt;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-&gt;sasl_conn) {
+            const void *uname = NULL;
+            sasl_getprop(c-&gt;sasl_conn, SASL_USERNAME, &amp;uname);
+            rv = uname != NULL;
+        }
+    }
+
+    if (settings.verbose &gt; 1) {
+        fprintf(stderr, &quot;authenticated() in cmd 0x%02x is %s\n&quot;,
+                c-&gt;cmd, rv ? &quot;true&quot; : &quot;false&quot;);
+    }
+
+    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-&gt;binary_header.request.keylen;
     uint32_t bodylen = c-&gt;binary_header.request.bodylen;
 
+    if (settings.sasl &amp;&amp; !authenticated(c)) {
+        write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, 0);
+        c-&gt;write_and_go = conn_closing;
+        return;
+    }
+
     MEMCACHED_PROCESS_COMMAND_START(c-&gt;sfd, c-&gt;rcurr, c-&gt;rbytes);
     c-&gt;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 &amp;&amp; keylen == 0 &amp;&amp; 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 &amp;&amp; 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, &quot;Not handling substate %d\n&quot;, c-&gt;substate);
         assert(0);
@@ -3814,6 +4027,9 @@ static void usage(void) {
     printf(&quot;-B            Binding protocol - one of ascii, binary, or auto (default)\n&quot;);
     printf(&quot;-I            Override the size of each slab page. Adjusts max item size\n&quot;
            &quot;              (default: 1mb, min: 1k, max: 128m)\n&quot;);
+#ifdef ENABLE_SASL
+    printf(&quot;-S            Turn on Sasl authentication\n&quot;);
+#endif
     return;
 }
 
@@ -4029,6 +4245,7 @@ int main (int argc, char **argv) {
           &quot;b:&quot;  /* backlog queue limit */
           &quot;B:&quot;  /* Binding protocol */
           &quot;I:&quot;  /* Max item size */
+          &quot;S&quot;   /* 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, &quot;This server is not built with SASL support.\n&quot;);
+            exit(EX_USAGE);
+#endif
+            settings.sasl = true;
+            break;
         default:
             fprintf(stderr, &quot;Illegal argument \&quot;%c\&quot;\n&quot;, 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>
      <filename>memcached.c</filename>
    </modified>
    <modified>
      <diff>@@ -18,10 +18,13 @@
 #include &lt;stdbool.h&gt;
 #include &lt;stdint.h&gt;
 #include &lt;pthread.h&gt;
+#include &lt;unistd.h&gt;
 
 #include &quot;protocol_binary.h&quot;
 #include &quot;cache.h&quot;
 
+#include &quot;sasl_defs.h&quot;
+
 /** 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>
      <filename>memcached.h</filename>
    </modified>
    <modified>
      <diff>@@ -69,6 +69,8 @@ extern &quot;C&quot;
         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 &quot;C&quot;
         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>
      <filename>protocol_binary.h</filename>
    </modified>
    <modified>
      <diff>@@ -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 ;-) */</diff>
      <filename>testapp.c</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>da3d85a5d0fa9aaf927371ed70dce2366956a173</id>
    </parent>
  </parents>
  <author>
    <name>Dustin Sallings</name>
    <email>dustin@spy.net</email>
  </author>
  <url>http://github.com/dormando/memcached/commit/f1307c4d9cadb94076a99cc2f88a00f7e0b4161f</url>
  <id>f1307c4d9cadb94076a99cc2f88a00f7e0b4161f</id>
  <committed-date>2009-10-25T13:19:14-07:00</committed-date>
  <authored-date>2009-05-10T00:50:47-07:00</authored-date>
  <message>SASL auth support.</message>
  <tree>2f7f1096abce3f8c986854b3681770dc8f6d7da4</tree>
  <committer>
    <name>Trond Norbye</name>
    <email>Trond.Norbye@sun.com</email>
  </committer>
</commit>
