27 RCSID(
"$Id: 2c940117f8fcf09a56948af614d2a021b82c0231 $")
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/libradius.h>
31 #include <freeradius-devel/modules.h>
32 #include <freeradius-devel/rad_assert.h>
34 #include <libcouchbase/couchbase.h>
35 #include "../rlm_json/json.h"
55 #ifdef WITH_ACCOUNTING
63 #ifdef WITH_SESSION_MGMT
84 static bool version_done;
91 INFO(
"rlm_couchbase: libcouchbase version: %s", lcb_get_version(NULL));
100 server = p = talloc_array(inst,
char, len);
101 for (i = 0; i < len; i++) {
107 if (sep ==
true)
continue;
135 ERROR(
"rlm_couchbase: failed to initiate connection pool");
147 ERROR(
"rlm_couchbase: failed to find client section while loading clients");
155 ERROR(
"rlm_couchbase: failed to find attribute subsection while loading clients");
163 DEBUG(
"rlm_couchbase: preparing to load client documents");
193 lcb_error_t cb_error = LCB_SUCCESS;
203 if ((dockey == buffer) &&
is_truncated((
size_t)slen,
sizeof(buffer))) {
204 REDEBUG(
"Key too long, expected < " STRINGIFY(
sizeof(buffer))
" bytes, got %zi bytes", slen);
215 lcb_t cb_inst = handle->
handle;
224 if (cb_error != LCB_SUCCESS || !cookie->
jobj) {
226 RERROR(
"failed to fetch document or parse return");
234 RDEBUG3(
"parsed user document == %s", json_object_to_json_string(cookie->
jobj));
246 json_object_put(cookie->
jobj);
259 #ifdef WITH_ACCOUNTING
285 lcb_error_t cb_error = LCB_SUCCESS;
294 RDEBUG(
"could not find status type in packet");
300 status = vp->vp_integer;
305 RDEBUG(
"handling accounting on/off request without action");
317 lcb_t cb_inst = handle->
handle;
328 if ((dockey == buffer) &&
is_truncated((
size_t)slen,
sizeof(buffer))) {
329 REDEBUG(
"Key too long, expected < " STRINGIFY(
sizeof(buffer))
" bytes, got %zi bytes", slen);
339 if (cb_error != LCB_SUCCESS || cookie->
jerr != json_tokener_success || !cookie->
jobj) {
341 RERROR(
"failed to execute get request or parse returned json object");
344 json_object_put(cookie->
jobj);
348 }
else if (cookie->
jobj) {
352 RDEBUG3(
"parsed json body from couchbase: %s", json_object_to_json_string(cookie->
jobj));
358 RDEBUG(
"no existing document found - creating new json document");
360 cookie->
jobj = json_object_new_object();
362 json_object_object_add(cookie->
jobj,
"docType", json_object_new_string(inst->
doctype));
364 json_object_object_add(cookie->
jobj,
"startTimestamp", NULL);
365 json_object_object_add(cookie->
jobj,
"stopTimestamp", NULL);
374 json_object_object_add(cookie->
jobj,
"startTimestamp",
383 json_object_object_add(cookie->
jobj,
"stopTimestamp",
414 if (
strlcpy(document, json_object_to_json_string(cookie->
jobj),
sizeof(document)) >=
sizeof(document)) {
416 RERROR(
"could not write json document - insufficient buffer space");
424 RDEBUG3(
"setting '%s' => '%s'", dockey, document);
430 if (cb_error != LCB_SUCCESS) {
431 RERROR(
"failed to store document (%s): %s (0x%x)", dockey, lcb_strerror(NULL, cb_error), cb_error);
437 json_object_put(cookie->
jobj);
451 #ifdef WITH_SESSION_MGMT
476 lcb_error_t cb_error = LCB_SUCCESS;
477 json_object *json, *jval;
478 json_object *jrows = NULL;
480 uint32_t client_ip_addr = 0;
481 char const *client_cs_id = NULL;
482 char *user_name = NULL;
483 char *session_id = NULL;
485 uint32_t nas_addr = 0;
487 uint32_t framed_ip_addr = 0;
488 char framed_proto = 0;
489 int session_time = 0;
494 RDEBUG3(
"mod_checksimul returning noop - not enabled");
500 RDEBUG3(
"mod_checksimul - invalid username");
506 if ((vkey == buffer) &&
is_truncated((
size_t)slen,
sizeof(buffer))) {
507 REDEBUG(
"Key too long, expected < " STRINGIFY(
sizeof(buffer))
" bytes, got %zi bytes", slen);
518 lcb_t cb_inst = handle->
handle;
524 snprintf(vpath,
sizeof(vpath),
"%s?key=\"%s\"&stale=update_after",
531 if (cb_error != LCB_SUCCESS || cookie->
jerr != json_tokener_success || !cookie->
jobj) {
533 RERROR(
"failed to execute view request or parse return");
541 RDEBUG3(
"cookie->jobj == %s", json_object_to_json_string(cookie->
jobj));
546 strlcpy(error, json_object_get_string(json),
sizeof(error));
550 strlcat(error,
" - ",
sizeof(error));
552 strlcat(error, json_object_get_string(json),
sizeof(error));
555 RERROR(
"view request failed with error: %s", error);
565 RERROR(
"failed to fetch rows from view payload");
573 jrows = json_object_get(json);
577 json_object_put(cookie->
jobj);
584 RERROR(
"no valid rows returned from view: %s", vpath);
592 RDEBUG3(
"jrows == %s", json_object_to_json_string(jrows));
595 request->
simul_count = json_object_array_length(jrows);
616 RDEBUG(
"verifying session count");
623 client_ip_addr = vp->vp_ipaddr;
628 client_cs_id = vp->vp_strvalue;
632 for (idx = 0; idx < json_object_array_length(jrows); idx++) {
634 memset(docid, 0,
sizeof(docid));
637 json = json_object_array_get_idx(jrows, idx);
642 if (
strlcpy(docid, json_object_get_string(jval),
sizeof(docid)) >=
sizeof(docid)) {
650 RWARN(
"failed to fetch document id from row - skipping");
658 if (cb_error != LCB_SUCCESS || cookie->
jerr != json_tokener_success || !cookie->
jobj) {
660 RERROR(
"failed to execute get request or parse return");
668 RDEBUG3(
"cookie->jobj == %s", json_object_to_json_string(cookie->
jobj));
674 RDEBUG(
"cannot zap stale entry without username");
681 RDEBUG(
"failed to find map entry for User-Name attribute");
690 RDEBUG(
"cannot zap stale entry without session id");
697 RDEBUG(
"failed to find map entry for Acct-Session-Id attribute");
706 nas_addr = inet_addr(json_object_get_string(jval));
714 nas_port = (uint32_t) json_object_get_int(jval);
719 int check =
rad_check_ts(nas_addr, nas_port, user_name, session_id);
729 framed_ip_addr = inet_addr(json_object_get_string(jval));
737 if (strcmp(json_object_get_string(jval),
"PPP") == 0) {
739 }
else if (strcmp(json_object_get_string(jval),
"SLIP") == 0) {
749 session_time = json_object_get_int(jval);
754 session_zap(request, nas_addr, nas_port, user_name, session_id,
755 framed_ip_addr, framed_proto, session_time);
757 }
else if (check == 1) {
765 framed_ip_addr = inet_addr(json_object_get_string(jval));
785 if (client_ip_addr && framed_ip_addr && framed_ip_addr == client_ip_addr) {
787 }
else if (client_cs_id && cs_id && !strncmp(cs_id, client_cs_id, 16)) {
793 REDEBUG(
"failed to check the terminal server for user '%s'", user_name);
799 if (user_name) TALLOC_FREE(user_name);
802 if (cs_id) TALLOC_FREE(cs_id);
805 if (session_id) TALLOC_FREE(session_id);
809 json_object_put(cookie->
jobj);
815 RDEBUG(
"Retained %d open sessions for %s after verification",
819 if (user_name) talloc_free(user_name);
820 if (cs_id) talloc_free(cs_id);
821 if (session_id) talloc_free(session_id);
824 if (jrows) json_object_put(jrows);
828 json_object_put(cookie->
jobj);
854 if (inst->
map) json_object_put(inst->
map);
869 .config = module_config,
874 #ifdef WITH_ACCOUNTING
877 #ifdef WITH_SESSION_MGMT
ssize_t tmpl_expand(char const **out, char *buff, size_t outlen, REQUEST *request, vp_tmpl_t const *vpt, xlat_escape_t escape, void *escape_ctx)
Expand a vp_tmpl_t to a string writing the result to a buffer.
bool read_clients
Toggle for loading client records.
json_object * map
Json object to hold user defined attribute map.
int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *tmpl, CONF_SECTION *map)
Load client entries from Couchbase client documents on startup.
uint32_t expire
Accounting document expire time in seconds.
int session_zap(REQUEST *request, uint32_t nasaddr, uint32_t nas_port, char const *user, char const *sessionid, uint32_t cliaddr, char proto, int session_time)
fr_connection_pool_t * pool
Connection pool.
static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
Write accounting data to Couchbase documents.
lcb_error_t couchbase_set_key(lcb_t instance, const char *key, const char *document, int expire)
Store a document by key in Couchbase.
The module is OK, continue.
Metadata exported by the module.
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
Handle authorization requests using Couchbase document data.
#define RLM_TYPE_THREAD_SAFE
Module is threadsafe.
VALUE_PAIR * username
Cached username VALUE_PAIR from request RADIUS_PACKET.
void * handle
Real couchbase instance.
lcb_error_t couchbase_get_key(lcb_t instance, const void *cookie, const char *key)
Retrieve a document by key from Couchbase.
bool delete_stale_sessions
Toggle to trigger zapping of stale sessions.
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
#define CONF_PARSER_TERMINATOR
Couchbase wrapper function prototypes and datatypes.
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
int simul_max
Maximum number of concurrent sessions for this user.
const char * simul_view
Couchbase view that returns accounting documents.
The module considers the request invalid.
char const * server
Couchbase server list.
#define PW_TYPE_SUBSECTION
struct rlm_couchbase_t rlm_couchbase_t
The main module instance.
Defines a CONF_PAIR to C data type mapping.
int mod_attribute_to_element(const char *name, json_object *map, void *buf)
Map attributes to JSON element names.
static const CONF_PARSER module_config[]
Module Configuration.
The main module instance.
#define is_truncated(_ret, _max)
fr_connection_pool_t * module_connection_pool_init(CONF_SECTION *module, void *opaque, fr_connection_create_t c, fr_connection_alive_t a, char const *prefix)
Initialise a module specific connection pool.
static int mod_instantiate(CONF_SECTION *conf, void *instance)
Initialize the rlm_couchbase module.
int simul_count
The current number of sessions for this user.
int json_object_object_get_ex(struct json_object *jso, const char *key, struct json_object **value)
vp_tmpl_t * simul_vkey
The query key to be used with simul_view.
static void * mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout)
Create a new memcached handle.
vp_tmpl_t * user_key
User document key.
vp_tmpl_t * acct_key
Accounting document key.
static int mod_detach(void *instance)
Detach the module.
4 methods index for checksimul section.
3 methods index for accounting section.
void * mod_json_object_to_value_pairs(json_object *json, const char *section, REQUEST *request)
Build value pairs from the passed JSON object and add to the request.
bool check_simul
Toggle to enable simultaneous use checking.
Stores an attribute, a value and various bits of other data.
static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request)
Check if a given user is already logged in.
json_object * jobj
JSON objects handled by the json-c library.
int rad_check_ts(uint32_t nasaddr, uint32_t nas_port, char const *user, char const *sessionid)
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
CONF_SECTION * cf_section_sub_find(CONF_SECTION const *, char const *name)
Find a sub-section in a section.
char const * server_raw
Raw server string before parsing.
void fr_json_version_print(void)
Print JSON-C version.
enum json_tokener_error jerr
Error values produced by the json-c library.
static const CONF_PARSER client_config[]
Client Configuration.
Module succeeded without doing anything.
char name[1]
Attribute name.
char const * doctype
Value of accounting 'docType' element name.
uint64_t magic
Used to validate module struct.
Module failed, don't reply.
#define FR_CONF_OFFSET(_n, _t, _s, _f)
lcb_error_t couchbase_query_view(lcb_t instance, const void *cookie, const char *path, const char *post)
Query a Couchbase design document view.
bool verify_simul
Toggle to enable user login state verification.
json_object * mod_value_pair_to_json_object(REQUEST *request, VALUE_PAIR *vp)
Convert value pairs to json objects.
void * fr_connection_get(fr_connection_pool_t *pool)
Reserve a connection in the connection pool.
RADIUS_PACKET * packet
Incoming request.
#define PW_STATUS_ACCOUNTING_ON
Information relating to the parsing of Couchbase document payloads.
void * cookie
Couchbase cookie (cookie_u cookie_t).
#define PW_TYPE_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
int mod_conn_alive(UNUSED void *instance, void *handle)
Check the health of a connection handle.
Couchbase instance specific information.
size_t strlcpy(char *dst, char const *src, size_t siz)
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
void fr_connection_release(fr_connection_pool_t *pool, void *conn)
Release a connection.
String of printable characters.
#define FR_CONF_POINTER(_n, _t, _p)
#define PW_TYPE_TMPL
CONF_PAIR should be parsed as a template.
1 methods index for authorize section.
#define fr_json_object_is_type(_obj, _type)
char * talloc_typed_strdup(void const *t, char const *p)
Call talloc strdup, setting the type on the new chunk correctly.
#define PW_STATUS_ACCOUNTING_OFF
int simul_mpp
WEIRD: 1 is false, 2 is true.
Function prototypes and datatypes used in the module.
void fr_connection_pool_free(fr_connection_pool_t *pool)
Delete a connection pool.
int mod_build_attribute_element_map(CONF_SECTION *conf, void *instance)
Build a JSON object map from the configuration "map" section.
int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps)
Ensure accounting documents always contain a valid timestamp.
size_t strlcat(char *dst, char const *src, size_t siz)