include file compilation fix for OpenBSD relative to inet_aton() IP verification
[fwknop.git] / lib / fko_message.c
1 /*
2  *****************************************************************************
3  *
4  * File:    fko_message.c
5  *
6  * Author:  Damien S. Stuart
7  *
8  * Purpose: Set/Get the spa message (access req/command/etc) based
9  *          on the current spa data.
10  *
11  * Copyright 2009-2010 Damien Stuart (dstuart@dstuart.org)
12  *
13  *  License (GNU Public License):
14  *
15  *  This program is free software; you can redistribute it and/or
16  *  modify it under the terms of the GNU General Public License
17  *  as published by the Free Software Foundation; either version 2
18  *  of the License, or (at your option) any later version.
19  *
20  *  This program is distributed in the hope that it will be useful,
21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  *  GNU General Public License for more details.
24  *
25  *  You should have received a copy of the GNU General Public License
26  *  along with this program; if not, write to the Free Software
27  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
28  *  USA
29  *
30  *****************************************************************************
31 */
32 #include "fko_common.h"
33 #include "fko_message.h"
34 #include "fko.h"
35
36 /* Set the SPA message type.
37 */
38 int
39 fko_set_spa_message_type(fko_ctx_t ctx, const short msg_type)
40 {
41     /* Must be initialized
42     */
43     if(!CTX_INITIALIZED(ctx))
44         return FKO_ERROR_CTX_NOT_INITIALIZED;
45
46     if(msg_type < 0 || msg_type >= FKO_LAST_MSG_TYPE)
47         return(FKO_ERROR_INVALID_DATA);
48
49     ctx->message_type = msg_type;
50
51     ctx->state |= FKO_SPA_MSG_TYPE_MODIFIED;
52
53     return(FKO_SUCCESS);
54 }
55
56 /* Return the SPA message type.
57 */
58 int
59 fko_get_spa_message_type(fko_ctx_t ctx, short *msg_type)
60 {
61     /* Must be initialized
62     */
63     if(!CTX_INITIALIZED(ctx))
64         return FKO_ERROR_CTX_NOT_INITIALIZED;
65
66     *msg_type = ctx->message_type;
67
68     return(FKO_SUCCESS);
69 }
70
71 /* Set the SPA MESSAGE data
72 */
73 int
74 fko_set_spa_message(fko_ctx_t ctx, const char *msg)
75 {
76     int res = FKO_ERROR_UNKNOWN;
77
78     /* Context must be initialized.
79     */
80     if(!CTX_INITIALIZED(ctx))
81         return FKO_ERROR_CTX_NOT_INITIALIZED;
82
83     /* Gotta have a valid string.
84     */
85     if(msg == NULL || strnlen(msg, MAX_SPA_MESSAGE_SIZE) == 0)
86         return(FKO_ERROR_INVALID_DATA);
87
88     /* --DSS XXX: Bail out for now.  But consider just
89      *            truncating in the future...
90     */
91     if(strnlen(msg, MAX_SPA_MESSAGE_SIZE) == MAX_SPA_MESSAGE_SIZE)
92         return(FKO_ERROR_DATA_TOO_LARGE);
93
94     /* Basic message type and format checking...
95     */
96     switch(ctx->message_type)
97     {
98         case FKO_COMMAND_MSG:
99             res = validate_cmd_msg(msg);
100             break;
101
102         case FKO_ACCESS_MSG:
103         case FKO_CLIENT_TIMEOUT_ACCESS_MSG:
104             res = validate_access_msg(msg);
105             break;
106
107         case FKO_NAT_ACCESS_MSG:
108         case FKO_LOCAL_NAT_ACCESS_MSG:
109         case FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG:
110         case FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG:
111             res = validate_nat_access_msg(msg);
112             break;
113     }
114
115     if(res != FKO_SUCCESS)
116         return(res);
117
118     /* Just in case this is a subsquent call to this function.  We
119      * do not want to be leaking memory.
120     */
121     if(ctx->message != NULL)
122         free(ctx->message);
123
124     ctx->message = strdup(msg);
125
126     ctx->state |= FKO_DATA_MODIFIED;
127
128     if(ctx->message == NULL)
129         return(FKO_ERROR_MEMORY_ALLOCATION);
130
131     return(FKO_SUCCESS);
132 }
133
134 /* Return the SPA message data.
135 */
136 int
137 fko_get_spa_message(fko_ctx_t ctx, char **msg)
138 {
139     /* Must be initialized
140     */
141     if(!CTX_INITIALIZED(ctx))
142         return(FKO_ERROR_CTX_NOT_INITIALIZED);
143
144     *msg = ctx->message;
145
146     return(FKO_SUCCESS);
147 }
148
149 /* Validate a command message format.
150 */
151 int
152 validate_cmd_msg(const char *msg)
153 {
154     const char   *ndx;
155     int     res         = FKO_SUCCESS;
156     int     startlen    = strnlen(msg, MAX_SPA_CMD_LEN);
157
158     if(startlen == MAX_SPA_CMD_LEN)
159         return(FKO_ERROR_INVALID_DATA);
160
161     /* Should have a valid allow IP.
162     */
163     if((res = got_allow_ip(msg)) != FKO_SUCCESS)
164         return(res);
165
166     /* Commands are fairly free-form so all we can really verify is
167      * there is something at all. Get past the IP and comma, and make
168      * sure we have some string leftover...
169     */
170     ndx = strchr(msg, ',');
171     if(ndx == NULL || (1+(ndx - msg)) >= startlen)
172         return(FKO_ERROR_INVALID_SPA_COMMAND_MSG);
173
174     return(FKO_SUCCESS);
175 }
176
177 int
178 validate_access_msg(const char *msg)
179 {
180     const char   *ndx;
181     int     res         = FKO_SUCCESS;
182     int     startlen    = strnlen(msg, MAX_SPA_MESSAGE_SIZE);
183
184     if(startlen == MAX_SPA_MESSAGE_SIZE)
185         return(FKO_ERROR_INVALID_DATA);
186
187     /* Should have a valid allow IP.
188     */
189     if((res = got_allow_ip(msg)) != FKO_SUCCESS)
190         return(res);
191
192     /* Position ourselves beyond the allow IP and make sure we are
193      * still good.
194     */
195     ndx = strchr(msg, ',');
196     if(ndx == NULL || (1+(ndx - msg)) >= startlen)
197         return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
198
199     /* Look for a comma to see if this is a multi-part access request.
200     */
201     do {
202         ndx++;
203         res = validate_proto_port_spec(ndx);
204         if(res != FKO_SUCCESS)
205             break;
206     } while((ndx = strchr(ndx, ',')));
207
208     return(res);
209 }
210
211 int
212 validate_proto_port_spec(const char *msg)
213 {
214     int     startlen    = strnlen(msg, MAX_SPA_MESSAGE_SIZE), port_str_len = 0;
215     const char   *ndx   = msg;
216
217     if(startlen == MAX_SPA_MESSAGE_SIZE)
218         return(FKO_ERROR_INVALID_DATA);
219
220     /* Now check for proto/port string.
221     */
222     if(strncmp(ndx, "tcp", 3)
223       && strncmp(ndx, "udp", 3)
224       && strncmp(ndx, "icmp", 4)
225       && strncmp(ndx, "none", 4))
226         return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
227
228     ndx = strchr(ndx, '/');
229     if(ndx == NULL || ((1+(ndx - msg)) > MAX_PROTO_STR_LEN))
230         return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
231
232     /* Skip over the '/' and make sure we only have digits.
233     */
234     ndx++;
235
236     /* Must have at least one digit for the port number
237     */
238     if(isdigit(*ndx) == 0)
239         return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
240
241     while(*ndx != '\0' && *ndx != ',')
242     {
243         port_str_len++;
244         if((isdigit(*ndx) == 0) || (port_str_len > MAX_PORT_STR_LEN))
245             return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
246         ndx++;
247     }
248     return(FKO_SUCCESS);
249 }
250
251 int
252 validate_nat_access_msg(const char *msg)
253 {
254     int res = FKO_SUCCESS;
255
256     /* Should have a valid access message.
257     */
258     if((res = validate_access_msg(msg)) != FKO_SUCCESS)
259         return(res);
260
261     // --DSS TODO: XXX: Put nat_access validation code here
262
263     return(FKO_SUCCESS);
264 }
265
266 int
267 got_allow_ip(const char *msg)
268 {
269     const char         *ndx     = msg;
270     char                ip_str[MAX_IPV4_STR_LEN];
271     int                 dot_ctr = 0, char_ctr = 0;
272     int                 res     = FKO_SUCCESS;
273 #if HAVE_SYS_SOCKET_H
274     struct in_addr      in;
275 #endif
276
277     while(*ndx != ',' && *ndx != '\0')
278     {
279         ip_str[char_ctr] = *ndx;
280         char_ctr++;
281         if(char_ctr >= MAX_IPV4_STR_LEN)
282         {
283             res = FKO_ERROR_INVALID_ALLOW_IP;
284             break;
285         }
286         if(*ndx == '.')
287             dot_ctr++;
288         else if(isdigit(*ndx) == 0)
289         {
290             res = FKO_ERROR_INVALID_ALLOW_IP;
291             break;
292         }
293         ndx++;
294     }
295
296     if(char_ctr < MAX_IPV4_STR_LEN)
297         ip_str[char_ctr] = '\0';
298     else
299         res = FKO_ERROR_INVALID_ALLOW_IP;
300
301     if ((res == FKO_SUCCESS) && (char_ctr < MIN_IPV4_STR_LEN))
302         res = FKO_ERROR_INVALID_ALLOW_IP;
303
304     if((res == FKO_SUCCESS) && dot_ctr != 3)
305         res = FKO_ERROR_INVALID_ALLOW_IP;
306
307 #if HAVE_SYS_SOCKET_H
308     /* Stronger IP validation now that we have a candidate that looks
309      * close enough
310     */
311     if((res == FKO_SUCCESS) && (inet_aton(ip_str, &in) == 0))
312         res = FKO_ERROR_INVALID_ALLOW_IP;
313 #endif
314
315     return(res);
316 }
317
318 /***EOF***/