source: unix.c @ 59e66ff

Last change on this file since 59e66ff was 59e66ff, checked in by dequis <dx@…>, at 2014-07-24T03:51:07Z

Fix the NSS init after fork bug, and clean up lies in unix.c

This might look like a simple diff, but those 'lies' made this not very
straightforward.

The NSS bug itself is simple: NSS detects a fork happened after the
initialization, and refuses to work because shared CSPRNG state is bad.
The bug has been around for long time. I've been aware of it for 5
months, which says something about this mess. Trac link:

http://bugs.bitlbee.org/bitlbee/ticket/785

This wasn't a big deal because the main users of NSS (redhat) already
applied a different patch in their packages that workarounded the issue
somewhat accidentally. And this is the ticket for the 'lies' in unix.c:

http://bugs.bitlbee.org/bitlbee/ticket/1159

Basically a conflict with libotr that doesn't happen anymore. Read that
ticket for details on why ignoring those comments is acceptable.

Anyway: yay!

  • 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., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  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 void sighandler( int signal );
51
52static int crypt_main( int argc, char *argv[] );
53
54int main( int argc, char *argv[] )
55{
56        int i = 0;
57        char *old_cwd = NULL;
58        struct sigaction sig, old;
59       
60        /* Required to make iconv to ASCII//TRANSLIT work. This makes BitlBee
61           system-locale-sensitive. :-( */
62        setlocale( LC_CTYPE, "" );
63       
64        if( argc > 1 && strcmp( argv[1], "-x" ) == 0 )
65                return crypt_main( argc, argv );
66       
67        log_init();
68       
69        global.conf_file = g_strdup( CONF_FILE_DEF );
70        global.conf = conf_load( argc, argv );
71        if( global.conf == NULL )
72                return( 1 );
73       
74        b_main_init();
75       
76        /* libpurple doesn't like fork()s after initializing itself, so if
77           we use it, do this init a little later (in case we're running in
78           ForkDaemon mode). */
79#ifndef WITH_PURPLE
80        nogaim_init();
81#endif
82       
83#ifdef OTR_BI
84        otr_init();
85#endif
86       
87        srand( time( NULL ) ^ getpid() );
88       
89        global.helpfile = g_strdup( HELP_FILE );
90        if( help_init( &global.help, global.helpfile ) == NULL )
91                log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE );
92
93        global.storage = storage_init( global.conf->primary_storage, global.conf->migrate_storage );
94        if( global.storage == NULL )
95        {
96                log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage );
97                return( 1 );
98        }
99       
100        if( global.conf->runmode == RUNMODE_INETD )
101        {
102                log_link( LOGLVL_ERROR, LOGOUTPUT_IRC );
103                log_link( LOGLVL_WARNING, LOGOUTPUT_IRC );
104       
105                i = bitlbee_inetd_init();
106                log_message( LOGLVL_INFO, "%s %s starting in inetd mode.", PACKAGE, BITLBEE_VERSION );
107
108        }
109        else if( global.conf->runmode == RUNMODE_DAEMON )
110        {
111                log_link( LOGLVL_ERROR, LOGOUTPUT_CONSOLE );
112                log_link( LOGLVL_WARNING, LOGOUTPUT_CONSOLE );
113
114                i = bitlbee_daemon_init();
115                log_message( LOGLVL_INFO, "%s %s starting in daemon mode.", PACKAGE, BITLBEE_VERSION );
116        }
117        else if( global.conf->runmode == RUNMODE_FORKDAEMON )
118        {
119                log_link( LOGLVL_ERROR, LOGOUTPUT_CONSOLE );
120                log_link( LOGLVL_WARNING, LOGOUTPUT_CONSOLE );
121
122                /* In case the operator requests a restart, we need this. */
123                old_cwd = g_malloc( 256 );
124                if( getcwd( old_cwd, 255 ) == NULL )
125                {
126                        log_message( LOGLVL_WARNING, "Could not save current directory: %s", strerror( errno ) );
127                        g_free( old_cwd );
128                        old_cwd = NULL;
129                }
130               
131                i = bitlbee_daemon_init();
132                log_message( LOGLVL_INFO, "%s %s starting in forking daemon mode.", PACKAGE, BITLBEE_VERSION );
133        }
134        if( i != 0 )
135                return( i );
136       
137        if( ( global.conf->user && *global.conf->user ) &&
138            ( global.conf->runmode == RUNMODE_DAEMON || 
139              global.conf->runmode == RUNMODE_FORKDAEMON ) &&
140            ( !getuid() || !geteuid() ) )
141        {
142                struct passwd *pw = NULL;
143                pw = getpwnam( global.conf->user );
144                if( pw )
145                {
146                        initgroups( global.conf->user, pw->pw_gid );
147                        setgid( pw->pw_gid );
148                        setuid( pw->pw_uid );
149                }
150                else
151                {
152                        log_message( LOGLVL_WARNING, "Failed to look up user %s.", global.conf->user );
153                }
154        }
155       
156        /* Catch some signals to tell the user what's happening before quitting */
157        memset( &sig, 0, sizeof( sig ) );
158        sig.sa_handler = sighandler;
159        sigaction( SIGCHLD, &sig, &old );
160        sigaction( SIGPIPE, &sig, &old );
161        sig.sa_flags = SA_RESETHAND;
162        sigaction( SIGINT,  &sig, &old );
163        sigaction( SIGILL,  &sig, &old );
164        sigaction( SIGBUS,  &sig, &old );
165        sigaction( SIGFPE,  &sig, &old );
166        sigaction( SIGSEGV, &sig, &old );
167        sigaction( SIGTERM, &sig, &old );
168        sigaction( SIGQUIT, &sig, &old );
169        sigaction( SIGXCPU, &sig, &old );
170       
171        if( !getuid() || !geteuid() )
172                log_message( LOGLVL_WARNING, "BitlBee is running with root privileges. Why?" );
173       
174        b_main_run();
175       
176        /* Mainly good for restarting, to make sure we close the help.txt fd. */
177        help_free( &global.help );
178       
179        if( global.restart )
180        {
181                char *fn = ipc_master_save_state();
182                char *env;
183               
184                env = g_strdup_printf( "_BITLBEE_RESTART_STATE=%s", fn );
185                putenv( env );
186                g_free( fn );
187                /* Looks like env should *not* be freed here as putenv
188                   doesn't make a copy. Odd. */
189               
190                i = chdir( old_cwd );
191                close( global.listen_socket );
192               
193                if( execv( argv[0], argv ) == -1 )
194                        /* Apparently the execve() failed, so let's just
195                           jump back into our own/current main(). */
196                        /* Need more cleanup code to make this work. */
197                        return 1; /* main( argc, argv ); */
198        }
199       
200        return( 0 );
201}
202
203static int crypt_main( int argc, char *argv[] )
204{
205        int pass_len;
206        unsigned char *pass_cr, *pass_cl;
207       
208        if( argc < 4 || ( strcmp( argv[2], "hash" ) != 0 &&
209                          strcmp( argv[2], "unhash" ) != 0 && argc < 5 ) )
210        {
211                printf( "Supported:\n"
212                        "  %s -x enc <key> <cleartext password>\n"
213                        "  %s -x dec <key> <encrypted password>\n"
214                        "  %s -x hash <cleartext password>\n"
215                        "  %s -x unhash <hashed password>\n"
216                        "  %s -x chkhash <hashed password> <cleartext password>\n",
217                        argv[0], argv[0], argv[0], argv[0], argv[0] );
218        }
219        else if( strcmp( argv[2], "enc" ) == 0 )
220        {
221                pass_len = arc_encode( argv[4], strlen( argv[4] ), (unsigned char**) &pass_cr, argv[3], 12 );
222                printf( "%s\n", base64_encode( pass_cr, pass_len ) );
223        }
224        else if( strcmp( argv[2], "dec" ) == 0 )
225        {
226                pass_len = base64_decode( argv[4], (unsigned char**) &pass_cr );
227                arc_decode( pass_cr, pass_len, (char**) &pass_cl, argv[3] );
228                printf( "%s\n", pass_cl );
229        }
230        else if( strcmp( argv[2], "hash" ) == 0 )
231        {
232                md5_byte_t pass_md5[21];
233                md5_state_t md5_state;
234               
235                random_bytes( pass_md5 + 16, 5 );
236                md5_init( &md5_state );
237                md5_append( &md5_state, (md5_byte_t*) argv[3], strlen( argv[3] ) );
238                md5_append( &md5_state, pass_md5 + 16, 5 ); /* Add the salt. */
239                md5_finish( &md5_state, pass_md5 );
240               
241                printf( "%s\n", base64_encode( pass_md5, 21 ) );
242        }
243        else if( strcmp( argv[2], "unhash" ) == 0 )
244        {
245                printf( "Hash %s submitted to a massive Beowulf cluster of\n"
246                        "overclocked 486s. Expect your answer next year somewhere around this time. :-)\n", argv[3] );
247        }
248        else if( strcmp( argv[2], "chkhash" ) == 0 )
249        {
250                char *hash = strncmp( argv[3], "md5:", 4 ) == 0 ? argv[3] + 4 : argv[3];
251                int st = md5_verify_password( argv[4], hash );
252               
253                printf( "Hash %s given password.\n", st == 0 ? "matches" : "does not match" );
254               
255                return st;
256        }
257       
258        return 0;
259}
260
261static void sighandler( int signal )
262{
263        /* FIXME: Calling log_message() here is not a very good idea! */
264       
265        if( signal == SIGTERM || signal == SIGQUIT || signal == SIGINT )
266        {
267                static int first = 1;
268               
269                if( first )
270                {
271                        /* We don't know what we were doing when this signal came in. It's not safe to touch
272                           the user data now (not to mention writing them to disk), so add a timer. */
273                       
274                        log_message( LOGLVL_ERROR, "SIGTERM received, cleaning up process." );
275                        b_timeout_add( 1, (b_event_handler) bitlbee_shutdown, NULL );
276                       
277                        first = 0;
278                }
279                else
280                {
281                        /* Well, actually, for now we'll never need this part because this signal handler
282                           will never be called more than once in a session for a non-SIGPIPE signal...
283                           But just in case we decide to change that: */
284                       
285                        log_message( LOGLVL_ERROR, "SIGTERM received twice, so long for a clean shutdown." );
286                        raise( signal );
287                }
288        }
289        else if( signal == SIGCHLD )
290        {
291                pid_t pid;
292                int st;
293               
294                while( ( pid = waitpid( 0, &st, WNOHANG ) ) > 0 )
295                {
296                        if( WIFSIGNALED( st ) )
297                                log_message( LOGLVL_INFO, "Client %d terminated normally. (status = %d)", (int) pid, WEXITSTATUS( st ) );
298                        else if( WIFEXITED( st ) )
299                                log_message( LOGLVL_INFO, "Client %d killed by signal %d.", (int) pid, WTERMSIG( st ) );
300                }
301        }
302        else if( signal != SIGPIPE )
303        {
304                log_message( LOGLVL_ERROR, "Fatal signal received: %d. That's probably a bug.", signal );
305                raise( signal );
306        }
307}
308
309double gettime()
310{
311        struct timeval time[1];
312
313        gettimeofday( time, 0 );
314        return( (double) time->tv_sec + (double) time->tv_usec / 1000000 );
315}
Note: See TracBrowser for help on using the repository browser.