Skip to content

Commit

Permalink
-Y [filename] for ascii authentication mode
Browse files Browse the repository at this point in the history
Loads "username:password\n" tokens (up to 8) out of a supplied authfile.

If enabled, disables binary protocol (though may be able to enable both
if sasl is also used?).

authentication is done via the "set" command. A separate handler is
used to avoid some hot path conditionals and narrow the code
executed in an unauthenticated state.

ie:

set foo 0 0 7\r\n
foo bar\r\n

returns "STORED" on success. Else returns CLIENT_ERROR with some
information.

Any key is accepted: if using a client that doesn't try to authenticate
when connecting to a pool of servers, the authentication set can be
tried with the same key as one that failed to coerce the client to
routing to the correct server. Else an "auth" or similar key would
always go to the same server.
  • Loading branch information
dormando committed May 20, 2019
1 parent c5a598e commit 4723d42
Show file tree
Hide file tree
Showing 9 changed files with 516 additions and 102 deletions.
3 changes: 2 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ memcached_SOURCES = memcached.c memcached.h \
logger.c logger.h \
crawler.c crawler.h \
itoa_ljust.c itoa_ljust.h \
slab_automove.c slab_automove.h
slab_automove.c slab_automove.h \
authfile.c authfile.h

if BUILD_CACHE
memcached_SOURCES += cache.c
Expand Down
124 changes: 124 additions & 0 deletions authfile.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "authfile.h"

// TODO: frontend needs a refactor so this can avoid global objects.

#define MAX_ENTRY_LEN 256
// Not supposed to be a huge database!
#define MAX_ENTRIES 8

typedef struct auth_entry {
char *user;
size_t ulen;
char *pass;
size_t plen;
} auth_t;

auth_t main_auth_entries[MAX_ENTRIES];
int entry_cnt = 0;
char *main_auth_data = NULL;

enum authfile_ret authfile_load(const char *file) {
struct stat sb;
char *auth_data = NULL;
auth_t auth_entries[MAX_ENTRIES];

if (stat(file, &sb) == -1) {
return AUTHFILE_MISSING;
}

auth_data = calloc(1, sb.st_size);

if (auth_data == NULL) {
return AUTHFILE_OOM;
}

FILE *pwfile = fopen(file, "r");
if (pwfile == NULL) {
// not strictly necessary but to be safe.
free(auth_data);
return AUTHFILE_OPENFAIL;
}

char *auth_cur = auth_data;
auth_t *entry_cur = auth_entries;
int used = 0;

while ((fgets(auth_cur, MAX_ENTRY_LEN, pwfile)) != NULL) {
int x;
int found = 0;

for (x = 0; x < MAX_ENTRY_LEN; x++) {
if (!found && auth_cur[x] == ':') {
entry_cur->user = auth_cur;
entry_cur->ulen = x;
entry_cur->pass = &auth_cur[x+1];
found = 1;
} else if (found) {
// Find end of password.
if (auth_cur[x] == '\n' ||
auth_cur[x] == '\r' ||
auth_cur[x] == '\0') {
entry_cur->plen = x - (entry_cur->ulen + 1);
break;
}
}
}

// malformed line.
if (!found) {
(void)fclose(pwfile);
free(auth_data);
return AUTHFILE_MALFORMED;
}

// FIXME: no silent truncation.
if (++used == MAX_ENTRIES) {
break;
}
// EOF
if (auth_cur[x] == '\0')
break;

auth_cur += x;
entry_cur++;
}

// swap the main pointer out now, so if there's an error reloading we
// don't break the existing authentication.
if (main_auth_data != NULL) {
free(main_auth_data);
}

entry_cnt = used;
main_auth_data = auth_data;
memcpy(main_auth_entries, auth_entries, sizeof(auth_entries));

(void)fclose(pwfile);

return AUTHFILE_OK;
}

// if only loading the file could be this short...
int authfile_check(const char *user, const char *pass) {
size_t ulen = strlen(user);
size_t plen = strlen(pass);

for (int x = 0; x < entry_cnt; x++) {
auth_t *e = &main_auth_entries[x];
if (ulen == e->ulen && plen == e->plen &&
memcmp(user, e->user, e->ulen) == 0 &&
memcmp(pass, e->pass, e->plen) == 0) {
return 1;
}
}

return 0;
}
16 changes: 16 additions & 0 deletions authfile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef AUTHFILE_H
#define AUTHFILE_H

enum authfile_ret {
AUTHFILE_OK = 0,
AUTHFILE_MISSING,
AUTHFILE_OOM,
AUTHFILE_OPENFAIL,
AUTHFILE_MALFORMED,
};

// FIXME: mc_authfile or something?
enum authfile_ret authfile_load(const char *file);
int authfile_check(const char *user, const char *pass);

#endif /* AUTHFILE_H */
17 changes: 17 additions & 0 deletions doc/protocol.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,23 @@ In the descriptions of individual commands below, these error lines
are not again specifically mentioned, but clients must allow for their
possibility.

Authentication
--------------

Optional username/password token authentication (see -Y option). Used by
sending a fake "set" command with any key:

set <key> <flags> <exptime> <bytes>\r\n
username password\r\n

key, flags, and exptime are ignored for authentication. Bytes is the length
of the username/password payload.

- "STORED\r\n" indicates success. After this point any command should work
normally.

- "CLIENT_ERROR [message]\r\n" will be returned if authentication fails for
any reason.

Storage commands
----------------
Expand Down
Loading

0 comments on commit 4723d42

Please sign in to comment.