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