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 - 2001 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 "version.h"
31
32 static void silc_usage();
33 static char *silc_server_create_identifier();
34 static int 
35 silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
36                             char *identifier, 
37                             SilcPublicKey *ret_pub_key,
38                             SilcPrivateKey *ret_prv_key);
39
40 /* Long command line options */
41 static struct option long_opts[] = 
42 {
43   { "config-file", 1, NULL, 'f' },
44   { "debug", 1, NULL, 'd' },
45   { "help", 0, NULL, 'h' },
46   { "foreground", 0, NULL, 'F' },
47   { "version", 0, NULL,'V' },
48
49   /* Key management options */
50   { "create-key-pair", 1, NULL, 'C' },
51   { "pkcs", 1, NULL, 10 },
52   { "bits", 1, NULL, 11 },
53   { "identifier", 1, NULL, 12 },
54
55   { NULL, 0, NULL, 0 }
56 };
57
58 /* Command line option variables */
59 static bool opt_create_keypair = FALSE;
60 static char *opt_keypath = NULL;
61 static char *opt_pkcs = "rsa";
62 static char *opt_identifier = NULL;
63 static int opt_bits = 1024;
64
65 /* Prints out the usage of silc client */
66
67 static void silc_usage()
68 {
69   printf("\
70 Usage: silcd [options]\n\
71 \n\
72   Generic Options:\n\
73   -f  --config-file=FILE        Alternate configuration file\n\
74   -d  --debug=string            Enable debugging (Implies --foreground)\n\
75   -h  --help                    Display this message\n\
76   -F  --foreground              Dont fork\n\
77   -V  --version                 Display version\n\
78 \n\
79   Key Management Options:\n\
80   -C, --create-key-pair=PATH    Create new public key pair\n\
81       --pkcs=PKCS               Set the PKCS of the public key pair\n\
82       --bits=VALUE              Set length of the public key pair\n\
83       --identifier=IDENTIFIER   Public key identifier\n\
84 \n\
85       The public key identifier may be of the following format:\n\
86 \n\
87       UN=<username>, HN=<hostname or IP>, RN=<real name>, E=<email>,\n\
88       O=<organization>, C=<country>\n\
89 \n\
90       The UN and HN must be provided, the others are optional.  If the\n\
91       --identifier option is not used an identifier will be created for\n\
92       the public key automatically.\n\
93 \n\
94       Example identifier: \"UN=foobar, HN=foo.bar.com, RN=Foo T. Bar, \n\
95                            E=foo@bar.com, C=FI\"\n\
96 \n");
97   exit(0);
98 }
99
100 /* Dies if a *valid* pid file exists already */
101
102 static void silc_checkpid(SilcServer silcd)
103 {
104   if (silcd->config->pidfile && silcd->config->pidfile->pid_file) {
105     int oldpid;
106     char *buf;
107     uint32 buf_len;
108
109     SILC_LOG_DEBUG(("Checking for another silcd running"));
110     buf = silc_file_readfile(silcd->config->pidfile->pid_file, &buf_len);
111     if (!buf)
112       return;
113     oldpid = atoi(buf);
114     silc_free(buf);
115     if (oldpid <= 0)
116       return;
117     kill(oldpid, SIGCHLD); /* this signal does nothing, check if alive */
118     if (errno != ESRCH) {
119       fprintf(stderr, "\nI detected another daemon running with the same pid file.\n");
120       fprintf(stderr, "Please change the config file, or erase the %s\n",
121         silcd->config->pidfile->pid_file);
122       exit(1);
123     }
124   }
125 }
126
127 int main(int argc, char **argv)
128 {
129   int ret, opt, option_index;
130   char *config_file = NULL;
131   bool foreground = FALSE;
132   SilcServer silcd;
133   struct sigaction sa;
134
135   /* Parse command line arguments */
136   if (argc > 1) {
137     while ((opt = getopt_long(argc, argv, "cf:d:hFVC:",
138                               long_opts, &option_index)) != EOF) {
139       switch(opt) 
140         {
141         case 'h':
142           silc_usage();
143           break;
144         case 'V':
145           printf("SILCd Secure Internet Live Conferencing daemon, "
146                  "version %s (base: SILC Toolkit %s)\n",
147                  silc_dist_version, silc_version);
148           printf("(c) 1997 - 2001 Pekka Riikonen "
149                  "<priikone@silcnet.org>\n");
150           exit(0);
151           break;
152         case 'd':
153 #ifdef SILC_DEBUG
154           silc_debug = TRUE;
155           silc_debug_hexdump = TRUE;
156           silc_log_set_debug_string(optarg);
157           foreground = TRUE;
158           silc_log_quick = TRUE;
159 #else
160           fprintf(stdout, 
161                   "Run-time debugging is not enabled. To enable it recompile\n"
162                   "the server with --enable-debug configuration option.\n");
163 #endif
164           break;
165         case 'f':
166           config_file = strdup(optarg);
167           break;
168         case 'F':
169           foreground = TRUE;
170           break;
171
172           /*
173            * Key management options
174            */
175         case 'C':
176           opt_create_keypair = TRUE;
177           if (optarg)
178             opt_keypath = strdup(optarg);
179           break;
180         case 10:
181           if (optarg)
182             opt_pkcs = strdup(optarg);
183           break;
184         case 11:
185           if (optarg)
186             opt_bits = atoi(optarg);
187           break;
188         case 12:
189           if (optarg)
190             opt_identifier = strdup(optarg);
191           break;
192
193         default:
194           silc_usage();
195           break;
196         }
197     }
198   }
199
200   if (opt_create_keypair == TRUE) {
201     /* Create new key pair and exit */
202     silc_cipher_register_default();
203     silc_pkcs_register_default();
204     silc_hash_register_default();
205     silc_hmac_register_default();
206     silc_server_create_key_pair(opt_pkcs, opt_bits, opt_keypath,
207                                 opt_identifier, NULL, NULL);
208     exit(0);
209   }
210
211   /* Default configuration file */
212   if (!config_file)
213     config_file = strdup(SILC_SERVER_CONFIG_FILE);
214
215   /* Create SILC Server object */
216   ret = silc_server_alloc(&silcd);
217   if (ret == FALSE)
218     goto fail;
219
220   /* Read configuration files */
221   silcd->config = silc_server_config_alloc(config_file);
222   if (silcd->config == NULL)
223     goto fail;
224
225   /* Check for another silcd running */
226   silc_checkpid(silcd);
227
228   /* Initialize the server */
229   ret = silc_server_init(silcd);
230   if (ret == FALSE)
231     goto fail;
232
233   /* Ignore SIGPIPE */
234   sa.sa_handler = SIG_IGN;
235   sa.sa_flags = 0;
236   sigemptyset(&sa.sa_mask);
237   sigaction(SIGPIPE, &sa, NULL);
238
239   /* Before running the server, fork to background. */
240   if (!foreground)
241     silc_server_daemonise(silcd);
242
243   /* If set, write pid to file */
244   if (silcd->config->pidfile && silcd->config->pidfile->pid_file) {
245     char buf[10];
246     unlink(silcd->config->pidfile->pid_file);
247     snprintf(buf, sizeof(buf) - 1, "%d\n", getpid());
248     silc_file_writefile(silcd->config->pidfile->pid_file, buf, strlen(buf));
249   }
250
251   /* Drop root. */
252   silc_server_drop(silcd);
253
254   /* Run the server. When this returns the server has been stopped
255      and we will exit. */
256   silc_server_run(silcd);
257   
258   /* Stop the server. This probably has been done already but it
259      doesn't hurt to do it here again. */
260   silc_server_stop(silcd);
261   silc_server_free(silcd);
262   
263   exit(0);
264  fail:
265   exit(1);
266 }
267
268 /* Returns identifier string for public key generation. */
269
270 static char *silc_server_create_identifier()
271 {
272   char *username = NULL, *realname = NULL;
273   char hostname[256], email[256];
274   
275   /* Get realname */
276   realname = silc_get_real_name();
277
278   /* Get hostname */
279   memset(hostname, 0, sizeof(hostname));
280   gethostname(hostname, sizeof(hostname));
281
282   /* Get username (mandatory) */
283   username = silc_get_username();
284   if (!username)
285     return NULL;
286
287   /* Create default email address, whether it is right or not */
288   snprintf(email, sizeof(email), "%s@%s", username, hostname);
289
290   return silc_pkcs_encode_identifier(username, hostname, realname, email,
291                                      NULL, NULL);
292 }
293
294 /* Creates new public key and private key pair. This is used only
295    when user wants to create new key pair from command line. */
296
297 static int 
298 silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
299                             char *identifier, 
300                             SilcPublicKey *ret_pub_key,
301                             SilcPrivateKey *ret_prv_key)
302 {
303   SilcPKCS pkcs;
304   SilcPublicKey pub_key;
305   SilcPrivateKey prv_key;
306   SilcRng rng;
307   unsigned char *key;
308   uint32 key_len;
309   char pkfile[256], prvfile[256];
310
311   if (!pkcs_name || !path)
312     return FALSE;
313
314   if (!silc_pkcs_is_supported(pkcs_name)) {
315     fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
316     return FALSE;
317   }
318
319   if (!bits)
320     bits = 1024;
321
322   if (!identifier)
323     identifier = silc_server_create_identifier();
324
325   rng = silc_rng_alloc();
326   silc_rng_init(rng);
327   silc_rng_global_init(rng);
328
329   snprintf(pkfile, sizeof(pkfile) - 1, "%s%s", path,
330            SILC_SERVER_PUBLIC_KEY_NAME);
331   snprintf(prvfile, sizeof(prvfile) - 1, "%s%s", path,
332            SILC_SERVER_PRIVATE_KEY_NAME);
333
334   /* Generate keys */
335   silc_pkcs_alloc(pkcs_name, &pkcs);
336   pkcs->pkcs->init(pkcs->context, bits, rng);
337
338   /* Save public key into file */
339   key = silc_pkcs_get_public_key(pkcs, &key_len);
340   pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
341                                        key, key_len);
342   silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
343   if (ret_pub_key)
344     *ret_pub_key = pub_key;
345   else
346     silc_pkcs_public_key_free(pub_key);
347
348   memset(key, 0, sizeof(key_len));
349   silc_free(key);
350
351   /* Save private key into file */
352   key = silc_pkcs_get_private_key(pkcs, &key_len);
353   prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
354   silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
355   if (ret_prv_key)
356     *ret_prv_key = prv_key;
357   else
358     silc_pkcs_private_key_free(prv_key);
359
360   printf("Public key has been saved into `%s'\n", pkfile);
361   printf("Private key has been saved into `%s'\n", prvfile);
362
363   memset(key, 0, sizeof(key_len));
364   silc_free(key);
365
366   silc_rng_free(rng);
367   silc_pkcs_free(pkcs);
368
369   return TRUE;
370 }