Radcli library 2.0.0
A simple radius library -- new API reference
Loading...
Searching...
No Matches
config2.c
1/*
2 * Copyright (C) 2026 Nikos Mavrogiannopoulos
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
35
41
42#include <config.h>
43#include <includes.h>
44#include <radcli/radcli.h>
45#include <radcli/radcli2.h>
46#include <options.h>
47#include "util.h"
48
65radcli_ctx *radcli_ctx_new(unsigned flags)
66{
67 rc_handle *rh;
68
69 if (flags & ~(unsigned)RADCLI_CTX_NO_BUILTIN_DICT)
70 return NULL;
71
72 rh = radcli2_priv_new();
73 if (rh == NULL)
74 return NULL;
75
76 /* radcli2_priv_config_init() already destroys rh and returns NULL on failure */
77 rh = radcli2_priv_config_init(rh);
78 if (rh == NULL)
79 return NULL;
80
81 if (!(flags & RADCLI_CTX_NO_BUILTIN_DICT) &&
82 radcli2_priv_load_builtin_dict(rh) != 0) {
83 radcli2_priv_destroy(rh);
84 return NULL;
85 }
86
87 return rh;
88}
89
108radcli_ctx *radcli_ctx_read_config(const char *filename, unsigned flags)
109{
110 if (flags & ~(unsigned)RADCLI_CTX_NO_BUILTIN_DICT)
111 return NULL;
112
113 return radcli2_priv_read_config(filename, !!(flags & RADCLI_CTX_NO_BUILTIN_DICT));
114}
115
129int radcli_ctx_read_dictionary(radcli_ctx *ctx, const char *path)
130{
131 return radcli2_priv_read_dictionary(ctx, path);
132}
133
148int radcli_ctx_read_dictionary_from_buffer(radcli_ctx *ctx, const char *buf, size_t size)
149{
150 return radcli2_priv_read_dictionary_from_buffer(ctx, buf, size);
151}
152
164int radcli_ctx_apply(radcli_ctx *ctx)
165{
166 return radcli2_priv_apply_config(ctx);
167}
168
173void radcli_ctx_free(radcli_ctx *ctx)
174{
175 if (ctx != NULL)
176 radcli2_priv_destroy(ctx);
177}
178
179/* radcli_opt_id (radcli2.h) shares its ordinal position with rc_option_id,
180 * both generated from RC_OPTION_TABLE (radcli-defs.h) -- so
181 * ctx->config_options[opt] is the same OPTION radcli2_priv_add_config(ctx, "the
182 * option's name", ...) would look up by string. */
183/*- Look up opt's OPTION table entry for ctx.
184 *
185 * @param ctx a context from radcli_ctx_new() or radcli_ctx_read_config().
186 * @param opt the option to look up.
187 * @return the option's OPTION entry, or NULL if ctx is NULL, has no
188 * option table, or opt is out of range.
189 -*/
190static const OPTION *radcli_opt_lookup(const radcli_ctx *ctx, radcli_opt_id opt)
191{
192 if (ctx == NULL || ctx->config_options == NULL || (unsigned)opt >= OPT_COUNT)
193 return NULL;
194 return &ctx->config_options[opt];
195}
196
197/* radcli2.h's set_opt_* enforce a stricter contract than radcli2_priv_add_config():
198 * every option, including authserver/acctserver, may be set at most once --
199 * a second call fails instead of silently overwriting (RADCLI_OPT_TYPE_STR/RADCLI_OPT_TYPE_INT, which
200 * radcli2_priv_add_config() itself does not actually guard against: option->status is
201 * checked but never set) or accumulating into a multi-server list (RADCLI_OPT_TYPE_SRV).
202 * This is what makes the new API's "one server per context" design
203 * (radcli_request_new(), REQ-NET2-INIT-003) an enforced invariant instead
204 * of an unenforced convention. Only applies to this typed setter path --
205 * radcli_ctx_read_config() is a direct alias of radcli2_priv_read_config()
206 * (REQ-CONFIG2-INIT-002) and keeps that function's existing behavior. */
207/*- Report whether o already carries a value set by a previous typed setter
208 * call.
209 *
210 * @param o the option to check.
211 * @return nonzero if o is already set (or, for RADCLI_OPT_TYPE_SRV, already has a
212 * configured server), zero otherwise.
213 -*/
214static int radcli_opt_already_set(const OPTION *o)
215{
216 if (o->val == NULL)
217 return 0;
218 if (o->type & RADCLI_OPT_TYPE_SRV)
219 /* radcli2_priv_config_init() pre-allocates an empty SERVER struct for
220 * authserver/acctserver, so a non-NULL val alone does not mean
221 * a server was actually configured yet. */
222 return ((SERVER *)o->val)->max > 0;
223 return 1;
224}
225
242int radcli_ctx_set_opt_str(radcli_ctx *ctx, radcli_opt_id opt, const char *val)
243{
244 const OPTION *o = radcli_opt_lookup(ctx, opt);
245
246 if (o == NULL || !(o->type & (RADCLI_OPT_TYPE_STR | RADCLI_OPT_TYPE_SRV)))
247 return -1;
248
249 if (radcli_opt_already_set(o)) {
250 rc_log(LOG_ERR, "radcli_ctx_set_opt_str: %s is already set", o->name);
251 return -1;
252 }
253
254 if ((o->type & RADCLI_OPT_TYPE_SRV) && val != NULL && strpbrk(val, ", \t") != NULL) {
255 /* The new API carries exactly one server per role
256 * (REQ-NET2-INIT-003) -- reject a comma/whitespace-separated
257 * multi-host value in one call, the same as a second call
258 * naming another host would be rejected above. */
259 rc_log(LOG_ERR, "radcli_ctx_set_opt_str: %s must name a single "
260 "server, not a list", o->name);
261 return -1;
262 }
263
264 return radcli2_priv_add_config(ctx, o->name, val, "radcli_ctx_set_opt_str", 0);
265}
266
274int radcli_ctx_set_opt_int(radcli_ctx *ctx, radcli_opt_id opt, long val)
275{
276 const OPTION *o = radcli_opt_lookup(ctx, opt);
277 char buf[32];
278
279 if (o == NULL || !(o->type & RADCLI_OPT_TYPE_INT))
280 return -1;
281
282 if (radcli_opt_already_set(o)) {
283 rc_log(LOG_ERR, "radcli_ctx_set_opt_int: %s is already set", o->name);
284 return -1;
285 }
286
287 snprintf(buf, sizeof(buf), "%ld", val);
288 return radcli2_priv_add_config(ctx, o->name, buf, "radcli_ctx_set_opt_int", 0);
289}
290
303const char *radcli_ctx_get_opt_str(const radcli_ctx *ctx, radcli_opt_id opt)
304{
305 const OPTION *o = radcli_opt_lookup(ctx, opt);
306
307 if (o == NULL || !(o->type & RADCLI_OPT_TYPE_STR))
308 return NULL;
309 return (const char *)o->val;
310}
311
320int radcli_ctx_get_opt_int(const radcli_ctx *ctx, radcli_opt_id opt, long *out)
321{
322 const OPTION *o = radcli_opt_lookup(ctx, opt);
323
324 if (o == NULL || !(o->type & RADCLI_OPT_TYPE_INT) || out == NULL || o->val == NULL)
325 return -1;
326
327 *out = *((int *)o->val);
328 return 0;
329}
330
331/* The new API is single-server-per-context, so only the first
332 * (only) entry of the SERVER list named by optname needs a secret. */
333/*- Set secret on the first entry of the SERVER list named by optname.
334 *
335 * @param ctx a context whose optname server is already configured.
336 * @param optname "authserver" or "acctserver".
337 * @param secret the shared secret to copy onto that server entry.
338 * @return 0 on success, -1 if that server type has not been configured
339 * yet or the secret could not be duplicated.
340 -*/
341static int radcli_set_one_secret(radcli_ctx *ctx, const char *optname, const char *secret)
342{
343 SERVER *serv = radcli2_priv_conf_srv(ctx, optname);
344 char *dup;
345
346 if (serv == NULL || serv->max == 0)
347 return -1;
348
349 dup = strdup(secret);
350 if (dup == NULL)
351 return -1;
352
353 free(serv->secret[0]);
354 serv->secret[0] = dup;
355 return 0;
356}
357
378int radcli_ctx_set_secret(radcli_ctx *ctx, unsigned target_mask, const char *secret)
379{
380 int ret = 0;
381
382 if (ctx == NULL || secret == NULL || target_mask == 0 ||
383 (target_mask & ~(unsigned)(RADCLI_SECRET_AUTH | RADCLI_SECRET_ACCT)))
384 return -1;
385
386 if (target_mask & RADCLI_SECRET_AUTH)
387 ret |= radcli_set_one_secret(ctx, "authserver", secret);
388 if (target_mask & RADCLI_SECRET_ACCT)
389 ret |= radcli_set_one_secret(ctx, "acctserver", secret);
390
391 return ret == 0 ? 0 : -1;
392}
393
417int radcli_ctx_set_tls_psk(radcli_ctx *ctx,
418 const void *identity, size_t identity_len,
419 const uint8_t *key, size_t keylen)
420{
421 char *id_copy;
422 void *key_copy;
423
424 if (ctx == NULL || identity == NULL || key == NULL || keylen == 0)
425 return -1;
426
427 id_copy = malloc(identity_len + 1);
428 if (id_copy == NULL)
429 return -1;
430 memcpy(id_copy, identity, identity_len);
431 id_copy[identity_len] = '\0';
432
433 key_copy = malloc(keylen);
434 if (key_copy == NULL) {
435 free(id_copy);
436 return -1;
437 }
438 memcpy(key_copy, key, keylen);
439
440 free(ctx->tls_psk_identity);
441 free(ctx->tls_psk_key);
442 ctx->tls_psk_identity = id_copy;
443 ctx->tls_psk_key = key_copy;
444 ctx->tls_psk_key_len = keylen;
445
446 return 0;
447}
448
450 /*
451 * Local Variables:
452 * c-basic-offset:8
453 * c-style: whitesmith
454 * End:
455 */
radcli_ctx * radcli_ctx_new(unsigned flags)
Create an empty context, ready for radcli_ctx_set_opt_str()/ _set_opt_int() and radcli_ctx_apply() – ...
Definition config2.c:65
int radcli_ctx_set_opt_str(radcli_ctx *ctx, radcli_opt_id opt, const char *val)
Set a string-typed configuration option.
Definition config2.c:242
const char * radcli_ctx_get_opt_str(const radcli_ctx *ctx, radcli_opt_id opt)
Read back a string-typed configuration option.
Definition config2.c:303
int radcli_ctx_set_opt_int(radcli_ctx *ctx, radcli_opt_id opt, long val)
Set an integer-typed configuration option.
Definition config2.c:274
int radcli_ctx_apply(radcli_ctx *ctx)
Validate the options set so far and initialise the transport.
Definition config2.c:164
radcli_opt_id
Definition radcli2.h:92
int radcli_ctx_read_dictionary(radcli_ctx *ctx, const char *path)
Load an additional attribute dictionary.
Definition config2.c:129
radcli_ctx * radcli_ctx_read_config(const char *filename, unsigned flags)
Create a context by parsing a config file – the main, recommended way to configure radcli.
Definition config2.c:108
void radcli_ctx_free(radcli_ctx *ctx)
Release a context.
Definition config2.c:173
int radcli_ctx_read_dictionary_from_buffer(radcli_ctx *ctx, const char *buf, size_t size)
Load an additional attribute dictionary from an in-memory buffer.
Definition config2.c:148
int radcli_ctx_set_tls_psk(radcli_ctx *ctx, const void *identity, size_t identity_len, const uint8_t *key, size_t keylen)
Set the RFC 6614/7360 TLS-transport Pre-Shared Key credentials for the configured authserver.
Definition config2.c:417
int radcli_ctx_set_secret(radcli_ctx *ctx, unsigned target_mask, const char *secret)
Set the RADIUS shared secret for the configured authserver and/or acctserver.
Definition config2.c:378
int radcli_ctx_get_opt_int(const radcli_ctx *ctx, radcli_opt_id opt, long *out)
Read back an integer-typed configuration option.
Definition config2.c:320
@ RADCLI_SECRET_AUTH
Applies to the configured authserver (Access-Request).
Definition radcli2.h:135
@ RADCLI_SECRET_ACCT
Applies to the configured acctserver (Accounting-Request).
Definition radcli2.h:136
@ RADCLI_CTX_NO_BUILTIN_DICT
Skip loading the built-in RFC 2865/2866/2869 dictionary.
Definition radcli2.h:105