source: conf.c @ 8e6ecfe

Last change on this file since 8e6ecfe was 8e6ecfe, checked in by Dennis Kaarsemaker <dennis@…>, at 2016-03-25T18:07:53Z

Authentication: scaffolding for multiple authentication backends

Instead of always putting users passwords in XML files, allow site
admins to configure a different authentication method to integrate
authentication with other systems.

This doesn't add any authentication backends yet, merely the
scaffolding. Notably:

  • Password checking and loading/removing from storage has been decoupled. A new auth_check_pass function is used to check passwords. It does check against the configured storage first, but will handle the authentication backends as well. The XML storage merely signals that a user's password should be checked using an authentication backend.
  • If unknown-to-bitlbee users identify using an authentication backend, they are automatically registered.
  • If an authentication backend is used, that fact is stored in the XML file, the password is not. Passwords are also stored unencrypted in this case, as the password used to encrypt them can change underneath us.
  • configure and Makefile changes for the backend objects
  • Property mode set to 100644
File size: 12.3 KB
Line 
1/********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2005 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* Configuration reading code                                           */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
24*/
25
26#include "bitlbee.h"
27
28#include <stdio.h>
29#include <string.h>
30#include <stdlib.h>
31#include "conf.h"
32#include "ini.h"
33#include "url.h"
34#include "ipc.h"
35
36#include "proxy.h"
37
38static int conf_loadini(conf_t *conf, char *file);
39static void conf_free(conf_t *conf);
40
41conf_t *conf_load(int argc, char *argv[])
42{
43        conf_t *conf;
44        int opt, i, config_missing = 0;
45
46        conf = g_new0(conf_t, 1);
47
48        conf->iface_in = NULL;
49        conf->iface_out = NULL;
50        conf->port = g_strdup("6667");
51        conf->nofork = 0;
52        conf->verbose = 0;
53        conf->primary_storage = g_strdup("xml");
54        conf->migrate_storage = g_strsplit("text", ",", -1);
55        conf->runmode = RUNMODE_INETD;
56        conf->authmode = AUTHMODE_OPEN;
57        conf->auth_backend = NULL;
58        conf->auth_pass = NULL;
59        conf->oper_pass = NULL;
60        conf->allow_account_add = 1;
61        conf->configdir = g_strdup(CONFIG);
62        conf->plugindir = g_strdup(PLUGINDIR);
63        conf->pidfile = g_strdup(PIDFILE);
64        conf->motdfile = g_strdup(ETCDIR "/motd.txt");
65        conf->ping_interval = 180;
66        conf->ping_timeout = 300;
67        conf->user = NULL;
68        conf->ft_max_size = SIZE_MAX;
69        conf->ft_max_kbps = G_MAXUINT;
70        conf->ft_listen = NULL;
71        conf->protocols = NULL;
72        conf->cafile = NULL;
73        proxytype = 0;
74
75        i = conf_loadini(conf, global.conf_file);
76        if (i == 0) {
77                fprintf(stderr, "Error: Syntax error in configuration file `%s'.\n", global.conf_file);
78                conf_free(conf);
79                return NULL;
80        } else if (i == -1) {
81                config_missing++;
82                /* Whine after parsing the options if there was no -c pointing
83                   at a *valid* configuration file. */
84        }
85
86        while (argc > 0 && (opt = getopt(argc, argv, "i:p:P:nvIDFc:d:hu:V")) >= 0) {
87                /*     ^^^^ Just to make sure we skip this step from the REHASH handler. */
88                if (opt == 'i') {
89                        conf->iface_in = g_strdup(optarg);
90                } else if (opt == 'p') {
91                        g_free(conf->port);
92                        conf->port = g_strdup(optarg);
93                } else if (opt == 'P') {
94                        g_free(conf->pidfile);
95                        conf->pidfile = g_strdup(optarg);
96                } else if (opt == 'n') {
97                        conf->nofork = 1;
98                } else if (opt == 'v') {
99                        conf->verbose = 1;
100                } else if (opt == 'I') {
101                        conf->runmode = RUNMODE_INETD;
102                } else if (opt == 'D') {
103                        conf->runmode = RUNMODE_DAEMON;
104                } else if (opt == 'F') {
105                        conf->runmode = RUNMODE_FORKDAEMON;
106                } else if (opt == 'c') {
107                        if (strcmp(global.conf_file, optarg) != 0) {
108                                g_free(global.conf_file);
109                                global.conf_file = g_strdup(optarg);
110                                conf_free(conf);
111                                /* Re-evaluate arguments. Don't use this option twice,
112                                   you'll end up in an infinite loop! Hope this trick
113                                   works with all libcs BTW.. */
114                                optind = 1;
115                                return conf_load(argc, argv);
116                        }
117                } else if (opt == 'd') {
118                        g_free(conf->configdir);
119                        conf->configdir = g_strdup(optarg);
120                } else if (opt == 'h') {
121                        printf("Usage: bitlbee [-D/-F [-i <interface>] [-p <port>] [-n] [-v]] [-I]\n"
122                               "               [-c <file>] [-d <dir>] [-x] [-h]\n"
123                               "\n"
124                               "An IRC-to-other-chat-networks gateway\n"
125                               "\n"
126                               "  -I  Classic/InetD mode. (Default)\n"
127                               "  -D  Daemon mode. (one process serves all)\n"
128                               "  -F  Forking daemon. (one process per client)\n"
129                               "  -u  Run daemon as specified user.\n"
130                               "  -P  Specify PID-file (not for inetd mode)\n"
131                               "  -i  Specify the interface (by IP address) to listen on.\n"
132                               "      (Default: 0.0.0.0 (any interface))\n"
133                               "  -p  Port number to listen on. (Default: 6667)\n"
134                               "  -n  Don't fork.\n"
135                               "  -v  Be verbose (only works in combination with -n)\n"
136                               "  -c  Load alternative configuration file\n"
137                               "  -d  Specify alternative user configuration directory\n"
138                               "  -x  Command-line interface to password encryption/hashing\n"
139                               "  -h  Show this help page.\n"
140                               "  -V  Show version info.\n");
141                        conf_free(conf);
142                        return NULL;
143                } else if (opt == 'V') {
144                        printf("BitlBee %s\nAPI version %06x\nConfigure args: %s\n",
145                               BITLBEE_VERSION, BITLBEE_VERSION_CODE, BITLBEE_CONFIGURE_ARGS);
146                        conf_free(conf);
147                        return NULL;
148                } else if (opt == 'u') {
149                        g_free(conf->user);
150                        conf->user = g_strdup(optarg);
151                }
152        }
153
154        if (conf->configdir[strlen(conf->configdir) - 1] != '/') {
155                char *s = g_new(char, strlen(conf->configdir) + 2);
156
157                sprintf(s, "%s/", conf->configdir);
158                g_free(conf->configdir);
159                conf->configdir = s;
160        }
161
162        if (config_missing) {
163                fprintf(stderr, "Warning: Unable to read configuration file `%s'.\n", global.conf_file);
164        }
165
166        if (conf->cafile && access(conf->cafile, R_OK) != 0) {
167                /* Let's treat this as a serious problem so people won't think
168                   they're secure when in fact they're not. */
169                fprintf(stderr, "Error: Could not read CA file %s: %s\n", conf->cafile, strerror(errno));
170                conf_free(conf);
171                return NULL;
172        }
173
174        return conf;
175}
176
177static void conf_free(conf_t *conf)
178{
179        /* Free software means users have the four essential freedoms:
180           0. to run the program,
181           2. to study and change the program in source code form,
182           2. to redistribute exact copies, and
183           3. to distribute modified versions
184        */
185        g_free(conf->auth_pass);
186        g_free(conf->cafile);
187        g_free(conf->configdir);
188        g_free(conf->ft_listen);
189        g_free(conf->hostname);
190        g_free(conf->iface_in);
191        g_free(conf->iface_out);
192        g_free(conf->motdfile);
193        g_free(conf->oper_pass);
194        g_free(conf->pidfile);
195        g_free(conf->plugindir);
196        g_free(conf->port);
197        g_free(conf->primary_storage);
198        g_free(conf->user);
199        g_strfreev(conf->migrate_storage);
200        g_strfreev(conf->protocols);
201        g_free(conf);
202
203}
204
205static int conf_loadini(conf_t *conf, char *file)
206{
207        ini_t *ini;
208        int i;
209
210        ini = ini_open(file);
211        if (ini == NULL) {
212                return -1;
213        }
214        while (ini_read(ini)) {
215                if (g_strcasecmp(ini->section, "settings") == 0) {
216                        if (g_strcasecmp(ini->key, "runmode") == 0) {
217                                if (g_strcasecmp(ini->value, "daemon") == 0) {
218                                        conf->runmode = RUNMODE_DAEMON;
219                                } else if (g_strcasecmp(ini->value, "forkdaemon") == 0) {
220                                        conf->runmode = RUNMODE_FORKDAEMON;
221                                } else {
222                                        conf->runmode = RUNMODE_INETD;
223                                }
224                        } else if (g_strcasecmp(ini->key, "pidfile") == 0) {
225                                g_free(conf->pidfile);
226                                conf->pidfile = g_strdup(ini->value);
227                        } else if (g_strcasecmp(ini->key, "daemoninterface") == 0) {
228                                g_free(conf->iface_in);
229                                conf->iface_in = g_strdup(ini->value);
230                        } else if (g_strcasecmp(ini->key, "daemonport") == 0) {
231                                g_free(conf->port);
232                                conf->port = g_strdup(ini->value);
233                        } else if (g_strcasecmp(ini->key, "clientinterface") == 0) {
234                                g_free(conf->iface_out);
235                                conf->iface_out = g_strdup(ini->value);
236                        } else if (g_strcasecmp(ini->key, "authmode") == 0) {
237                                if (g_strcasecmp(ini->value, "registered") == 0) {
238                                        conf->authmode = AUTHMODE_REGISTERED;
239                                } else if (g_strcasecmp(ini->value, "closed") == 0) {
240                                        conf->authmode = AUTHMODE_CLOSED;
241                                } else {
242                                        conf->authmode = AUTHMODE_OPEN;
243                                }
244                        } else if (g_strcasecmp(ini->key, "authbackend") == 0) {
245                                if (g_strcasecmp(ini->value, "storage") == 0) {
246                                        conf->auth_backend = NULL;
247                                } else {
248                                        fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
249                                        return 0;
250                                }
251                        } else if (g_strcasecmp(ini->key, "authpassword") == 0) {
252                                g_free(conf->auth_pass);
253                                conf->auth_pass = g_strdup(ini->value);
254                        } else if (g_strcasecmp(ini->key, "operpassword") == 0) {
255                                g_free(conf->oper_pass);
256                                conf->oper_pass = g_strdup(ini->value);
257                        } else if (g_strcasecmp(ini->key, "allowaccountadd") == 0) {
258                                if (!is_bool(ini->value)) {
259                                        fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
260                                        return 0;
261                                }
262                                conf->allow_account_add = bool2int(ini->value);
263                        } else if (g_strcasecmp(ini->key, "hostname") == 0) {
264                                g_free(conf->hostname);
265                                conf->hostname = g_strdup(ini->value);
266                        } else if (g_strcasecmp(ini->key, "configdir") == 0) {
267                                g_free(conf->configdir);
268                                conf->configdir = g_strdup(ini->value);
269                        } else if (g_strcasecmp(ini->key, "plugindir") == 0) {
270                                g_free(conf->plugindir);
271                                conf->plugindir = g_strdup(ini->value);
272                        } else if (g_strcasecmp(ini->key, "motdfile") == 0) {
273                                g_free(conf->motdfile);
274                                conf->motdfile = g_strdup(ini->value);
275                        } else if (g_strcasecmp(ini->key, "account_storage") == 0) {
276                                g_free(conf->primary_storage);
277                                conf->primary_storage = g_strdup(ini->value);
278                        } else if (g_strcasecmp(ini->key, "account_storage_migrate") == 0) {
279                                g_strfreev(conf->migrate_storage);
280                                conf->migrate_storage = g_strsplit_set(ini->value, " \t,;", -1);
281                        } else if (g_strcasecmp(ini->key, "pinginterval") == 0) {
282                                if (sscanf(ini->value, "%d", &i) != 1) {
283                                        fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
284                                        return 0;
285                                }
286                                conf->ping_interval = i;
287                        } else if (g_strcasecmp(ini->key, "pingtimeout") == 0) {
288                                if (sscanf(ini->value, "%d", &i) != 1) {
289                                        fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
290                                        return 0;
291                                }
292                                conf->ping_timeout = i;
293                        } else if (g_strcasecmp(ini->key, "proxy") == 0) {
294                                url_t *url = g_new0(url_t, 1);
295
296                                if (!url_set(url, ini->value)) {
297                                        fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
298                                        g_free(url);
299                                        return 0;
300                                }
301
302                                strncpy(proxyhost, url->host, sizeof(proxyhost));
303                                strncpy(proxyuser, url->user, sizeof(proxyuser));
304                                strncpy(proxypass, url->pass, sizeof(proxypass));
305                                proxyport = url->port;
306                                if (url->proto == PROTO_HTTP) {
307                                        proxytype = PROXY_HTTP;
308                                } else if (url->proto == PROTO_SOCKS4) {
309                                        proxytype = PROXY_SOCKS4;
310                                } else if (url->proto == PROTO_SOCKS5) {
311                                        proxytype = PROXY_SOCKS5;
312                                } else if (url->proto == PROTO_SOCKS4A) {
313                                        proxytype = PROXY_SOCKS4A;
314                                }
315
316                                g_free(url);
317                        } else if (g_strcasecmp(ini->key, "user") == 0) {
318                                g_free(conf->user);
319                                conf->user = g_strdup(ini->value);
320                        } else if (g_strcasecmp(ini->key, "ft_max_size") == 0) {
321                                size_t ft_max_size;
322                                if (sscanf(ini->value, "%zu", &ft_max_size) != 1) {
323                                        fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
324                                        return 0;
325                                }
326                                conf->ft_max_size = ft_max_size;
327                        } else if (g_strcasecmp(ini->key, "ft_max_kbps") == 0) {
328                                if (sscanf(ini->value, "%d", &i) != 1) {
329                                        fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
330                                        return 0;
331                                }
332                                conf->ft_max_kbps = i;
333                        } else if (g_strcasecmp(ini->key, "ft_listen") == 0) {
334                                g_free(conf->ft_listen);
335                                conf->ft_listen = g_strdup(ini->value);
336                        } else if (g_strcasecmp(ini->key, "protocols") == 0) {
337                                g_strfreev(conf->protocols);
338                                conf->protocols = g_strsplit_set(ini->value, " \t,;", -1);
339                        } else if (g_strcasecmp(ini->key, "cafile") == 0) {
340                                g_free(conf->cafile);
341                                conf->cafile = g_strdup(ini->value);
342                        } else {
343                                fprintf(stderr, "Error: Unknown setting `%s` in configuration file (line %d).\n",
344                                        ini->key, ini->line);
345                                return 0;
346                                /* For now just ignore unknown keys... */
347                        }
348                } else if (g_strcasecmp(ini->section, "defaults") != 0) {
349                        fprintf(stderr, "Error: Unknown section [%s] in configuration file (line %d). "
350                                "BitlBee configuration must be put in a [settings] section!\n", ini->section,
351                                ini->line);
352                        return 0;
353                }
354        }
355        ini_close(ini);
356
357        return 1;
358}
359
360void conf_loaddefaults(irc_t *irc)
361{
362        ini_t *ini;
363
364        ini = ini_open(global.conf_file);
365        if (ini == NULL) {
366                return;
367        }
368        while (ini_read(ini)) {
369                if (g_strcasecmp(ini->section, "defaults") == 0) {
370                        set_t *s = set_find(&irc->b->set, ini->key);
371
372                        if (s) {
373                                if (s->def) {
374                                        g_free(s->def);
375                                }
376                                s->def = g_strdup(ini->value);
377                        }
378                }
379        }
380        ini_close(ini);
381}
Note: See TracBrowser for help on using the repository browser.