Add getopt_long from NetBSD
[dragonfly.git] / lib / libipsec / policy_parse.y
1 /*      $FreeBSD: src/lib/libipsec/policy_parse.y,v 1.1.2.1 2000/07/15 07:24:04 kris Exp $      */
2 /*      $DragonFly: src/lib/libipsec/policy_parse.y,v 1.3 2003/11/12 20:21:30 eirikn Exp $      */
3 /*      $KAME: policy_parse.y,v 1.10 2000/05/07 05:25:03 itojun Exp $   */
4
5 /*
6  * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the project nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 /*
35  * IN/OUT bound policy configuration take place such below:
36  *      in <policy>
37  *      out <policy>
38  *
39  * <policy> is one of following:
40  *      "discard", "none", "ipsec <requests>", "entrust", "bypass",
41  *
42  * The following requests are accepted as <requests>:
43  *
44  *      protocol/mode/src-dst/level
45  *      protocol/mode/src-dst           parsed as protocol/mode/src-dst/default
46  *      protocol/mode/src-dst/          parsed as protocol/mode/src-dst/default
47  *      protocol/transport              parsed as protocol/mode/any-any/default
48  *      protocol/transport//level       parsed as protocol/mode/any-any/level
49  *
50  * You can concatenate these requests with either ' '(single space) or '\n'.
51  */
52
53 %{
54 #include <sys/types.h>
55 #include <sys/param.h>
56 #include <sys/socket.h>
57
58 #include <netinet/in.h>
59 #include <netinet6/ipsec.h>
60
61 #include <stdlib.h>
62 #include <stdio.h>
63 #include <string.h>
64 #include <netdb.h>
65
66 #include "ipsec_strerror.h"
67
68 #define ATOX(c) \
69   (isdigit(c) ? (c - '0') : (isupper(c) ? (c - 'A' + 10) : (c - 'a' + 10) ))
70
71 static caddr_t pbuf = NULL;             /* sadb_x_policy buffer */
72 static int tlen = 0;                    /* total length of pbuf */
73 static int offset = 0;                  /* offset of pbuf */
74 static int p_dir, p_type, p_protocol, p_mode, p_level, p_reqid;
75 static struct sockaddr *p_src = NULL;
76 static struct sockaddr *p_dst = NULL;
77
78 struct _val;
79 extern void yyerror (char *msg);
80 static struct sockaddr *parse_sockaddr (struct _val *buf);
81 static int rule_check (void);
82 static int init_x_policy (void);
83 static int set_x_request (struct sockaddr *src, struct sockaddr *dst);
84 static int set_sockaddr (struct sockaddr *addr);
85 static void policy_parse_request_init (void);
86 static caddr_t policy_parse (char *msg, int msglen);
87
88 extern void __policy__strbuffer__init__ (char *msg);
89 extern int yyparse (void);
90 extern int yylex (void);
91
92 %}
93
94 %union {
95         u_int num;
96         struct _val {
97                 int len;
98                 char *buf;
99         } val;
100 }
101
102 %token DIR ACTION PROTOCOL MODE LEVEL LEVEL_SPECIFY
103 %token IPADDRESS
104 %token ME ANY
105 %token SLASH HYPHEN
106 %type <num> DIR ACTION PROTOCOL MODE LEVEL
107 %type <val> IPADDRESS LEVEL_SPECIFY
108
109 %%
110 policy_spec
111         :       DIR ACTION
112                 {
113                         p_dir = $1;
114                         p_type = $2;
115
116                         if (init_x_policy())
117                                 return -1;
118                 }
119                 rules
120         |       DIR
121                 {
122                         p_dir = $1;
123                         p_type = 0;     /* ignored it by kernel */
124
125                         if (init_x_policy())
126                                 return -1;
127                 }
128         ;
129
130 rules
131         :       /*NOTHING*/
132         |       rules rule {
133                         if (rule_check() < 0)
134                                 return -1;
135
136                         if (set_x_request(p_src, p_dst) < 0)
137                                 return -1;
138
139                         policy_parse_request_init();
140                 }
141         ;
142
143 rule
144         :       protocol SLASH mode SLASH addresses SLASH level
145         |       protocol SLASH mode SLASH addresses SLASH
146         |       protocol SLASH mode SLASH addresses
147         |       protocol SLASH mode SLASH
148         |       protocol SLASH mode SLASH SLASH level
149         |       protocol SLASH mode
150         |       protocol SLASH {
151                         __ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
152                         return -1;
153                 }
154         |       protocol {
155                         __ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
156                         return -1;
157                 }
158         ;
159
160 protocol
161         :       PROTOCOL { p_protocol = $1; }
162         ;
163
164 mode
165         :       MODE { p_mode = $1; }
166         ;
167
168 level
169         :       LEVEL {
170                         p_level = $1;
171                         p_reqid = 0;
172                 }
173         |       LEVEL_SPECIFY {
174                         p_level = IPSEC_LEVEL_UNIQUE;
175                         p_reqid = atol($1.buf); /* atol() is good. */
176                 }
177         ;
178
179 addresses
180         :       IPADDRESS {
181                         p_src = parse_sockaddr(&$1);
182                         if (p_src == NULL)
183                                 return -1;
184                 }
185                 HYPHEN
186                 IPADDRESS {
187                         p_dst = parse_sockaddr(&$4);
188                         if (p_dst == NULL)
189                                 return -1;
190                 }
191         |       ME HYPHEN ANY {
192                         if (p_dir != IPSEC_DIR_OUTBOUND) {
193                                 __ipsec_errcode = EIPSEC_INVAL_DIR;
194                                 return -1;
195                         }
196                 }
197         |       ANY HYPHEN ME {
198                         if (p_dir != IPSEC_DIR_INBOUND) {
199                                 __ipsec_errcode = EIPSEC_INVAL_DIR;
200                                 return -1;
201                         }
202                 }
203                 /*
204         |       ME HYPHEN ME
205                 */
206         ;
207
208 %%
209
210 void
211 yyerror(msg)
212         char *msg;
213 {
214         extern char *__libipsecyytext;  /*XXX*/
215
216         fprintf(stderr, "libipsec: %s while parsing \"%s\"\n",
217                 msg, __libipsecyytext);
218
219         return;
220 }
221
222 static struct sockaddr *
223 parse_sockaddr(buf)
224         struct _val *buf;
225 {
226         struct addrinfo hints, *res;
227         char *serv = NULL;
228         int error;
229         struct sockaddr *newaddr = NULL;
230
231         memset(&hints, 0, sizeof(hints));
232         hints.ai_family = PF_UNSPEC;
233         hints.ai_flags = AI_NUMERICHOST;
234         error = getaddrinfo(buf->buf, serv, &hints, &res);
235         if (error != 0) {
236                 yyerror("invalid IP address");
237                 __ipsec_set_strerror(gai_strerror(error));
238                 return NULL;
239         }
240
241         if (res->ai_addr == NULL) {
242                 yyerror("invalid IP address");
243                 __ipsec_set_strerror(gai_strerror(error));
244                 return NULL;
245         }
246
247         newaddr = malloc(res->ai_addr->sa_len);
248         if (newaddr == NULL) {
249                 __ipsec_errcode = EIPSEC_NO_BUFS;
250                 freeaddrinfo(res);
251                 return NULL;
252         }
253         memcpy(newaddr, res->ai_addr, res->ai_addr->sa_len);
254
255         freeaddrinfo(res);
256
257         __ipsec_errcode = EIPSEC_NO_ERROR;
258         return newaddr;
259 }
260
261 static int
262 rule_check()
263 {
264         if (p_type == IPSEC_POLICY_IPSEC) {
265                 if (p_protocol == IPPROTO_IP) {
266                         __ipsec_errcode = EIPSEC_NO_PROTO;
267                         return -1;
268                 }
269
270                 if (p_mode != IPSEC_MODE_TRANSPORT
271                  && p_mode != IPSEC_MODE_TUNNEL) {
272                         __ipsec_errcode = EIPSEC_INVAL_MODE;
273                         return -1;
274                 }
275
276                 if (p_src == NULL && p_dst == NULL) {
277                          if (p_mode != IPSEC_MODE_TRANSPORT) {
278                                 __ipsec_errcode = EIPSEC_INVAL_ADDRESS;
279                                 return -1;
280                         }
281                 }
282                 else if (p_src->sa_family != p_dst->sa_family) {
283                         __ipsec_errcode = EIPSEC_FAMILY_MISMATCH;
284                         return -1;
285                 }
286         }
287
288         __ipsec_errcode = EIPSEC_NO_ERROR;
289         return 0;
290 }
291
292 static int
293 init_x_policy()
294 {
295         struct sadb_x_policy *p;
296
297         tlen = sizeof(struct sadb_x_policy);
298
299         pbuf = malloc(tlen);
300         if (pbuf == NULL) {
301                 __ipsec_errcode = EIPSEC_NO_BUFS;
302                 return -1;
303         }
304         p = (struct sadb_x_policy *)pbuf;
305         p->sadb_x_policy_len = 0;       /* must update later */
306         p->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
307         p->sadb_x_policy_type = p_type;
308         p->sadb_x_policy_dir = p_dir;
309         p->sadb_x_policy_reserved = 0;
310         offset = tlen;
311
312         __ipsec_errcode = EIPSEC_NO_ERROR;
313         return 0;
314 }
315
316 static int
317 set_x_request(src, dst)
318         struct sockaddr *src, *dst;
319 {
320         struct sadb_x_ipsecrequest *p;
321         int reqlen;
322
323         reqlen = sizeof(*p)
324                 + (src ? src->sa_len : 0)
325                 + (dst ? dst->sa_len : 0);
326         tlen += reqlen;         /* increment to total length */
327
328         pbuf = realloc(pbuf, tlen);
329         if (pbuf == NULL) {
330                 __ipsec_errcode = EIPSEC_NO_BUFS;
331                 return -1;
332         }
333         p = (struct sadb_x_ipsecrequest *)&pbuf[offset];
334         p->sadb_x_ipsecrequest_len = reqlen;
335         p->sadb_x_ipsecrequest_proto = p_protocol;
336         p->sadb_x_ipsecrequest_mode = p_mode;
337         p->sadb_x_ipsecrequest_level = p_level;
338         p->sadb_x_ipsecrequest_reqid = p_reqid;
339         offset += sizeof(*p);
340
341         if (set_sockaddr(src) || set_sockaddr(dst))
342                 return -1;
343
344         __ipsec_errcode = EIPSEC_NO_ERROR;
345         return 0;
346 }
347
348 static int
349 set_sockaddr(addr)
350         struct sockaddr *addr;
351 {
352         if (addr == NULL) {
353                 __ipsec_errcode = EIPSEC_NO_ERROR;
354                 return 0;
355         }
356
357         /* tlen has already incremented */
358
359         memcpy(&pbuf[offset], addr, addr->sa_len);
360
361         offset += addr->sa_len;
362
363         __ipsec_errcode = EIPSEC_NO_ERROR;
364         return 0;
365 }
366
367 static void
368 policy_parse_request_init()
369 {
370         p_protocol = IPPROTO_IP;
371         p_mode = IPSEC_MODE_ANY;
372         p_level = IPSEC_LEVEL_DEFAULT;
373         p_reqid = 0;
374         if (p_src != NULL) {
375                 free(p_src);
376                 p_src = NULL;
377         }
378         if (p_dst != NULL) {
379                 free(p_dst);
380                 p_dst = NULL;
381         }
382
383         return;
384 }
385
386 static caddr_t
387 policy_parse(msg, msglen)
388         char *msg;
389         int msglen;
390 {
391         int error;
392         pbuf = NULL;
393         tlen = 0;
394
395         /* initialize */
396         p_dir = IPSEC_DIR_INVALID;
397         p_type = IPSEC_POLICY_DISCARD;
398         policy_parse_request_init();
399         __policy__strbuffer__init__(msg);
400
401         error = yyparse();      /* it must be set errcode. */
402         if (error) {
403                 if (pbuf != NULL)
404                         free(pbuf);
405                 return NULL;
406         }
407
408         /* update total length */
409         ((struct sadb_x_policy *)pbuf)->sadb_x_policy_len = PFKEY_UNIT64(tlen);
410
411         __ipsec_errcode = EIPSEC_NO_ERROR;
412
413         return pbuf;
414 }
415
416 caddr_t
417 ipsec_set_policy(msg, msglen)
418         char *msg;
419         int msglen;
420 {
421         caddr_t policy;
422
423         policy = policy_parse(msg, msglen);
424         if (policy == NULL) {
425                 if (__ipsec_errcode == EIPSEC_NO_ERROR)
426                         __ipsec_errcode = EIPSEC_INVAL_ARGUMENT;
427                 return NULL;
428         }
429
430         __ipsec_errcode = EIPSEC_NO_ERROR;
431         return policy;
432 }
433