Skip to content

Commit 4723d42

Browse files
committed
-Y [filename] for ascii authentication mode
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.
1 parent c5a598e commit 4723d42

File tree

9 files changed

+516
-102
lines changed

9 files changed

+516
-102
lines changed

Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ memcached_SOURCES = memcached.c memcached.h \
2323
logger.c logger.h \
2424
crawler.c crawler.h \
2525
itoa_ljust.c itoa_ljust.h \
26-
slab_automove.c slab_automove.h
26+
slab_automove.c slab_automove.h \
27+
authfile.c authfile.h
2728

2829
if BUILD_CACHE
2930
memcached_SOURCES += cache.c

authfile.c

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <string.h>
5+
#include <sys/types.h>
6+
#include <sys/stat.h>
7+
#include <unistd.h>
8+
9+
#include "authfile.h"
10+
11+
// TODO: frontend needs a refactor so this can avoid global objects.
12+
13+
#define MAX_ENTRY_LEN 256
14+
// Not supposed to be a huge database!
15+
#define MAX_ENTRIES 8
16+
17+
typedef struct auth_entry {
18+
char *user;
19+
size_t ulen;
20+
char *pass;
21+
size_t plen;
22+
} auth_t;
23+
24+
auth_t main_auth_entries[MAX_ENTRIES];
25+
int entry_cnt = 0;
26+
char *main_auth_data = NULL;
27+
28+
enum authfile_ret authfile_load(const char *file) {
29+
struct stat sb;
30+
char *auth_data = NULL;
31+
auth_t auth_entries[MAX_ENTRIES];
32+
33+
if (stat(file, &sb) == -1) {
34+
return AUTHFILE_MISSING;
35+
}
36+
37+
auth_data = calloc(1, sb.st_size);
38+
39+
if (auth_data == NULL) {
40+
return AUTHFILE_OOM;
41+
}
42+
43+
FILE *pwfile = fopen(file, "r");
44+
if (pwfile == NULL) {
45+
// not strictly necessary but to be safe.
46+
free(auth_data);
47+
return AUTHFILE_OPENFAIL;
48+
}
49+
50+
char *auth_cur = auth_data;
51+
auth_t *entry_cur = auth_entries;
52+
int used = 0;
53+
54+
while ((fgets(auth_cur, MAX_ENTRY_LEN, pwfile)) != NULL) {
55+
int x;
56+
int found = 0;
57+
58+
for (x = 0; x < MAX_ENTRY_LEN; x++) {
59+
if (!found && auth_cur[x] == ':') {
60+
entry_cur->user = auth_cur;
61+
entry_cur->ulen = x;
62+
entry_cur->pass = &auth_cur[x+1];
63+
found = 1;
64+
} else if (found) {
65+
// Find end of password.
66+
if (auth_cur[x] == '\n' ||
67+
auth_cur[x] == '\r' ||
68+
auth_cur[x] == '\0') {
69+
entry_cur->plen = x - (entry_cur->ulen + 1);
70+
break;
71+
}
72+
}
73+
}
74+
75+
// malformed line.
76+
if (!found) {
77+
(void)fclose(pwfile);
78+
free(auth_data);
79+
return AUTHFILE_MALFORMED;
80+
}
81+
82+
// FIXME: no silent truncation.
83+
if (++used == MAX_ENTRIES) {
84+
break;
85+
}
86+
// EOF
87+
if (auth_cur[x] == '\0')
88+
break;
89+
90+
auth_cur += x;
91+
entry_cur++;
92+
}
93+
94+
// swap the main pointer out now, so if there's an error reloading we
95+
// don't break the existing authentication.
96+
if (main_auth_data != NULL) {
97+
free(main_auth_data);
98+
}
99+
100+
entry_cnt = used;
101+
main_auth_data = auth_data;
102+
memcpy(main_auth_entries, auth_entries, sizeof(auth_entries));
103+
104+
(void)fclose(pwfile);
105+
106+
return AUTHFILE_OK;
107+
}
108+
109+
// if only loading the file could be this short...
110+
int authfile_check(const char *user, const char *pass) {
111+
size_t ulen = strlen(user);
112+
size_t plen = strlen(pass);
113+
114+
for (int x = 0; x < entry_cnt; x++) {
115+
auth_t *e = &main_auth_entries[x];
116+
if (ulen == e->ulen && plen == e->plen &&
117+
memcmp(user, e->user, e->ulen) == 0 &&
118+
memcmp(pass, e->pass, e->plen) == 0) {
119+
return 1;
120+
}
121+
}
122+
123+
return 0;
124+
}

authfile.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef AUTHFILE_H
2+
#define AUTHFILE_H
3+
4+
enum authfile_ret {
5+
AUTHFILE_OK = 0,
6+
AUTHFILE_MISSING,
7+
AUTHFILE_OOM,
8+
AUTHFILE_OPENFAIL,
9+
AUTHFILE_MALFORMED,
10+
};
11+
12+
// FIXME: mc_authfile or something?
13+
enum authfile_ret authfile_load(const char *file);
14+
int authfile_check(const char *user, const char *pass);
15+
16+
#endif /* AUTHFILE_H */

doc/protocol.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,23 @@ In the descriptions of individual commands below, these error lines
123123
are not again specifically mentioned, but clients must allow for their
124124
possibility.
125125

126+
Authentication
127+
--------------
128+
129+
Optional username/password token authentication (see -Y option). Used by
130+
sending a fake "set" command with any key:
131+
132+
set <key> <flags> <exptime> <bytes>\r\n
133+
username password\r\n
134+
135+
key, flags, and exptime are ignored for authentication. Bytes is the length
136+
of the username/password payload.
137+
138+
- "STORED\r\n" indicates success. After this point any command should work
139+
normally.
140+
141+
- "CLIENT_ERROR [message]\r\n" will be returned if authentication fails for
142+
any reason.
126143

127144
Storage commands
128145
----------------

0 commit comments

Comments
 (0)