Applied silc-rehash-plus-misc-stuff.patch.
[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 bool opt_create_keypair = FALSE;
64 static char *opt_keypath = NULL;
65 static char *opt_pkcs = "rsa";
66 static char *opt_identifier = NULL;
67 static int opt_bits = 1024;
68
69 /* Prints out the usage of silc client */
70
71 static void silc_usage(void)
72 {
73   printf(""
74 "Usage: silcd [options]\n"
75 "\n"
76 "  Generic Options:\n"
77 "  -f  --config-file=FILE        Alternate configuration file\n"
78 "  -d  --debug=string            Enable debugging (Implies --foreground)\n"
79 "  -x  --hexdump                 Enable hexdumps (Implies --debug)\n"
80 "  -h  --help                    Display this message\n"
81 "  -F  --foreground              Dont fork\n"
82 "  -V  --version                 Display version\n"
83 "\n"
84 "  Key Management Options:\n"
85 "  -C, --create-key-pair=PATH    Create new public key pair\n"
86 "      --pkcs=PKCS               Set the PKCS of the public key pair\n"
87 "      --bits=VALUE              Set length of the public key pair\n"
88 "      --identifier=IDENTIFIER   Public key identifier\n"
89 "\n"
90 "      The public key identifier may be of the following format:\n"
91 "\n"
92 "      UN=<username>, HN=<hostname or IP>, RN=<real name>, E=<email>,\n"
93 "      O=<organization>, C=<country>\n"
94 "\n"
95 "      The UN and HN must be provided, the others are optional.  If the\n"
96 "      --identifier option is not used an identifier will be created for\n"
97 "      the public key automatically.\n"
98 "\n"
99 "      Example identifier: \"UN=foobar, HN=foo.bar.com, RN=Foo T. Bar, \n"
100 "                           E=foo@bar.com, C=FI\"\n"
101 "\n");
102   exit(0);
103 }
104
105 /* Dies if a *valid* pid file exists already */
106
107 static void silc_server_checkpid(SilcServer silcd)
108 {
109   if (silcd->config->server_info->pid_file) {
110     int oldpid;
111     char *buf;
112     SilcUInt32 buf_len;
113
114     SILC_LOG_DEBUG(("Checking for another silcd running"));
115     buf = silc_file_readfile(silcd->config->server_info->pid_file, &buf_len);
116     if (!buf)
117       return;
118     oldpid = atoi(buf);
119     silc_free(buf);
120     if (oldpid <= 0)
121       return;
122     kill(oldpid, SIGCHLD); /* this signal does nothing, check if alive */
123     if (errno != ESRCH) {
124       fprintf(stderr, "\nI detected another daemon running with the "
125               "same pid file.\n");
126       fprintf(stderr, "Please change the config file, or erase the %s\n",
127         silcd->config->server_info->pid_file);
128       exit(1);
129     }
130   }
131 }
132
133 static void signal_handler(int sig)
134 {
135   /* Mark the signal to be caller after this signal is over. */
136   silc_schedule_signal_call(silcd->schedule, sig);
137 }
138
139 SILC_TASK_CALLBACK(got_hup)
140 {
141   /* First, reset all log files (they might have been deleted) */
142   /* XXX this may be redundant with the silc_server_config_setlogfiles() call.
143    * merge these two with the appropriate checking. */
144   silc_log_reset_all();
145   /* Rehash the configuration file */
146   silc_server_rehash(silcd);
147 }
148
149 SILC_TASK_CALLBACK(stop_server)
150 {
151   /* Stop scheduler, the program will stop eventually after noticing
152      that the scheduler is down. */
153   silc_schedule_stop(silcd->schedule);
154 }
155
156 void silc_server_stderr(char *message)
157 {
158   if (silcd->background)
159     silc_log_output(SILC_LOG_ERROR, message);
160   else {
161     fprintf(stderr, "%s", message);
162     silc_free(message);
163   }
164 }
165
166 int main(int argc, char **argv)
167 {
168   int ret, opt, option_index;
169   bool foreground = FALSE;
170   char *silcd_config_file = NULL;
171   struct sigaction sa;
172
173   /* Parse command line arguments */
174   if (argc > 1) {
175     while ((opt = getopt_long(argc, argv, "f:p:d::xhFVC:",
176                               long_opts, &option_index)) != EOF) {
177       switch(opt)
178         {
179         case 'h':
180           silc_usage();
181           break;
182         case 'V':
183           printf("SILCd Secure Internet Live Conferencing daemon, "
184                  "version %s (base: SILC Toolkit %s)\n",
185                  silc_dist_version, silc_version);
186           printf("(c) 1997 - 2002 Pekka Riikonen "
187                  "<priikone@silcnet.org>\n");
188           exit(0);
189           break;
190         case 'd':
191 #ifdef SILC_DEBUG
192           silc_debug = TRUE;
193           if (optarg)
194             silc_log_set_debug_string(optarg);
195           foreground = TRUE; /* implied */
196           silc_log_quick = TRUE; /* implied */
197 #else
198           fprintf(stderr,
199                   "Run-time debugging is not enabled. To enable it recompile\n"
200                   "the server with --enable-debug configuration option.\n");
201 #endif
202           break;
203         case 'x':
204 #ifdef SILC_DEBUG
205           silc_debug_hexdump = TRUE;
206           silc_debug = TRUE; /* implied */
207           foreground = TRUE; /* implied */
208           silc_log_quick = TRUE; /* implied */
209 #else
210           fprintf(stderr,
211                   "Run-time debugging is not enabled. To enable it recompile\n"
212                   "the server with --enable-debug configuration option.\n");
213 #endif
214           break;
215         case 'f':
216           silcd_config_file = strdup(optarg);
217           break;
218         case 'F':
219           foreground = TRUE;
220           break;
221
222           /*
223            * Key management options
224            */
225         case 'C':
226           opt_create_keypair = TRUE;
227           if (optarg)
228             opt_keypath = strdup(optarg);
229           break;
230         case 10:
231           if (optarg)
232             opt_pkcs = strdup(optarg);
233           break;
234         case 11:
235           if (optarg)
236             opt_bits = atoi(optarg);
237           break;
238         case 12:
239           if (optarg)
240             opt_identifier = strdup(optarg);
241           break;
242
243         default:
244           silc_usage();
245           break;
246         }
247     }
248   }
249
250   if (opt_create_keypair == TRUE) {
251     /* Create new key pair and exit */
252     silc_cipher_register_default();
253     silc_pkcs_register_default();
254     silc_hash_register_default();
255     silc_hmac_register_default();
256     silc_server_create_key_pair(opt_pkcs, opt_bits, opt_keypath,
257                                 opt_identifier, NULL, NULL);
258     exit(0);
259   }
260
261   /* Default configuration file */
262   if (!silcd_config_file)
263     silcd_config_file = strdup(SILC_SERVER_CONFIG_FILE);
264
265   /* Create SILC Server object */
266   ret = silc_server_alloc(&silcd);
267   if (ret == FALSE)
268     goto fail;
269
270   /* Read configuration files */
271   silcd->config = silc_server_config_alloc(silcd_config_file);
272   if (silcd->config == NULL)
273     goto fail;
274   silc_server_config_ref(&silcd->config_ref, silcd->config,
275                          (void *)silcd->config);
276   silcd->config_file = silcd_config_file;
277
278   /* Check for another silcd running */
279   silc_server_checkpid(silcd);
280
281   /* Initialize the server */
282   if (silc_server_init(silcd) == FALSE)
283     goto fail;
284
285   /* Ignore SIGPIPE */
286   sa.sa_handler = SIG_IGN;
287   sa.sa_flags = 0;
288   sigemptyset(&sa.sa_mask);
289   sigaction(SIGPIPE, &sa, NULL);
290   sa.sa_handler = signal_handler;
291   sigaction(SIGHUP, &sa, NULL);
292   sigaction(SIGTERM, &sa, NULL);
293   sigaction(SIGINT, &sa, NULL);
294   silc_schedule_signal_register(silcd->schedule, SIGHUP, got_hup, NULL);
295   silc_schedule_signal_register(silcd->schedule, SIGTERM, stop_server, NULL);
296   silc_schedule_signal_register(silcd->schedule, SIGINT, stop_server, NULL);
297
298   if (!foreground) {
299     /* Drop root. */
300     silc_server_drop(silcd);
301
302     /* Before running the server, fork to background. */
303     silc_server_daemonise(silcd);
304
305     /* If set, write pid to file */
306     if (silcd->config->server_info->pid_file) {
307       char buf[10], *pidfile = silcd->config->server_info->pid_file;
308       unlink(pidfile);
309       snprintf(buf, sizeof(buf) - 1, "%d\n", getpid());
310       silc_file_writefile(pidfile, buf, strlen(buf));
311     }
312   }
313
314   /* Run the server. When this returns the server has been stopped
315      and we will exit. */
316   silc_server_run(silcd);
317
318   /* Stop the server and free it. */
319   silc_server_stop(silcd);
320   silc_server_free(silcd);
321
322   /* Flush the logging system */
323   silc_log_flush_all();
324
325   exit(0);
326  fail:
327   exit(1);
328 }
329
330 /* Returns identifier string for public key generation. */
331
332 static char *silc_server_create_identifier(void)
333 {
334   char *username = NULL, *realname = NULL;
335   char hostname[256], email[256];
336
337   /* Get realname */
338   realname = silc_get_real_name();
339
340   /* Get hostname */
341   memset(hostname, 0, sizeof(hostname));
342   gethostname(hostname, sizeof(hostname));
343
344   /* Get username (mandatory) */
345   username = silc_get_username();
346   if (!username)
347     return NULL;
348
349   /* Create default email address, whether it is right or not */
350   snprintf(email, sizeof(email), "%s@%s", username, hostname);
351
352   return silc_pkcs_encode_identifier(username, hostname, realname, email,
353                                      NULL, NULL);
354 }
355
356 /* Creates new public key and private key pair. This is used only
357    when user wants to create new key pair from command line. */
358
359 static int
360 silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
361                             char *identifier,
362                             SilcPublicKey *ret_pub_key,
363                             SilcPrivateKey *ret_prv_key)
364 {
365   SilcPKCS pkcs;
366   SilcPublicKey pub_key;
367   SilcPrivateKey prv_key;
368   SilcRng rng;
369   unsigned char *key;
370   SilcUInt32 key_len;
371   char pkfile[256], prvfile[256];
372
373   if (!pkcs_name || !path)
374     return FALSE;
375
376   if (!silc_pkcs_is_supported(pkcs_name)) {
377     fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
378     return FALSE;
379   }
380
381   if (!bits)
382     bits = 1024;
383
384   if (!identifier)
385     identifier = silc_server_create_identifier();
386
387   rng = silc_rng_alloc();
388   silc_rng_init(rng);
389   silc_rng_global_init(rng);
390
391   snprintf(pkfile, sizeof(pkfile) - 1, "%s%s", path,
392            SILC_SERVER_PUBLIC_KEY_NAME);
393   snprintf(prvfile, sizeof(prvfile) - 1, "%s%s", path,
394            SILC_SERVER_PRIVATE_KEY_NAME);
395
396   /* Generate keys */
397   silc_pkcs_alloc(pkcs_name, &pkcs);
398   silc_pkcs_generate_key(pkcs, bits, rng);
399
400   /* Save public key into file */
401   key = silc_pkcs_get_public_key(pkcs, &key_len);
402   pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
403                                        key, key_len);
404   silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
405   if (ret_pub_key)
406     *ret_pub_key = pub_key;
407   else
408     silc_pkcs_public_key_free(pub_key);
409
410   memset(key, 0, sizeof(key_len));
411   silc_free(key);
412
413   /* Save private key into file */
414   key = silc_pkcs_get_private_key(pkcs, &key_len);
415   prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
416   silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
417   if (ret_prv_key)
418     *ret_prv_key = prv_key;
419   else
420     silc_pkcs_private_key_free(prv_key);
421
422   printf("Public key has been saved into `%s'\n", pkfile);
423   printf("Private key has been saved into `%s'\n", prvfile);
424
425   memset(key, 0, sizeof(key_len));
426   silc_free(key);
427
428   silc_rng_free(rng);
429   silc_pkcs_free(pkcs);
430
431   return TRUE;
432 }