Merging, with changes, --file for memcat.
[awesomized/libmemcached] / libmemcached / sasl.c
1 /* LibMemcached
2 * Copyright (C) 2006-2010 Brian Aker
3 * All rights reserved.
4 *
5 * Use and distribution licensed under the BSD license. See
6 * the COPYING file in the parent directory for full text.
7 *
8 * Summary: interface for memcached server
9 * Description: main include file for libmemcached
10 *
11 */
12 #include "common.h"
13
14 void memcached_set_sasl_callbacks(memcached_st *ptr,
15 const sasl_callback_t *callbacks)
16 {
17 ptr->sasl->callbacks= callbacks;
18 ptr->sasl->is_allocated= false;
19 }
20
21 const sasl_callback_t *memcached_get_sasl_callbacks(memcached_st *ptr)
22 {
23 return ptr->sasl->callbacks;
24 }
25
26 /**
27 * Resolve the names for both ends of a connection
28 * @param fd socket to check
29 * @param laddr local address (out)
30 * @param raddr remote address (out)
31 * @return true on success false otherwise (errno contains more info)
32 */
33 static bool resolve_names(int fd, char *laddr, char *raddr)
34 {
35 char host[NI_MAXHOST];
36 char port[NI_MAXSERV];
37 struct sockaddr_storage saddr;
38 socklen_t salen= sizeof(saddr);
39
40 if ((getsockname(fd, (struct sockaddr *)&saddr, &salen) < 0) ||
41 (getnameinfo((struct sockaddr *)&saddr, salen, host, sizeof(host),
42 port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) < 0))
43 {
44 return false;
45 }
46
47 (void)sprintf(laddr, "%s;%s", host, port);
48 salen= sizeof(saddr);
49
50 if ((getpeername(fd, (struct sockaddr *)&saddr, &salen) < 0) ||
51 (getnameinfo((struct sockaddr *)&saddr, salen, host, sizeof(host),
52 port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) < 0))
53 {
54 return false;
55 }
56
57 (void)sprintf(raddr, "%s;%s", host, port);
58
59 return true;
60 }
61
62 memcached_return_t memcached_sasl_authenticate_connection(memcached_server_st *server)
63 {
64 memcached_return_t rc;
65
66 /* SANITY CHECK: SASL can only be used with the binary protocol */
67 unlikely (!server->root->flags.binary_protocol)
68 return MEMCACHED_FAILURE;
69
70 /* Try to get the supported mech from the server. Servers without SASL
71 * support will return UNKNOWN COMMAND, so we can just treat that
72 * as authenticated
73 */
74 protocol_binary_request_no_extras request= {
75 .message.header.request= {
76 .magic= PROTOCOL_BINARY_REQ,
77 .opcode= PROTOCOL_BINARY_CMD_SASL_LIST_MECHS
78 }
79 };
80
81 if (memcached_io_write(server, request.bytes,
82 sizeof(request.bytes), 1) != sizeof(request.bytes))
83 {
84 return MEMCACHED_WRITE_FAILURE;
85 }
86
87 memcached_server_response_increment(server);
88
89 char mech[MEMCACHED_MAX_BUFFER];
90 rc= memcached_response(server, mech, sizeof(mech), NULL);
91 if (rc != MEMCACHED_SUCCESS)
92 {
93 if (rc == MEMCACHED_PROTOCOL_ERROR)
94 {
95 /* If the server doesn't support SASL it will return PROTOCOL_ERROR.
96 * This error may also be returned for other errors, but let's assume
97 * that the server don't support SASL and treat it as success and
98 * let the client fail with the next operation if the error was
99 * caused by another problem....
100 */
101 rc= MEMCACHED_SUCCESS;
102 }
103
104 return rc;
105 }
106
107 /* set ip addresses */
108 char laddr[NI_MAXHOST + NI_MAXSERV];
109 char raddr[NI_MAXHOST + NI_MAXSERV];
110
111 unlikely (!resolve_names(server->fd, laddr, raddr))
112 {
113 server->cached_errno= errno;
114 return MEMCACHED_ERRNO;
115 }
116
117 sasl_conn_t *conn;
118 int ret= sasl_client_new("memcached", server->hostname, laddr, raddr,
119 server->root->sasl->callbacks, 0, &conn);
120 if (ret != SASL_OK)
121 {
122 return MEMCACHED_AUTH_PROBLEM;
123 }
124
125 const char *data;
126 const char *chosenmech;
127 unsigned int len;
128 ret= sasl_client_start(conn, mech, NULL, &data, &len, &chosenmech);
129
130 if (ret != SASL_OK && ret != SASL_CONTINUE)
131 {
132 rc= MEMCACHED_AUTH_PROBLEM;
133 goto end;
134 }
135
136 uint16_t keylen= (uint16_t)strlen(chosenmech);
137 request.message.header.request.opcode= PROTOCOL_BINARY_CMD_SASL_AUTH;
138 request.message.header.request.keylen= htons(keylen);
139 request.message.header.request.bodylen= htonl(len + keylen);
140
141 do {
142 /* send the packet */
143
144 struct __write_vector_st vector[]=
145 {
146 { .length= sizeof(request.bytes), .buffer= request.bytes },
147 { .length= keylen, .buffer= chosenmech },
148 { .length= len, .buffer= data }
149 };
150
151 if (memcached_io_writev(server, vector, 3, true) == -1)
152 {
153 rc= MEMCACHED_WRITE_FAILURE;
154 goto end;
155 }
156 memcached_server_response_increment(server);
157
158 /* read the response */
159 rc= memcached_response(server, NULL, 0, NULL);
160 if (rc != MEMCACHED_AUTH_CONTINUE)
161 {
162 goto end;
163 }
164
165 ret= sasl_client_step(conn, memcached_result_value(&server->root->result),
166 (unsigned int)memcached_result_length(&server->root->result),
167 NULL, &data, &len);
168
169 if (ret != SASL_OK && ret != SASL_CONTINUE)
170 {
171 rc= MEMCACHED_AUTH_PROBLEM;
172 goto end;
173 }
174
175 request.message.header.request.opcode= PROTOCOL_BINARY_CMD_SASL_STEP;
176 request.message.header.request.bodylen= htonl(len + keylen);
177 } while (true);
178
179 end:
180 /* Release resources */
181 sasl_dispose(&conn);
182
183 return rc;
184 }
185
186 static int get_username(void *context, int id, const char **result,
187 unsigned int *len)
188 {
189 if (!context || !result || (id != SASL_CB_USER && id != SASL_CB_AUTHNAME))
190 {
191 return SASL_BADPARAM;
192 }
193
194 *result= context;
195 if (len)
196 {
197 *len= (unsigned int)strlen(*result);
198 }
199
200 return SASL_OK;
201 }
202
203 static int get_password(sasl_conn_t *conn, void *context, int id,
204 sasl_secret_t **psecret)
205 {
206 if (!conn || ! psecret || id != SASL_CB_PASS)
207 {
208 return SASL_BADPARAM;
209 }
210
211 *psecret= context;
212
213 return SASL_OK;
214 }
215
216 memcached_return_t memcached_set_sasl_auth_data(memcached_st *ptr,
217 const char *username,
218 const char *password)
219 {
220 if (ptr == NULL || username == NULL ||
221 password == NULL || ptr->sasl->callbacks != NULL)
222 {
223 return MEMCACHED_FAILURE;
224 }
225
226 sasl_callback_t *cb= libmemcached_calloc(ptr, 4, sizeof(sasl_callback_t));
227 char *name= libmemcached_malloc(ptr, strlen(username) + 1);
228 sasl_secret_t *secret= libmemcached_malloc(ptr, strlen(password) + 1 + sizeof(*secret))
229 ;
230 if (cb == NULL || name == NULL || secret == NULL)
231 {
232 libmemcached_free(ptr, cb);
233 libmemcached_free(ptr, name);
234 libmemcached_free(ptr, secret);
235 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
236 }
237
238 secret->len= strlen(password);
239 strcpy((void*)secret->data, password);
240
241 cb[0].id= SASL_CB_USER;
242 cb[0].proc= get_username;
243 cb[0].context= strcpy(name, username);
244 cb[1].id= SASL_CB_AUTHNAME;
245 cb[1].proc= get_username;
246 cb[1].context= name;
247 cb[2].id= SASL_CB_PASS;
248 cb[2].proc= get_password;
249 cb[2].context= secret;
250 cb[3].id= SASL_CB_LIST_END;
251
252 ptr->sasl->callbacks= cb;
253 ptr->sasl->is_allocated= true;
254
255 return MEMCACHED_SUCCESS;
256 }
257
258 memcached_return_t memcached_destroy_sasl_auth_data(memcached_st *ptr)
259 {
260 if (ptr == NULL || ptr->sasl->callbacks == NULL)
261 {
262 return MEMCACHED_FAILURE;
263 }
264
265 if (ptr->sasl->is_allocated)
266 {
267 libmemcached_free(ptr, ptr->sasl->callbacks[0].context);
268 libmemcached_free(ptr, ptr->sasl->callbacks[2].context);
269 libmemcached_free(ptr, (void*)ptr->sasl->callbacks);
270 ptr->sasl->is_allocated= false;
271 }
272
273 ptr->sasl->callbacks= NULL;
274 libmemcached_free(ptr, ptr->sasl);
275 ptr->sasl= NULL;
276
277 return MEMCACHED_SUCCESS;
278 }
279
280 memcached_return_t memcached_clone_sasl(memcached_st *clone, const memcached_st *source)
281 {
282 if (source->sasl == NULL)
283 {
284 return MEMCACHED_SUCCESS;
285 }
286 else
287 {
288 clone->sasl= libmemcached_malloc(source, sizeof(struct memcached_sasl_st));
289
290 if (clone->sasl == NULL)
291 {
292 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
293 }
294 }
295
296 /* Hopefully we are using our own callback mechanisms.. */
297 if (source->sasl->callbacks[0].id == SASL_CB_USER &&
298 source->sasl->callbacks[0].proc == get_username &&
299 source->sasl->callbacks[1].id == SASL_CB_AUTHNAME &&
300 source->sasl->callbacks[1].proc == get_username &&
301 source->sasl->callbacks[2].id == SASL_CB_PASS &&
302 source->sasl->callbacks[2].proc == get_password &&
303 source->sasl->callbacks[3].id == SASL_CB_LIST_END)
304 {
305 sasl_secret_t *secret= source->sasl->callbacks[2].context;
306 return memcached_set_sasl_auth_data(clone,
307 source->sasl->callbacks[0].context,
308 (const char*)secret->data);
309 }
310
311 /*
312 * But we're not. It may work if we know what the user tries to pass
313 * into the list, but if we don't know the ID we don't know how to handle
314 * the context...
315 */
316 size_t total= 0;
317
318 while (source->sasl->callbacks[total].id != SASL_CB_LIST_END)
319 {
320 switch (source->sasl->callbacks[total].id)
321 {
322 case SASL_CB_USER:
323 case SASL_CB_AUTHNAME:
324 case SASL_CB_PASS:
325 break;
326 default:
327 /* I don't know how to deal with this... */
328 return MEMCACHED_NOT_SUPPORTED;
329 }
330
331 ++total;
332 }
333
334 sasl_callback_t *cb= libmemcached_calloc(clone, total + 1, sizeof(sasl_callback_t));
335 if (cb == NULL)
336 {
337 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
338 }
339 memcpy(cb, source->sasl->callbacks, (total + 1) * sizeof(sasl_callback_t));
340
341 /* Now update the context... */
342 for (size_t x= 0; x < total; ++x)
343 {
344 if (cb[x].id == SASL_CB_USER || cb[x].id == SASL_CB_AUTHNAME)
345 {
346 cb[x].context= libmemcached_malloc(clone, strlen(source->sasl->callbacks[x].context));
347
348 if (cb[x].context == NULL)
349 {
350 /* Failed to allocate memory, clean up previously allocated memory */
351 for (size_t y= 0; y < x; ++y)
352 {
353 libmemcached_free(clone, clone->sasl->callbacks[y].context);
354 }
355
356 libmemcached_free(clone, cb);
357 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
358 }
359 strcpy(cb[x].context, source->sasl->callbacks[x].context);
360 }
361 else
362 {
363 sasl_secret_t *src = source->sasl->callbacks[x].context;
364 sasl_secret_t *n = libmemcached_malloc(clone, src->len + 1 + sizeof(*n));
365 if (n == NULL)
366 {
367 /* Failed to allocate memory, clean up previously allocated memory */
368 for (size_t y= 0; y < x; ++y)
369 {
370 libmemcached_free(clone, clone->sasl->callbacks[y].context);
371 }
372
373 libmemcached_free(clone, cb);
374 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
375 }
376 memcpy(n, src, src->len + 1 + sizeof(*n));
377 cb[x].context= n;
378 }
379 }
380
381 clone->sasl->callbacks= cb;
382 clone->sasl->is_allocated= true;
383
384 return MEMCACHED_SUCCESS;
385 }