Better SPA message validation upon SPA decrypt/decode.
[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.h"
34
35 /* Set the SPA message type.
36 */
37 int
38 fko_set_spa_message_type(fko_ctx_t ctx, const short msg_type)
39 {
40     /* Must be initialized
41     */
42     if(!CTX_INITIALIZED(ctx))
43         return FKO_ERROR_CTX_NOT_INITIALIZED;
44
45     if(msg_type < 0 || msg_type >= FKO_LAST_MSG_TYPE)
46         return(FKO_ERROR_INVALID_DATA);
47
48     ctx->message_type = msg_type;
49
50     ctx->state |= FKO_SPA_MSG_TYPE_MODIFIED;
51
52     return(FKO_SUCCESS);
53 }
54
55 /* Return the SPA message type.
56 */
57 int
58 fko_get_spa_message_type(fko_ctx_t ctx, short *msg_type)
59 {
60     /* Must be initialized
61     */
62     if(!CTX_INITIALIZED(ctx))
63         return FKO_ERROR_CTX_NOT_INITIALIZED;
64
65     *msg_type = ctx->message_type;
66
67     return(FKO_SUCCESS);
68 }
69
70 /* Set the SPA MESSAGE data
71 */
72 int
73 fko_set_spa_message(fko_ctx_t ctx, const char *msg)
74 {
75     int res = FKO_ERROR_UNKNOWN;
76
77     /* Context must be initialized.
78     */
79     if(!CTX_INITIALIZED(ctx))
80         return FKO_ERROR_CTX_NOT_INITIALIZED;
81
82     /* Gotta have a valid string.
83     */
84     if(msg == NULL || strnlen(msg, MAX_SPA_MESSAGE_SIZE) == 0)
85         return(FKO_ERROR_INVALID_DATA);
86
87     /* --DSS XXX: Bail out for now.  But consider just
88      *            truncating in the future...
89     */
90     if(strnlen(msg, MAX_SPA_MESSAGE_SIZE) == MAX_SPA_MESSAGE_SIZE)
91         return(FKO_ERROR_DATA_TOO_LARGE);
92
93     /* Basic message type and format checking...
94     */
95     switch(ctx->message_type)
96     {
97         case FKO_COMMAND_MSG:
98             res = validate_cmd_msg(msg);
99             break;
100
101         case FKO_ACCESS_MSG:
102         case FKO_CLIENT_TIMEOUT_ACCESS_MSG:
103             res = validate_access_msg(msg);
104             break;
105
106         case FKO_NAT_ACCESS_MSG:
107         case FKO_LOCAL_NAT_ACCESS_MSG:
108         case FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG:
109         case FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG:
110             res = validate_nat_access_msg(msg);
111             break;
112     }
113
114     if(res != FKO_SUCCESS)
115         return(res);
116
117     /* Just in case this is a subsquent call to this function.  We
118      * do not want to be leaking memory.
119     */
120     if(ctx->message != NULL)
121         free(ctx->message);
122
123     ctx->message = strdup(msg);
124
125     ctx->state |= FKO_DATA_MODIFIED;
126
127     if(ctx->message == NULL)
128         return(FKO_ERROR_MEMORY_ALLOCATION);
129
130     return(FKO_SUCCESS);
131 }
132
133 /* Return the SPA message data.
134 */
135 int
136 fko_get_spa_message(fko_ctx_t ctx, char **msg)
137 {
138     /* Must be initialized
139     */
140     if(!CTX_INITIALIZED(ctx))
141         return(FKO_ERROR_CTX_NOT_INITIALIZED);
142
143     *msg = ctx->message;
144
145     return(FKO_SUCCESS);
146 }
147
148 /* Validate a command message format.
149 */
150 int
151 validate_cmd_msg(const char *msg)
152 {
153     const char   *ndx;
154     int     res         = FKO_SUCCESS;
155     int     startlen    = strlen(msg);
156
157
158     /* Should have a valid allow IP.
159     */
160     if((res = got_allow_ip(msg)) != FKO_SUCCESS)
161         return(res);
162
163     /* Commands are fairly free-form so all we can really verify is
164      * there is something at all. Get past the IP and comma, and make
165      * sure we have some string leftover...
166     */
167     ndx = strchr(msg, ',');
168     if(ndx == NULL || (1+(ndx - msg)) >= startlen)
169         return(FKO_ERROR_INVALID_SPA_COMMAND_MSG);
170
171     return(FKO_SUCCESS);
172 }
173
174 int
175 validate_access_msg(const char *msg)
176 {
177     const char   *ndx;
178     int     res         = FKO_SUCCESS;
179     int     startlen    = strlen(msg);
180
181     /* Should have a valid allow IP.
182     */
183     if((res = got_allow_ip(msg)) != FKO_SUCCESS)
184         return(res);
185
186     /* Position ourselves beyond the allow IP and make sure we are
187      * still good.
188     */
189     ndx = strchr(msg, ',');
190     if(ndx == NULL || (1+(ndx - msg)) >= startlen)
191         return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
192
193     /* Look for a comma to see if this is a multi-part access request.
194     */
195     do {
196         ndx++;
197         res = validate_proto_port_spec(ndx);
198     } while((ndx = strchr(ndx, ',')));
199
200     return(res);
201 }
202
203 int
204 validate_proto_port_spec(const char *msg)
205 {
206     int     startlen    = strlen(msg);
207
208     const char   *ndx   = msg;
209
210     /* Now check for proto/port string.  Currenly we only allow protos
211      * 'tcp', 'udp', and 'icmp'.
212     */
213     if(strncmp(ndx, "tcp", 3)
214       && strncmp(ndx, "udp", 3)
215       && strncmp(ndx, "icmp", 4)
216       && strncmp(ndx, "none", 4))
217         return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
218
219     ndx = strchr(ndx, '/');
220     if(ndx == NULL || (1+(ndx - msg)) >= startlen)
221         return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
222
223     /* Skip over the ',' and make sure we only have digits.
224     */
225     ndx++;
226     while(*ndx != '\0')
227     {
228         if(isdigit(*ndx) == 0)
229             return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
230         ndx++;
231     }
232
233     return(FKO_SUCCESS);
234 }
235
236 int
237 validate_nat_access_msg(const char *msg)
238 {
239     int res = FKO_SUCCESS;
240
241     /* Should have a valid access message.
242     */
243     if((res = validate_access_msg(msg)) != FKO_SUCCESS)
244         return(res);
245
246     // --DSS TODO: XXX: Put nat_access validation code here
247
248     return(FKO_SUCCESS);
249 }
250
251 int
252 got_allow_ip(const char *msg)
253 {
254     const char *ndx     = msg;
255     int         dot_cnt = 0;
256     int         res     = FKO_SUCCESS;
257
258     while(*ndx != ',' && *ndx != '\0')
259     {
260         if(*ndx == '.')
261             dot_cnt++;
262         else if(isdigit(*ndx) == 0)
263         {
264             res = FKO_ERROR_INVALID_ALLOW_IP;
265             break;
266         }
267
268         ndx++;
269     }
270
271     if(dot_cnt != 3)
272         res = FKO_ERROR_INVALID_ALLOW_IP;
273
274     return(res);
275 }
276
277 /***EOF***/