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