From: Arran Cudbard-Bell Date: Mon, 29 Dec 2014 17:41:59 +0000 (-0500) Subject: Allow coa_server in client stanzas to be a section as well as a pair X-Git-Tag: release_3_0_7~393 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6953f30a5572dbbbe0190572ea891a630b52cc32;p=thirdparty%2Ffreeradius-server.git Allow coa_server in client stanzas to be a section as well as a pair When used as a section it defines a new home_server with fields inherited from the client. This can be used to turn all clients into CoA home servers. --- diff --git a/src/include/radiusd.h b/src/include/radiusd.h index f54af1e2241..6c212831e9b 100644 --- a/src/include/radiusd.h +++ b/src/include/radiusd.h @@ -123,6 +123,7 @@ typedef struct radclient { char const *coa_name; home_server_t *coa_server; home_pool_t *coa_pool; + bool defines_coa_server; //!< Client also defines a home_server. #endif } RADCLIENT; diff --git a/src/include/realms.h b/src/include/realms.h index 29566a719a9..d8571155f7b 100644 --- a/src/include/realms.h +++ b/src/include/realms.h @@ -189,8 +189,8 @@ int realm_realm_add( REALM *r, CONF_SECTION *cs); void home_server_update_request(home_server_t *home, REQUEST *request); home_server_t *home_server_ldb(char const *realmname, home_pool_t *pool, REQUEST *request); home_server_t *home_server_find(fr_ipaddr_t *ipaddr, uint16_t port, int proto); -home_server_t *home_server_afrom_cs(realm_config_t *rc, CONF_SECTION *cs); - +home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SECTION *cs); +CONF_SECTION *home_server_cs_afrom_client(CONF_SECTION *client); #ifdef WITH_COA home_server_t *home_server_byname(char const *name, int type); #endif diff --git a/src/main/client.c b/src/main/client.c index 335a1853f61..142fc5624a7 100644 --- a/src/main/client.c +++ b/src/main/client.c @@ -217,6 +217,11 @@ bool client_add(RADCLIENT_LIST *clients, RADCLIENT *client) fr_ntop(buffer, sizeof(buffer), &client->ipaddr); DEBUG3("Adding client %s (%s) to prefix tree %i", buffer, client->longname, client->ipaddr.prefix); + /* + * If the client also defines a server, do that now. + */ + if (client->defines_coa_server) if (!realm_home_server_add(client->coa_server)) return false; + /* * If "clients" is NULL, it means add to the global list, * unless we're trying to add it to a virtual server... @@ -506,10 +511,6 @@ static const CONF_PARSER client_config[] = { { "rate_limit", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, rate_limit), NULL }, #endif -#ifdef WITH_COA - { "coa_server", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, coa_name), NULL }, -#endif - { NULL, -1, 0, NULL, NULL } }; @@ -768,9 +769,11 @@ error: * @param ctx to allocate new clients in. * @param cs to process as a client. * @param in_server Whether the client should belong to a specific virtual server. + * @param with_coa If true and coa_server or coa_pool aren't specified automatically + * create a coa home_server section and add it to the client CONF_SECTION. * @return new RADCLIENT struct. */ -RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server) +RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bool with_coa) { RADCLIENT *c; char const *name2; @@ -953,19 +956,54 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server) } #ifdef WITH_COA - /* - * Point the client to the home server pool, OR to the - * home server. This gets around the problem of figuring - * out which port to use. - */ - if (c->coa_name) { - c->coa_pool = home_pool_byname(c->coa_name, HOME_TYPE_COA); - if (!c->coa_pool) { - c->coa_server = home_server_byname(c->coa_name, HOME_TYPE_COA); - } - if (!c->coa_pool && !c->coa_server) { - cf_log_err_cs(cs, "No such home_server or home_server_pool \"%s\"", c->coa_name); - goto error; + { + CONF_PAIR *cp; + + /* + * Point the client to the home server pool, OR to the + * home server. This gets around the problem of figuring + * out which port to use. + */ + cp = cf_pair_find(cs, "coa_server"); + if (cp) { + c->coa_name = cf_pair_value(cp); + c->coa_pool = home_pool_byname(c->coa_name, HOME_TYPE_COA); + if (!c->coa_pool) { + c->coa_server = home_server_byname(c->coa_name, HOME_TYPE_COA); + } + if (!c->coa_pool && !c->coa_server) { + cf_log_err_cs(cs, "No such home_server or home_server_pool \"%s\"", c->coa_name); + goto error; + } + /* + * If we're implicitly adding a CoA home server for + * every client, or there's a server subsection, + * create a home server CONF_SECTION and then parse + * it into a home_server_t. + */ + } else if (with_coa || cf_section_sub_find(cs, "coa_server")) { + CONF_SECTION *server; + home_server_t *home; + + server = home_server_cs_afrom_client(cs); + if (!server) goto error; + + /* + * Must be allocated in the context of the client, + * as allocating using the context of the + * realm_config_t without a mutex, by one of the + * workers, would be bad. + */ + home = home_server_afrom_cs(c, NULL, server); + if (!home) { + talloc_free(server); + goto error; + } + + rad_assert(home->type == HOME_TYPE_COA); + + c->coa_server = home; + c->defines_coa_server = true; } } #endif diff --git a/src/main/realms.c b/src/main/realms.c index 9aa0fdfedc6..d43b0aae174 100644 --- a/src/main/realms.c +++ b/src/main/realms.c @@ -835,6 +835,77 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE return home; } +/** Fixup a client configuration section to specify a home server + * + * This is used to create the equivalent CoA home server entry for a client, + * so that the server can originate CoA messages. + * + * The server section automatically inherits the following fields from the client: + * - ipaddr/ipv4addr/ipv6addr + * - secret + * - src_ipaddr + * + * @note new CONF_SECTION will be allocated in the context of the client, but the client + * CONF_SECTION will not be modified. + * + * @param client CONF_SECTION to inherit values from. + * @return a new server CONF_SCTION, or a pointer to the existing CONF_SECTION in the client. + */ +CONF_SECTION *home_server_cs_afrom_client(CONF_SECTION *client) +{ + CONF_SECTION *server, *cs; + CONF_PAIR *cp; + + /* + * Alloc a plain home server for both cases + * + * There's no way these can be referenced by a pool, + * and they may conflict with home servers in proxy.conf + * so it's easier to not set a name. + */ + + /* + * + * Duplicate the server section, so we don't mangle + * the client CONF_SECTION we were passed. + */ + cs = cf_section_sub_find(client, "coa_server"); + if (cs) { + server = cf_section_dup(client, cs, "home_server", NULL); + } else { + server = cf_section_alloc(client, "home_server", NULL); + } + + if (!cs || (!cf_pair_find(cs, "ipaddr") && !cf_pair_find(cs, "ipv4addr") && !cf_pair_find(cs, "ipv6addr"))) { + cp = cf_pair_find(client, "ipaddr"); + if (!cp) cp = cf_pair_find(client, "ipv4addr"); + if (!cp) cp = cf_pair_find(client, "ipv6addr"); + + cf_pair_add(server, cf_pair_dup(server, cp)); + } + + if (!cs || !cf_pair_find(cs, "secret")) { + cp = cf_pair_find(client, "secret"); + if (cp) cf_pair_add(server, cp); + } + + if (!cs || !cf_pair_find(cs, "src_ipaddr")) { + cp = cf_pair_find(client, "src_ipaddr"); + if (cp) cf_pair_add(server, cf_pair_dup(server, cp)); + } + + if (!cs || !(cp = cf_pair_find(cs, "type"))) { + cp = cf_pair_alloc(server, "type", "coa", T_OP_EQ, T_SINGLE_QUOTED_STRING); + if (cp) cf_pair_add(server, cf_pair_dup(server, cp)); + } else if (strcmp(cf_pair_value(cp), "coa") != 0) { + talloc_free(server); + cf_log_err_cs(server, "server.type must be \"coa\""); + return NULL; + } + + return server; +} + static home_pool_t *server_pool_alloc(char const *name, home_pool_type_t type, home_type_t server_type, int num_home_servers) { diff --git a/src/modules/rlm_couchbase/mod.c b/src/modules/rlm_couchbase/mod.c index ef6a5e4b304..7e2a3e4ec00 100644 --- a/src/modules/rlm_couchbase/mod.c +++ b/src/modules/rlm_couchbase/mod.c @@ -746,7 +746,7 @@ int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *cs) /* * @todo These should be parented from something. */ - c = client_afrom_cs(NULL, client, false); + c = client_afrom_cs(NULL, client, false, false); if (!c) { ERROR("rlm_couchbase: failed to allocate client"); /* free config setion */ diff --git a/src/modules/rlm_ldap/clients.c b/src/modules/rlm_ldap/clients.c index 776a1a046c4..df69120d2a4 100644 --- a/src/modules/rlm_ldap/clients.c +++ b/src/modules/rlm_ldap/clients.c @@ -254,7 +254,7 @@ int rlm_ldap_client_load(ldap_instance_t const *inst, CONF_SECTION *cs) /* *@todo these should be parented from something */ - c = client_afrom_cs(NULL, cc, false); + c = client_afrom_cs(NULL, cc, false, false); if (!c) { talloc_free(cc); ret = -1;