Fixed topic annoucning, server signoff handling, added -D option,
[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   /* Basic stuff from silcd/ */
322   { 5, "silc_server_*" },
323
324   /* All from silcd/ */
325   { 10, "*silcd*,*serverid*,silc_server_*,*idlist*" },
326
327   /* All from silcd/ and basic stuff from libs */
328   { 15, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,*silcske*" },
329
330   /* All from silcd/ and more stuff from libs */
331   { 20, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
332     "*silcpacket*,*ske*,*silcrng*" },
333
334   /* All from silcd/ and even more stuff from libs */
335   { 25, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
336     "*silcpacket*,*ske*,*silcrng*,*command*,*channel*,*private*,*notify*" },
337
338   /* All from silcd/ and even more stuff from libs + all from silccore */
339   { 30, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
340     "*silcpacket*,*ske*,*silcrng*,*command*,*channel*,*private*,*notify*"
341     "*silcid*,*argument*" },
342
343   /* All from silcd/, all from silccore, silccrypt and silcmath */
344   { 35, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
345     "*silcpacket*,*ske*,*silcrng*,*command*,*channel*,*private*,*notify*"
346     "*silcid*,*argument*,*pkcs*,*hmac*,*hash*,*cipher*,silc_math*" },
347
348   /* All from silcd/, all from silccore, silccrypt and silcmath + stuff
349      from silcutil */
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*,*sim*"
353     "*sockconn*" },
354
355   /* All from silcd/, all from silccore, silccrypt and silcmath + more stuff
356      from silcutil */
357   { 45, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
358     "*silcpacket*,*ske*,*silcrng*,*command*,*channel*,*private*,*notify*"
359     "*silcid*,*argument*,*pkcs*,*hmac*,*hash*,*cipher*,silc_math*,*sim*"
360     "*sockconn*,*net*" },
361
362   /* All from silcd/, all from silccore, silccrypt and silcmath + more stuff
363      from silcutil */
364   { 50, "*silcd*,*serverid*,silc_server_*,*idlist*,*silcauth*,"
365     "*silcpacket*,*ske*,*silcrng*,*command*,*channel*,*private*,*notify*"
366     "*silcid*,*argument*,*pkcs*,*hmac*,*hash*,*cipher*,silc_math*,*sim*"
367     "*sockconn*,*net*,*log*,*config*" },
368
369   /* All */
370   { 90, "*" },
371
372   { -1, NULL },
373 };
374
375 static void silc_get_debug_level(int level)
376 {
377   int i;
378
379   if (level < 0)
380     return;
381
382   for (i = 0; debug_levels[i].string; i++)
383     if (level <= debug_levels[i].level) {
384       silc_log_set_debug_string(debug_levels[i].string);
385       break;
386     }
387 }
388
389 /* This function should not be called directly but throught the wrapper
390    macro SILC_SERVER_LOG_STDERR() */
391
392 void silc_server_stderr(char *message)
393 {
394   if (silcd->background)
395     silc_log_output(SILC_LOG_ERROR, message);
396   else {
397     fprintf(stderr, "%s", message);
398     silc_free(message);
399   }
400 }
401
402 int main(int argc, char **argv)
403 {
404   int ret, opt, option_index;
405   bool foreground = FALSE;
406   bool opt_create_keypair = FALSE;
407   char *silcd_config_file = NULL;
408   struct sigaction sa;
409
410   /* Parse command line arguments */
411   if (argc > 1) {
412     while ((opt = getopt_long(argc, argv, "f:p:d:D:xhFVC:",
413                               long_opts, &option_index)) != EOF) {
414       switch(opt) {
415         case 'h':
416           silc_usage();
417           break;
418         case 'V':
419           printf("SILCd Secure Internet Live Conferencing daemon, "
420                  "version %s (base: SILC Toolkit %s)\n",
421                  silc_dist_version, silc_version);
422           printf("(c) 1997 - 2002 Pekka Riikonen "
423                  "<priikone@silcnet.org>\n");
424           exit(0);
425           break;
426         case 'd':
427 #ifdef SILC_DEBUG
428           silc_debug = TRUE;
429           if (optarg)
430             silc_log_set_debug_string(optarg);
431           foreground = TRUE;        /* implied */
432           silc_log_quick = TRUE;    /* implied */
433 #else
434           fprintf(stderr,
435                   "Run-time debugging is not enabled. To enable it recompile\n"
436                   "the server with --enable-debug configuration option.\n");
437 #endif
438           break;
439         case 'D':
440 #ifdef SILC_DEBUG
441           silc_debug = TRUE;
442           if (optarg)
443             silc_get_debug_level(atoi(optarg));
444           foreground = TRUE;        /* implied */
445           silc_log_quick = TRUE;    /* implied */
446 #else
447           fprintf(stderr,
448                   "Run-time debugging is not enabled. To enable it recompile\n"
449                   "the server with --enable-debug configuration option.\n");
450 #endif
451           break;
452         case 'x':
453 #ifdef SILC_DEBUG
454           silc_debug_hexdump = TRUE;
455           silc_debug = TRUE; /* implied */
456           foreground = TRUE; /* implied */
457           silc_log_quick = TRUE; /* implied */
458 #else
459           fprintf(stderr,
460                   "Run-time debugging is not enabled. To enable it recompile\n"
461                   "the server with --enable-debug configuration option.\n");
462 #endif
463           break;
464         case 'f':
465           silcd_config_file = strdup(optarg);
466           break;
467         case 'F':
468           foreground = TRUE;
469           break;
470
471           /*
472            * Key management options
473            */
474         case 'C':
475           opt_create_keypair = TRUE;
476           if (optarg)
477             opt_keypath = strdup(optarg);
478           break;
479         case 10:
480           if (optarg)
481             opt_pkcs = strdup(optarg);
482           break;
483         case 11:
484           if (optarg)
485             opt_bits = atoi(optarg);
486           break;
487         case 12:
488           if (optarg)
489             opt_identifier = strdup(optarg);
490           break;
491
492         default:
493           silc_usage();
494           break;
495       }
496     }
497   }
498
499   if (opt_create_keypair == TRUE) {
500     /* Create new key pair and exit */
501     silc_cipher_register_default();
502     silc_pkcs_register_default();
503     silc_hash_register_default();
504     silc_hmac_register_default();
505     silc_server_create_key_pair(opt_pkcs, opt_bits, opt_keypath,
506                                 opt_identifier, NULL, NULL);
507     exit(0);
508   }
509
510   /* Default configuration file */
511   if (!silcd_config_file)
512     silcd_config_file = strdup(SILC_SERVER_CONFIG_FILE);
513
514   /* Create SILC Server object */
515   ret = silc_server_alloc(&silcd);
516   if (ret == FALSE)
517     goto fail;
518
519   /* Read configuration files */
520   silcd->config = silc_server_config_alloc(silcd_config_file);
521   if (silcd->config == NULL)
522     goto fail;
523   silcd->config_file = silcd_config_file;
524
525   /* Check for another silcd running */
526   silc_server_checkpid(silcd);
527
528   /* Initialize the server */
529   if (silc_server_init(silcd) == FALSE)
530     goto fail;
531
532   /* Ignore SIGPIPE */
533   sa.sa_handler = SIG_IGN;
534   sa.sa_flags = 0;
535   sigemptyset(&sa.sa_mask);
536   sigaction(SIGPIPE, &sa, NULL);
537   sa.sa_handler = signal_handler;
538   sigaction(SIGHUP, &sa, NULL);
539   sigaction(SIGTERM, &sa, NULL);
540   sigaction(SIGINT, &sa, NULL);
541   sigaction(SIGUSR1, &sa, NULL);
542   silc_schedule_signal_register(silcd->schedule, SIGHUP, got_hup, NULL);
543   silc_schedule_signal_register(silcd->schedule, SIGTERM, stop_server, NULL);
544   silc_schedule_signal_register(silcd->schedule, SIGINT, stop_server, NULL);
545   silc_schedule_signal_register(silcd->schedule, SIGUSR1, dump_stats, NULL);
546
547   if (!foreground) {
548     /* Before running the server, fork to background. */
549     silc_server_daemonise(silcd);
550
551     /* If set, write pid to file */
552     if (silcd->config->server_info->pid_file) {
553       char buf[10], *pidfile = silcd->config->server_info->pid_file;
554       unlink(pidfile);
555       snprintf(buf, sizeof(buf) - 1, "%d\n", getpid());
556       silc_file_writefile(pidfile, buf, strlen(buf));
557     }
558   }
559
560   /* Drop root if we are not in debug mode, so you don't need to bother about
561      file writing permissions and so on */
562   if (!silc_debug)
563     silc_server_drop_privs(silcd);
564
565   /* Run the server. When this returns the server has been stopped
566      and we will exit. */
567   silc_server_run(silcd);
568
569   /* Stop the server and free it. */
570   silc_server_stop(silcd);
571   silc_server_config_destroy(silcd->config);
572   silc_server_free(silcd);
573
574   /* Flush the logging system */
575   silc_log_flush_all();
576
577   silc_free(silcd_config_file);
578   silc_free(opt_identifier);
579   silc_free(opt_keypath);
580   exit(0);
581
582  fail:
583   silc_free(silcd_config_file);
584   silc_free(opt_identifier);
585   silc_free(opt_keypath);
586   exit(1);
587 }
588
589 /* Returns identifier string for public key generation. */
590
591 static char *silc_server_create_identifier(void)
592 {
593   char *username = NULL, *realname = NULL;
594   char hostname[256], email[256];
595
596   /* Get realname */
597   realname = silc_get_real_name();
598
599   /* Get hostname */
600   memset(hostname, 0, sizeof(hostname));
601   gethostname(hostname, sizeof(hostname));
602
603   /* Get username (mandatory) */
604   username = silc_get_username();
605   if (!username)
606     return NULL;
607
608   /* Create default email address, whether it is right or not */
609   snprintf(email, sizeof(email), "%s@%s", username, hostname);
610
611   return silc_pkcs_encode_identifier(username, hostname, realname, email,
612                                      NULL, NULL);
613 }
614
615 /* Creates new public key and private key pair. This is used only
616    when user wants to create new key pair from command line. */
617
618 static int
619 silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
620                             char *identifier,
621                             SilcPublicKey *ret_pub_key,
622                             SilcPrivateKey *ret_prv_key)
623 {
624   SilcPKCS pkcs;
625   SilcPublicKey pub_key;
626   SilcPrivateKey prv_key;
627   SilcRng rng;
628   unsigned char *key;
629   SilcUInt32 key_len;
630   char pkfile[256], prvfile[256];
631
632   if (!pkcs_name || !path)
633     return FALSE;
634
635   if (!silc_pkcs_is_supported(pkcs_name)) {
636     fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
637     return FALSE;
638   }
639
640   if (!bits)
641     bits = 2048;
642
643   if (!identifier)
644     identifier = silc_server_create_identifier();
645
646   rng = silc_rng_alloc();
647   silc_rng_init(rng);
648   silc_rng_global_init(rng);
649
650   snprintf(pkfile, sizeof(pkfile) - 1, "%s%s", path,
651            SILC_SERVER_PUBLIC_KEY_NAME);
652   snprintf(prvfile, sizeof(prvfile) - 1, "%s%s", path,
653            SILC_SERVER_PRIVATE_KEY_NAME);
654
655   /* Generate keys */
656   silc_pkcs_alloc(pkcs_name, &pkcs);
657   silc_pkcs_generate_key(pkcs, bits, rng);
658
659   /* Save public key into file */
660   key = silc_pkcs_get_public_key(pkcs, &key_len);
661   pub_key = silc_pkcs_public_key_alloc(silc_pkcs_get_name(pkcs), identifier,
662                                        key, key_len);
663   silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
664   if (ret_pub_key)
665     *ret_pub_key = pub_key;
666   else
667     silc_pkcs_public_key_free(pub_key);
668
669   memset(key, 0, sizeof(key_len));
670   silc_free(key);
671
672   /* Save private key into file */
673   key = silc_pkcs_get_private_key(pkcs, &key_len);
674   prv_key = silc_pkcs_private_key_alloc(silc_pkcs_get_name(pkcs),
675                                         key, key_len);
676   silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
677   if (ret_prv_key)
678     *ret_prv_key = prv_key;
679   else
680     silc_pkcs_private_key_free(prv_key);
681
682   printf("Public key has been saved into `%s'\n", pkfile);
683   printf("Private key has been saved into `%s'\n", prvfile);
684
685   memset(key, 0, sizeof(key_len));
686   silc_free(key);
687
688   silc_rng_free(rng);
689   silc_pkcs_free(pkcs);
690
691   return TRUE;
692 }