source: unix.c @ 9b767e9

Last change on this file since 9b767e9 was ad46e4d, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-12-22T11:23:18Z

Use initgroups() as well when dropping privileges. Closes bug #852.

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