Added stacktrace support with --enable-stack-trace option.
[silc.git] / apps / silcd / silcd.c
1 /*
2
3   silcd.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2002 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /*
21  * Created: Wed Mar 19 00:17:12 1997
22  *
23  * This is the main program for the SILC daemon. This parses command
24  * line arguments and creates the server object.
25  */
26 /* $Id$ */
27
28 #include "serverincludes.h"
29 #include "server_internal.h"
30 #include "silcversion.h"
31
32 /* For now, we'll have this one server context global for this module. */
33 static SilcServer silcd;
34
35 static void silc_usage(void);
36 static char *silc_server_create_identifier(void);
37 static int silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
38                                        char *identifier,
39                                        SilcPublicKey *ret_pub_key,
40                                        SilcPrivateKey *ret_prv_key);
41
42 /* Long command line options */
43 static struct option long_opts[] =
44 {
45   { "config-file", 1, NULL, 'f' },
46   { "passphrase", 1, NULL, 'p' },
47   { "debug", 2, NULL, 'd' },
48   { "hexdump", 0, NULL, 'x' },
49   { "help", 0, NULL, 'h' },
50   { "foreground", 0, NULL, 'F' },
51   { "version", 0, NULL,'V' },
52
53   /* Key management options */
54   { "create-key-pair", 1, NULL, 'C' },
55   { "pkcs", 1, NULL, 10 },
56   { "bits", 1, NULL, 11 },
57   { "identifier", 1, NULL, 12 },
58
59   { NULL, 0, NULL, 0 }
60 };
61
62 /* Command line option variables */
63 static char *opt_keypath = NULL;
64 static char *opt_pkcs = "rsa";
65 static char *opt_identifier = NULL;
66 static int opt_bits = 2048;
67
68 /* Prints out the usage of silc client */
69
70 static void silc_usage(void)
71 {
72   printf(""
73 "Usage: silcd [options]\n"
74 "\n"
75 "  Generic Options:\n"
76 "  -f  --config-file=FILE        Alternate configuration file\n"
77 "  -d  --debug=string            Enable debugging (Implies --foreground)\n"
78 "  -x  --hexdump                 Enable hexdumps (Implies --debug)\n"
79 "  -h  --help                    Display this message\n"
80 "  -F  --foreground              Dont fork\n"
81 "  -V  --version                 Display version\n"
82 "\n"
83 "  Key Management Options:\n"
84 "  -C, --create-key-pair=PATH    Create new public key pair\n"
85 "      --pkcs=PKCS               Set the PKCS of the public key pair\n"
86 "      --bits=VALUE              Set length of the public key pair\n"
87 "      --identifier=IDENTIFIER   Public key identifier\n"
88 "\n"
89 "      The public key identifier may be of the following format:\n"
90 "\n"
91 "      UN=<username>, HN=<hostname or IP>, RN=<real name>, E=<email>,\n"
92 "      O=<organization>, C=<country>\n"
93 "\n"
94 "      The UN and HN must be provided, the others are optional.  If the\n"
95 "      --identifier option is not used an identifier will be created for\n"
96 "      the public key automatically.\n"
97 "\n"
98 "      Example identifier: \"UN=foobar, HN=foo.bar.com, RN=Foo T. Bar, \n"
99 "                           E=foo@bar.com, C=FI\"\n"
100 "\n");
101   exit(0);
102 }
103
104 /* Die if a *valid* pid file exists already */
105
106 static void silc_server_checkpid(SilcServer silcd)
107 {
108   if (silcd->config->server_info->pid_file) {
109     int oldpid;
110     char *buf;
111     SilcUInt32 buf_len;
112
113     SILC_LOG_DEBUG(("Checking for another silcd running"));
114     buf = silc_file_readfile(silcd->config->server_info->pid_file, &buf_len);
115     if (!buf)
116       return;
117     oldpid = atoi(buf);
118     silc_free(buf);
119     if (oldpid <= 0)
120       return;
121     kill(oldpid, SIGCHLD); /* this signal does nothing, check if alive */
122     if (errno != ESRCH) {
123       fprintf(stderr, "\nI detected another daemon running with the "
124               "same pid file.\n");
125       fprintf(stderr, "Please change the config file, or erase the %s\n",
126         silcd->config->server_info->pid_file);
127       exit(1);
128     }
129   }
130 }
131
132 /* Drop root privileges. If some system call fails, die. */
133
134 static void silc_server_drop_privs(SilcServer server)
135 {
136   /* Are we executing silcd as root or a regular user? */
137   if (geteuid()) {
138     SILC_LOG_DEBUG(("Server started as user"));
139   }
140   else {
141     struct passwd *pw;
142     struct group *gr;
143     char *user, *group;
144
145     SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
146
147     /* Get the values given for user and group in configuration file */
148     user = server->config->server_info->user;
149     group = server->config->server_info->group;
150
151     if (!user || !group) {
152       fprintf(stderr, "Error:"
153        "\tSILC server must not be run as root.  For the security of your\n"
154        "\tsystem it is strongly suggested that you run SILC under dedicated\n"
155        "\tuser account.  Modify the ServerInfo configuration section to run\n"
156        "\tthe server as non-root user.\n");
157       exit(1);
158     }
159
160     /* Check whether the user/group does not begin with a number */
161     if (isdigit(user[0]) || isdigit(group[0])) {
162       SILC_LOG_DEBUG(("User and/or group starts with a number"));
163       fprintf(stderr, "Invalid user and/or group information\n");
164       fprintf(stderr, "Please assign them as names, not numbers\n");
165       exit(1);
166     }
167
168     if (!(pw = getpwnam(user))) {
169       fprintf(stderr, "Error: No such user %s found.\n", user);
170       exit(1);
171     }
172     if (!(gr = getgrnam(group))) {
173       fprintf(stderr, "Error: No such group %s found.\n", group);
174       exit(1);
175     }
176
177     /* Check whether user and/or group is set to root. If yes, exit
178        immediately. Otherwise, setgid and setuid server to user.group */
179     if ((gr->gr_gid == 0) || (pw->pw_uid == 0)) {
180       fprintf(stderr, "Error:"
181        "\tSILC server must not be run as root.  For the security of your\n"
182        "\tsystem it is strongly suggested that you run SILC under dedicated\n"
183        "\tuser account.  Modify the ServerInfo configuration section to run\n"
184        "\tthe server as non-root user.\n");
185       exit(1);
186     }
187
188     SILC_LOG_DEBUG(("Changing to group %s (gid=%u)", group, gr->gr_gid));
189     if (setgid(gr->gr_gid) != 0) {
190       fprintf(stderr, "Error: Failed setgid() to %s (gid=%u). Exiting.\n",
191               group, gr->gr_gid);
192       exit(1);
193     }
194 #if defined HAVE_SETGROUPS && defined HAVE_INITGROUPS
195     SILC_LOG_DEBUG(("Removing supplementary groups"));
196     if (setgroups(0, NULL) != 0) {
197       fprintf(stderr, "Error: Failed setgroups() to NULL. Exiting.\n");
198       exit(1);
199     }
200     SILC_LOG_DEBUG(("Setting supplementary groups for user %s", user));
201     if (initgroups(user, gr->gr_gid) != 0) {
202       fprintf(stderr, "Error: Failed initgroups() for user %s (gid=%u). "
203               "Exiting.\n", user, gr->gr_gid);
204       exit(1);
205     }
206 #endif
207     SILC_LOG_DEBUG(("Changing to user %s (uid=%u)", user, pw->pw_uid));
208     if (setuid(pw->pw_uid) != 0) {
209       fprintf(stderr, "Error: Failed to setuid() to %s (gid=%u). Exiting.\n",
210               user, pw->pw_uid);
211       exit(1);
212     }
213   }
214 }
215
216 /* Fork server to background */
217
218 static void silc_server_daemonise(SilcServer server)
219 {
220   int i;
221
222   SILC_LOG_DEBUG(("Forking SILC server to background"));
223
224   if ((i = fork()) < 0) {
225     fprintf(stderr, "Error: fork() failed: %s\n", strerror(errno));
226     exit(1);
227   }
228
229   if (i) /* Kill the parent */
230     exit(0);
231
232   server->background = TRUE;
233   setsid();
234
235   /* XXX close stdin, stdout, stderr -- before this, check that all writes
236      to stderr are changed to SILC_SERVER_LOG_ERROR() */
237 }
238
239 static void signal_handler(int sig)
240 {
241   /* Mark the signal to be caller after this signal is over. */
242   silc_schedule_signal_call(silcd->schedule, sig);
243 }
244
245 SILC_TASK_CALLBACK(got_hup)
246 {
247   /* First, reset all log files (they might have been deleted) */
248   silc_log_reset_all();
249
250   /* Rehash the configuration file */
251   silc_server_rehash(silcd);
252 }
253
254 SILC_TASK_CALLBACK(stop_server)
255 {
256   /* Stop scheduler, the program will stop eventually after noticing
257      that the scheduler is down. */
258   silc_schedule_stop(silcd->schedule);
259 }
260
261 /* Dump server statistics into a file into /tmp directory */
262
263 SILC_TASK_CALLBACK(dump_stats)
264 {
265   FILE *fdd;
266   char filename[256];
267
268   memset(filename, 0, sizeof(filename));
269   snprintf(filename, sizeof(filename) - 1, "/tmp/silcd.%d.stats", getpid());
270   fdd = fopen(filename, "w+");
271   if (!fdd)
272     return;
273
274 #define STAT_OUTPUT(fmt, stat) fprintf(fdd, fmt "\n", (int)stat);
275
276   fprintf(fdd, "SILC Server %s Statistics\n\n", silcd->server_name);
277   fprintf(fdd, "Local Stats:\n");
278   STAT_OUTPUT("  My clients              : %d", silcd->stat.my_clients);
279   STAT_OUTPUT("  My servers              : %d", silcd->stat.my_servers);
280   STAT_OUTPUT("  My routers              : %d", silcd->stat.my_routers);
281   STAT_OUTPUT("  My channels             : %d", silcd->stat.my_channels);
282   STAT_OUTPUT("  My joined users         : %d", silcd->stat.my_chanclients);
283   STAT_OUTPUT("  My aways                : %d", silcd->stat.my_aways);
284   STAT_OUTPUT("  My detached clients     : %d", silcd->stat.my_detached);
285   STAT_OUTPUT("  My server operators     : %d", silcd->stat.my_server_ops);
286   STAT_OUTPUT("  My router operators     : %d", silcd->stat.my_router_ops);
287   fprintf(fdd, "\nGlobal Stats:\n");
288   STAT_OUTPUT("  Cell clients            : %d", silcd->stat.cell_clients);
289   STAT_OUTPUT("  Cell servers            : %d", silcd->stat.cell_servers);
290   STAT_OUTPUT("  Cell channels           : %d", silcd->stat.cell_channels);
291   STAT_OUTPUT("  Cell joined users       : %d", silcd->stat.cell_chanclients);
292   STAT_OUTPUT("  All clients             : %d", silcd->stat.clients);
293   STAT_OUTPUT("  All servers             : %d", silcd->stat.servers);
294   STAT_OUTPUT("  All routers             : %d", silcd->stat.routers);
295   STAT_OUTPUT("  All channels            : %d", silcd->stat.channels);
296   STAT_OUTPUT("  All joined users        : %d", silcd->stat.chanclients);
297   STAT_OUTPUT("  All aways               : %d", silcd->stat.aways);
298   STAT_OUTPUT("  All detached clients    : %d", silcd->stat.detached);
299   STAT_OUTPUT("  All server operators    : %d", silcd->stat.server_ops);
300   STAT_OUTPUT("  All router operators    : %d", silcd->stat.router_ops);
301   fprintf(fdd, "\nGeneral Stats:\n");
302   STAT_OUTPUT("  Connection attempts     : %d", silcd->stat.conn_attempts);
303   STAT_OUTPUT("  Connection failures     : %d", silcd->stat.conn_failures);
304   STAT_OUTPUT("  Authentication attempts : %d", silcd->stat.auth_attempts);
305   STAT_OUTPUT("  Authentication failures : %d", silcd->stat.auth_failures);
306   STAT_OUTPUT("  Packets sent            : %d", silcd->stat.packets_sent);
307   STAT_OUTPUT("  Packets received        : %d", silcd->stat.packets_received);
308
309   fflush(fdd);
310   fclose(fdd);
311 }
312
313 /* This function should not be called directly but throught the wrapper
314    macro SILC_SERVER_LOG_STDERR() */
315
316 void silc_server_stderr(char *message)
317 {
318   if (silcd->background)
319     silc_log_output(SILC_LOG_ERROR, message);
320   else {
321     fprintf(stderr, "%s", message);
322     silc_free(message);
323   }
324 }
325
326 int main(int argc, char **argv)
327 {
328   int ret, opt, option_index;
329   bool foreground = FALSE;
330   bool opt_create_keypair = FALSE;
331   char *silcd_config_file = NULL;
332   struct sigaction sa;
333
334   /* Parse command line arguments */
335   if (argc > 1) {
336     while ((opt = getopt_long(argc, argv, "f:p:d:xhFVC:",
337                               long_opts, &option_index)) != EOF) {
338       switch(opt) {
339         case 'h':
340           silc_usage();
341           break;
342         case 'V':
343           printf("SILCd Secure Internet Live Conferencing daemon, "
344                  "version %s (base: SILC Toolkit %s)\n",
345                  silc_dist_version, silc_version);
346           printf("(c) 1997 - 2002 Pekka Riikonen "
347                  "<priikone@silcnet.org>\n");
348           exit(0);
349           break;
350         case 'd':
351 #ifdef SILC_DEBUG
352           silc_debug = TRUE;
353           if (optarg)
354             silc_log_set_debug_string(optarg);
355           foreground = TRUE; /* implied */
356           silc_log_quick = TRUE; /* implied */
357 #else
358           fprintf(stderr,
359                   "Run-time debugging is not enabled. To enable it recompile\n"
360                   "the server with --enable-debug configuration option.\n");
361 #endif
362           break;
363         case 'x':
364 #ifdef SILC_DEBUG
365           silc_debug_hexdump = TRUE;
366           silc_debug = TRUE; /* implied */
367           foreground = TRUE; /* implied */
368           silc_log_quick = TRUE; /* implied */
369 #else
370           fprintf(stderr,
371                   "Run-time debugging is not enabled. To enable it recompile\n"
372                   "the server with --enable-debug configuration option.\n");
373 #endif
374           break;
375         case 'f':
376           silcd_config_file = strdup(optarg);
377           break;
378         case 'F':
379           foreground = TRUE;
380           break;
381
382           /*
383            * Key management options
384            */
385         case 'C':
386           opt_create_keypair = TRUE;
387           if (optarg)
388             opt_keypath = strdup(optarg);
389           break;
390         case 10:
391           if (optarg)
392             opt_pkcs = strdup(optarg);
393           break;
394         case 11:
395           if (optarg)
396             opt_bits = atoi(optarg);
397           break;
398         case 12:
399           if (optarg)
400             opt_identifier = strdup(optarg);
401           break;
402
403         default:
404           silc_usage();
405           break;
406       }
407     }
408   }
409
410   if (opt_create_keypair == TRUE) {
411     /* Create new key pair and exit */
412     silc_cipher_register_default();
413     silc_pkcs_register_default();
414     silc_hash_register_default();
415     silc_hmac_register_default();
416     silc_server_create_key_pair(opt_pkcs, opt_bits, opt_keypath,
417                                 opt_identifier, NULL, NULL);
418     exit(0);
419   }
420
421   /* Default configuration file */
422   if (!silcd_config_file)
423     silcd_config_file = strdup(SILC_SERVER_CONFIG_FILE);
424
425   /* Create SILC Server object */
426   ret = silc_server_alloc(&silcd);
427   if (ret == FALSE)
428     goto fail;
429
430   /* Read configuration files */
431   silcd->config = silc_server_config_alloc(silcd_config_file);
432   if (silcd->config == NULL)
433     goto fail;
434   silcd->config_file = silcd_config_file;
435
436   /* Check for another silcd running */
437   silc_server_checkpid(silcd);
438
439   /* Initialize the server */
440   if (silc_server_init(silcd) == FALSE)
441     goto fail;
442
443   /* Ignore SIGPIPE */
444   sa.sa_handler = SIG_IGN;
445   sa.sa_flags = 0;
446   sigemptyset(&sa.sa_mask);
447   sigaction(SIGPIPE, &sa, NULL);
448   sa.sa_handler = signal_handler;
449   sigaction(SIGHUP, &sa, NULL);
450   sigaction(SIGTERM, &sa, NULL);
451   sigaction(SIGINT, &sa, NULL);
452   sigaction(SIGUSR1, &sa, NULL);
453   silc_schedule_signal_register(silcd->schedule, SIGHUP, got_hup, NULL);
454   silc_schedule_signal_register(silcd->schedule, SIGTERM, stop_server, NULL);
455   silc_schedule_signal_register(silcd->schedule, SIGINT, stop_server, NULL);
456   silc_schedule_signal_register(silcd->schedule, SIGUSR1, dump_stats, NULL);
457
458   if (!foreground) {
459     /* Before running the server, fork to background. */
460     silc_server_daemonise(silcd);
461
462     /* If set, write pid to file */
463     if (silcd->config->server_info->pid_file) {
464       char buf[10], *pidfile = silcd->config->server_info->pid_file;
465       unlink(pidfile);
466       snprintf(buf, sizeof(buf) - 1, "%d\n", getpid());
467       silc_file_writefile(pidfile, buf, strlen(buf));
468     }
469   }
470
471   /* Drop root if we are not in debug mode, so you don't need to bother about
472      file writing permissions and so on */
473   if (!silc_debug)
474     silc_server_drop_privs(silcd);
475
476   /* Run the server. When this returns the server has been stopped
477      and we will exit. */
478   silc_server_run(silcd);
479
480   /* Stop the server and free it. */
481   silc_server_stop(silcd);
482   silc_server_config_destroy(silcd->config);
483   silc_server_free(silcd);
484
485   /* Flush the logging system */
486   silc_log_flush_all();
487
488   silc_free(silcd_config_file);
489   silc_free(opt_identifier);
490   silc_free(opt_keypath);
491   exit(0);
492
493  fail:
494   silc_free(silcd_config_file);
495   silc_free(opt_identifier);
496   silc_free(opt_keypath);
497   exit(1);
498 }
499
500 /* Returns identifier string for public key generation. */
501
502 static char *silc_server_create_identifier(void)
503 {
504   char *username = NULL, *realname = NULL;
505   char hostname[256], email[256];
506
507   /* Get realname */
508   realname = silc_get_real_name();
509
510   /* Get hostname */
511   memset(hostname, 0, sizeof(hostname));
512   gethostname(hostname, sizeof(hostname));
513
514   /* Get username (mandatory) */
515   username = silc_get_username();
516   if (!username)
517     return NULL;
518
519   /* Create default email address, whether it is right or not */
520   snprintf(email, sizeof(email), "%s@%s", username, hostname);
521
522   return silc_pkcs_encode_identifier(username, hostname, realname, email,
523                                      NULL, NULL);
524 }
525
526 /* Creates new public key and private key pair. This is used only
527    when user wants to create new key pair from command line. */
528
529 static int
530 silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
531                             char *identifier,
532                             SilcPublicKey *ret_pub_key,
533                             SilcPrivateKey *ret_prv_key)
534 {
535   SilcPKCS pkcs;
536   SilcPublicKey pub_key;
537   SilcPrivateKey prv_key;
538   SilcRng rng;
539   unsigned char *key;
540   SilcUInt32 key_len;
541   char pkfile[256], prvfile[256];
542
543   if (!pkcs_name || !path)
544     return FALSE;
545
546   if (!silc_pkcs_is_supported(pkcs_name)) {
547     fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
548     return FALSE;
549   }
550
551   if (!bits)
552     bits = 2048;
553
554   if (!identifier)
555     identifier = silc_server_create_identifier();
556
557   rng = silc_rng_alloc();
558   silc_rng_init(rng);
559   silc_rng_global_init(rng);
560
561   snprintf(pkfile, sizeof(pkfile) - 1, "%s%s", path,
562            SILC_SERVER_PUBLIC_KEY_NAME);
563   snprintf(prvfile, sizeof(prvfile) - 1, "%s%s", path,
564            SILC_SERVER_PRIVATE_KEY_NAME);
565
566   /* Generate keys */
567   silc_pkcs_alloc(pkcs_name, &pkcs);
568   silc_pkcs_generate_key(pkcs, bits, rng);
569
570   /* Save public key into file */
571   key = silc_pkcs_get_public_key(pkcs, &key_len);
572   pub_key = silc_pkcs_public_key_alloc(silc_pkcs_get_name(pkcs), identifier,
573                                        key, key_len);
574   silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
575   if (ret_pub_key)
576     *ret_pub_key = pub_key;
577   else
578     silc_pkcs_public_key_free(pub_key);
579
580   memset(key, 0, sizeof(key_len));
581   silc_free(key);
582
583   /* Save private key into file */
584   key = silc_pkcs_get_private_key(pkcs, &key_len);
585   prv_key = silc_pkcs_private_key_alloc(silc_pkcs_get_name(pkcs),
586                                         key, key_len);
587   silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
588   if (ret_prv_key)
589     *ret_prv_key = prv_key;
590   else
591     silc_pkcs_private_key_free(prv_key);
592
593   printf("Public key has been saved into `%s'\n", pkfile);
594   printf("Private key has been saved into `%s'\n", prvfile);
595
596   memset(key, 0, sizeof(key_len));
597   silc_free(key);
598
599   silc_rng_free(rng);
600   silc_pkcs_free(pkcs);
601
602   return TRUE;
603 }