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_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   /* 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   /* Drop root if we are not in debug mode, so you don't need to bother about
406      file writing permissions and so on */
407   if (!silc_debug)
408     silc_server_drop_privs(silcd);
409
410   if (!foreground) {
411     /* Before running the server, fork to background. */
412     silc_server_daemonise(silcd);
413
414     /* If set, write pid to file */
415     if (silcd->config->server_info->pid_file) {
416       char buf[10], *pidfile = silcd->config->server_info->pid_file;
417       unlink(pidfile);
418       snprintf(buf, sizeof(buf) - 1, "%d\n", getpid());
419       silc_file_writefile(pidfile, buf, strlen(buf));
420     }
421   }
422
423   /* Run the server. When this returns the server has been stopped
424      and we will exit. */
425   silc_server_run(silcd);
426
427   /* Stop the server and free it. */
428   silc_server_stop(silcd);
429   silc_server_config_destroy(silcd->config);
430   silc_server_free(silcd);
431
432   /* Flush the logging system */
433   silc_log_flush_all();
434
435   exit(0);
436  fail:
437   exit(1);
438 }
439
440 /* Returns identifier string for public key generation. */
441
442 static char *silc_server_create_identifier(void)
443 {
444   char *username = NULL, *realname = NULL;
445   char hostname[256], email[256];
446
447   /* Get realname */
448   realname = silc_get_real_name();
449
450   /* Get hostname */
451   memset(hostname, 0, sizeof(hostname));
452   gethostname(hostname, sizeof(hostname));
453
454   /* Get username (mandatory) */
455   username = silc_get_username();
456   if (!username)
457     return NULL;
458
459   /* Create default email address, whether it is right or not */
460   snprintf(email, sizeof(email), "%s@%s", username, hostname);
461
462   return silc_pkcs_encode_identifier(username, hostname, realname, email,
463                                      NULL, NULL);
464 }
465
466 /* Creates new public key and private key pair. This is used only
467    when user wants to create new key pair from command line. */
468
469 static int
470 silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
471                             char *identifier,
472                             SilcPublicKey *ret_pub_key,
473                             SilcPrivateKey *ret_prv_key)
474 {
475   SilcPKCS pkcs;
476   SilcPublicKey pub_key;
477   SilcPrivateKey prv_key;
478   SilcRng rng;
479   unsigned char *key;
480   SilcUInt32 key_len;
481   char pkfile[256], prvfile[256];
482
483   if (!pkcs_name || !path)
484     return FALSE;
485
486   if (!silc_pkcs_is_supported(pkcs_name)) {
487     fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
488     return FALSE;
489   }
490
491   if (!bits)
492     bits = 1024;
493
494   if (!identifier)
495     identifier = silc_server_create_identifier();
496
497   rng = silc_rng_alloc();
498   silc_rng_init(rng);
499   silc_rng_global_init(rng);
500
501   snprintf(pkfile, sizeof(pkfile) - 1, "%s%s", path,
502            SILC_SERVER_PUBLIC_KEY_NAME);
503   snprintf(prvfile, sizeof(prvfile) - 1, "%s%s", path,
504            SILC_SERVER_PRIVATE_KEY_NAME);
505
506   /* Generate keys */
507   silc_pkcs_alloc(pkcs_name, &pkcs);
508   silc_pkcs_generate_key(pkcs, bits, rng);
509
510   /* Save public key into file */
511   key = silc_pkcs_get_public_key(pkcs, &key_len);
512   pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
513                                        key, key_len);
514   silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
515   if (ret_pub_key)
516     *ret_pub_key = pub_key;
517   else
518     silc_pkcs_public_key_free(pub_key);
519
520   memset(key, 0, sizeof(key_len));
521   silc_free(key);
522
523   /* Save private key into file */
524   key = silc_pkcs_get_private_key(pkcs, &key_len);
525   prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
526   silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
527   if (ret_prv_key)
528     *ret_prv_key = prv_key;
529   else
530     silc_pkcs_private_key_free(prv_key);
531
532   printf("Public key has been saved into `%s'\n", pkfile);
533   printf("Private key has been saved into `%s'\n", prvfile);
534
535   memset(key, 0, sizeof(key_len));
536   silc_free(key);
537
538   silc_rng_free(rng);
539   silc_pkcs_free(pkcs);
540
541   return TRUE;
542 }