Move a comment, for clarification.
[libradsec.git] / lib / conf.c
1 /* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
2    See the file COPYING for licensing information.  */
3
4 #if defined HAVE_CONFIG_H
5 #include <config.h>
6 #endif
7
8 #include <confuse.h>
9 #include <string.h>
10 #include <assert.h>
11 #include <radsec/radsec.h>
12 #include <radsec/radsec-impl.h>
13 #include "peer.h"
14 #include "debug.h"
15
16 #if 0
17   # common config options
18   dictionary = STRING
19
20   # common realm config options
21   realm NAME {
22       type = "UDP"|"TCP"|"TLS"|"DTLS"
23       timeout = INT
24       retries = INT
25       cacertfile = STRING
26       #cacertpath = STRING
27       certfile = STRING
28       certkeyfile = STRING
29       psk = STRING              # Transport pre-shared key.
30       pskid = STRING
31       pskex = "PSK"|"DHE_PSK"|"RSA_PSK"
32   }
33
34   # client specific realm config options
35   realm NAME {
36       server {
37           hostname = STRING
38           service = STRING
39           secret = STRING       # RADIUS secret
40       }
41   }
42 #endif
43
44 /* FIXME: Leaking memory in error cases?  */
45 int
46 rs_context_read_config(struct rs_context *ctx, const char *config_file)
47 {
48   cfg_t *cfg, *cfg_realm, *cfg_server;
49   int err = 0;
50   int i, j;
51   const char *s;
52   struct rs_config *config = NULL;
53
54   cfg_opt_t server_opts[] =
55     {
56       CFG_STR ("hostname", NULL, CFGF_NONE),
57       CFG_STR ("service", "2083", CFGF_NONE),
58       CFG_STR ("secret", "radsec", CFGF_NONE),
59       CFG_END ()
60     };
61   cfg_opt_t realm_opts[] =
62     {
63       CFG_STR ("type", "UDP", CFGF_NONE),
64       CFG_INT ("timeout", 2, CFGF_NONE), /* FIXME: Remove?  */
65       CFG_INT ("retries", 2, CFGF_NONE), /* FIXME: Remove?  */
66       CFG_STR ("cacertfile", NULL, CFGF_NONE),
67       /*CFG_STR ("cacertpath", NULL, CFGF_NONE),*/
68       CFG_STR ("certfile", NULL, CFGF_NONE),
69       CFG_STR ("certkeyfile", NULL, CFGF_NONE),
70       CFG_STR ("psk", NULL, CFGF_NONE),
71       CFG_STR ("pskid", NULL, CFGF_NONE),
72       CFG_STR ("pskex", "PSK", CFGF_NONE),
73       CFG_SEC ("server", server_opts, CFGF_MULTI),
74       CFG_END ()
75     };
76   cfg_opt_t opts[] =
77     {
78       CFG_STR ("dictionary", NULL, CFGF_NONE),
79       CFG_SEC ("realm", realm_opts, CFGF_TITLE | CFGF_MULTI),
80       CFG_END ()
81     };
82
83   cfg = cfg_init (opts, CFGF_NONE);
84   if (cfg == NULL)
85     return rs_err_ctx_push (ctx, RSE_CONFIG, "unable to initialize libconfuse");
86   err = cfg_parse (cfg, config_file);
87   switch (err)
88     {
89     case  CFG_SUCCESS:
90       break;
91     case CFG_FILE_ERROR:
92       return rs_err_ctx_push (ctx, RSE_CONFIG,
93                               "%s: unable to open configuration file",
94                               config_file);
95     case CFG_PARSE_ERROR:
96       return rs_err_ctx_push (ctx, RSE_CONFIG, "%s: invalid configuration file",
97                               config_file);
98     default:
99         return rs_err_ctx_push (ctx, RSE_CONFIG, "%s: unknown parse error",
100                                 config_file);
101     }
102
103   config = rs_calloc (ctx, 1, sizeof (*config));
104   if (config == NULL)
105     return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL);
106   ctx->config = config;
107   config->dictionary = cfg_getstr (cfg, "dictionary");
108
109   for (i = 0; i < cfg_size (cfg, "realm"); i++)
110     {
111       struct rs_realm *r = NULL;
112       const char *typestr;
113       char *psk;
114
115       r = rs_calloc (ctx, 1, sizeof(*r));
116       if (r == NULL)
117         return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL);
118       if (config->realms != NULL)
119         {
120           r->next = config->realms->next;
121           config->realms->next = r;
122         }
123       else
124         {
125           config->realms = r;
126         }
127       cfg_realm = cfg_getnsec (cfg, "realm", i);
128       s = cfg_title (cfg_realm);
129       if (s == NULL)
130         return rs_err_ctx_push_fl (ctx, RSE_CONFIG, __FILE__, __LINE__,
131                                    "missing realm name");
132       /* We use a copy of the return value of cfg_title() since it's const.  */
133       r->name = strdup (s);
134       if (r->name == NULL)
135         return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL);
136
137       typestr = cfg_getstr (cfg_realm, "type");
138       if (strcmp (typestr, "UDP") == 0)
139         r->type = RS_CONN_TYPE_UDP;
140       else if (strcmp (typestr, "TCP") == 0)
141         r->type = RS_CONN_TYPE_TCP;
142       else if (strcmp (typestr, "TLS") == 0)
143         r->type = RS_CONN_TYPE_TLS;
144       else if (strcmp (typestr, "DTLS") == 0)
145         r->type = RS_CONN_TYPE_DTLS;
146       else
147         return rs_err_ctx_push_fl (ctx, RSE_CONFIG, __FILE__, __LINE__,
148                                    "invalid connection type: %s", typestr);
149       r->timeout = cfg_getint (cfg_realm, "timeout");
150       r->retries = cfg_getint (cfg_realm, "retries");
151
152       r->cacertfile = cfg_getstr (cfg_realm, "cacertfile");
153       /*r->cacertpath = cfg_getstr (cfg_realm, "cacertpath");*/
154       r->certfile = cfg_getstr (cfg_realm, "certfile");
155       r->certkeyfile = cfg_getstr (cfg_realm, "certkeyfile");
156
157       psk = cfg_getstr (cfg_realm, "psk");
158       if (psk)
159         {
160           char *kex = cfg_getstr (cfg_realm, "pskex");
161           rs_cred_type_t type = RS_CRED_NONE;
162           struct rs_credentials *cred = NULL;
163           assert (kex != NULL);
164
165           if (!strcmp (kex, "PSK"))
166             type = RS_CRED_TLS_PSK;
167           else
168             {
169               /* TODO: push a warning, using a separate warn stack or
170                  onto the ordinary error stack?  */
171               /* rs_err_ctx_push (ctx, FIXME, "%s: unsupported PSK key exchange"
172                  " algorithm -- PSK not used", kex);*/
173             }
174
175           if (type != RS_CRED_NONE)
176             {
177               cred = rs_calloc (ctx, 1, sizeof (*cred));
178               if (cred == NULL)
179                 return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__,
180                                            NULL);
181               cred->type = type;
182               cred->identity = cfg_getstr (cfg_realm, "pskid");
183               cred->secret = psk;
184               r->transport_cred = cred;
185             }
186         }
187
188       /* Add peers, one per server stanza.  */
189       for (j = 0; j < cfg_size (cfg_realm, "server"); j++)
190         {
191           struct rs_peer *p = peer_create (ctx, &r->peers);
192           if (p == NULL)
193             return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__,
194                                        NULL);
195           p->realm = r;
196
197           cfg_server = cfg_getnsec (cfg_realm, "server", j);
198           /* FIXME: Handle resolve errors, possibly by postponing name
199              resolution.  */
200           rs_resolv (&p->addr, r->type, cfg_getstr (cfg_server, "hostname"),
201                      cfg_getstr (cfg_server, "service"));
202           p->secret = cfg_getstr (cfg_server, "secret");
203         }
204     }
205
206   /* Save config object in context, for freeing in rs_context_destroy().  */
207   ctx->config->cfg = cfg;
208
209   return RSE_OK;
210 }
211
212 struct rs_realm *
213 rs_conf_find_realm(struct rs_context *ctx, const char *name)
214 {
215   struct rs_realm *r;
216
217   for (r = ctx->config->realms; r; r = r->next)
218     if (strcmp (r->name, name) == 0)
219         return r;
220
221   return NULL;
222 }