updates.
[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 = 1024;
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(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   /* XXX this may be redundant with the silc_server_config_setlogfiles() call.
249    * merge these two with the appropriate checking. */
250   silc_log_reset_all();
251   /* Rehash the configuration file */
252   silc_server_rehash(silcd);
253 }
254
255 SILC_TASK_CALLBACK(stop_server)
256 {
257   /* Stop scheduler, the program will stop eventually after noticing
258      that the scheduler is down. */
259   silc_schedule_stop(silcd->schedule);
260 }
261
262 /* This function should not be called directly but throught the wrapper
263    macro SILC_SERVER_LOG_STDERR() */
264
265 void silc_server_stderr(char *message)
266 {
267   if (silcd->background)
268     silc_log_output(SILC_LOG_ERROR, message);
269   else {
270     fprintf(stderr, "%s", message);
271     silc_free(message);
272   }
273 }
274
275 int main(int argc, char **argv)
276 {
277   int ret, opt, option_index;
278   bool foreground = FALSE;
279   bool opt_create_keypair = FALSE;
280   char *silcd_config_file = NULL;
281   struct sigaction sa;
282
283   /* Parse command line arguments */
284   if (argc > 1) {
285     while ((opt = getopt_long(argc, argv, "f:p:d::xhFVC:",
286                               long_opts, &option_index)) != EOF) {
287       switch(opt) {
288         case 'h':
289           silc_usage();
290           break;
291         case 'V':
292           printf("SILCd Secure Internet Live Conferencing daemon, "
293                  "version %s (base: SILC Toolkit %s)\n",
294                  silc_dist_version, silc_version);
295           printf("(c) 1997 - 2002 Pekka Riikonen "
296                  "<priikone@silcnet.org>\n");
297           exit(0);
298           break;
299         case 'd':
300 #ifdef SILC_DEBUG
301           silc_debug = TRUE;
302           if (optarg)
303             silc_log_set_debug_string(optarg);
304           foreground = TRUE; /* implied */
305           silc_log_quick = TRUE; /* implied */
306 #else
307           fprintf(stderr,
308                   "Run-time debugging is not enabled. To enable it recompile\n"
309                   "the server with --enable-debug configuration option.\n");
310 #endif
311           break;
312         case 'x':
313 #ifdef SILC_DEBUG
314           silc_debug_hexdump = TRUE;
315           silc_debug = TRUE; /* implied */
316           foreground = TRUE; /* implied */
317           silc_log_quick = TRUE; /* implied */
318 #else
319           fprintf(stderr,
320                   "Run-time debugging is not enabled. To enable it recompile\n"
321                   "the server with --enable-debug configuration option.\n");
322 #endif
323           break;
324         case 'f':
325           silcd_config_file = strdup(optarg);
326           break;
327         case 'F':
328           foreground = TRUE;
329           break;
330
331           /*
332            * Key management options
333            */
334         case 'C':
335           opt_create_keypair = TRUE;
336           if (optarg)
337             opt_keypath = strdup(optarg);
338           break;
339         case 10:
340           if (optarg)
341             opt_pkcs = strdup(optarg);
342           break;
343         case 11:
344           if (optarg)
345             opt_bits = atoi(optarg);
346           break;
347         case 12:
348           if (optarg)
349             opt_identifier = strdup(optarg);
350           break;
351
352         default:
353           silc_usage();
354           break;
355       }
356     }
357   }
358
359   if (opt_create_keypair == TRUE) {
360     /* Create new key pair and exit */
361     silc_cipher_register_default();
362     silc_pkcs_register_default();
363     silc_hash_register_default();
364     silc_hmac_register_default();
365     silc_server_create_key_pair(opt_pkcs, opt_bits, opt_keypath,
366                                 opt_identifier, NULL, NULL);
367     exit(0);
368   }
369
370   /* Default configuration file */
371   if (!silcd_config_file)
372     silcd_config_file = strdup(SILC_SERVER_CONFIG_FILE);
373
374   /* Create SILC Server object */
375   ret = silc_server_alloc(&silcd);
376   if (ret == FALSE)
377     goto fail;
378
379   /* Read configuration files */
380   silcd->config = silc_server_config_alloc(silcd_config_file);
381   if (silcd->config == NULL)
382     goto fail;
383   silcd->config_file = silcd_config_file;
384
385   /* Check for another silcd running */
386   silc_server_checkpid(silcd);
387
388   /* Initialize the server */
389   if (silc_server_init(silcd) == FALSE)
390     goto fail;
391
392   /* Ignore SIGPIPE */
393   sa.sa_handler = SIG_IGN;
394   sa.sa_flags = 0;
395   sigemptyset(&sa.sa_mask);
396   sigaction(SIGPIPE, &sa, NULL);
397   sa.sa_handler = signal_handler;
398   sigaction(SIGHUP, &sa, NULL);
399   sigaction(SIGTERM, &sa, NULL);
400   sigaction(SIGINT, &sa, NULL);
401   silc_schedule_signal_register(silcd->schedule, SIGHUP, got_hup, NULL);
402   silc_schedule_signal_register(silcd->schedule, SIGTERM, stop_server, NULL);
403   silc_schedule_signal_register(silcd->schedule, SIGINT, stop_server, NULL);
404
405   if (!foreground) {
406     /* Drop root. */
407     silc_server_drop(silcd);
408
409     /* Before running the server, fork to background. */
410     silc_server_daemonise(silcd);
411
412     /* If set, write pid to file */
413     if (silcd->config->server_info->pid_file) {
414       char buf[10], *pidfile = silcd->config->server_info->pid_file;
415       unlink(pidfile);
416       snprintf(buf, sizeof(buf) - 1, "%d\n", getpid());
417       silc_file_writefile(pidfile, buf, strlen(buf));
418     }
419   }
420
421   /* Run the server. When this returns the server has been stopped
422      and we will exit. */
423   silc_server_run(silcd);
424
425   /* Stop the server and free it. */
426   silc_server_stop(silcd);
427   silc_server_free(silcd);
428   silc_server_config_destroy(silcd->config);
429
430   /* Flush the logging system */
431   silc_log_flush_all();
432
433   exit(0);
434  fail:
435   exit(1);
436 }
437
438 /* Returns identifier string for public key generation. */
439
440 static char *silc_server_create_identifier(void)
441 {
442   char *username = NULL, *realname = NULL;
443   char hostname[256], email[256];
444
445   /* Get realname */
446   realname = silc_get_real_name();
447
448   /* Get hostname */
449   memset(hostname, 0, sizeof(hostname));
450   gethostname(hostname, sizeof(hostname));
451
452   /* Get username (mandatory) */
453   username = silc_get_username();
454   if (!username)
455     return NULL;
456
457   /* Create default email address, whether it is right or not */
458   snprintf(email, sizeof(email), "%s@%s", username, hostname);
459
460   return silc_pkcs_encode_identifier(username, hostname, realname, email,
461                                      NULL, NULL);
462 }
463
464 /* Creates new public key and private key pair. This is used only
465    when user wants to create new key pair from command line. */
466
467 static int
468 silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
469                             char *identifier,
470                             SilcPublicKey *ret_pub_key,
471                             SilcPrivateKey *ret_prv_key)
472 {
473   SilcPKCS pkcs;
474   SilcPublicKey pub_key;
475   SilcPrivateKey prv_key;
476   SilcRng rng;
477   unsigned char *key;
478   SilcUInt32 key_len;
479   char pkfile[256], prvfile[256];
480
481   if (!pkcs_name || !path)
482     return FALSE;
483
484   if (!silc_pkcs_is_supported(pkcs_name)) {
485     fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
486     return FALSE;
487   }
488
489   if (!bits)
490     bits = 1024;
491
492   if (!identifier)
493     identifier = silc_server_create_identifier();
494
495   rng = silc_rng_alloc();
496   silc_rng_init(rng);
497   silc_rng_global_init(rng);
498
499   snprintf(pkfile, sizeof(pkfile) - 1, "%s%s", path,
500            SILC_SERVER_PUBLIC_KEY_NAME);
501   snprintf(prvfile, sizeof(prvfile) - 1, "%s%s", path,
502            SILC_SERVER_PRIVATE_KEY_NAME);
503
504   /* Generate keys */
505   silc_pkcs_alloc(pkcs_name, &pkcs);
506   silc_pkcs_generate_key(pkcs, bits, rng);
507
508   /* Save public key into file */
509   key = silc_pkcs_get_public_key(pkcs, &key_len);
510   pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
511                                        key, key_len);
512   silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
513   if (ret_pub_key)
514     *ret_pub_key = pub_key;
515   else
516     silc_pkcs_public_key_free(pub_key);
517
518   memset(key, 0, sizeof(key_len));
519   silc_free(key);
520
521   /* Save private key into file */
522   key = silc_pkcs_get_private_key(pkcs, &key_len);
523   prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
524   silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
525   if (ret_prv_key)
526     *ret_prv_key = prv_key;
527   else
528     silc_pkcs_private_key_free(prv_key);
529
530   printf("Public key has been saved into `%s'\n", pkfile);
531   printf("Private key has been saved into `%s'\n", prvfile);
532
533   memset(key, 0, sizeof(key_len));
534   silc_free(key);
535
536   silc_rng_free(rng);
537   silc_pkcs_free(pkcs);
538
539   return TRUE;
540 }