Merge commit 'origin/silc.1.1.branch'
[silc.git] / apps / silc / clientconfig.c
1 /*
2
3   serverconfig.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
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 /* $Id$ */
21
22 #include "clientincludes.h"
23 #include "clientconfig.h"
24
25 /* 
26    All possible configuration sections for SILC client.
27 */
28 SilcClientConfigSection silc_client_config_sections[] = {
29   { "[cipher]", 
30     SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER, 4 },
31   { "[pkcs]", 
32     SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS, 1 },
33   { "[hash]", 
34     SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION, 4 },
35   { "[hmac]", 
36     SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC, 3 },
37   { "[connection]", 
38     SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION, 4 },
39   { "[commands]", 
40     SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND, 0 },
41   
42   { NULL, SILC_CLIENT_CONFIG_SECTION_TYPE_NONE, 0 }
43 };
44
45 /* Allocates a new configuration object, opens configuration file and
46    parses the file. The parsed data is returned to the newly allocated
47    configuration object. */
48
49 SilcClientConfig silc_client_config_alloc(char *filename)
50 {
51   SilcClientConfig new;
52   SilcBuffer buffer;
53   SilcClientConfigParse config_parse;
54
55   SILC_LOG_DEBUG(("Allocating new configuration object"));
56
57   new = silc_calloc(1, sizeof(*new));
58   new->filename = filename;
59
60   /* Open configuration file and parse it */
61   config_parse = NULL;
62   buffer = NULL;
63   silc_config_open(filename, &buffer);
64   if (!buffer)
65     goto fail;
66   if ((silc_client_config_parse(new, buffer, &config_parse)) == FALSE)
67     goto fail;
68   if ((silc_client_config_parse_lines(new, config_parse)) == FALSE)
69     goto fail;
70
71   silc_free(buffer);
72
73   return new;
74
75  fail:
76   silc_free(new);
77   return NULL;
78 }
79
80 /* Free's a configuration object. */
81
82 void silc_client_config_free(SilcClientConfig config)
83 {
84   if (config) {
85
86     silc_free(config);
87   }
88 }
89
90 /* Parses the the buffer and returns the parsed lines into return_config
91    argument. The return_config argument doesn't have to be initialized 
92    before calling this. It will be initialized during the parsing. The
93    buffer sent as argument can be safely free'd after this function has
94    succesfully returned. */
95
96 int silc_client_config_parse(SilcClientConfig config, SilcBuffer buffer, 
97                              SilcClientConfigParse *return_config)
98 {
99   int i, begin;
100   int linenum;
101   char line[1024], *cp;
102   SilcClientConfigSection *cptr = NULL;
103   SilcClientConfigParse parse = *return_config, first = NULL;
104
105   SILC_LOG_DEBUG(("Parsing configuration file"));
106
107   begin = 0;
108   linenum = 0;
109   while((begin = silc_gets(line, sizeof(line), 
110                            buffer->data, buffer->len, begin)) != EOF) {
111     cp = line;
112     linenum++;
113
114     /* Check for bad line */
115     if (silc_check_line(cp))
116       continue;
117
118     /* Remove tabs and whitespaces from the line */
119     if (strchr(cp, '\t')) {
120       i = 0;
121       while(strchr(cp + i, '\t')) {
122         *strchr(cp + i, '\t') = ' ';
123         i++;
124       }
125     }
126     for (i = 0; i < strlen(cp); i++) {
127       if (cp[i] != ' ') {
128         if (i)
129           cp++;
130         break;
131       }
132       cp++;
133     }
134
135     /* Parse line */
136     switch(cp[0]) {
137     case '[':
138       /*
139        * Start of a section
140        */
141
142       /* Remove new line sign */
143       if (strchr(cp, '\n'))
144         *strchr(cp, '\n') = '\0';
145       
146       /* Check for matching sections */
147       for (cptr = silc_client_config_sections; cptr->section; cptr++)
148         if (!strncasecmp(cp, cptr->section, strlen(cptr->section)))
149           break;
150
151       if (!cptr->section) {
152         fprintf(stderr, "%s:%d: Unknown section `%s'\n", 
153                         config->filename, linenum, cp);
154         return FALSE;
155       }
156
157       break;
158     default:
159       /*
160        * Start of a configuration line
161        */
162
163       /* Handle config section */
164       if (cptr->type != SILC_CLIENT_CONFIG_SECTION_TYPE_NONE) {
165         
166         if (strchr(cp, '\n'))
167             *strchr(cp, '\n') = ':';
168
169         if (parse == NULL) {
170           parse = silc_calloc(1, sizeof(*parse));
171           parse->line = NULL;
172           parse->section = NULL;
173           parse->next = NULL;
174           parse->prev = NULL;
175         } else {
176           if (parse->next == NULL) {
177             parse->next = silc_calloc(1, sizeof(*parse->next));
178             parse->next->line = NULL;
179             parse->next->section = NULL;
180             parse->next->next = NULL;
181             parse->next->prev = parse;
182             parse = parse->next;
183           }
184         }
185         
186         if (first == NULL)
187           first = parse;
188
189         /* Add the line to parsing structure for further parsing. */
190         if (parse) {
191           parse->section = cptr;
192           parse->line = silc_buffer_alloc(strlen(cp) + 1);
193           parse->linenum = linenum;
194           silc_buffer_pull_tail(parse->line, strlen(cp));
195           silc_buffer_put(parse->line, cp, strlen(cp));
196         }
197       }
198       break;
199     }
200   }
201   
202   /* Set the return_config argument to its first value so that further
203      parsing can be started from the first line. */
204   *return_config = first;
205
206   return TRUE;
207 }
208
209 /* Parses the lines earlier read from configuration file. The config object
210    must not be initialized, it will be initialized in this function. The
211    parse_config argument is uninitialized automatically during this
212    function. */
213
214 int silc_client_config_parse_lines(SilcClientConfig config, 
215                                    SilcClientConfigParse parse_config)
216 {
217   int ret, check = FALSE;
218   char *tmp;
219   SilcClientConfigParse pc = parse_config;
220   SilcBuffer line;
221
222   SILC_LOG_DEBUG(("Parsing configuration lines"));
223   
224   if (!config)
225     return FALSE;
226   
227   while(pc) {
228     check = FALSE;
229     line = pc->line;
230
231     /* Get number of tokens in line (command section is handeled
232        specially and has no tokens at all). */
233     ret = silc_config_check_num_token(line);
234     if (ret != pc->section->maxfields && 
235         pc->section->type != SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND) {
236       /* Bad line */
237       fprintf(stderr, "%s:%d: Missing tokens, %d tokens (should be %d)\n",
238               config->filename, pc->linenum, ret, 
239               pc->section->maxfields);
240       break;
241     }
242
243     /* Parse the line */
244     switch(pc->section->type) {
245     case SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER:
246
247       if (!config->cipher) {
248         config->cipher = silc_calloc(1, sizeof(*config->cipher));
249         config->cipher->next = NULL;
250         config->cipher->prev = NULL;
251       } else {
252         if (!config->cipher->next) {
253           config->cipher->next = 
254             silc_calloc(1, sizeof(*config->cipher->next));
255           config->cipher->next->next = NULL;
256           config->cipher->next->prev = config->cipher;
257           config->cipher = config->cipher->next;
258         }
259       }
260
261       /* Get cipher name */
262       ret = silc_config_get_token(line, &config->cipher->alg_name);
263       if (ret < 0)
264         break;
265       if (ret == 0) {
266         fprintf(stderr, "%s:%d: Cipher name not defined\n",
267                 config->filename, pc->linenum);
268         break;
269       }
270
271       /* Get module name */
272       config->cipher->sim_name = NULL;
273       ret = silc_config_get_token(line, &config->cipher->sim_name);
274       if (ret < 0)
275         break;
276
277       /* Get key length */
278       ret = silc_config_get_token(line, &tmp);
279       if (ret < 0)
280         break;
281       if (ret == 0) {
282         fprintf(stderr, "%s:%d: Cipher key length not defined\n",
283                 config->filename, pc->linenum);
284         break;
285       }
286       config->cipher->key_len = atoi(tmp);
287       silc_free(tmp);
288
289       /* Get block length */
290       ret = silc_config_get_token(line, &tmp);
291       if (ret < 0)
292         break;
293       if (ret == 0) {
294         fprintf(stderr, "%s:%d: Cipher block length not defined\n",
295                 config->filename, pc->linenum);
296         break;
297       }
298       config->cipher->block_len = atoi(tmp);
299       silc_free(tmp);
300
301       check = TRUE;
302       break;
303
304     case SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS:
305
306       if (!config->pkcs) {
307         config->pkcs = silc_calloc(1, sizeof(*config->pkcs));
308         config->pkcs->next = NULL;
309         config->pkcs->prev = NULL;
310       } else {
311         if (!config->pkcs->next) {
312           config->pkcs->next = 
313             silc_calloc(1, sizeof(*config->pkcs->next));
314           config->pkcs->next->next = NULL;
315           config->pkcs->next->prev = config->pkcs;
316           config->pkcs = config->pkcs->next;
317         }
318       }
319
320       /* Get PKCS name */
321       ret = silc_config_get_token(line, &config->pkcs->alg_name);
322       if (ret < 0)
323         break;
324       if (ret == 0) {
325         fprintf(stderr, "%s:%d: PKCS name not defined\n",
326                 config->filename, pc->linenum);
327         break;
328       }
329
330       check = TRUE;
331       break;
332
333     case SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION:
334
335       if (!config->hash_func) {
336         config->hash_func = silc_calloc(1, sizeof(*config->hash_func));
337         config->hash_func->next = NULL;
338         config->hash_func->prev = NULL;
339       } else {
340         if (!config->hash_func->next) {
341           config->hash_func->next = 
342             silc_calloc(1, sizeof(*config->hash_func->next));
343           config->hash_func->next->next = NULL;
344           config->hash_func->next->prev = config->hash_func;
345           config->hash_func = config->hash_func->next;
346         }
347       }
348
349       /* Get Hash function name */
350       ret = silc_config_get_token(line, &config->hash_func->alg_name);
351       if (ret < 0)
352         break;
353       if (ret == 0) {
354         fprintf(stderr, "%s:%d: Hash function name not defined\n",
355                 config->filename, pc->linenum);
356         break;
357       }
358       
359       /* Get Hash function module name */
360       config->hash_func->sim_name = NULL;
361       ret = silc_config_get_token(line, &config->hash_func->sim_name);
362       if (ret < 0)
363         break;
364
365       /* Get block length */
366       ret = silc_config_get_token(line, &tmp);
367       if (ret < 0)
368         break;
369       if (ret == 0) {
370         fprintf(stderr, "%s:%d: Hash function block length not defined\n",
371                 config->filename, pc->linenum);
372         break;
373       }
374       config->hash_func->block_len = atoi(tmp);
375       silc_free(tmp);
376
377       /* Get hash length */
378       ret = silc_config_get_token(line, &tmp);
379       if (ret < 0)
380         break;
381       if (ret == 0) {
382         fprintf(stderr, "%s:%d: Hash function hash length not defined\n",
383                 config->filename, pc->linenum);
384         break;
385       }
386       config->hash_func->key_len = atoi(tmp);
387       silc_free(tmp);
388
389       check = TRUE;
390       break;
391
392     case SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC:
393
394       if (!config->hmac) {
395         config->hmac = silc_calloc(1, sizeof(*config->hmac));
396         config->hmac->next = NULL;
397         config->hmac->prev = NULL;
398       } else {
399         if (!config->hmac->next) {
400           config->hmac->next = 
401             silc_calloc(1, sizeof(*config->hmac->next));
402           config->hmac->next->next = NULL;
403           config->hmac->next->prev = config->hmac;
404           config->hmac = config->hmac->next;
405         }
406       }
407
408       /* Get HMAC name */
409       ret = silc_config_get_token(line, &config->hmac->alg_name);
410       if (ret < 0)
411         break;
412       if (ret == 0) {
413         fprintf(stderr, "%s:%d: HMAC name not defined\n",
414                 config->filename, pc->linenum);
415         break;
416       }
417       
418       /* Get Hash function name */
419       ret = silc_config_get_token(line, &config->hmac->sim_name);
420       if (ret < 0)
421         break;
422       if (ret == 0) {
423         fprintf(stderr, "%s:%d: Hash function name not defined\n",
424                 config->filename, pc->linenum);
425         break;
426       }
427
428       /* Get MAC length */
429       ret = silc_config_get_token(line, &tmp);
430       if (ret < 0)
431         break;
432       if (ret == 0) {
433         fprintf(stderr, "%s:%d: HMAC's MAC length not defined\n",
434                 config->filename, pc->linenum);
435         break;
436       }
437       config->hmac->key_len = atoi(tmp);
438       silc_free(tmp);
439
440       check = TRUE;
441       break;
442
443     case SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION:
444
445       if (!config->conns) {
446         config->conns = silc_calloc(1, sizeof(*config->conns));
447         config->conns->next = NULL;
448         config->conns->prev = NULL;
449       } else {
450         if (!config->conns->next) {
451           config->conns->next = silc_calloc(1, sizeof(*config->conns));
452           config->conns->next->next = NULL;
453           config->conns->next->prev = config->conns;
454           config->conns = config->conns->next;
455         }
456       }
457       
458       /* Get host */
459       ret = silc_config_get_token(line, &config->conns->host);
460       if (ret < 0)
461         break;
462       if (ret == 0)
463         /* Any host */
464         config->conns->host = strdup("*");
465
466       /* Get authentication method */
467       ret = silc_config_get_token(line, &tmp);
468       if (ret < 0)
469         break;
470       if (ret) {
471         if (strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD) &&
472             strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY)) {
473           fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
474                   config->filename, pc->linenum, tmp);
475           break;
476         }
477
478         if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD))
479           config->conns->auth_meth = SILC_AUTH_PASSWORD;
480
481         if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY))
482           config->conns->auth_meth = SILC_AUTH_PUBLIC_KEY;
483
484         silc_free(tmp);
485       }
486
487       /* Get authentication data */
488       ret = silc_config_get_token(line, &config->conns->auth_data);
489       if (ret < 0)
490         break;
491
492       /* Get port */
493       ret = silc_config_get_token(line, &tmp);
494       if (ret < 0)
495         break;
496       if (ret) {
497         config->conns->port = atoi(tmp);
498         silc_free(tmp);
499       }
500
501       check = TRUE;
502       break;
503
504     case SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND:
505
506       if (!config->commands) {
507         config->commands = silc_calloc(1, sizeof(*config->commands));
508         config->commands->next = NULL;
509         config->commands->prev = NULL;
510       } else {
511         if (!config->commands->next) {
512           config->commands->next = silc_calloc(1, sizeof(*config->commands));
513           config->commands->next->next = NULL;
514           config->commands->next->prev = config->commands;
515           config->commands = config->commands->next;
516         }
517       }
518       
519       /* Get command line (this may include parameters as well. They
520          will be parsed later with standard command parser when
521          executing particular command.) */
522       config->commands->command = silc_calloc(strlen(line->data), 
523                                               sizeof(char));
524       memcpy(config->commands->command, line->data, strlen(line->data) - 1);
525       if (ret < 0)
526         break;
527
528       check = TRUE;
529       break;
530
531     case SILC_CLIENT_CONFIG_SECTION_TYPE_NONE:
532     default:
533       break;
534     }
535
536     /* Check for error */
537     if (check == FALSE) {
538       /* Line could not be parsed */
539       fprintf(stderr, "%s:%d: Parse error\n", config->filename, pc->linenum);
540       break;
541     }
542
543     pc = pc->next;
544   }
545
546   if (check == FALSE)
547     return FALSE;;
548
549   /* Before returning all the lists in the config object must be set
550      to their first values (the last value is first here). */
551   while (config->cipher && config->cipher->prev)
552     config->cipher = config->cipher->prev;
553   while (config->pkcs && config->pkcs->prev)
554     config->pkcs = config->pkcs->prev;
555   while (config->hash_func && config->hash_func->prev)
556     config->hash_func = config->hash_func->prev;
557   while (config->hmac && config->hmac->prev)
558     config->hmac = config->hmac->prev;
559   while (config->conns && config->conns->prev)
560     config->conns = config->conns->prev;
561   while (config->commands && config->commands->prev)
562     config->commands = config->commands->prev;
563   
564   SILC_LOG_DEBUG(("Done"));
565   
566   return TRUE;
567 }
568
569 /* Registers configured ciphers. These can then be allocated by the
570    client when needed. */
571
572 bool silc_client_config_register_ciphers(SilcClientConfig config)
573 {
574   SilcClientConfigSectionAlg *alg;
575   SilcClientInternal app = (SilcClientInternal)config->client;
576   SilcClient client = app->client;
577
578   SILC_LOG_DEBUG(("Registering configured ciphers"));
579
580   if (!config->cipher)
581     return FALSE;
582
583   alg = config->cipher;
584   while(alg) {
585
586     if (!alg->sim_name) {
587       /* Crypto module is supposed to be built in. Get the pointer to the
588          built in cipher and register it. */
589       int i;
590
591       for (i = 0; silc_default_ciphers[i].name; i++)
592         if (!strcmp(silc_default_ciphers[i].name, alg->alg_name)) {
593           silc_cipher_register(&silc_default_ciphers[i]);
594           break;
595         }
596
597       if (!silc_cipher_is_supported(alg->alg_name)) {
598         SILC_LOG_ERROR(("Unknown cipher `%s'", alg->alg_name));
599         silc_client_stop(client);
600         exit(1);
601       }
602
603 #ifdef SILC_SIM
604     } else {
605       /* Load (try at least) the crypto SIM module */
606       SilcCipherObject cipher;
607       SilcSimContext *sim;
608       char *alg_name;
609
610       memset(&cipher, 0, sizeof(cipher));
611       cipher.name = alg->alg_name;
612       cipher.block_len = alg->block_len;
613       cipher.key_len = alg->key_len * 8;
614
615       sim = silc_sim_alloc();
616       sim->type = SILC_SIM_CIPHER;
617       sim->libname = alg->sim_name;
618
619       alg_name = strdup(alg->alg_name);
620       if (strchr(alg_name, '-'))
621         *strchr(alg_name, '-') = '\0';
622
623       if ((silc_sim_load(sim))) {
624         cipher.set_key = 
625           silc_sim_getsym(sim, silc_sim_symname(alg_name, 
626                                                 SILC_CIPHER_SIM_SET_KEY));
627         SILC_LOG_DEBUG(("set_key=%p", cipher.set_key));
628         cipher.set_key_with_string = 
629           silc_sim_getsym(sim, silc_sim_symname(alg_name, 
630                                          SILC_CIPHER_SIM_SET_KEY_WITH_STRING));
631         SILC_LOG_DEBUG(("set_key_with_string=%p", cipher.set_key_with_string));
632         cipher.encrypt = 
633           silc_sim_getsym(sim, silc_sim_symname(alg_name,
634                                                 SILC_CIPHER_SIM_ENCRYPT_CBC));
635         SILC_LOG_DEBUG(("encrypt_cbc=%p", cipher.encrypt));
636         cipher.decrypt = 
637           silc_sim_getsym(sim, silc_sim_symname(alg_name,
638                                                 SILC_CIPHER_SIM_DECRYPT_CBC));
639         SILC_LOG_DEBUG(("decrypt_cbc=%p", cipher.decrypt));
640         cipher.context_len = 
641           silc_sim_getsym(sim, silc_sim_symname(alg_name,
642                                                 SILC_CIPHER_SIM_CONTEXT_LEN));
643         SILC_LOG_DEBUG(("context_len=%p", cipher.context_len));
644
645         /* Put the SIM to the table of all SIM's in client */
646         app->sim = silc_realloc(app->sim,
647                                    sizeof(*app->sim) * 
648                                    (app->sim_count + 1));
649         app->sim[app->sim_count] = sim;
650         app->sim_count++;
651
652         silc_free(alg_name);
653       } else {
654         SILC_LOG_ERROR(("Error configuring ciphers"));
655         silc_client_stop(client);
656         exit(1);
657       }
658
659       /* Register the cipher */
660       silc_cipher_register(&cipher);
661 #endif
662     }
663
664     alg = alg->next;
665   }
666
667   return TRUE;
668 }
669
670 /* Registers configured PKCS's. */
671
672 bool silc_client_config_register_pkcs(SilcClientConfig config)
673 {
674   SilcClientConfigSectionAlg *alg = config->pkcs;
675   SilcClientInternal app = (SilcClientInternal)config->client;
676   SilcClient client = app->client;
677
678   SILC_LOG_DEBUG(("Registering configured PKCS"));
679
680   if (!alg)
681     return FALSE;
682
683   while(alg) {
684     int i;
685     
686     for (i = 0; silc_default_pkcs[i].name; i++)
687       if (!strcmp(silc_default_pkcs[i].name, alg->alg_name)) {
688         silc_pkcs_register(&silc_default_pkcs[i]);
689         break;
690       }
691     
692     if (!silc_pkcs_is_supported(alg->alg_name)) {
693       SILC_LOG_ERROR(("Unknown PKCS `%s'", alg->alg_name));
694       silc_client_stop(client);
695       exit(1);
696     }
697
698     alg = alg->next;
699   }
700
701   return TRUE;
702 }
703
704 /* Registers configured hash funtions. These can then be allocated by the
705    client when needed. */
706
707 bool silc_client_config_register_hashfuncs(SilcClientConfig config)
708 {
709   SilcClientConfigSectionAlg *alg;
710   SilcClientInternal app = (SilcClientInternal)config->client;
711   SilcClient client = app->client;
712
713   SILC_LOG_DEBUG(("Registering configured hash functions"));
714
715   if (!config->hash_func)
716     return FALSE;
717
718   alg = config->hash_func;
719   while(alg) {
720     if (!alg->sim_name) {
721       int i;
722       
723       for (i = 0; silc_default_hash[i].name; i++)
724         if (!strcmp(silc_default_hash[i].name, alg->alg_name)) {
725           silc_hash_register(&silc_default_hash[i]);
726           break;
727         }
728       
729       if (!silc_hash_is_supported(alg->alg_name)) {
730         SILC_LOG_ERROR(("Unknown hash function `%s'", alg->alg_name));
731         silc_client_stop(client);
732         exit(1);
733       }
734 #ifdef SILC_SIM
735     } else {
736       /* Load (try at least) the hash SIM module */
737       SilcHashObject hash;
738       SilcSimContext *sim;
739
740       memset(&hash, 0, sizeof(hash));
741       hash.name = alg->alg_name;
742       hash.block_len = alg->block_len;
743       hash.hash_len = alg->key_len;
744
745       sim = silc_sim_alloc();
746       sim->type = SILC_SIM_HASH;
747       sim->libname = alg->sim_name;
748
749       if ((silc_sim_load(sim))) {
750         hash.init = 
751           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
752                                                 SILC_HASH_SIM_INIT));
753         SILC_LOG_DEBUG(("init=%p", hash.init));
754         hash.update = 
755           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
756                                                 SILC_HASH_SIM_UPDATE));
757         SILC_LOG_DEBUG(("update=%p", hash.update));
758         hash.final = 
759           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
760                                                 SILC_HASH_SIM_FINAL));
761         SILC_LOG_DEBUG(("final=%p", hash.final));
762         hash.context_len = 
763           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
764                                                 SILC_HASH_SIM_CONTEXT_LEN));
765         SILC_LOG_DEBUG(("context_len=%p", hash.context_len));
766
767         /* Put the SIM to the table of all SIM's in client */
768         app->sim = silc_realloc(app->sim,
769                                    sizeof(*app->sim) * 
770                                    (app->sim_count + 1));
771         app->sim[app->sim_count] = sim;
772         app->sim_count++;
773       } else {
774         SILC_LOG_ERROR(("Error configuring hash functions"));
775         silc_client_stop(client);
776         exit(1);
777       }
778
779       /* Register the hash function */
780       silc_hash_register(&hash);
781 #endif
782     }
783     alg = alg->next;
784   }
785
786   return TRUE;
787 }
788
789 /* Registers configured HMACs. These can then be allocated by the
790    client when needed. */
791
792 bool silc_client_config_register_hmacs(SilcClientConfig config)
793 {
794   SilcClientConfigSectionAlg *alg;
795   SilcClientInternal app = (SilcClientInternal)config->client;
796   SilcClient client = app->client;
797
798   SILC_LOG_DEBUG(("Registering configured HMACs"));
799
800   if (!config->hmac)
801     return FALSE;
802
803   alg = config->hmac;
804   while(alg) {
805     SilcHmacObject hmac;
806     
807     if (!silc_hash_is_supported(alg->sim_name)) {
808       SILC_LOG_ERROR(("Unknown hash function `%s' for HMAC `%s'", 
809                       alg->sim_name, alg->alg_name));
810       silc_client_stop(client);
811       exit(1);
812     }
813     
814     /* Register the HMAC */
815     memset(&hmac, 0, sizeof(hmac));
816     hmac.name = alg->alg_name;
817     hmac.len = alg->key_len;
818     silc_hmac_register(&hmac);
819
820     alg = alg->next;
821   }
822
823   return TRUE;
824 }
825
826 SilcClientConfigSectionConnection *
827 silc_client_config_find_connection(SilcClientConfig config, 
828                                    char *host, int port)
829 {
830   int i;
831   SilcClientConfigSectionConnection *conn = NULL;
832
833   SILC_LOG_DEBUG(("Finding connection"));
834
835   if (!host)
836     return NULL;
837
838   if (!config->conns)
839     return NULL;
840
841   conn = config->conns;
842   for (i = 0; conn; i++) {
843     if (silc_string_compare(conn->host, host))
844       break;
845     conn = conn->next;
846   }
847
848   if (!conn)
849     return NULL;
850
851   SILC_LOG_DEBUG(("Found match"));
852
853   return conn;
854 }