5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2000 Pekka Riikonen
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.
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.
23 * Revision 1.3 2000/07/05 06:12:05 priikone
24 * Global cosmetic changes.
26 * Revision 1.2 2000/07/03 05:49:11 priikone
27 * Remove ':' from the end of line when in commands section. Command
28 * execution should work now from config file ok.
30 * Revision 1.1.1.1 2000/06/27 11:36:56 priikone
31 * Imported from internal CVS/Added Log headers.
36 #include "clientincludes.h"
37 #include "clientconfig.h"
40 All possible configuration sections for SILC client.
42 SilcClientConfigSection silc_client_config_sections[] = {
44 SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER, 4 },
46 SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS, 2 },
48 SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION, 4 },
50 SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION, 4 },
52 SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND, 0 },
54 { NULL, SILC_CLIENT_CONFIG_SECTION_TYPE_NONE, 0 }
57 /* Allocates a new configuration object, opens configuration file and
58 parses the file. The parsed data is returned to the newly allocated
59 configuration object. */
61 SilcClientConfig silc_client_config_alloc(char *filename)
65 SilcClientConfigParse config_parse;
67 SILC_LOG_DEBUG(("Allocating new configuration object"));
69 new = silc_calloc(1, sizeof(*new));
70 new->filename = filename;
72 /* Open configuration file and parse it */
75 silc_config_open(filename, &buffer);
78 if ((silc_client_config_parse(new, buffer, &config_parse)) == FALSE)
80 if ((silc_client_config_parse_lines(new, config_parse)) == FALSE)
92 /* Free's a configuration object. */
94 void silc_client_config_free(SilcClientConfig config)
102 /* Parses the the buffer and returns the parsed lines into return_config
103 argument. The return_config argument doesn't have to be initialized
104 before calling this. It will be initialized during the parsing. The
105 buffer sent as argument can be safely free'd after this function has
106 succesfully returned. */
108 int silc_client_config_parse(SilcClientConfig config, SilcBuffer buffer,
109 SilcClientConfigParse *return_config)
112 unsigned int linenum;
113 char line[1024], *cp;
114 SilcClientConfigSection *cptr = NULL;
115 SilcClientConfigParse parse = *return_config, first = NULL;
117 SILC_LOG_DEBUG(("Parsing configuration file"));
121 while((begin = silc_gets(line, sizeof(line),
122 buffer->data, buffer->len, begin)) != EOF) {
126 /* Check for bad line */
127 if (silc_check_line(cp))
130 /* Remove tabs and whitespaces from the line */
131 if (strchr(cp, '\t')) {
133 while(strchr(cp + i, '\t')) {
134 *strchr(cp + i, '\t') = ' ';
138 for (i = 0; i < strlen(cp); i++) {
154 /* Remove new line sign */
155 if (strchr(cp, '\n'))
156 *strchr(cp, '\n') = '\0';
158 /* Check for matching sections */
159 for (cptr = silc_client_config_sections; cptr->section; cptr++)
160 if (!strncasecmp(cp, cptr->section, strlen(cptr->section)))
163 if (!cptr->section) {
164 fprintf(stderr, "%s:%d: Unknown section `%s'\n",
165 config->filename, linenum, cp);
172 * Start of a configuration line
175 /* Handle config section */
176 if (cptr->type != SILC_CLIENT_CONFIG_SECTION_TYPE_NONE) {
178 if (strchr(cp, '\n'))
179 *strchr(cp, '\n') = ':';
182 parse = silc_calloc(1, sizeof(*parse));
184 parse->section = NULL;
188 if (parse->next == NULL) {
189 parse->next = silc_calloc(1, sizeof(*parse->next));
190 parse->next->line = NULL;
191 parse->next->section = NULL;
192 parse->next->next = NULL;
193 parse->next->prev = parse;
201 /* Add the line to parsing structure for further parsing. */
203 parse->section = cptr;
204 parse->line = silc_buffer_alloc(strlen(cp) + 1);
205 parse->linenum = linenum;
206 silc_buffer_pull_tail(parse->line, strlen(cp));
207 silc_buffer_put(parse->line, cp, strlen(cp));
214 /* Set the return_config argument to its first value so that further
215 parsing can be started from the first line. */
216 *return_config = first;
221 /* Parses the lines earlier read from configuration file. The config object
222 must not be initialized, it will be initialized in this function. The
223 parse_config argument is uninitialized automatically during this
226 int silc_client_config_parse_lines(SilcClientConfig config,
227 SilcClientConfigParse parse_config)
229 int ret, check = FALSE;
231 SilcClientConfigParse pc = parse_config;
234 SILC_LOG_DEBUG(("Parsing configuration lines"));
243 /* Get number of tokens in line (command section is handeled
244 specially and has no tokens at all). */
245 ret = silc_config_check_num_token(line);
246 if (ret != pc->section->maxfields &&
247 pc->section->type != SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND) {
249 fprintf(stderr, "%s:%d: Missing tokens, %d tokens (should be %d)\n",
250 config->filename, pc->linenum, ret,
251 pc->section->maxfields);
256 switch(pc->section->type) {
257 case SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER:
259 if (!config->cipher) {
260 config->cipher = silc_calloc(1, sizeof(*config->cipher));
261 config->cipher->next = NULL;
262 config->cipher->prev = NULL;
264 if (!config->cipher->next) {
265 config->cipher->next =
266 silc_calloc(1, sizeof(*config->cipher->next));
267 config->cipher->next->next = NULL;
268 config->cipher->next->prev = config->cipher;
269 config->cipher = config->cipher->next;
273 /* Get cipher name */
274 ret = silc_config_get_token(line, &config->cipher->alg_name);
278 fprintf(stderr, "%s:%d: Cipher name not defined\n",
279 config->filename, pc->linenum);
283 /* Get module name */
284 config->cipher->sim_name = NULL;
285 ret = silc_config_get_token(line, &config->cipher->sim_name);
289 /* Get block length */
290 ret = silc_config_get_token(line, &tmp);
294 fprintf(stderr, "%s:%d: Cipher block length not defined\n",
295 config->filename, pc->linenum);
298 config->cipher->block_len = atoi(tmp);
302 ret = silc_config_get_token(line, &tmp);
306 fprintf(stderr, "%s:%d: Cipher key length not defined\n",
307 config->filename, pc->linenum);
310 config->cipher->key_len = atoi(tmp);
316 case SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS:
319 config->pkcs = silc_calloc(1, sizeof(*config->pkcs));
320 config->pkcs->next = NULL;
321 config->pkcs->prev = NULL;
323 if (!config->pkcs->next) {
325 silc_calloc(1, sizeof(*config->pkcs->next));
326 config->pkcs->next->next = NULL;
327 config->pkcs->next->prev = config->pkcs;
328 config->pkcs = config->pkcs->next;
333 ret = silc_config_get_token(line, &config->pkcs->alg_name);
337 fprintf(stderr, "%s:%d: PKCS name not defined\n",
338 config->filename, pc->linenum);
343 ret = silc_config_get_token(line, &tmp);
347 fprintf(stderr, "%s:%d: PKCS key length not defined\n",
348 config->filename, pc->linenum);
351 config->pkcs->key_len = atoi(tmp);
357 case SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION:
359 if (!config->hash_func) {
360 config->hash_func = silc_calloc(1, sizeof(*config->hash_func));
361 config->hash_func->next = NULL;
362 config->hash_func->prev = NULL;
364 if (!config->hash_func->next) {
365 config->hash_func->next =
366 silc_calloc(1, sizeof(*config->hash_func->next));
367 config->hash_func->next->next = NULL;
368 config->hash_func->next->prev = config->hash_func;
369 config->hash_func = config->hash_func->next;
373 /* Get Hash function name */
374 ret = silc_config_get_token(line, &config->hash_func->alg_name);
378 fprintf(stderr, "%s:%d: Hash function name not defined\n",
379 config->filename, pc->linenum);
383 /* Get Hash function module name */
384 config->hash_func->sim_name = NULL;
385 ret = silc_config_get_token(line, &config->hash_func->sim_name);
389 /* Get block length */
390 ret = silc_config_get_token(line, &tmp);
394 fprintf(stderr, "%s:%d: Hash function block length not defined\n",
395 config->filename, pc->linenum);
398 config->hash_func->block_len = atoi(tmp);
401 /* Get hash length */
402 ret = silc_config_get_token(line, &tmp);
406 fprintf(stderr, "%s:%d: Hash function hash length not defined\n",
407 config->filename, pc->linenum);
410 config->hash_func->key_len = atoi(tmp);
416 case SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION:
418 if (!config->conns) {
419 config->conns = silc_calloc(1, sizeof(*config->conns));
420 config->conns->next = NULL;
421 config->conns->prev = NULL;
423 if (!config->conns->next) {
424 config->conns->next = silc_calloc(1, sizeof(*config->conns));
425 config->conns->next->next = NULL;
426 config->conns->next->prev = config->conns;
427 config->conns = config->conns->next;
432 ret = silc_config_get_token(line, &config->conns->host);
437 config->conns->host = strdup("*");
439 /* Get authentication method */
440 ret = silc_config_get_token(line, &tmp);
444 if (strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD) &&
445 strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY)) {
446 fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
447 config->filename, pc->linenum, tmp);
451 if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD))
452 config->conns->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
454 if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY))
455 config->conns->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
460 /* Get authentication data */
461 ret = silc_config_get_token(line, &config->conns->auth_data);
466 ret = silc_config_get_token(line, &tmp);
470 config->conns->port = atoi(tmp);
477 case SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND:
479 if (!config->commands) {
480 config->commands = silc_calloc(1, sizeof(*config->commands));
481 config->commands->next = NULL;
482 config->commands->prev = NULL;
484 if (!config->commands->next) {
485 config->commands->next = silc_calloc(1, sizeof(*config->commands));
486 config->commands->next->next = NULL;
487 config->commands->next->prev = config->commands;
488 config->commands = config->commands->next;
492 /* Get command line (this may include parameters as well. They
493 will be parsed later with standard command parser when
494 executing particular command.) */
495 config->commands->command = silc_calloc(strlen(line->data),
497 memcpy(config->commands->command, line->data, strlen(line->data) - 1);
504 case SILC_CLIENT_CONFIG_SECTION_TYPE_NONE:
509 /* Check for error */
510 if (check == FALSE) {
511 /* Line could not be parsed */
512 fprintf(stderr, "%s:%d: Parse error\n", config->filename, pc->linenum);
522 /* Before returning all the lists in the config object must be set
523 to their first values (the last value is first here). */
524 while (config->cipher && config->cipher->prev)
525 config->cipher = config->cipher->prev;
526 while (config->pkcs && config->pkcs->prev)
527 config->pkcs = config->pkcs->prev;
528 while (config->hash_func && config->hash_func->prev)
529 config->hash_func = config->hash_func->prev;
530 while (config->conns && config->conns->prev)
531 config->conns = config->conns->prev;
532 while (config->commands && config->commands->prev)
533 config->commands = config->commands->prev;
535 SILC_LOG_DEBUG(("Done"));
540 /* Registers configured ciphers. These can then be allocated by the
541 client when needed. */
543 void silc_client_config_register_ciphers(SilcClientConfig config)
545 SilcClientConfigSectionAlg *alg;
546 SilcClient client = (SilcClient)config->client;
548 SILC_LOG_DEBUG(("Registering configured ciphers"));
550 alg = config->cipher;
553 if (!alg->sim_name) {
554 /* Crypto module is supposed to be built in. Nothing to be done
555 here except to test that the cipher really is built in. */
556 SilcCipher tmp = NULL;
558 if (silc_cipher_alloc(alg->alg_name, &tmp) == FALSE) {
559 SILC_LOG_ERROR(("Unsupported cipher `%s'", alg->alg_name));
560 silc_client_stop(client);
563 silc_cipher_free(tmp);
567 /* Load (try at least) the crypto SIM module */
568 SilcCipherObject cipher;
571 memset(&cipher, 0, sizeof(cipher));
572 cipher.name = alg->alg_name;
573 cipher.block_len = alg->block_len;
574 cipher.key_len = alg->key_len * 8;
576 sim = silc_sim_alloc();
577 sim->type = SILC_SIM_CIPHER;
578 sim->libname = alg->sim_name;
580 if ((silc_sim_load(sim))) {
582 silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
583 SILC_CIPHER_SIM_SET_KEY));
584 SILC_LOG_DEBUG(("set_key=%p", cipher.set_key));
585 cipher.set_key_with_string =
586 silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
587 SILC_CIPHER_SIM_SET_KEY_WITH_STRING));
588 SILC_LOG_DEBUG(("set_key_with_string=%p", cipher.set_key_with_string));
590 silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
591 SILC_CIPHER_SIM_ENCRYPT_CBC));
592 SILC_LOG_DEBUG(("encrypt_cbc=%p", cipher.encrypt));
594 silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
595 SILC_CIPHER_SIM_DECRYPT_CBC));
596 SILC_LOG_DEBUG(("decrypt_cbc=%p", cipher.decrypt));
598 silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
599 SILC_CIPHER_SIM_CONTEXT_LEN));
600 SILC_LOG_DEBUG(("context_len=%p", cipher.context_len));
602 /* Put the SIM to the table of all SIM's in client */
603 client->sim = silc_realloc(client->sim,
604 sizeof(*client->sim) *
605 (client->sim_count + 1));
606 client->sim[client->sim_count] = sim;
609 SILC_LOG_ERROR(("Error configuring ciphers"));
610 silc_client_stop(client);
614 /* Register the cipher */
615 silc_cipher_register(&cipher);
623 /* Registers configured PKCS's. */
624 /* XXX: This really doesn't do anything now since we have statically
625 registered our PKCS's. This should be implemented when PKCS works
626 as SIM's. This checks now only that the PKCS user requested is
629 void silc_client_config_register_pkcs(SilcClientConfig config)
631 SilcClientConfigSectionAlg *alg = config->pkcs;
632 SilcClient client = (SilcClient)config->client;
635 SILC_LOG_DEBUG(("Registering configured PKCS"));
639 if (silc_pkcs_alloc(alg->alg_name, &tmp) == FALSE) {
640 SILC_LOG_ERROR(("Unsupported PKCS `%s'", alg->alg_name));
641 silc_client_stop(client);
650 /* Registers configured hash functions. These can then be allocated by the
651 client when needed. */
653 void silc_client_config_register_hashfuncs(SilcClientConfig config)
655 SilcClientConfigSectionAlg *alg;
656 SilcClient client = (SilcClient)config->client;
658 SILC_LOG_DEBUG(("Registering configured hash functions"));
660 alg = config->hash_func;
663 if (!alg->sim_name) {
664 /* Hash module is supposed to be built in. Nothing to be done
665 here except to test that the hash function really is built in. */
668 if (silc_hash_alloc(alg->alg_name, &tmp) == FALSE) {
669 SILC_LOG_ERROR(("Unsupported hash function `%s'", alg->alg_name));
670 silc_client_stop(client);
677 /* Load (try at least) the hash SIM module */
681 memset(&hash, 0, sizeof(hash));
682 hash.name = alg->alg_name;
683 hash.block_len = alg->block_len;
684 hash.hash_len = alg->key_len;
686 sim = silc_sim_alloc();
687 sim->type = SILC_SIM_HASH;
688 sim->libname = alg->sim_name;
690 if ((silc_sim_load(sim))) {
692 silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
693 SILC_HASH_SIM_INIT));
694 SILC_LOG_DEBUG(("init=%p", hash.init));
696 silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
697 SILC_HASH_SIM_UPDATE));
698 SILC_LOG_DEBUG(("update=%p", hash.update));
700 silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
701 SILC_HASH_SIM_FINAL));
702 SILC_LOG_DEBUG(("final=%p", hash.final));
704 silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
705 SILC_HASH_SIM_CONTEXT_LEN));
706 SILC_LOG_DEBUG(("context_len=%p", hash.context_len));
708 /* Put the SIM to the table of all SIM's in client */
709 client->sim = silc_realloc(client->sim,
710 sizeof(*client->sim) *
711 (client->sim_count + 1));
712 client->sim[client->sim_count] = sim;
715 SILC_LOG_ERROR(("Error configuring hash functions"));
716 silc_client_stop(client);
720 /* Register the cipher */
721 silc_hash_register(&hash);
730 SilcClientConfigSectionConnection *
731 silc_client_config_find_connection(SilcClientConfig config,
732 char *host, int port)
735 SilcClientConfigSectionConnection *conn = NULL;
737 SILC_LOG_DEBUG(("Finding connection"));
745 conn = config->conns;
746 for (i = 0; conn; i++) {
747 if (silc_string_compare(conn->host, host))
755 SILC_LOG_DEBUG(("Found match"));