2 * Copyright (C) 2006-2010 Brian Aker
5 * Use and distribution licensed under the BSD license. See
6 * the COPYING file in the parent directory for full text.
8 * Summary: interface for memcached server
9 * Description: main include file for libmemcached
14 void memcached_set_sasl_callbacks(memcached_st
*ptr
,
15 const sasl_callback_t
*callbacks
)
17 ptr
->sasl
->callbacks
= callbacks
;
18 ptr
->sasl
->is_allocated
= false;
21 const sasl_callback_t
*memcached_get_sasl_callbacks(memcached_st
*ptr
)
24 return ptr
->sasl
->callbacks
;
30 * Resolve the names for both ends of a connection
31 * @param fd socket to check
32 * @param laddr local address (out)
33 * @param raddr remote address (out)
34 * @return true on success false otherwise (errno contains more info)
36 static bool resolve_names(int fd
, char *laddr
, char *raddr
)
38 char host
[NI_MAXHOST
];
39 char port
[NI_MAXSERV
];
40 struct sockaddr_storage saddr
;
41 socklen_t salen
= sizeof(saddr
);
43 if ((getsockname(fd
, (struct sockaddr
*)&saddr
, &salen
) < 0) ||
44 (getnameinfo((struct sockaddr
*)&saddr
, salen
, host
, sizeof(host
),
45 port
, sizeof(port
), NI_NUMERICHOST
| NI_NUMERICSERV
) < 0))
50 (void)sprintf(laddr
, "%s;%s", host
, port
);
53 if ((getpeername(fd
, (struct sockaddr
*)&saddr
, &salen
) < 0) ||
54 (getnameinfo((struct sockaddr
*)&saddr
, salen
, host
, sizeof(host
),
55 port
, sizeof(port
), NI_NUMERICHOST
| NI_NUMERICSERV
) < 0))
60 (void)sprintf(raddr
, "%s;%s", host
, port
);
65 memcached_return_t
memcached_sasl_authenticate_connection(memcached_server_st
*server
)
67 memcached_return_t rc
;
69 /* SANITY CHECK: SASL can only be used with the binary protocol */
70 unlikely (!server
->root
->flags
.binary_protocol
)
71 return MEMCACHED_FAILURE
;
73 /* Try to get the supported mech from the server. Servers without SASL
74 * support will return UNKNOWN COMMAND, so we can just treat that
77 protocol_binary_request_no_extras request
= {
78 .message
.header
.request
= {
79 .magic
= PROTOCOL_BINARY_REQ
,
80 .opcode
= PROTOCOL_BINARY_CMD_SASL_LIST_MECHS
84 if (memcached_io_write(server
, request
.bytes
,
85 sizeof(request
.bytes
), 1) != sizeof(request
.bytes
))
87 return MEMCACHED_WRITE_FAILURE
;
90 memcached_server_response_increment(server
);
92 char mech
[MEMCACHED_MAX_BUFFER
];
93 rc
= memcached_response(server
, mech
, sizeof(mech
), NULL
);
94 if (rc
!= MEMCACHED_SUCCESS
)
96 if (rc
== MEMCACHED_PROTOCOL_ERROR
)
98 /* If the server doesn't support SASL it will return PROTOCOL_ERROR.
99 * This error may also be returned for other errors, but let's assume
100 * that the server don't support SASL and treat it as success and
101 * let the client fail with the next operation if the error was
102 * caused by another problem....
104 rc
= MEMCACHED_SUCCESS
;
110 /* set ip addresses */
111 char laddr
[NI_MAXHOST
+ NI_MAXSERV
];
112 char raddr
[NI_MAXHOST
+ NI_MAXSERV
];
114 unlikely (!resolve_names(server
->fd
, laddr
, raddr
))
116 server
->cached_errno
= errno
;
117 return MEMCACHED_ERRNO
;
121 int ret
= sasl_client_new("memcached", server
->hostname
, laddr
, raddr
,
122 server
->root
->sasl
->callbacks
, 0, &conn
);
125 return MEMCACHED_AUTH_PROBLEM
;
129 const char *chosenmech
;
131 ret
= sasl_client_start(conn
, mech
, NULL
, &data
, &len
, &chosenmech
);
133 if (ret
!= SASL_OK
&& ret
!= SASL_CONTINUE
)
135 rc
= MEMCACHED_AUTH_PROBLEM
;
139 uint16_t keylen
= (uint16_t)strlen(chosenmech
);
140 request
.message
.header
.request
.opcode
= PROTOCOL_BINARY_CMD_SASL_AUTH
;
141 request
.message
.header
.request
.keylen
= htons(keylen
);
142 request
.message
.header
.request
.bodylen
= htonl(len
+ keylen
);
145 /* send the packet */
147 struct __write_vector_st vector
[]=
149 { .length
= sizeof(request
.bytes
), .buffer
= request
.bytes
},
150 { .length
= keylen
, .buffer
= chosenmech
},
151 { .length
= len
, .buffer
= data
}
154 if (memcached_io_writev(server
, vector
, 3, true) == -1)
156 rc
= MEMCACHED_WRITE_FAILURE
;
159 memcached_server_response_increment(server
);
161 /* read the response */
162 rc
= memcached_response(server
, NULL
, 0, NULL
);
163 if (rc
!= MEMCACHED_AUTH_CONTINUE
)
168 ret
= sasl_client_step(conn
, memcached_result_value(&server
->root
->result
),
169 (unsigned int)memcached_result_length(&server
->root
->result
),
172 if (ret
!= SASL_OK
&& ret
!= SASL_CONTINUE
)
174 rc
= MEMCACHED_AUTH_PROBLEM
;
178 request
.message
.header
.request
.opcode
= PROTOCOL_BINARY_CMD_SASL_STEP
;
179 request
.message
.header
.request
.bodylen
= htonl(len
+ keylen
);
183 /* Release resources */
189 static int get_username(void *context
, int id
, const char **result
,
192 if (!context
|| !result
|| (id
!= SASL_CB_USER
&& id
!= SASL_CB_AUTHNAME
))
194 return SASL_BADPARAM
;
200 *len
= (unsigned int)strlen(*result
);
206 static int get_password(sasl_conn_t
*conn
, void *context
, int id
,
207 sasl_secret_t
**psecret
)
209 if (!conn
|| ! psecret
|| id
!= SASL_CB_PASS
)
211 return SASL_BADPARAM
;
219 memcached_return_t
memcached_set_sasl_auth_data(memcached_st
*ptr
,
220 const char *username
,
221 const char *password
)
223 if (ptr
== NULL
|| username
== NULL
||
224 password
== NULL
|| ptr
->sasl
->callbacks
!= NULL
)
226 return MEMCACHED_FAILURE
;
229 sasl_callback_t
*cb
= libmemcached_calloc(ptr
, 4, sizeof(sasl_callback_t
));
230 char *name
= libmemcached_malloc(ptr
, strlen(username
) + 1);
231 sasl_secret_t
*secret
= libmemcached_malloc(ptr
, strlen(password
) + 1 + sizeof(*secret
))
233 if (cb
== NULL
|| name
== NULL
|| secret
== NULL
)
235 libmemcached_free(ptr
, cb
);
236 libmemcached_free(ptr
, name
);
237 libmemcached_free(ptr
, secret
);
238 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
241 secret
->len
= strlen(password
);
242 strcpy((void*)secret
->data
, password
);
244 cb
[0].id
= SASL_CB_USER
;
245 cb
[0].proc
= get_username
;
246 cb
[0].context
= strcpy(name
, username
);
247 cb
[1].id
= SASL_CB_AUTHNAME
;
248 cb
[1].proc
= get_username
;
250 cb
[2].id
= SASL_CB_PASS
;
251 cb
[2].proc
= get_password
;
252 cb
[2].context
= secret
;
253 cb
[3].id
= SASL_CB_LIST_END
;
255 ptr
->sasl
->callbacks
= cb
;
256 ptr
->sasl
->is_allocated
= true;
258 return MEMCACHED_SUCCESS
;
261 memcached_return_t
memcached_destroy_sasl_auth_data(memcached_st
*ptr
)
263 if (ptr
== NULL
|| ptr
->sasl
->callbacks
== NULL
)
265 return MEMCACHED_FAILURE
;
268 if (ptr
->sasl
->is_allocated
)
270 libmemcached_free(ptr
, ptr
->sasl
->callbacks
[0].context
);
271 libmemcached_free(ptr
, ptr
->sasl
->callbacks
[2].context
);
272 libmemcached_free(ptr
, (void*)ptr
->sasl
->callbacks
);
273 ptr
->sasl
->is_allocated
= false;
276 ptr
->sasl
->callbacks
= NULL
;
277 libmemcached_free(ptr
, ptr
->sasl
);
280 return MEMCACHED_SUCCESS
;
283 memcached_return_t
memcached_clone_sasl(memcached_st
*clone
, const memcached_st
*source
)
285 if (source
->sasl
== NULL
)
287 return MEMCACHED_SUCCESS
;
291 clone
->sasl
= libmemcached_malloc(source
, sizeof(struct memcached_sasl_st
));
293 if (clone
->sasl
== NULL
)
295 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
299 /* Hopefully we are using our own callback mechanisms.. */
300 if (source
->sasl
->callbacks
[0].id
== SASL_CB_USER
&&
301 source
->sasl
->callbacks
[0].proc
== get_username
&&
302 source
->sasl
->callbacks
[1].id
== SASL_CB_AUTHNAME
&&
303 source
->sasl
->callbacks
[1].proc
== get_username
&&
304 source
->sasl
->callbacks
[2].id
== SASL_CB_PASS
&&
305 source
->sasl
->callbacks
[2].proc
== get_password
&&
306 source
->sasl
->callbacks
[3].id
== SASL_CB_LIST_END
)
308 sasl_secret_t
*secret
= source
->sasl
->callbacks
[2].context
;
309 return memcached_set_sasl_auth_data(clone
,
310 source
->sasl
->callbacks
[0].context
,
311 (const char*)secret
->data
);
315 * But we're not. It may work if we know what the user tries to pass
316 * into the list, but if we don't know the ID we don't know how to handle
321 while (source
->sasl
->callbacks
[total
].id
!= SASL_CB_LIST_END
)
323 switch (source
->sasl
->callbacks
[total
].id
)
326 case SASL_CB_AUTHNAME
:
330 /* I don't know how to deal with this... */
331 return MEMCACHED_NOT_SUPPORTED
;
337 sasl_callback_t
*cb
= libmemcached_calloc(clone
, total
+ 1, sizeof(sasl_callback_t
));
340 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
342 memcpy(cb
, source
->sasl
->callbacks
, (total
+ 1) * sizeof(sasl_callback_t
));
344 /* Now update the context... */
345 for (size_t x
= 0; x
< total
; ++x
)
347 if (cb
[x
].id
== SASL_CB_USER
|| cb
[x
].id
== SASL_CB_AUTHNAME
)
349 cb
[x
].context
= libmemcached_malloc(clone
, strlen(source
->sasl
->callbacks
[x
].context
));
351 if (cb
[x
].context
== NULL
)
353 /* Failed to allocate memory, clean up previously allocated memory */
354 for (size_t y
= 0; y
< x
; ++y
)
356 libmemcached_free(clone
, clone
->sasl
->callbacks
[y
].context
);
359 libmemcached_free(clone
, cb
);
360 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
362 strcpy(cb
[x
].context
, source
->sasl
->callbacks
[x
].context
);
366 sasl_secret_t
*src
= source
->sasl
->callbacks
[x
].context
;
367 sasl_secret_t
*n
= libmemcached_malloc(clone
, src
->len
+ 1 + sizeof(*n
));
370 /* Failed to allocate memory, clean up previously allocated memory */
371 for (size_t y
= 0; y
< x
; ++y
)
373 libmemcached_free(clone
, clone
->sasl
->callbacks
[y
].context
);
376 libmemcached_free(clone
, cb
);
377 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
379 memcpy(n
, src
, src
->len
+ 1 + sizeof(*n
));
384 clone
->sasl
->callbacks
= cb
;
385 clone
->sasl
->is_allocated
= true;
387 return MEMCACHED_SUCCESS
;