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
38 silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
39                             char *identifier,
40                             SilcPublicKey *ret_pub_key,
41                             SilcPrivateKey *ret_prv_key);
42
43 /* Long command line options */
44 static struct option long_opts[] =
45 {
46   { "config-file", 1, NULL, 'f' },
47   { "passphrase", 1, NULL, 'p' },
48   { "debug", 1, NULL, 'd' },
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   -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 /* Dies 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 static void signal_handler(int sig)
133 {
134   /* Mark the signal to be caller after this signal is over. */
135   silc_schedule_signal_call(silcd->schedule, sig);
136 }
137
138 SILC_TASK_CALLBACK(got_hup)
139 {
140   /* First, reset all log files (they might have been deleted) */
141   silc_log_reset_all();
142   silc_log_flush_all();
143 }
144
145 SILC_TASK_CALLBACK(stop_server)
146 {
147   /* Stop scheduler, the program will stop eventually after noticing
148      that the scheduler is down. */
149   silc_schedule_stop(silcd->schedule); 
150 }
151
152 int main(int argc, char **argv)
153 {
154   int ret, opt, option_index;
155   char *config_file = NULL;
156   bool foreground = FALSE;
157   struct sigaction sa;
158
159   /* Parse command line arguments */
160   if (argc > 1) {
161     while ((opt = getopt_long(argc, argv, "f:d:hFVC:",
162                               long_opts, &option_index)) != EOF) {
163       switch(opt)
164         {
165         case 'h':
166           silc_usage();
167           break;
168         case 'V':
169           printf("SILCd Secure Internet Live Conferencing daemon, "
170                  "version %s (base: SILC Toolkit %s)\n",
171                  silc_dist_version, silc_version);
172           printf("(c) 1997 - 2002 Pekka Riikonen "
173                  "<priikone@silcnet.org>\n");
174           exit(0);
175           break;
176         case 'd':
177 #ifdef SILC_DEBUG
178           silc_debug = TRUE;
179           silc_debug_hexdump = TRUE;
180           silc_log_set_debug_string(optarg);
181           foreground = TRUE;
182           silc_log_quick = TRUE;
183 #else
184           fprintf(stdout,
185                   "Run-time debugging is not enabled. To enable it recompile\n"
186                   "the server with --enable-debug configuration option.\n");
187 #endif
188           break;
189         case 'f':
190           config_file = strdup(optarg);
191           break;
192         case 'F':
193           foreground = TRUE;
194           break;
195
196           /*
197            * Key management options
198            */
199         case 'C':
200           opt_create_keypair = TRUE;
201           if (optarg)
202             opt_keypath = strdup(optarg);
203           break;
204         case 10:
205           if (optarg)
206             opt_pkcs = strdup(optarg);
207           break;
208         case 11:
209           if (optarg)
210             opt_bits = atoi(optarg);
211           break;
212         case 12:
213           if (optarg)
214             opt_identifier = strdup(optarg);
215           break;
216
217         default:
218           silc_usage();
219           break;
220         }
221     }
222   }
223
224   if (opt_create_keypair == TRUE) {
225     /* Create new key pair and exit */
226     silc_cipher_register_default();
227     silc_pkcs_register_default();
228     silc_hash_register_default();
229     silc_hmac_register_default();
230     silc_server_create_key_pair(opt_pkcs, opt_bits, opt_keypath,
231                                 opt_identifier, NULL, NULL);
232     exit(0);
233   }
234
235   /* Default configuration file */
236   if (!config_file)
237     config_file = strdup(SILC_SERVER_CONFIG_FILE);
238
239   /* Create SILC Server object */
240   ret = silc_server_alloc(&silcd);
241   if (ret == FALSE)
242     goto fail;
243
244   /* Read configuration files */
245   silcd->config = silc_server_config_alloc(config_file);
246   if (silcd->config == NULL)
247     goto fail;
248
249   /* Check for another silcd running */
250   silc_server_checkpid(silcd);
251
252   /* Initialize the server */
253   ret = silc_server_init(silcd);
254   if (ret == FALSE)
255     goto fail;
256
257   /* Ignore SIGPIPE */
258   sa.sa_handler = SIG_IGN;
259   sa.sa_flags = 0;
260   sigemptyset(&sa.sa_mask);
261   sigaction(SIGPIPE, &sa, NULL);
262   sa.sa_handler = signal_handler;
263   sigaction(SIGHUP, &sa, NULL);
264   sigaction(SIGTERM, &sa, NULL);
265   sigaction(SIGINT, &sa, NULL);
266   silc_schedule_signal_register(silcd->schedule, SIGHUP, got_hup, NULL);
267   silc_schedule_signal_register(silcd->schedule, SIGTERM, stop_server, NULL);
268   silc_schedule_signal_register(silcd->schedule, SIGINT, stop_server, NULL);
269
270   /* Before running the server, fork to background. */
271   if (!foreground)
272     silc_server_daemonise(silcd);
273
274   /* If set, write pid to file */
275   if (silcd->config->server_info->pid_file) {
276     char buf[10], *pidfile = silcd->config->server_info->pid_file;
277     unlink(pidfile);
278     snprintf(buf, sizeof(buf) - 1, "%d\n", getpid());
279     silc_file_writefile(pidfile, buf, strlen(buf));
280   }
281
282   /* Drop root. */
283   silc_server_drop(silcd);
284
285   /* Run the server. When this returns the server has been stopped
286      and we will exit. */
287   silc_server_run(silcd);
288   
289   /* Stop the server and free it. */
290   silc_server_stop(silcd);
291   silc_server_free(silcd);
292
293   /* Flush the logging system */
294   silc_log_flush_all();
295
296   exit(0);
297  fail:
298   exit(1);
299 }
300
301 /* Returns identifier string for public key generation. */
302
303 static char *silc_server_create_identifier(void)
304 {
305   char *username = NULL, *realname = NULL;
306   char hostname[256], email[256];
307   
308   /* Get realname */
309   realname = silc_get_real_name();
310
311   /* Get hostname */
312   memset(hostname, 0, sizeof(hostname));
313   gethostname(hostname, sizeof(hostname));
314
315   /* Get username (mandatory) */
316   username = silc_get_username();
317   if (!username)
318     return NULL;
319
320   /* Create default email address, whether it is right or not */
321   snprintf(email, sizeof(email), "%s@%s", username, hostname);
322
323   return silc_pkcs_encode_identifier(username, hostname, realname, email,
324                                      NULL, NULL);
325 }
326
327 /* Creates new public key and private key pair. This is used only
328    when user wants to create new key pair from command line. */
329
330 static int 
331 silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
332                             char *identifier, 
333                             SilcPublicKey *ret_pub_key,
334                             SilcPrivateKey *ret_prv_key)
335 {
336   SilcPKCS pkcs;
337   SilcPublicKey pub_key;
338   SilcPrivateKey prv_key;
339   SilcRng rng;
340   unsigned char *key;
341   SilcUInt32 key_len;
342   char pkfile[256], prvfile[256];
343
344   if (!pkcs_name || !path)
345     return FALSE;
346
347   if (!silc_pkcs_is_supported(pkcs_name)) {
348     fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
349     return FALSE;
350   }
351
352   if (!bits)
353     bits = 1024;
354
355   if (!identifier)
356     identifier = silc_server_create_identifier();
357
358   rng = silc_rng_alloc();
359   silc_rng_init(rng);
360   silc_rng_global_init(rng);
361
362   snprintf(pkfile, sizeof(pkfile) - 1, "%s%s", path,
363            SILC_SERVER_PUBLIC_KEY_NAME);
364   snprintf(prvfile, sizeof(prvfile) - 1, "%s%s", path,
365            SILC_SERVER_PRIVATE_KEY_NAME);
366
367   /* Generate keys */
368   silc_pkcs_alloc(pkcs_name, &pkcs);
369   silc_pkcs_generate_key(pkcs, bits, rng);
370
371   /* Save public key into file */
372   key = silc_pkcs_get_public_key(pkcs, &key_len);
373   pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
374                                        key, key_len);
375   silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
376   if (ret_pub_key)
377     *ret_pub_key = pub_key;
378   else
379     silc_pkcs_public_key_free(pub_key);
380
381   memset(key, 0, sizeof(key_len));
382   silc_free(key);
383
384   /* Save private key into file */
385   key = silc_pkcs_get_private_key(pkcs, &key_len);
386   prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
387   silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
388   if (ret_prv_key)
389     *ret_prv_key = prv_key;
390   else
391     silc_pkcs_private_key_free(prv_key);
392
393   printf("Public key has been saved into `%s'\n", pkfile);
394   printf("Private key has been saved into `%s'\n", prvfile);
395
396   memset(key, 0, sizeof(key_len));
397   silc_free(key);
398
399   silc_rng_free(rng);
400   silc_pkcs_free(pkcs);
401
402   return TRUE;
403 }