Browse code

- TLS configuration file parser (not yet integrated with rest of tls module)

Jan Janak authored on 22/02/2006 23:37:19
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,997 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * TLS configuration file parser
5
+ *
6
+ * Copyright (C) 2001-2003 FhG FOKUS
7
+ * Copyright (C) 2004,2005 Free Software Foundation, Inc.
8
+ * Copyright (C) 2005,2006 iptelorg GmbH
9
+ *
10
+ * This file is part of ser, a free SIP server.
11
+ *
12
+ * ser is free software; you can redistribute it and/or modify
13
+ * it under the terms of the GNU General Public License as published by
14
+ * the Free Software Foundation; either version 2 of the License, or
15
+ * (at your option) any later version
16
+ *
17
+ * For a license to use the ser software under conditions
18
+ * other than those described here, or to purchase support for this
19
+ * software, please contact iptel.org by e-mail at the following addresses:
20
+ *    info@iptel.org
21
+ *
22
+ * ser is distributed in the hope that it will be useful,
23
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
+ * GNU General Public License for more details.
26
+ *
27
+ * You should have received a copy of the GNU General Public License
28
+ * along with this program; if not, write to the Free Software
29
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30
+ *
31
+ */
32
+
33
+#include <stdio.h>
34
+#include <libgen.h>
35
+#include "../../dprint.h"
36
+#include "../../trim.h"
37
+#include "../../ut.h"
38
+#include "tls_config.h"
39
+#include "tls_domain.h"
40
+
41
+#define MAX_TOKEN_LEN 256
42
+
43
+
44
+/*
45
+ * Parser state
46
+ */
47
+static struct {
48
+	FILE* f;
49
+	char* file;
50
+	int line;
51
+	int col;
52
+} pstate;
53
+
54
+
55
+/*
56
+ * Structure representing lexical token
57
+ */
58
+typedef struct token {
59
+	char buf [MAX_TOKEN_LEN];
60
+	int type;  /* Token type */
61
+	str val;   /* Token value */
62
+
63
+	struct {   /* Position of first and last character of
64
+		    * token in file
65
+		    */
66
+		int line;
67
+		int col;
68
+	} start, end;
69
+} token_t;
70
+
71
+
72
+typedef int (*parser_func_f)(token_t* token);
73
+
74
+
75
+struct parser_tab {
76
+	str token;
77
+	union {
78
+		int ival;
79
+		str sval;
80
+		parser_func_f fval;
81
+	} u;
82
+};
83
+
84
+static struct parser_tab option_name[];
85
+static struct parser_tab token_method[];
86
+static struct parser_tab token_bool[];
87
+static struct parser_tab token_type[];
88
+static struct parser_tab token_default[];
89
+
90
+/*
91
+ * States of lexical scanner
92
+ */
93
+enum st {
94
+	ST_S,  /* Begin */
95
+	ST_A,  /* Alphanumeric */
96
+	ST_AE, /* Alphanumeric escaped */
97
+	ST_Q,  /* Quoted */
98
+	ST_QE, /* Quoted escaped */
99
+	ST_C,  /* Comment */
100
+	ST_CE, /* Comment escaped */
101
+	ST_E,  /* Escaped */
102
+};
103
+
104
+
105
+/* If the lexer finds a token then all characters
106
+ * untill EOL is scanned would belong to the token
107
+ */
108
+#define EXTENDED_ALPHA_TOKEN (1 << 0)
109
+
110
+
111
+/*
112
+ * Test for alphanumeric characters
113
+ */
114
+#define IS_ALPHA(c) \
115
+    ((c) >= 'a' && (c) <= 'z' || \
116
+     (c) >= 'A' && (c) <= 'Z' || \
117
+     (c) >= '0' && (c) <= '9' || \
118
+     (c) == '_')
119
+
120
+
121
+/*
122
+ * Test for delimiter characters
123
+ */
124
+#define IS_DELIM(c) \
125
+    ((c) == '=' || \
126
+     (c) == ':' || \
127
+     (c) == ';' || \
128
+     (c) == '.' || \
129
+     (c) == ',' || \
130
+     (c) == '?' || \
131
+     (c) == '[' || \
132
+     (c) == ']' || \
133
+     (c) == '/' || \
134
+     (c) == '@' || \
135
+     (c) == '!' || \
136
+     (c) == '$' || \
137
+     (c) == '%' || \
138
+     (c) == '&' || \
139
+     (c) == '*' || \
140
+     (c) == '(' || \
141
+     (c) == ')' || \
142
+     (c) == '-' || \
143
+     (c) == '+' || \
144
+     (c) == '|' || \
145
+     (c) == '\'')
146
+
147
+/* Whitespace characters */
148
+#define IS_WHITESPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r') 
149
+#define IS_QUOTE(c)      ((c) == '\"')  /* Quote characters */
150
+#define IS_COMMENT(c)    ((c) == '#')   /* Characters that start comments */
151
+#define IS_ESCAPE(c)     ((c) == '\\')  /* Escape characters */
152
+#define IS_EOL(c)        ((c) == '\n')  /* End of line */
153
+
154
+
155
+/*
156
+ * Append character to the value of current
157
+ * token
158
+ */
159
+#define PUSH(c)                                    \
160
+    if (token->val.len >= MAX_TOKEN_LEN) {         \
161
+        ERR("%s:%d:%d: Token too long\n",          \
162
+	    pstate.file, pstate.line, pstate.col); \
163
+        return -1;                                 \
164
+    }                                              \
165
+    if (token->val.len == 0) {                     \
166
+         token->start.line = pstate.line;          \
167
+         token->start.col = pstate.col;            \
168
+    }                                              \
169
+    token->val.s[token->val.len++] = (c);
170
+
171
+
172
+/*
173
+ * Return current token from the lexical analyzer
174
+ */    
175
+#define RETURN(c)                  \
176
+    token->end.line = pstate.line; \
177
+    token->end.col = pstate.col;   \
178
+    token->type = (c);             \
179
+    print_token(token);            \
180
+    return 1;
181
+
182
+
183
+/*
184
+ * Get next character and update counters
185
+ */
186
+#define READ_CHAR         \
187
+     c = fgetc(pstate.f); \
188
+     if (IS_EOL(c)) {     \
189
+         pstate.line++;   \
190
+	 pstate.col = 0;  \
191
+     } else {             \
192
+	 pstate.col++;    \
193
+     }
194
+
195
+
196
+enum {
197
+	TOKEN_EOF = -1,
198
+	TOKEN_ALPHA = -2,
199
+	TOKEN_STRING = -3
200
+};
201
+
202
+
203
+static void print_token(struct token* token)
204
+{
205
+	DBG("token(%d, '%.*s', <%d,%d>-<%d,%d>)\n", 
206
+	    token->type, token->val.len, ZSW(token->val.s),
207
+	    token->start.line, token->start.col, 
208
+	    token->end.line, token->end.col);
209
+}
210
+
211
+
212
+int tls_lex(token_t* token, unsigned int flags)
213
+{
214
+	static int look_ahead = EOF;
215
+	int c;
216
+	enum st state;
217
+
218
+	state = ST_S;
219
+	
220
+	token->val.s = token->buf;
221
+	token->val.len = 0;
222
+
223
+	if (look_ahead != EOF) {
224
+		c = look_ahead;
225
+		look_ahead = EOF;
226
+	} else {
227
+		READ_CHAR;
228
+	}
229
+
230
+	while(c != EOF) {
231
+		switch(state) {
232
+		case ST_S:
233
+			if (flags & EXTENDED_ALPHA_TOKEN) {
234
+				if (IS_WHITESPACE(c)) {
235
+					     /* Do nothing */
236
+				} else if (IS_ALPHA(c) ||
237
+					   IS_ESCAPE(c) ||
238
+					   IS_DELIM(c)) {
239
+					PUSH(c);
240
+					state = ST_A;
241
+				} else if (IS_QUOTE(c)) {
242
+					state = ST_Q;
243
+				} else if (IS_COMMENT(c)) {
244
+					state = ST_C;
245
+				} else if (IS_EOL(c)) {
246
+					PUSH(c);
247
+					RETURN(c);
248
+				} else {
249
+					ERR("%s:%d:%d: Invalid character 0x%x\n", 
250
+					    pstate.file, pstate.line, pstate.col, c);
251
+					return -1;
252
+				}
253
+			} else {
254
+				if (IS_WHITESPACE(c)) {
255
+					     /* Do nothing */
256
+				} else if (IS_ALPHA(c)) {
257
+					PUSH(c);
258
+					state = ST_A;
259
+				} else if (IS_QUOTE(c)) {
260
+					state = ST_Q;
261
+				} else if (IS_COMMENT(c)) {
262
+					state = ST_C;
263
+				} else if (IS_ESCAPE(c)) {
264
+					state = ST_E;
265
+				} else if (IS_DELIM(c) || IS_EOL(c)) {
266
+					PUSH(c);
267
+					RETURN(c);
268
+				} else {
269
+					ERR("%s:%d:%d: Invalid character 0x%x\n", 
270
+					    pstate.file, pstate.line, pstate.col, c);
271
+					return -1;
272
+				}
273
+			}
274
+			break;
275
+
276
+		case ST_A:
277
+			if (flags & EXTENDED_ALPHA_TOKEN) {
278
+				if (IS_ALPHA(c) ||
279
+				    IS_DELIM(c) ||
280
+				    IS_QUOTE(c)) {
281
+					PUSH(c);
282
+				} else if (IS_ESCAPE(c)) {
283
+					state = ST_AE;
284
+				} else if (IS_COMMENT(c) || IS_EOL(c) || IS_WHITESPACE(c)) {
285
+					look_ahead = c;
286
+					RETURN(TOKEN_ALPHA);
287
+				} else {
288
+					ERR("%s:%d:%d: Invalid character 0x%x\n", 
289
+					    pstate.file, pstate.line, pstate.col, c);
290
+					return -1;
291
+				}
292
+			} else {
293
+				if (IS_ALPHA(c)) {
294
+					PUSH(c);
295
+				} else if (IS_ESCAPE(c)) {
296
+					state = ST_AE;
297
+				} else if (IS_WHITESPACE(c) ||
298
+					   IS_DELIM(c) ||
299
+					   IS_QUOTE(c) ||
300
+					   IS_COMMENT(c) ||
301
+					   IS_EOL(c)) {
302
+					look_ahead = c;
303
+					RETURN(TOKEN_ALPHA);
304
+				} else {
305
+					ERR("%s:%d:%d: Invalid character 0x%x\n", 
306
+					    pstate.file, pstate.line, pstate.col, c);
307
+					return -1;
308
+				}
309
+			}
310
+			break;
311
+
312
+		case ST_AE:
313
+			if (IS_COMMENT(c) ||
314
+			    IS_QUOTE(c) ||
315
+			    IS_ESCAPE(c)) {
316
+				PUSH(c);
317
+			} else if (c == 'r') {
318
+				PUSH('\r');
319
+			} else if (c == 'n') {
320
+				PUSH('\n');
321
+			} else if (c == 't') {
322
+				PUSH('\t');
323
+			} else if (c == ' ') {
324
+				PUSH(' ');
325
+			} else if (IS_EOL(c)) {
326
+				     /* Do nothing */
327
+			} else {
328
+				ERR("%s:%d:%d: Unsupported escape character 0x%x\n", 
329
+				    pstate.file, pstate.line, pstate.col, c);
330
+				return -1;
331
+			}
332
+			state = ST_A;
333
+			break;
334
+
335
+		case ST_Q:
336
+			if (IS_QUOTE(c)) {
337
+				RETURN(TOKEN_STRING);
338
+			} else if (IS_ESCAPE(c)) {
339
+				state = ST_QE;
340
+				break;
341
+			} else {
342
+				PUSH(c);
343
+			}
344
+			break;
345
+
346
+		case ST_QE:
347
+			if (IS_ESCAPE(c) ||
348
+			    IS_QUOTE(c)) {
349
+				PUSH(c);
350
+			} else if (c == 'n') {
351
+				PUSH('\n');
352
+			} else if (c == 'r') {
353
+				PUSH('\r');
354
+			} else if (c == 't') {
355
+				PUSH('\t');
356
+			} else if (IS_EOL(c)) {
357
+				     /* Do nothing */
358
+			} else {
359
+				ERR("%s:%d:%d: Unsupported escape character 0x%x\n", 
360
+				    pstate.file, pstate.line, pstate.col, c);
361
+				return -1;
362
+			}
363
+			state = ST_Q;
364
+			break;
365
+
366
+		case ST_C:
367
+			if (IS_ESCAPE(c)) {
368
+				state = ST_CE;
369
+			} else if (IS_EOL(c)) {
370
+				state = ST_S;
371
+				continue; /* Do not read a new char, return EOL */
372
+			} else {
373
+				     /* Do nothing */
374
+			}
375
+			break;
376
+
377
+		case ST_CE:
378
+			state = ST_C;
379
+			break;
380
+
381
+		case ST_E:
382
+			if (IS_COMMENT(c) ||
383
+			    IS_QUOTE(c) ||
384
+			    IS_ESCAPE(c)) {
385
+				PUSH(c);
386
+				RETURN(c);
387
+			} else if (c == 'r') {
388
+				PUSH('\r');
389
+				RETURN('\r');
390
+			} else if (c == 'n') {
391
+				PUSH('\n');
392
+				RETURN('\n');
393
+			} else if (c == 't') {
394
+				PUSH('\t');
395
+				RETURN('\t');
396
+			} else if (c == ' ') {
397
+				PUSH(' ');
398
+				RETURN(' ');
399
+			} else if (IS_EOL(c)) {
400
+				     /* Escped eol means no eol */
401
+				state = ST_S;
402
+			} else {
403
+				ERR("%s:%d:%d: Unsupported escape character 0x%x\n", 
404
+				    pstate.file, pstate.line, pstate.col, c);
405
+				return -1;
406
+			}
407
+			break;
408
+		}
409
+
410
+		READ_CHAR;
411
+	};
412
+
413
+	switch(state) {
414
+	case ST_S: 
415
+	case ST_C:
416
+	case ST_CE:
417
+		return 0;
418
+
419
+	case ST_A:
420
+		RETURN(TOKEN_ALPHA);
421
+
422
+	case ST_Q:
423
+		ERR("%s:%d:%d: Premature end of file, missing closing quote in string constant\n", 
424
+		    pstate.file, pstate.line, pstate.col, c);
425
+		return -1;
426
+
427
+	case ST_QE:
428
+	case ST_E:
429
+	case ST_AE:
430
+		ERR("%s:%d:%d: Premature end of file, missing escaped character\n", 
431
+		    pstate.file, pstate.line, pstate.col);
432
+		return -1;
433
+	}
434
+}
435
+
436
+
437
+static struct parser_tab* lookup_token(struct parser_tab* table, str* token)
438
+{
439
+	struct parser_tab* ptr;
440
+
441
+	ptr = table;
442
+	while(ptr->token.s && ptr->token.len) {
443
+		if (token->len == ptr->token.len && 
444
+		    !strncasecmp(token->s, ptr->token.s, token->len)) {
445
+			return ptr;
446
+		}
447
+		ptr++;
448
+	}
449
+}
450
+
451
+
452
+static int parse_string_val(str* res, token_t* token)
453
+{
454
+	int ret;
455
+	token_t t;
456
+
457
+	ret = tls_lex(&t, 0);
458
+	if (ret < 0) return -1;
459
+	if (ret == 0) {
460
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n", 
461
+		    pstate.file, token->start.line, token->start.col);
462
+		return -1;
463
+	}
464
+
465
+	if (t.type != '=') {
466
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, '=' expected\n", 
467
+		    pstate.file, t.start.line, t.start.col);
468
+		return -1;
469
+	}
470
+
471
+	ret = tls_lex(&t, EXTENDED_ALPHA_TOKEN);
472
+	if (ret < 0) return -1;
473
+	if (ret == 0) {
474
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n",
475
+		    pstate.file, t.start.line, t.start.col);
476
+		return -1;
477
+	}	
478
+
479
+	if (t.type != TOKEN_ALPHA && t.type != TOKEN_STRING) {
480
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid option value '%.*s'\n", 
481
+		    pstate.file, t.start.line, t.start.col,
482
+		    t.val.len, ZSW(t.val.s));
483
+		return -1;
484
+	}
485
+
486
+	*res = t.val;;
487
+	return 0;
488
+}
489
+
490
+
491
+/*
492
+ * Parse method option
493
+ */
494
+static int parse_method_opt(token_t* token)
495
+{
496
+	int ret;
497
+	token_t t;
498
+	struct parser_tab* r;
499
+
500
+	ret = tls_lex(&t, 0);
501
+	if (ret < 0) return -1;
502
+	if (ret == 0) {
503
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n", 
504
+		    pstate.file, token->start.line, token->start.col);
505
+		return -1;
506
+	}
507
+
508
+	if (t.type != '=') {
509
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, '=' expected\n", 
510
+		    pstate.file, t.start.line, t.start.col);
511
+		return -1;
512
+	}
513
+
514
+	ret = tls_lex(&t, EXTENDED_ALPHA_TOKEN);
515
+	if (ret < 0) return -1;
516
+	if (ret == 0) {
517
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n",
518
+		    pstate.file, t.start.line, t.start.col);
519
+		return -1;
520
+	}	
521
+
522
+	if (t.type != TOKEN_ALPHA && t.type != TOKEN_STRING) {
523
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid option value '%.*s'\n", 
524
+		    pstate.file, t.start.line, t.start.col,
525
+		    t.val.len, ZSW(t.val.s));
526
+		return -1;
527
+	}
528
+
529
+	r = lookup_token(token_method, &t.val);
530
+	if (!r) {
531
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid option value '%.*s'\n", 
532
+		    pstate.file, t.start.line, t.start.col,
533
+		    t.val.len, ZSW(t.val.s));
534
+		return -1;
535
+	}
536
+
537
+	return 0;
538
+}
539
+
540
+
541
+/*
542
+ * Parse boolean option value
543
+ */
544
+static int parse_bool_val(int* res, token_t* token)
545
+{
546
+	int ret;
547
+	token_t t;
548
+	struct parser_tab* r;
549
+	
550
+	ret = tls_lex(&t, 0);
551
+	if (ret < 0) return -1;
552
+
553
+	     /* No token or EOL means that the option did not have value, and
554
+	      * since we are parsing a boolean option we would assume true
555
+	      */
556
+	if (ret == 0 || t.type == '\n') {
557
+		*res = 1;
558
+		return 0;
559
+	}
560
+	
561
+	if (t.type != '=') {
562
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, '=' expected\n", 
563
+		    pstate.file, t.start.line, t.start.col);
564
+		return -1;
565
+	}
566
+
567
+	ret = tls_lex(&t, EXTENDED_ALPHA_TOKEN);
568
+	if (ret < 0) return -1;
569
+	if (ret == 0) {
570
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n", 
571
+		    pstate.file, t.start.line, t.start.col);
572
+		return -1;
573
+	}
574
+
575
+	if (t.type != TOKEN_ALPHA && t.type != TOKEN_STRING) {
576
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid option value '%.*s', boolean expected\n", 
577
+		    pstate.file, t.start.line, t.start.col,
578
+		    t.val.len, ZSW(t.val.s));
579
+		return -1;
580
+	}
581
+
582
+	r = lookup_token(token_bool, &t.val);
583
+	if (!r) {
584
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid option value '%.*s', boolean expected\n", 
585
+		    pstate.file, t.start.line, t.start.col,
586
+		    t.val.len, ZSW(t.val.s));
587
+		return -1;
588
+	}
589
+	
590
+	*res = r->u.ival;
591
+	return 0;
592
+
593
+}
594
+
595
+
596
+static int parse_verify_cert_opt(token_t* token)
597
+{
598
+	int ret;
599
+	return parse_bool_val(&ret, token);
600
+}
601
+
602
+
603
+static int parse_verify_depth_opt(token_t* token)
604
+{
605
+	unsigned int val;
606
+	int ret;
607
+	token_t t;
608
+
609
+	ret = tls_lex(&t, 0);
610
+	if (ret < 0) return -1;
611
+	if (ret == 0) {
612
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n", 
613
+		    pstate.file, token->start.line, token->start.col);
614
+		return -1;
615
+	}
616
+
617
+	if (t.type != '=') {
618
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, '=' expected\n", 
619
+		    pstate.file, t.start.line, t.start.col);
620
+		return -1;
621
+	}
622
+
623
+	ret = tls_lex(&t, EXTENDED_ALPHA_TOKEN);
624
+	if (ret < 0) return -1;
625
+	if (ret == 0) {
626
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n", 
627
+		    pstate.file, t.start.line, t.start.col);
628
+		return -1;
629
+	}	
630
+
631
+	if (t.type != TOKEN_ALPHA) {
632
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid option value '%.*s', number expected\n", 
633
+		    pstate.file, t.start.line, t.start.col,
634
+		    t.val.len, ZSW(t.val.s));
635
+		return -1;
636
+	}
637
+
638
+	if (str2int(&t.val, &val) < 0) {
639
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid number '%.*s'\n", 
640
+		    pstate.file, t.start.line, t.start.col,
641
+		    t.val.len, ZSW(t.val.s));
642
+
643
+		return -1;
644
+	}
645
+	return 0;
646
+}
647
+
648
+
649
+static int parse_req_cert_opt(token_t* token)
650
+{
651
+	str val;
652
+	return parse_string_val(&val, token);
653
+}
654
+
655
+
656
+static int parse_pkey_opt(token_t* token)
657
+{
658
+	str val;
659
+	return parse_string_val(&val, token);
660
+}
661
+
662
+
663
+static int parse_ca_list_opt(token_t* token)
664
+{
665
+	str val;
666
+	return parse_string_val(&val, token);
667
+}
668
+
669
+
670
+static int parse_cert_opt(token_t* token)
671
+{
672
+	str val;
673
+	return parse_string_val(&val, token);
674
+}
675
+
676
+static int parse_cipher_list_opt(token_t* token)
677
+{
678
+	str val;
679
+	return parse_string_val(&val, token);
680
+}
681
+
682
+static int parse_ipv6(token_t* token)
683
+{
684
+	char buf[IP_ADDR_MAX_STR_SIZE];
685
+	int len, ret;
686
+	token_t t;
687
+	struct ip_addr ip;
688
+
689
+	ip.af = AF_INET6;
690
+	ip.len = 16;
691
+	len = 0;
692
+
693
+	while(1) {
694
+		ret = tls_lex(&t, 0);
695
+		if (ret <= 0) goto err;
696
+		if (t.type == ']') break;
697
+		if (t.type != TOKEN_ALPHA && t.type != ':') goto err;
698
+		if (len + t.val.len >= IP_ADDR_MAX_STR_SIZE) goto err;
699
+		memcpy(buf + len, t.val.s, t.val.len);
700
+		len += t.val.len;
701
+	}
702
+	buf[len] = '\0';
703
+
704
+	if (inet_pton(ip.af, buf, ip.u.addr) <= 0)  goto err;
705
+	return 0;
706
+ err:
707
+	LOG(L_ERR, "ERROR:%s:%d:%d: Invalid IPv6 address\n", 
708
+	    pstate.file, token->start.line, token->start.col);
709
+	return -1;
710
+}
711
+
712
+static int parse_ipv4(token_t* token)
713
+{
714
+	struct ip_addr ip;
715
+	int ret, i;
716
+	token_t  t;
717
+	unsigned int v;
718
+
719
+	ip.af = AF_INET;
720
+	ip.len = 4;
721
+
722
+	if (str2int(&token->val, &v) < 0) goto err;
723
+	if (v < 0 || v > 255) goto err;
724
+
725
+	ip.u.addr[0] = v;
726
+
727
+	for(i = 1; i < 4; i++) {
728
+		ret = tls_lex(&t, 0);
729
+		if (ret < 0) return -1;
730
+		if (ret == 0 || t.type != '.')  goto err;
731
+		
732
+		ret = tls_lex(&t, 0);
733
+		if (ret < 0) return -1;
734
+		if (ret == 0 || t.type != TOKEN_ALPHA) goto err;
735
+		if (str2int(&t.val, &v) < 0)  goto err;
736
+		if (v < 0 || v > 255) goto err;
737
+		ip.u.addr[i] = v;
738
+	}
739
+
740
+	return 0;
741
+ err:
742
+	LOG(L_ERR, "ERROR:%s:%d:%d: Invalid IPv4 address\n", 
743
+	    pstate.file, token->start.line, token->start.col);
744
+	return -1;
745
+}
746
+
747
+static int parse_hostport(token_t* token)
748
+{
749
+	unsigned int port;
750
+	int ret;
751
+	token_t t;
752
+	struct parser_tab* r;
753
+
754
+	ret = tls_lex(&t, 0);
755
+	if (ret < 0) return -1;
756
+	if (ret == 0) {
757
+		LOG(L_ERR, "ERROR:%s:%d:%d: Missing IP address\n", 
758
+		    pstate.file, token->start.line, token->start.col);
759
+		return -1;
760
+	}
761
+
762
+	if (t.type == '[') {
763
+		if (parse_ipv6(&t) < 0) return -1;
764
+	} else if (t.type == TOKEN_ALPHA) {
765
+		r = lookup_token(token_default, &t.val);
766
+		if (r) {
767
+			     /* Default domain */
768
+			return 0;
769
+		} else {
770
+			if (parse_ipv4(&t) < 0) return -1;
771
+		}
772
+	} else {
773
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, IP address expected\n", 
774
+		    pstate.file, t.start.line, t.start.col);
775
+		return -1;
776
+	}
777
+
778
+	     /* Parse port */
779
+	ret = tls_lex(&t, 0);
780
+	if (ret < 0) return -1;
781
+	if (ret == 0) {
782
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, ':' expected\n", 
783
+		    pstate.file, pstate.line, pstate.col);
784
+		return -1;
785
+	}
786
+
787
+	if (t.type != ':') {
788
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, ':' expected\n", 
789
+		    pstate.file, t.start.line, t.start.col);
790
+		return -1;
791
+	}	
792
+
793
+	ret = tls_lex(&t, 0);
794
+	if (ret < 0) return -1;
795
+	if (ret == 0) {
796
+		LOG(L_ERR, "ERROR:%s:%d:%d: Premature end of file, port number missing\n", 
797
+		    pstate.file, t.start.line, t.start.col);
798
+		return -1;
799
+	}
800
+
801
+	if (t.type != TOKEN_ALPHA || (str2int(&t.val, &port) < 0)) {
802
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid port number '%.*s'\n", 
803
+		    pstate.file, t.start.line, t.start.col,
804
+		    t.val.len, ZSW(t.val.s));
805
+		return -1;
806
+	}		
807
+	return 0;
808
+}
809
+
810
+static int parse_domain(token_t* token)
811
+{
812
+	token_t t;
813
+	int ret;
814
+	struct parser_tab* r;
815
+
816
+	ret = tls_lex(&t, 0);
817
+	if (ret < 0) return -1;
818
+	if (ret == 0) {
819
+		LOG(L_ERR, "ERROR:%s:%d:%d: TLS domain type missing\n", 
820
+		    pstate.file, token->start.line, token->start.col);
821
+		return -1;
822
+	}
823
+
824
+	if (t.type != TOKEN_ALPHA || 
825
+	    ((r = lookup_token(token_type, &t.val)) == NULL)) {
826
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid TLS domain type %d:'%.*s'\n", 
827
+		    pstate.file, t.start.line, t.start.col,
828
+		    t.type, t.val.len, ZSW(t.val.s));
829
+		return -1;
830
+	}
831
+	
832
+	ret = tls_lex(&t, 0);
833
+	if (ret < 0) return -1;
834
+	if (ret == 0) {
835
+		LOG(L_ERR, "ERROR:%s:%d:%d: TLS domain IP address missing\n", 
836
+		    pstate.file, t.start.line, t.start.col);
837
+		return -1;
838
+	}
839
+	if (t.type != ':') {
840
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, ':' expected\n", 
841
+		    pstate.file, t.start.line, t.start.col);
842
+		return -1;
843
+	}	
844
+
845
+	if (parse_hostport(&t) < 0) return -1;
846
+
847
+	ret = tls_lex(&t, 0);
848
+	if (ret < 0) return -1;
849
+	if (ret == 0) {
850
+		LOG(L_ERR, "ERROR:%s:%d:%d: Closing ']' missing\n", 
851
+		    pstate.file, t.start.line, t.start.col);
852
+		return -1;
853
+	}
854
+	if (t.type != ']') {
855
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, ']' expected\n", 
856
+		    pstate.file, t.start.line, t.start.col);
857
+		return -1;
858
+	}	
859
+
860
+	return 0;
861
+}
862
+
863
+static int parse_config(void)
864
+{
865
+	int ret;
866
+	token_t token;
867
+	struct parser_tab* r;
868
+
869
+	while(1) {
870
+		     /* Get option name */
871
+		ret = tls_lex(&token, 0);
872
+		if (ret <= 0) return ret;
873
+
874
+		switch(token.type) {
875
+		case TOKEN_ALPHA:
876
+			     /* Lookup the option name */
877
+			r = lookup_token(option_name, &token.val);
878
+			if (!r) {
879
+				LOG(L_ERR, "ERROR:%s:%d: Unsupported option '%.*s'\n", 
880
+				    pstate.file, token.start.line, 
881
+				    token.val.len, ZSW(token.val.s));
882
+				return -1;
883
+			}
884
+			     /* Parse the option value */
885
+			if (r->u.fval(&token) < 0) return -1;
886
+			break;
887
+
888
+		case '[': 
889
+			if (parse_domain(&token) < 0) return -1;
890
+			break;
891
+
892
+			     /* Empty line */
893
+		case '\n': continue;
894
+
895
+		default:
896
+			LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error\n", 
897
+			    pstate.file, token.start.line, token.start.col);
898
+			return -1;
899
+		}
900
+
901
+		     /* Skip EOL */
902
+		ret = tls_lex(&token, 0);
903
+		if (ret <= 0) return ret;
904
+		if (token.type != '\n') {
905
+			LOG(L_ERR, "ERROR:%s:%d:%d: End of line expected\n", 
906
+			    pstate.file, token.start.line, token.start.col);
907
+			return -1;
908
+		}
909
+	}
910
+	return 0;
911
+}
912
+
913
+
914
+int tls_load_config(char* filename)
915
+{
916
+	str val;
917
+	struct token token;
918
+
919
+	pstate.f = fopen(filename, "r");
920
+	if (pstate.f == NULL) {
921
+		ERR("Unable to open TLS config file '%s'\n", filename);
922
+		return -1;
923
+	}
924
+	pstate.file = basename(filename);
925
+	pstate.line = 1;
926
+	pstate.col = 0;
927
+
928
+	if (parse_config() < 0) goto error;
929
+
930
+	fclose(pstate.f);
931
+	return 0;
932
+
933
+ error:
934
+	fclose(pstate.f);
935
+	return -1;
936
+}
937
+
938
+
939
+static struct parser_tab token_type[] = {
940
+	{STR_STATIC_INIT("server"), {.ival = TLS_DOMAIN_SRV}},
941
+	{STR_STATIC_INIT("srv"),    {.ival = TLS_DOMAIN_SRV}},
942
+	{STR_STATIC_INIT("s"),      {.ival = TLS_DOMAIN_SRV}},
943
+	{STR_STATIC_INIT("client"), {.ival = TLS_DOMAIN_CLI}},
944
+	{STR_STATIC_INIT("cli"),    {.ival = TLS_DOMAIN_CLI}},
945
+	{STR_STATIC_INIT("c"),      {.ival = TLS_DOMAIN_CLI}}, 
946
+	{STR_NULL}
947
+};
948
+
949
+
950
+static struct parser_tab token_bool[] = { 
951
+	{STR_STATIC_INIT("yes"),      {.ival = 1}},
952
+	{STR_STATIC_INIT("true"),     {.ival = 1}},
953
+	{STR_STATIC_INIT("enable"),   {.ival = 1}},
954
+	{STR_STATIC_INIT("enabled"),  {.ival = 1}},
955
+	{STR_STATIC_INIT("1"),        {.ival = 1}},
956
+	{STR_STATIC_INIT("on"),       {.ival = 1}},
957
+	{STR_STATIC_INIT("no"),       {.ival = 0}},
958
+	{STR_STATIC_INIT("false"),    {.ival = 0}},
959
+	{STR_STATIC_INIT("disable"),  {.ival = 0}},
960
+	{STR_STATIC_INIT("disabled"), {.ival = 0}},
961
+	{STR_STATIC_INIT("0"),        {.ival = 0}},
962
+	{STR_STATIC_INIT("off"),      {.ival = 0}},
963
+	{STR_NULL}
964
+};
965
+
966
+static struct parser_tab token_default[] = { 
967
+	{STR_STATIC_INIT("default")},
968
+	{STR_STATIC_INIT("def")},
969
+	{STR_STATIC_INIT("*")},
970
+	{STR_NULL}
971
+};
972
+
973
+static struct parser_tab token_method[] = { 
974
+	{STR_STATIC_INIT("SSLv2"),  {.ival = TLS_USE_SSLv2}},
975
+	{STR_STATIC_INIT("SSLv3"),  {.ival = TLS_USE_SSLv3}},
976
+	{STR_STATIC_INIT("SSLv23"), {.ival = TLS_USE_SSLv23}},
977
+	{STR_STATIC_INIT("TLSv1"),  {.ival = TLS_USE_TLSv1}},
978
+	{STR_NULL}
979
+};
980
+
981
+static struct parser_tab option_name[] = {
982
+	{STR_STATIC_INIT("method"),              {.fval = parse_method_opt}},
983
+	{STR_STATIC_INIT("tls_method"),          {.fval = parse_method_opt}},
984
+	{STR_STATIC_INIT("verify_certificate"),  {.fval = parse_verify_cert_opt}},
985
+	{STR_STATIC_INIT("verify_cert"),         {.fval = parse_verify_cert_opt}},
986
+	{STR_STATIC_INIT("verify_depth"),        {.fval = parse_verify_depth_opt}},
987
+	{STR_STATIC_INIT("require_certificate"), {.fval = parse_req_cert_opt}},
988
+	{STR_STATIC_INIT("require_cert"),        {.fval = parse_req_cert_opt}},
989
+	{STR_STATIC_INIT("private_key"),         {.fval = parse_pkey_opt}},
990
+	{STR_STATIC_INIT("pkey_file"),           {.fval = parse_pkey_opt}},
991
+	{STR_STATIC_INIT("ca_list"),             {.fval = parse_ca_list_opt}},
992
+	{STR_STATIC_INIT("calist_file"),         {.fval = parse_ca_list_opt}},
993
+	{STR_STATIC_INIT("certificate"),         {.fval = parse_cert_opt}},
994
+	{STR_STATIC_INIT("cert_file"),           {.fval = parse_cert_opt}},
995
+	{STR_STATIC_INIT("cipher_list"),         {.fval = parse_cipher_list_opt}},
996
+	{STR_NULL}
997
+};
0 998
new file mode 100644
... ...
@@ -0,0 +1,40 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * TLS configuration file parser
5
+ *
6
+ * Copyright (C) 2001-2003 FhG FOKUS
7
+ * Copyright (C) 2004,2005 Free Software Foundation, Inc.
8
+ * Copyright (C) 2005,2006 iptelorg GmbH
9
+ *
10
+ * This file is part of ser, a free SIP server.
11
+ *
12
+ * ser is free software; you can redistribute it and/or modify
13
+ * it under the terms of the GNU General Public License as published by
14
+ * the Free Software Foundation; either version 2 of the License, or
15
+ * (at your option) any later version
16
+ *
17
+ * For a license to use the ser software under conditions
18
+ * other than those described here, or to purchase support for this
19
+ * software, please contact iptel.org by e-mail at the following addresses:
20
+ *    info@iptel.org
21
+ *
22
+ * ser is distributed in the hope that it will be useful,
23
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
+ * GNU General Public License for more details.
26
+ *
27
+ * You should have received a copy of the GNU General Public License
28
+ * along with this program; if not, write to the Free Software
29
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30
+ *
31
+ */
32
+
33
+#ifndef _TLS_CONFIG_H
34
+#define _TLS_CONFIG_H
35
+
36
+#include "../../str.h"
37
+
38
+int load_tls_config(char* filename);
39
+
40
+#endif /* _TLS_CONFIG_H */
... ...
@@ -50,8 +50,10 @@
50 50
 #include "tls_server.h"
51 51
 #include "tls_domain.h"
52 52
 #include "tls_select.h"
53
+#include "tls_config.h"
53 54
 #include "tls_mod.h"
54 55
 
56
+
55 57
 /*
56 58
  * FIXME:
57 59
  * - How do we ask for secret key password ? Mod_init is called after
... ...
@@ -85,6 +87,8 @@ int tls_conn_timeout = 600;
85 87
 int tls_log = 3;
86 88
 int tls_session_cache = 0;
87 89
 str tls_session_id = STR_STATIC_INIT("ser-tls-0.9.0");
90
+char* tls_config = 0;
91
+
88 92
 
89 93
 /*
90 94
  * Exported functions
... ...
@@ -112,6 +116,7 @@ static param_export_t params[] = {
112 116
 	{"tls_log",             PARAM_INT,                     &tls_log               },
113 117
 	{"session_cache",       PARAM_INT,                     &tls_session_cache     },
114 118
 	{"session_id",          PARAM_STR,                     &tls_session_id        },
119
+	{"config",              PARAM_STRING,                  &tls_config            },
115 120
 	{0, 0, 0}
116 121
 };
117 122
 
... ...
@@ -153,6 +158,10 @@ transport_t tls_transport = {
153 158
 
154 159
 static int mod_init(void)
155 160
 {
161
+	if (tls_config) {
162
+		if (tls_load_config(tls_config) < 0) return -1;
163
+	}
164
+
156 165
 	if (init_tls() < 0) return -1;
157 166
 	
158 167
 	tls = &tls_transport;
... ...
@@ -41,5 +41,6 @@ extern int tls_conn_timeout;
41 41
 extern int tls_log;
42 42
 extern int tls_session_cache;
43 43
 extern str tls_session_id;
44
+extern char* tls_config;
44 45
 
45 46
 #endif /* _TLS_MOD_H */