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