1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 * Copyright (C) 2006-2009 Brian Aker All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #include <libmemcached/common.h>
40 void memcached_set_sasl_callbacks(memcached_st
*ptr
,
41 const sasl_callback_t
*callbacks
)
43 ptr
->sasl
.callbacks
= callbacks
;
44 ptr
->sasl
.is_allocated
= false;
47 const sasl_callback_t
*memcached_get_sasl_callbacks(memcached_st
*ptr
)
49 return ptr
->sasl
.callbacks
;
53 * Resolve the names for both ends of a connection
54 * @param fd socket to check
55 * @param laddr local address (out)
56 * @param raddr remote address (out)
57 * @return true on success false otherwise (errno contains more info)
59 static bool resolve_names(int fd
, char *laddr
, size_t laddr_length
, char *raddr
, size_t raddr_length
)
61 char host
[NI_MAXHOST
];
62 char port
[NI_MAXSERV
];
63 struct sockaddr_storage saddr
;
64 socklen_t salen
= sizeof(saddr
);
66 if ((getsockname(fd
, (struct sockaddr
*)&saddr
, &salen
) < 0) ||
67 (getnameinfo((struct sockaddr
*)&saddr
, salen
, host
, sizeof(host
),
68 port
, sizeof(port
), NI_NUMERICHOST
| NI_NUMERICSERV
) < 0))
73 (void)snprintf(laddr
, laddr_length
, "%s;%s", host
, port
);
76 if ((getpeername(fd
, (struct sockaddr
*)&saddr
, &salen
) < 0) ||
77 (getnameinfo((struct sockaddr
*)&saddr
, salen
, host
, sizeof(host
),
78 port
, sizeof(port
), NI_NUMERICHOST
| NI_NUMERICSERV
) < 0))
83 (void)snprintf(raddr
, raddr_length
, "%s;%s", host
, port
);
88 memcached_return_t
memcached_sasl_authenticate_connection(memcached_server_st
*server
)
90 memcached_return_t rc
;
92 /* SANITY CHECK: SASL can only be used with the binary protocol */
93 if (!server
->root
->flags
.binary_protocol
)
94 return MEMCACHED_FAILURE
;
96 /* Try to get the supported mech from the server. Servers without SASL
97 * support will return UNKNOWN COMMAND, so we can just treat that
100 protocol_binary_request_no_extras request
= {
101 .message
.header
.request
= {
102 .magic
= PROTOCOL_BINARY_REQ
,
103 .opcode
= PROTOCOL_BINARY_CMD_SASL_LIST_MECHS
107 if (memcached_io_write(server
, request
.bytes
,
108 sizeof(request
.bytes
), 1) != sizeof(request
.bytes
))
110 return MEMCACHED_WRITE_FAILURE
;
113 memcached_server_response_increment(server
);
115 char mech
[MEMCACHED_MAX_BUFFER
];
116 rc
= memcached_response(server
, mech
, sizeof(mech
), NULL
);
117 if (rc
!= MEMCACHED_SUCCESS
)
119 if (rc
== MEMCACHED_PROTOCOL_ERROR
)
121 /* If the server doesn't support SASL it will return PROTOCOL_ERROR.
122 * This error may also be returned for other errors, but let's assume
123 * that the server don't support SASL and treat it as success and
124 * let the client fail with the next operation if the error was
125 * caused by another problem....
127 rc
= MEMCACHED_SUCCESS
;
133 /* set ip addresses */
134 char laddr
[NI_MAXHOST
+ NI_MAXSERV
];
135 char raddr
[NI_MAXHOST
+ NI_MAXSERV
];
137 unlikely (!resolve_names(server
->fd
, laddr
, sizeof(laddr
), raddr
, sizeof(raddr
)))
139 server
->cached_errno
= errno
;
140 return MEMCACHED_ERRNO
;
144 int ret
= sasl_client_new("memcached", server
->hostname
, laddr
, raddr
,
145 server
->root
->sasl
.callbacks
, 0, &conn
);
148 return MEMCACHED_AUTH_PROBLEM
;
152 const char *chosenmech
;
154 ret
= sasl_client_start(conn
, mech
, NULL
, &data
, &len
, &chosenmech
);
156 if (ret
!= SASL_OK
&& ret
!= SASL_CONTINUE
)
158 rc
= MEMCACHED_AUTH_PROBLEM
;
162 uint16_t keylen
= (uint16_t)strlen(chosenmech
);
163 request
.message
.header
.request
.opcode
= PROTOCOL_BINARY_CMD_SASL_AUTH
;
164 request
.message
.header
.request
.keylen
= htons(keylen
);
165 request
.message
.header
.request
.bodylen
= htonl(len
+ keylen
);
168 /* send the packet */
170 struct libmemcached_io_vector_st vector
[]=
172 { .length
= sizeof(request
.bytes
), .buffer
= request
.bytes
},
173 { .length
= keylen
, .buffer
= chosenmech
},
174 { .length
= len
, .buffer
= data
}
177 if (memcached_io_writev(server
, vector
, 3, true) == -1)
179 rc
= MEMCACHED_WRITE_FAILURE
;
182 memcached_server_response_increment(server
);
184 /* read the response */
185 rc
= memcached_response(server
, NULL
, 0, NULL
);
186 if (rc
!= MEMCACHED_AUTH_CONTINUE
)
191 ret
= sasl_client_step(conn
, memcached_result_value(&server
->root
->result
),
192 (unsigned int)memcached_result_length(&server
->root
->result
),
195 if (ret
!= SASL_OK
&& ret
!= SASL_CONTINUE
)
197 rc
= MEMCACHED_AUTH_PROBLEM
;
201 request
.message
.header
.request
.opcode
= PROTOCOL_BINARY_CMD_SASL_STEP
;
202 request
.message
.header
.request
.bodylen
= htonl(len
+ keylen
);
206 /* Release resources */
212 static int get_username(void *context
, int id
, const char **result
,
215 if (!context
|| !result
|| (id
!= SASL_CB_USER
&& id
!= SASL_CB_AUTHNAME
))
217 return SASL_BADPARAM
;
223 *len
= (unsigned int)strlen(*result
);
229 static int get_password(sasl_conn_t
*conn
, void *context
, int id
,
230 sasl_secret_t
**psecret
)
232 if (!conn
|| ! psecret
|| id
!= SASL_CB_PASS
)
234 return SASL_BADPARAM
;
242 memcached_return_t
memcached_set_sasl_auth_data(memcached_st
*ptr
,
243 const char *username
,
244 const char *password
)
246 if (ptr
== NULL
|| username
== NULL
||
247 password
== NULL
|| ptr
->sasl
.callbacks
!= NULL
)
249 return MEMCACHED_FAILURE
;
252 sasl_callback_t
*cb
= libmemcached_calloc(ptr
, 4, sizeof(sasl_callback_t
));
253 char *name
= libmemcached_malloc(ptr
, strlen(username
) + 1);
254 size_t password_length
= strlen(password
);
255 sasl_secret_t
*secret
= libmemcached_malloc(ptr
, password_length
+1 + sizeof(*secret
));
256 if (cb
== NULL
|| name
== NULL
|| secret
== NULL
)
258 libmemcached_free(ptr
, cb
);
259 libmemcached_free(ptr
, name
);
260 libmemcached_free(ptr
, secret
);
261 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
264 secret
->len
= strlen(password
);
265 memcpy(secret
->data
, password
, password_length
);
266 secret
->data
[password_length
]= 0;
268 cb
[0].id
= SASL_CB_USER
;
269 cb
[0].proc
= get_username
;
270 cb
[0].context
= strncpy(name
, username
, sizeof(cb
[0].context
));
271 cb
[1].id
= SASL_CB_AUTHNAME
;
272 cb
[1].proc
= get_username
;
274 cb
[2].id
= SASL_CB_PASS
;
275 cb
[2].proc
= get_password
;
276 cb
[2].context
= secret
;
277 cb
[3].id
= SASL_CB_LIST_END
;
279 ptr
->sasl
.callbacks
= cb
;
280 ptr
->sasl
.is_allocated
= true;
282 return MEMCACHED_SUCCESS
;
285 memcached_return_t
memcached_destroy_sasl_auth_data(memcached_st
*ptr
)
287 if (ptr
== NULL
|| ptr
->sasl
.callbacks
== NULL
)
289 return MEMCACHED_FAILURE
;
292 if (ptr
->sasl
.is_allocated
)
294 libmemcached_free(ptr
, ptr
->sasl
.callbacks
[0].context
);
295 libmemcached_free(ptr
, ptr
->sasl
.callbacks
[2].context
);
296 libmemcached_free(ptr
, (void*)ptr
->sasl
.callbacks
);
297 ptr
->sasl
.is_allocated
= false;
300 ptr
->sasl
.callbacks
= NULL
;
302 return MEMCACHED_SUCCESS
;
305 memcached_return_t
memcached_clone_sasl(memcached_st
*clone
, const memcached_st
*source
)
308 if (source
->sasl
.callbacks
== NULL
)
310 return MEMCACHED_SUCCESS
;
313 /* Hopefully we are using our own callback mechanisms.. */
314 if (source
->sasl
.callbacks
[0].id
== SASL_CB_USER
&&
315 source
->sasl
.callbacks
[0].proc
== get_username
&&
316 source
->sasl
.callbacks
[1].id
== SASL_CB_AUTHNAME
&&
317 source
->sasl
.callbacks
[1].proc
== get_username
&&
318 source
->sasl
.callbacks
[2].id
== SASL_CB_PASS
&&
319 source
->sasl
.callbacks
[2].proc
== get_password
&&
320 source
->sasl
.callbacks
[3].id
== SASL_CB_LIST_END
)
322 sasl_secret_t
*secret
= source
->sasl
.callbacks
[2].context
;
323 return memcached_set_sasl_auth_data(clone
,
324 source
->sasl
.callbacks
[0].context
,
325 (const char*)secret
->data
);
329 * But we're not. It may work if we know what the user tries to pass
330 * into the list, but if we don't know the ID we don't know how to handle
335 while (source
->sasl
.callbacks
[total
].id
!= SASL_CB_LIST_END
)
337 switch (source
->sasl
.callbacks
[total
].id
)
340 case SASL_CB_AUTHNAME
:
344 /* I don't know how to deal with this... */
345 return MEMCACHED_NOT_SUPPORTED
;
351 sasl_callback_t
*cb
= libmemcached_calloc(clone
, total
+ 1, sizeof(sasl_callback_t
));
354 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
356 memcpy(cb
, source
->sasl
.callbacks
, (total
+ 1) * sizeof(sasl_callback_t
));
358 /* Now update the context... */
359 for (size_t x
= 0; x
< total
; ++x
)
361 if (cb
[x
].id
== SASL_CB_USER
|| cb
[x
].id
== SASL_CB_AUTHNAME
)
363 cb
[x
].context
= libmemcached_malloc(clone
, strlen(source
->sasl
.callbacks
[x
].context
));
365 if (cb
[x
].context
== NULL
)
367 /* Failed to allocate memory, clean up previously allocated memory */
368 for (size_t y
= 0; y
< x
; ++y
)
370 libmemcached_free(clone
, clone
->sasl
.callbacks
[y
].context
);
373 libmemcached_free(clone
, cb
);
374 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
376 strncpy(cb
[x
].context
, source
->sasl
.callbacks
[x
].context
, sizeof(cb
[x
].context
));
380 sasl_secret_t
*src
= source
->sasl
.callbacks
[x
].context
;
381 sasl_secret_t
*n
= libmemcached_malloc(clone
, src
->len
+ 1 + sizeof(*n
));
384 /* Failed to allocate memory, clean up previously allocated memory */
385 for (size_t y
= 0; y
< x
; ++y
)
387 libmemcached_free(clone
, clone
->sasl
.callbacks
[y
].context
);
390 libmemcached_free(clone
, cb
);
391 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
393 memcpy(n
, src
, src
->len
+ 1 + sizeof(*n
));
398 clone
->sasl
.callbacks
= cb
;
399 clone
->sasl
.is_allocated
= true;
401 return MEMCACHED_SUCCESS
;