Send CMODE_CHANGE and CUMODE_CHANGE notifys to the original
[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   { "debug-level", 1, NULL, 'D' },
49   { "hexdump", 0, NULL, 'x' },
50   { "help", 0, NULL, 'h' },
51   { "foreground", 0, NULL, 'F' },
52   { "version", 0, NULL,'V' },
53
54   /* Key management options */
55   { "create-key-pair", 1, NULL, 'C' },
56   { "pkcs", 1, NULL, 10 },
57   { "bits", 1, NULL, 11 },
58   { "identifier", 1, NULL, 12 },
59
60   { NULL, 0, NULL, 0 }
61 };
62
63 /* Command line option variables */
64 static char *opt_keypath = NULL;
65 static char *opt_pkcs = "rsa";
66 static char *opt_identifier = NULL;
67 static int opt_bits = 2048;
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 "  -D  --debug-level=level       Enable debugging (Implies --foreground)\n"
80 "  -x  --hexdump                 Enable hexdumps (Implies --debug)\n"
81 "  -h  --help                    Display this message\n"
82 "  -F  --foreground              Dont fork\n"
83 "  -V  --version                 Display version\n"
84 "\n"
85 "  Key Management Options:\n"
86 "  -C, --create-key-pair=PATH    Create new public key pair\n"
87 "      --pkcs=PKCS               Set the PKCS of the public key pair\n"
88 "      --bits=VALUE              Set length of the public key pair\n"
89 "      --identifier=IDENTIFIER   Public key identifier\n"
90 "\n"
91 "      The public key identifier may be of the following format:\n"
92 "\n"
93 "      UN=<username>, HN=<hostname or IP>, RN=<real name>, E=<email>,\n"
94 "      O=<organization>, C=<country>\n"
95 "\n"
96 "      The UN and HN must be provided, the others are optional.  If the\n"
97 "      --identifier option is not used an identifier will be created for\n"
98 "      the public key automatically.\n"
99 "\n"
100 "      Example identifier: \"UN=foobar, HN=foo.bar.com, RN=Foo T. Bar, \n"
101 "                           E=foo@bar.com, C=FI\"\n"
102 "\n");
103   exit(0);
104 }
105
106 /* Die if a *valid* pid file exists already */
107
108 static void silc_server_checkpid(SilcServer silcd)
109 {
110   if (silcd->config->server_info->pid_file) {
111     int oldpid;
112     char *buf;
113     SilcUInt32 buf_len;
114
115     SILC_LOG_DEBUG(("Checking for another silcd running"));
116     buf = silc_file_readfile(silcd->config->server_info->pid_file, &buf_len);
117     if (!buf)
118       return;
119     oldpid = atoi(buf);
120     silc_free(buf);
121     if (oldpid <= 0)
122       return;
123     kill(oldpid, SIGCHLD); /* this signal does nothing, check if alive */
124     if (errno != ESRCH) {
125       fprintf(stderr, "\nI detected another daemon running with the "
126               "same pid file.\n");
127       fprintf(stderr, "Please change the config file, or erase the %s\n",
128         silcd->config->server_info->pid_file);
129       exit(1);
130     }
131   }
132 }
133
134 /* Drop root privileges. If some system call fails, die. */
135
136 static void silc_server_drop_privs(SilcServer server)
137 {
138   /* Are we executing silcd as root or a regular user? */
139   if (geteuid()) {
140     SILC_LOG_DEBUG(("Server started as user"));
141   }
142   else {
143     struct passwd *pw;
144     struct group *gr;
145     char *user, *group;
146
147     SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
148
149     /* Get the values given for user and group in configuration file */
150     user = server->config->server_info->user;
151     group = server->config->server_info->group;
152
153     if (!user || !group) {
154       fprintf(stderr, "Error:"
155        "\tSILC server must not be run as root.  For the security of your\n"
156        "\tsystem it is strongly suggested that you run SILC under dedicated\n"
157        "\tuser account.  Modify the ServerInfo configuration section to run\n"
158        "\tthe server as non-root user.\n");
159       exit(1);
160     }
161
162     /* Check whether the user/group does not begin with a number */
163     if (isdigit(user[0]) || isdigit(group[0])) {
164       SILC_LOG_DEBUG(("User and/or group starts with a number"));
165       fprintf(stderr, "Invalid user and/or group information\n");
166       fprintf(stderr, "Please assign them as names, not numbers\n");
167       exit(1);
168     }
169
170     if (!(pw = getpwnam(user))) {
171       fprintf(stderr, "Error: No such user %s found.\n", user);
172       exit(1);
173     }
174     if (!(gr = getgrnam(group))) {
175       fprintf(stderr, "Error: No such group %s found.\n", group);
176       exit(1);
177     }
178
179     /* Check whether user and/or group is set to root. If yes, exit
180        immediately. Otherwise, setgid and setuid server to user.group */
181     if ((gr->gr_gid == 0) || (pw->pw_uid == 0)) {
182       fprintf(stderr, "Error:"
183        "\tSILC server must not be run as root.  For the security of your\n"
184        "\tsystem it is strongly suggested that you run SILC under dedicated\n"
185        "\tuser account.  Modify the ServerInfo configuration section to run\n"
186        "\tthe server as non-root user.\n");
187       exit(1);
188     }
189
190     SILC_LOG_DEBUG(("Changing to group %s (gid=%u)", group, gr->gr_gid));
191     if (setgid(gr->gr_gid) != 0) {
192       fprintf(stderr, "Error: Failed setgid() to %s (gid=%u). Exiting.\n",
193               group, gr->gr_gid);
194       exit(1);
195     }
196 #if defined HAVE_SETGROUPS && defined HAVE_INITGROUPS
197     SILC_LOG_DEBUG(("Removing supplementary groups"));
198     if (setgroups(0, NULL) != 0) {
199       fprintf(stderr, "Error: Failed setgroups() to NULL. Exiting.\n");
200       exit(1);
201     }
202     SILC_LOG_DEBUG(("Setting supplementary groups for user %s", user));
203     if (initgroups(user, gr->gr_gid) != 0) {
204       fprintf(stderr, "Error: Failed initgroups() for user %s (gid=%u). "
205               "Exiting.\n", user, gr->gr_gid);
206       exit(1);
207     }
208 #endif
209     SILC_LOG_DEBUG(("Changing to user %s (uid=%u)", user, pw->pw_uid));
210     if (setuid(pw->pw_uid) != 0) {
211       fprintf(stderr, "Error: Failed to setuid() to %s (gid=%u). Exiting.\n",
212               user, pw->pw_uid);
213       exit(1);
214     }
215   }
216 }
217
218 /* Fork server to background */
219
220 static void silc_server_daemonise(SilcServer server)
221 {
222   int i;
223
224   SILC_LOG_DEBUG(("Forking SILC server to background"));
225
226   if ((i = fork()) < 0) {
227     fprintf(stderr, "Error: fork() failed: %s\n", strerror(errno));
228     exit(1);
229   }
230
231   if (i) /* Kill the parent */
232     exit(0);
233
234   server->background = TRUE;
235   setsid();
236
237   /* XXX close stdin, stdout, stderr -- before this, check that all writes
238      to stderr are changed to SILC_SERVER_LOG_ERROR() */
239 }
240
241 static void signal_handler(int sig)
242 {
243   /* Mark the signal to be caller after this signal is over. */
244   silc_schedule_signal_call(silcd->schedule, sig);
245 }
246
247 SILC_TASK_CALLBACK(got_hup)
248 {
249   /* First, reset all log files (they might have been deleted) */
250   silc_log_reset_all();
251
252   /* Rehash the configuration file */
253   silc_server_rehash(silcd);
254 }
255
256 SILC_TASK_CALLBACK(stop_server)
257 {
258   /* Stop scheduler, the program will stop eventually after noticing
259      that the scheduler is down. */
260   silc_schedule_stop(silcd->schedule);
261 }
262
263 /* Dump server statistics into a file into /tmp directory */
264
265 SILC_TASK_CALLBACK(dump_stats)
266 {
267   FILE *fdd;
268   char filename[256];
269
270   memset(filename, 0, sizeof(filename));
271   snprintf(filename, sizeof(filename) - 1, "/tmp/silcd.%d.stats", getpid());
272   fdd = fopen(filename, "w+");
273   if (!fdd)
274     return;
275
276 #define STAT_OUTPUT(fmt, stat) fprintf(fdd, fmt "\n", (int)stat);
277
278   fprintf(fdd, "SILC Server %s Statistics\n\n", silcd->server_name);
279   fprintf(fdd, "Local Stats:\n");
280   STAT_OUTPUT("  My clients              : %d", silcd->stat.my_clients);
281   STAT_OUTPUT("  My servers              : %d", silcd->stat.my_servers);
282   STAT_OUTPUT("  My routers              : %d", silcd->stat.my_routers);
283   STAT_OUTPUT("  My channels             : %d", silcd->stat.my_channels);
284   STAT_OUTPUT("  My joined users         : %d", silcd->stat.my_chanclients);
285   STAT_OUTPUT("  My aways                : %d", silcd->stat.my_aways);
286   STAT_OUTPUT("  My detached clients     : %d", silcd->stat.my_detached);
287   STAT_OUTPUT("  My server operators     : %d", silcd->stat.my_server_ops);
288   STAT_OUTPUT("  My router operators     : %d", silcd->stat.my_router_ops);
289   fprintf(fdd, "\nGlobal Stats:\n");
290   STAT_OUTPUT("  Cell clients            : %d", silcd->stat.cell_clients);
291   STAT_OUTPUT("  Cell servers            : %d", silcd->stat.cell_servers);
292   STAT_OUTPUT("  Cell channels           : %d", silcd->stat.cell_channels);
293   STAT_OUTPUT("  Cell joined users       : %d", silcd->stat.cell_chanclients);
294   STAT_OUTPUT("  All clients             : %d", silcd->stat.clients);
295   STAT_OUTPUT("  All servers             : %d", silcd->stat.servers);
296   STAT_OUTPUT("  All routers             : %d", silcd->stat.routers);
297   STAT_OUTPUT("  All channels            : %d", silcd->stat.channels);
298   STAT_OUTPUT("  All joined users        : %d", silcd->stat.chanclients);
299   STAT_OUTPUT("  All aways               : %d", silcd->stat.aways);
300   STAT_OUTPUT("  All detached clients    : %d", silcd->stat.detached);
301   STAT_OUTPUT("  All server operators    : %d", silcd->stat.server_ops);
302   STAT_OUTPUT("  All router operators    : %d", silcd->stat.router_ops);
303   fprintf(fdd, "\nGeneral Stats:\n");
304   STAT_OUTPUT("  Connection attempts     : %d", silcd->stat.conn_attempts);
305   STAT_OUTPUT("  Connection failures     : %d", silcd->stat.conn_failures);
306   STAT_OUTPUT("  Authentication attempts : %d", silcd->stat.auth_attempts);
307   STAT_OUTPUT("  Authentication failures : %d", silcd->stat.auth_failures);
308   STAT_OUTPUT("  Packets sent            : %d", silcd->stat.packets_sent);
309   STAT_OUTPUT("  Packets received        : %d", silcd->stat.packets_received);
310
311   fflush(fdd);
312   fclose(fdd);
313 }
314
315 typedef struct {
316   int level;
317   const char *string;
318 } DebugLevel;
319
320 static DebugLevel debug_levels[] = {
321   /* Very basic stuff from silcd/ */
322   { 3, "silcd\\.c,server\\.c" },
323
324   /* More stuff from silcd/ */
325   { 7, "silcd\\.c,server\\.c,command\\.c" },
326
327   /* All basic stuff from silcd/ */
328   { 10, "silc_server_*" },
329
330   /* All from silcd/ */
331   { 15, "*silcd*,*serverid*,silc_server_*,*idlist*" },
332
333   /* All from silcd/ and basic stuff from libs */
334   { 20, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,*silcske*" },
335
336   /* All from silcd/ and more stuff from libs */
337   { 25, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
338     "*silcpacket*,*ske*,*silcrng*" },
339
340   /* All from silcd/ and even more stuff from libs */
341   { 30, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
342     "*silcpacket*,*ske*,*silcrng*,*command*,*channel*,*private*,*notify*" },
343
344   /* All from silcd/ and even more stuff from libs + all from silccore */
345   { 35, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
346     "*silcpacket*,*ske*,*silcrng*,*command*,*channel*,*private*,*notify*"
347     "*silcid*,*argument*" },
348
349   /* All from silcd/, all from silccore, silccrypt and silcmath */
350   { 40, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
351     "*silcpacket*,*ske*,*silcrng*,*command*,*channel*,*private*,*notify*"
352     "*silcid*,*argument*,*pkcs*,*hmac*,*hash*,*cipher*,silc_math*" },
353
354   /* All from silcd/, all from silccore, silccrypt and silcmath + stuff
355      from silcutil */
356   { 45, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
357     "*silcpacket*,*ske*,*silcrng*,*command*,*channel*,*private*,*notify*"
358     "*silcid*,*argument*,*pkcs*,*hmac*,*hash*,*cipher*,silc_math*,*sim*"
359     "*sockconn*" },
360
361   /* All from silcd/, all from silccore, silccrypt and silcmath + more stuff
362      from silcutil */
363   { 50, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
364     "*silcpacket*,*ske*,*silcrng*,*command*,*channel*,*private*,*notify*"
365     "*silcid*,*argument*,*pkcs*,*hmac*,*hash*,*cipher*,silc_math*,*sim*"
366     "*sockconn*,*net*" },
367
368   /* All from silcd/, all from silccore, silccrypt and silcmath + more stuff
369      from silcutil */
370   { 55, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
371     "*silcpacket*,*ske*,*silcrng*,*command*,*channel*,*private*,*notify*"
372     "*silcid*,*argument*,*pkcs*,*hmac*,*hash*,*cipher*,silc_math*,*sim*"
373     "*sockconn*,*net*,*log*,*config*" },
374
375   /* All */
376   { 90, "*" },
377
378   { -1, NULL },
379 };
380
381 static void silc_get_debug_level(int level)
382 {
383   int i;
384
385   if (level < 0)
386     return;
387
388   for (i = 0; debug_levels[i].string; i++)
389     if (level <= debug_levels[i].level) {
390       silc_log_set_debug_string(debug_levels[i].string);
391       break;
392     }
393 }
394
395 /* This function should not be called directly but throught the wrapper
396    macro SILC_SERVER_LOG_STDERR() */
397
398 void silc_server_stderr(char *message)
399 {
400   if (silcd->background)
401     silc_log_output(SILC_LOG_ERROR, message);
402   else {
403     fprintf(stderr, "%s", message);
404     silc_free(message);
405   }
406 }
407
408 int main(int argc, char **argv)
409 {
410   int ret, opt, option_index;
411   bool foreground = FALSE;
412   bool opt_create_keypair = FALSE;
413   char *silcd_config_file = NULL;
414   struct sigaction sa;
415
416   /* Parse command line arguments */
417   if (argc > 1) {
418     while ((opt = getopt_long(argc, argv, "f:p:d:D:xhFVC:",
419                               long_opts, &option_index)) != EOF) {
420       switch(opt) {
421         case 'h':
422           silc_usage();
423           break;
424         case 'V':
425           printf("SILCd Secure Internet Live Conferencing daemon, "
426                  "version %s (base: SILC Toolkit %s)\n",
427                  silc_dist_version, silc_version);
428           printf("(c) 1997 - 2002 Pekka Riikonen "
429                  "<priikone@silcnet.org>\n");
430           exit(0);
431           break;
432         case 'd':
433 #ifdef SILC_DEBUG
434           silc_debug = TRUE;
435           if (optarg)
436             silc_log_set_debug_string(optarg);
437           foreground = TRUE;        /* implied */
438           silc_log_quick = TRUE;    /* implied */
439 #else
440           fprintf(stderr,
441                   "Run-time debugging is not enabled. To enable it recompile\n"
442                   "the server with --enable-debug configuration option.\n");
443 #endif
444           break;
445         case 'D':
446 #ifdef SILC_DEBUG
447           silc_debug = TRUE;
448           if (optarg)
449             silc_get_debug_level(atoi(optarg));
450           foreground = TRUE;        /* implied */
451           silc_log_quick = TRUE;    /* implied */
452 #else
453           fprintf(stderr,
454                   "Run-time debugging is not enabled. To enable it recompile\n"
455                   "the server with --enable-debug configuration option.\n");
456 #endif
457           break;
458         case 'x':
459 #ifdef SILC_DEBUG
460           silc_debug_hexdump = TRUE;
461           silc_debug = TRUE; /* implied */
462           foreground = TRUE; /* implied */
463           silc_log_quick = TRUE; /* implied */
464 #else
465           fprintf(stderr,
466                   "Run-time debugging is not enabled. To enable it recompile\n"
467                   "the server with --enable-debug configuration option.\n");
468 #endif
469           break;
470         case 'f':
471           silcd_config_file = strdup(optarg);
472           break;
473         case 'F':
474           foreground = TRUE;
475           break;
476
477           /*
478            * Key management options
479            */
480         case 'C':
481           opt_create_keypair = TRUE;
482           if (optarg)
483             opt_keypath = strdup(optarg);
484           break;
485         case 10:
486           if (optarg)
487             opt_pkcs = strdup(optarg);
488           break;
489         case 11:
490           if (optarg)
491             opt_bits = atoi(optarg);
492           break;
493         case 12:
494           if (optarg)
495             opt_identifier = strdup(optarg);
496           break;
497
498         default:
499           silc_usage();
500           break;
501       }
502     }
503   }
504
505   if (opt_create_keypair == TRUE) {
506     /* Create new key pair and exit */
507     silc_cipher_register_default();
508     silc_pkcs_register_default();
509     silc_hash_register_default();
510     silc_hmac_register_default();
511     silc_server_create_key_pair(opt_pkcs, opt_bits, opt_keypath,
512                                 opt_identifier, NULL, NULL);
513     exit(0);
514   }
515
516   /* Default configuration file */
517   if (!silcd_config_file)
518     silcd_config_file = strdup(SILC_SERVER_CONFIG_FILE);
519
520   /* Create SILC Server object */
521   ret = silc_server_alloc(&silcd);
522   if (ret == FALSE)
523     goto fail;
524
525   /* Read configuration files */
526   silcd->config = silc_server_config_alloc(silcd_config_file);
527   if (silcd->config == NULL)
528     goto fail;
529   silcd->config_file = silcd_config_file;
530
531   /* Check for another silcd running */
532   silc_server_checkpid(silcd);
533
534   /* Initialize the server */
535   if (silc_server_init(silcd) == FALSE)
536     goto fail;
537
538   /* Ignore SIGPIPE */
539   sa.sa_handler = SIG_IGN;
540   sa.sa_flags = 0;
541   sigemptyset(&sa.sa_mask);
542   sigaction(SIGPIPE, &sa, NULL);
543   sa.sa_handler = signal_handler;
544   sigaction(SIGHUP, &sa, NULL);
545   sigaction(SIGTERM, &sa, NULL);
546   sigaction(SIGINT, &sa, NULL);
547   sigaction(SIGUSR1, &sa, NULL);
548   silc_schedule_signal_register(silcd->schedule, SIGHUP, got_hup, NULL);
549   silc_schedule_signal_register(silcd->schedule, SIGTERM, stop_server, NULL);
550   silc_schedule_signal_register(silcd->schedule, SIGINT, stop_server, NULL);
551   silc_schedule_signal_register(silcd->schedule, SIGUSR1, dump_stats, NULL);
552
553   if (!foreground) {
554     /* Before running the server, fork to background. */
555     silc_server_daemonise(silcd);
556
557     /* If set, write pid to file */
558     if (silcd->config->server_info->pid_file) {
559       char buf[10], *pidfile = silcd->config->server_info->pid_file;
560       unlink(pidfile);
561       snprintf(buf, sizeof(buf) - 1, "%d\n", getpid());
562       silc_file_writefile(pidfile, buf, strlen(buf));
563     }
564   }
565
566   /* Drop root if we are not in debug mode, so you don't need to bother about
567      file writing permissions and so on */
568   if (!silc_debug)
569     silc_server_drop_privs(silcd);
570
571   /* Run the server. When this returns the server has been stopped
572      and we will exit. */
573   silc_server_run(silcd);
574
575   /* Stop the server and free it. */
576   silc_server_stop(silcd);
577   silc_server_config_destroy(silcd->config);
578   silc_server_free(silcd);
579
580   /* Flush the logging system */
581   silc_log_flush_all();
582
583   silc_free(silcd_config_file);
584   silc_free(opt_identifier);
585   silc_free(opt_keypath);
586   exit(0);
587
588  fail:
589   silc_free(silcd_config_file);
590   silc_free(opt_identifier);
591   silc_free(opt_keypath);
592   exit(1);
593 }
594
595 /* Returns identifier string for public key generation. */
596
597 static char *silc_server_create_identifier(void)
598 {
599   char *username = NULL, *realname = NULL;
600   char hostname[256], email[256];
601
602   /* Get realname */
603   realname = silc_get_real_name();
604
605   /* Get hostname */
606   memset(hostname, 0, sizeof(hostname));
607   gethostname(hostname, sizeof(hostname));
608
609   /* Get username (mandatory) */
610   username = silc_get_username();
611   if (!username)
612     return NULL;
613
614   /* Create default email address, whether it is right or not */
615   snprintf(email, sizeof(email), "%s@%s", username, hostname);
616
617   return silc_pkcs_encode_identifier(username, hostname, realname, email,
618                                      NULL, NULL);
619 }
620
621 /* Creates new public key and private key pair. This is used only
622    when user wants to create new key pair from command line. */
623
624 static int
625 silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
626                             char *identifier,
627                             SilcPublicKey *ret_pub_key,
628                             SilcPrivateKey *ret_prv_key)
629 {
630   SilcPKCS pkcs;
631   SilcPublicKey pub_key;
632   SilcPrivateKey prv_key;
633   SilcRng rng;
634   unsigned char *key;
635   SilcUInt32 key_len;
636   char pkfile[256], prvfile[256];
637
638   if (!pkcs_name || !path)
639     return FALSE;
640
641   if (!silc_pkcs_is_supported(pkcs_name)) {
642     fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
643     return FALSE;
644   }
645
646   if (!bits)
647     bits = 2048;
648
649   if (!identifier)
650     identifier = silc_server_create_identifier();
651
652   rng = silc_rng_alloc();
653   silc_rng_init(rng);
654   silc_rng_global_init(rng);
655
656   snprintf(pkfile, sizeof(pkfile) - 1, "%s%s", path,
657            SILC_SERVER_PUBLIC_KEY_NAME);
658   snprintf(prvfile, sizeof(prvfile) - 1, "%s%s", path,
659            SILC_SERVER_PRIVATE_KEY_NAME);
660
661   /* Generate keys */
662   silc_pkcs_alloc(pkcs_name, &pkcs);
663   silc_pkcs_generate_key(pkcs, bits, rng);
664
665   /* Save public key into file */
666   key = silc_pkcs_get_public_key(pkcs, &key_len);
667   pub_key = silc_pkcs_public_key_alloc(silc_pkcs_get_name(pkcs), identifier,
668                                        key, key_len);
669   silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
670   if (ret_pub_key)
671     *ret_pub_key = pub_key;
672   else
673     silc_pkcs_public_key_free(pub_key);
674
675   memset(key, 0, sizeof(key_len));
676   silc_free(key);
677
678   /* Save private key into file */
679   key = silc_pkcs_get_private_key(pkcs, &key_len);
680   prv_key = silc_pkcs_private_key_alloc(silc_pkcs_get_name(pkcs),
681                                         key, key_len);
682   silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
683   if (ret_prv_key)
684     *ret_prv_key = prv_key;
685   else
686     silc_pkcs_private_key_free(prv_key);
687
688   printf("Public key has been saved into `%s'\n", pkfile);
689   printf("Private key has been saved into `%s'\n", prvfile);
690
691   memset(key, 0, sizeof(key_len));
692   silc_free(key);
693
694   silc_rng_free(rng);
695   silc_pkcs_free(pkcs);
696
697   return TRUE;
698 }