source: unix.c @ 720f7a9

Last change on this file since 720f7a9 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: 9.1 KB
Line 
1/********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* Main file (Unix specific part)                                       */
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 "arc.h"
29#include "base64.h"
30#include "commands.h"
31#include "protocols/nogaim.h"
32#include "help.h"
33#include "ipc.h"
34#include "md5.h"
35#include "misc.h"
36#include <signal.h>
37#include <unistd.h>
38#include <sys/time.h>
39#include <sys/wait.h>
40#include <pwd.h>
41#include <locale.h>
42#include <grp.h>
43
44#if defined(OTR_BI) || defined(OTR_PI)
45#include "otr.h"
46#endif
47
48global_t global;        /* Against global namespace pollution */
49
50static struct {
51        int fd[2];
52        int tag;
53} shutdown_pipe = {{-1 , -1}, 0};
54
55static void sighandler_shutdown(int signal);
56static void sighandler_crash(int signal);
57
58static int crypt_main(int argc, char *argv[]);
59
60int main(int argc, char *argv[])
61{
62        int i = 0;
63        char *old_cwd = NULL;
64        struct sigaction sig, old;
65
66        /* Required to make iconv to ASCII//TRANSLIT work. This makes BitlBee
67           system-locale-sensitive. :-( */
68        setlocale(LC_CTYPE, "");
69
70        if (argc > 1 && strcmp(argv[1], "-x") == 0) {
71                return crypt_main(argc, argv);
72        }
73
74        log_init();
75
76        global.conf_file = g_strdup(CONF_FILE_DEF);
77        global.conf = conf_load(argc, argv);
78        if (global.conf == NULL) {
79                return(1);
80        }
81
82        b_main_init();
83
84        /* libpurple doesn't like fork()s after initializing itself, so if
85           we use it, do this init a little later (in case we're running in
86           ForkDaemon mode). */
87#ifndef WITH_PURPLE
88        nogaim_init();
89#endif
90
91#ifdef OTR_BI
92        otr_init();
93#endif
94
95        global.helpfile = g_strdup(HELP_FILE);
96        if (help_init(&global.help, global.helpfile) == NULL) {
97                log_message(LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE);
98        }
99
100        global.storage = storage_init(global.conf->primary_storage, global.conf->migrate_storage);
101        if (global.storage == NULL) {
102                log_message(LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage);
103                return(1);
104        }
105
106        global.auth = auth_init(global.conf->auth_backend);
107        if (global.conf->auth_backend && global.auth == NULL) {
108                log_message(LOGLVL_ERROR, "Unable to load authentication backend '%s'", global.conf->auth_backend);
109                return(1);
110        }
111
112        if (global.conf->runmode == RUNMODE_INETD) {
113                log_link(LOGLVL_ERROR, LOGOUTPUT_IRC);
114                log_link(LOGLVL_WARNING, LOGOUTPUT_IRC);
115
116                i = bitlbee_inetd_init();
117                log_message(LOGLVL_INFO, "%s %s starting in inetd mode.", PACKAGE, BITLBEE_VERSION);
118
119        } else if (global.conf->runmode == RUNMODE_DAEMON) {
120                log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
121                log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
122
123                i = bitlbee_daemon_init();
124                log_message(LOGLVL_INFO, "%s %s starting in daemon mode.", PACKAGE, BITLBEE_VERSION);
125        } else if (global.conf->runmode == RUNMODE_FORKDAEMON) {
126                log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
127                log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
128
129                /* In case the operator requests a restart, we need this. */
130                old_cwd = g_malloc(256);
131                if (getcwd(old_cwd, 255) == NULL) {
132                        log_message(LOGLVL_WARNING, "Could not save current directory: %s", strerror(errno));
133                        g_free(old_cwd);
134                        old_cwd = NULL;
135                }
136
137                i = bitlbee_daemon_init();
138                log_message(LOGLVL_INFO, "%s %s starting in forking daemon mode.", PACKAGE, BITLBEE_VERSION);
139        }
140        if (i != 0) {
141                return(i);
142        }
143
144        if ((global.conf->user && *global.conf->user) &&
145            (global.conf->runmode == RUNMODE_DAEMON ||
146             global.conf->runmode == RUNMODE_FORKDAEMON) &&
147            (!getuid() || !geteuid())) {
148                struct passwd *pw = NULL;
149                pw = getpwnam(global.conf->user);
150                if (pw) {
151                        initgroups(global.conf->user, pw->pw_gid);
152                        setgid(pw->pw_gid);
153                        setuid(pw->pw_uid);
154                } else {
155                        log_message(LOGLVL_WARNING, "Failed to look up user %s.", global.conf->user);
156                }
157        }
158
159        /* Catch some signals to tell the user what's happening before quitting */
160        memset(&sig, 0, sizeof(sig));
161        sig.sa_handler = SIG_IGN;
162        sigaction(SIGCHLD, &sig, &old);
163        sigaction(SIGPIPE, &sig, &old);
164        sig.sa_flags = SA_RESETHAND;
165        sig.sa_handler = sighandler_crash;
166        sigaction(SIGSEGV, &sig, &old);
167
168        sighandler_shutdown_setup();
169
170        sig.sa_handler = sighandler_shutdown;
171        sigaction(SIGINT, &sig, &old);
172        sigaction(SIGTERM, &sig, &old);
173
174        if (!getuid() || !geteuid()) {
175                log_message(LOGLVL_WARNING, "BitlBee is running with root privileges. Why?");
176        }
177
178        b_main_run();
179
180        /* Mainly good for restarting, to make sure we close the help.txt fd. */
181        help_free(&global.help);
182
183        if (global.restart) {
184                char *fn = ipc_master_save_state();
185                char *env;
186
187                env = g_strdup_printf("_BITLBEE_RESTART_STATE=%s", fn);
188                putenv(env);
189                g_free(fn);
190                /* Looks like env should *not* be freed here as putenv
191                   doesn't make a copy. Odd. */
192
193                i = chdir(old_cwd);
194                close(global.listen_socket);
195
196                if (execv(argv[0], argv) == -1) {
197                        /* Apparently the execve() failed, so let's just
198                           jump back into our own/current main(). */
199                        /* Need more cleanup code to make this work. */
200                        return 1; /* main( argc, argv ); */
201                }
202        }
203
204        return(0);
205}
206
207static int crypt_main(int argc, char *argv[])
208{
209        int pass_len;
210        unsigned char *pass_cr, *pass_cl;
211
212        if (argc < 4 || (strcmp(argv[2], "hash") != 0 &&
213                         strcmp(argv[2], "unhash") != 0 && argc < 5)) {
214                printf("Supported:\n"
215                       "  %s -x enc <key> <cleartext password>\n"
216                       "  %s -x dec <key> <encrypted password>\n"
217                       "  %s -x hash <cleartext password>\n"
218                       "  %s -x unhash <hashed password>\n"
219                       "  %s -x chkhash <hashed password> <cleartext password>\n",
220                       argv[0], argv[0], argv[0], argv[0], argv[0]);
221        } else if (strcmp(argv[2], "enc") == 0) {
222                char *encoded;
223
224                pass_len = arc_encode(argv[4], strlen(argv[4]), &pass_cr, argv[3], 12);
225
226                encoded = base64_encode(pass_cr, pass_len);
227                printf("%s\n", encoded);
228                g_free(encoded);
229                g_free(pass_cr);
230        } else if (strcmp(argv[2], "dec") == 0) {
231                pass_len = base64_decode(argv[4], &pass_cr);
232                arc_decode(pass_cr, pass_len, (char **) &pass_cl, argv[3]);
233                printf("%s\n", pass_cl);
234
235                g_free(pass_cr);
236                g_free(pass_cl);
237        } else if (strcmp(argv[2], "hash") == 0) {
238                md5_byte_t pass_md5[21];
239                md5_state_t md5_state;
240                char *encoded;
241
242                random_bytes(pass_md5 + 16, 5);
243                md5_init(&md5_state);
244                md5_append(&md5_state, (md5_byte_t *) argv[3], strlen(argv[3]));
245                md5_append(&md5_state, pass_md5 + 16, 5);   /* Add the salt. */
246                md5_finish(&md5_state, pass_md5);
247
248                encoded = base64_encode(pass_md5, 21);
249                printf("%s\n", encoded);
250                g_free(encoded);
251        } else if (strcmp(argv[2], "unhash") == 0) {
252                printf("Hash %s submitted to a massive Beowulf cluster of\n"
253                       "overclocked 486s. Expect your answer next year somewhere around this time. :-)\n", argv[3]);
254        } else if (strcmp(argv[2], "chkhash") == 0) {
255                char *hash = strncmp(argv[3], "md5:", 4) == 0 ? argv[3] + 4 : argv[3];
256                int st = md5_verify_password(argv[4], hash);
257
258                printf("Hash %s given password.\n", st == 0 ? "matches" : "does not match");
259
260                return st;
261        }
262
263        return 0;
264}
265
266/* Set up a pipe for SIGTERM/SIGINT so the actual signal handler doesn't do anything unsafe */
267void sighandler_shutdown_setup()
268{
269        if (shutdown_pipe.fd[0] != -1) {
270                /* called again from a forked process, clean up to avoid propagating the signal */
271                b_event_remove(shutdown_pipe.tag);
272                close(shutdown_pipe.fd[0]);
273                close(shutdown_pipe.fd[1]);
274        }
275
276        if (pipe(shutdown_pipe.fd) == 0) {
277                shutdown_pipe.tag = b_input_add(shutdown_pipe.fd[0], B_EV_IO_READ, bitlbee_shutdown, NULL);
278        }
279}
280
281/* Signal handler for SIGTERM and SIGINT */
282static void sighandler_shutdown(int signal)
283{
284        /* Write a single null byte to the pipe, just to send a message to the main loop.
285         * This gets handled by bitlbee_shutdown (the b_input_add callback for this pipe) */
286        write(shutdown_pipe.fd[1], "", 1);
287}
288
289/* Signal handler for SIGSEGV
290 * A desperate attempt to tell the user that everything is wrong in the world.
291 * Avoids using irc_abort() because it has several unsafe calls to malloc */
292static void sighandler_crash(int signal)
293{
294        GSList *l;
295        const char *message = "ERROR :BitlBee crashed! (SIGSEGV received)\r\n";
296        int len = strlen(message);
297
298        for (l = irc_connection_list; l; l = l->next) {
299                irc_t *irc = l->data;
300                sock_make_blocking(irc->fd);
301                write(irc->fd, message, len);
302        }
303
304        raise(signal);
305}
306
307double gettime()
308{
309        struct timeval time[1];
310
311        gettimeofday(time, 0);
312        return((double) time->tv_sec + (double) time->tv_usec / 1000000);
313}
Note: See TracBrowser for help on using the repository browser.