[server] (Vlad Glagolev) Submitted a patch to fix command exec mode
[fwknop.git] / lib / fko_decode.c
1 /*
2  *****************************************************************************
3  *
4  * File:    fko_decode.c
5  *
6  * Author:  Damien S. Stuart
7  *
8  * Purpose: Decode an FKO SPA message after decryption.
9  *
10  * Copyright 2009-2010 Damien Stuart (dstuart@dstuart.org)
11  *
12  *  License (GNU Public License):
13  *
14  *  This program is free software; you can redistribute it and/or
15  *  modify it under the terms of the GNU General Public License
16  *  as published by the Free Software Foundation; either version 2
17  *  of the License, or (at your option) any later version.
18  *
19  *  This program is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *
24  *  You should have received a copy of the GNU General Public License
25  *  along with this program; if not, write to the Free Software
26  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
27  *  USA
28  *
29  *****************************************************************************
30 */
31 #include "fko_common.h"
32 #include "fko.h"
33 #include "cipher_funcs.h"
34 #include "base64.h"
35 #include "digest.h"
36
37 /* Decode the encoded SPA data.
38 */
39 int
40 fko_decode_spa_data(fko_ctx_t ctx)
41 {
42     char       *tbuf, *ndx, *tmp;
43     int         t_size, i;
44
45     /* Check for required data.
46     */
47     if(ctx->encoded_msg == NULL || strnlen(ctx->encoded_msg,
48             MAX_SPA_ENCODED_MSG_SIZE) < MIN_SPA_ENCODED_MSG_SIZE)
49         return(FKO_ERROR_INVALID_DATA);
50
51     if(strnlen(ctx->encoded_msg,
52             MAX_SPA_ENCODED_MSG_SIZE) == MAX_SPA_ENCODED_MSG_SIZE)
53         return(FKO_ERROR_INVALID_DATA);
54
55     /* Make sure there are enough fields in the SPA packet
56      * delimited with ':' chars
57     */
58     ndx = ctx->encoded_msg;
59     for (i=0; i < MAX_SPA_FIELDS; i++)
60     {
61         if ((tmp = strchr(ndx, ':')) == NULL)
62             break;
63
64         ndx = tmp;
65         ndx++;
66     }
67
68     if (i < MIN_SPA_FIELDS)
69         return(FKO_ERROR_INVALID_DATA);
70
71     t_size = strlen(ndx);
72
73     switch(t_size)
74     {
75         case MD5_B64_LENGTH:
76             ctx->digest_type = FKO_DIGEST_MD5;
77             break;
78
79         case SHA1_B64_LENGTH:
80             ctx->digest_type = FKO_DIGEST_SHA1;
81             break;
82
83         case SHA256_B64_LENGTH:
84             ctx->digest_type = FKO_DIGEST_SHA256;
85             break;
86
87         case SHA384_B64_LENGTH:
88             ctx->digest_type = FKO_DIGEST_SHA384;
89             break;
90
91         case SHA512_B64_LENGTH:
92             ctx->digest_type = FKO_DIGEST_SHA512;
93             break;
94
95         default: /* Invalid or unsupported digest */
96             return(FKO_ERROR_INVALID_DIGEST_TYPE);
97     }
98
99     /* Copy the digest into the context and terminate the encoded data
100      * at that point so the original digest is not part of the
101      * encoded string.
102     */
103     ctx->digest = strdup(ndx);
104     if(ctx->digest == NULL)
105         return(FKO_ERROR_MEMORY_ALLOCATION);
106
107     /* Zero out the rest of the encoded_msg bucket...
108     */
109     bzero((ndx-1), t_size);
110
111     /* Make a tmp bucket for processing base64 encoded data and
112      * other general use.
113     */
114     tbuf = malloc(FKO_ENCODE_TMP_BUF_SIZE);
115     if(tbuf == NULL)
116         return(FKO_ERROR_MEMORY_ALLOCATION);
117
118     /* Can now verify the digest.
119     */
120     switch(ctx->digest_type)
121     {
122         case FKO_DIGEST_MD5:
123             md5_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
124             break;
125
126         case FKO_DIGEST_SHA1:
127             sha1_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
128             break;
129
130         case FKO_DIGEST_SHA256:
131             sha256_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
132             break;
133
134         case FKO_DIGEST_SHA384:
135             sha384_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
136             break;
137
138         case FKO_DIGEST_SHA512:
139             sha512_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
140             break;
141
142     }
143
144     /* We give up here if the computed digest does not match the
145      * digest in the message data.
146     */
147     if(strncmp(ctx->digest, tbuf, t_size))
148     {
149         free(tbuf);
150         return(FKO_ERROR_DIGEST_VERIFICATION_FAILED);
151     }
152
153     /* Now we will work through the encoded data and extract (and base64-
154      * decode where necessary), the SPA data fields and populate the context.
155     */
156     ndx = ctx->encoded_msg;
157
158     /* The rand val data */
159     if((t_size = strcspn(ndx, ":")) < FKO_RAND_VAL_SIZE)
160     {
161         free(tbuf);
162         return(FKO_ERROR_INVALID_DATA);
163     }
164
165     ctx->rand_val = calloc(1, FKO_RAND_VAL_SIZE+1);
166     if(ctx->rand_val == NULL)
167     {
168         free(tbuf);
169         return(FKO_ERROR_MEMORY_ALLOCATION);
170     }
171     ctx->rand_val = strncpy(ctx->rand_val, ndx, FKO_RAND_VAL_SIZE);
172
173     /* Jump to the next field (username).  We need to use the temp buffer
174      * for the base64 decode step.
175     */
176     ndx += t_size + 1;
177     if((t_size = strcspn(ndx, ":")) < 1)
178     {
179         free(tbuf);
180         return(FKO_ERROR_INVALID_DATA);
181     }
182
183     if (t_size > MAX_SPA_USERNAME_SIZE)
184     {
185         free(tbuf);
186         return(FKO_ERROR_INVALID_DATA);
187     }
188
189     strlcpy(tbuf, ndx, t_size+1);
190
191     ctx->username = malloc(t_size+1); /* Yes, more than we need */
192     if(ctx->username == NULL)
193     {
194         free(tbuf);
195         return(FKO_ERROR_MEMORY_ALLOCATION);
196     }
197
198     b64_decode(tbuf, (unsigned char*)ctx->username);
199
200     /* Extract the timestamp value.
201     */
202     ndx += t_size + 1;
203     if((t_size = strcspn(ndx, ":")) < 1)
204     {
205         free(tbuf);
206         return(FKO_ERROR_INVALID_DATA);
207     }
208
209     if (t_size > MAX_SPA_TIMESTAMP_SIZE)
210     {
211         free(tbuf);
212         return(FKO_ERROR_INVALID_DATA);
213     }
214
215     strlcpy(tbuf, ndx, t_size+1);
216
217     ctx->timestamp = (unsigned int)atoi(tbuf);
218
219     /* Extract the version string.
220     */
221     ndx += t_size + 1;
222     if((t_size = strcspn(ndx, ":")) < 1)
223     {
224         free(tbuf);
225         return(FKO_ERROR_INVALID_DATA);
226     }
227
228     if (t_size > MAX_SPA_VERSION_SIZE)
229     {
230         free(tbuf);
231         return(FKO_ERROR_INVALID_DATA);
232     }
233
234     ctx->version = malloc(t_size+1);
235     if(ctx->version == NULL)
236     {
237         free(tbuf);
238         return(FKO_ERROR_MEMORY_ALLOCATION);
239     }
240
241     strlcpy(ctx->version, ndx, t_size+1);
242
243     /* Extract the message type value.
244     */
245     ndx += t_size + 1;
246     if((t_size = strcspn(ndx, ":")) < 1)
247     {
248         free(tbuf);
249         return(FKO_ERROR_INVALID_DATA);
250     }
251
252     if (t_size > MAX_SPA_MESSAGE_TYPE_SIZE)
253     {
254         free(tbuf);
255         return(FKO_ERROR_INVALID_DATA);
256     }
257
258     strlcpy(tbuf, ndx, t_size+1);
259
260     ctx->message_type = (unsigned int)atoi(tbuf);
261
262     if(ctx->message_type < 0 || ctx->message_type >= FKO_LAST_MSG_TYPE)
263     {
264         free(tbuf);
265         return(FKO_ERROR_INVALID_DATA);
266     }
267
268     /* Extract the SPA message string.
269     */
270     ndx += t_size + 1;
271     if((t_size = strcspn(ndx, ":")) < 1)
272     {
273         free(tbuf);
274         return(FKO_ERROR_INVALID_DATA);
275     }
276
277     if (t_size > MAX_SPA_MESSAGE_SIZE)
278     {
279         free(tbuf);
280         return(FKO_ERROR_INVALID_DATA);
281     }
282
283     strlcpy(tbuf, ndx, t_size+1);
284
285     ctx->message = malloc(t_size+1); /* Yes, more than we need */
286     if(ctx->message == NULL)
287     {
288         free(tbuf);
289         return(FKO_ERROR_MEMORY_ALLOCATION);
290     }
291
292     b64_decode(tbuf, (unsigned char*)ctx->message);
293
294     if(ctx->message_type == FKO_COMMAND_MSG)
295     {
296         /* Require a message similar to: 1.2.3.4,<command>
297         */
298         if(validate_cmd_msg(ctx->message) != FKO_SUCCESS)
299         {
300             free(tbuf);
301             return(FKO_ERROR_INVALID_DATA);
302         }
303     }
304     else
305     {
306         /* Require a message similar to: 1.2.3.4,tcp/22
307         */
308         if(validate_access_msg(ctx->message) != FKO_SUCCESS)
309         {
310             free(tbuf);
311             return(FKO_ERROR_INVALID_DATA);
312         }
313     }
314
315     /* Extract nat_access string if the message_type indicates so.
316     */
317     if(  ctx->message_type == FKO_NAT_ACCESS_MSG
318       || ctx->message_type == FKO_LOCAL_NAT_ACCESS_MSG
319       || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
320       || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
321     {
322         ndx += t_size + 1;
323         if((t_size = strcspn(ndx, ":")) < 1)
324         {
325             free(tbuf);
326             return(FKO_ERROR_INVALID_DATA);
327         }
328
329         if (t_size > MAX_SPA_MESSAGE_SIZE)
330         {
331             free(tbuf);
332             return(FKO_ERROR_INVALID_DATA);
333         }
334
335         strlcpy(tbuf, ndx, t_size+1);
336
337         ctx->nat_access = malloc(t_size+1); /* Yes, more than we need */
338         if(ctx->nat_access == NULL)
339         {
340             free(tbuf);
341             return(FKO_ERROR_MEMORY_ALLOCATION);
342         }
343
344         b64_decode(tbuf, (unsigned char*)ctx->nat_access);
345     }
346
347     /* Now look for a server_auth string.
348     */
349     ndx += t_size + 1;
350     if((t_size = strlen(ndx)) > 0)
351     {
352         if (t_size > MAX_SPA_MESSAGE_SIZE)
353         {
354             free(tbuf);
355             return(FKO_ERROR_INVALID_DATA);
356         }
357
358         /* There is data, but what is it?
359          * If the message_type does not have a timeout, assume it is a
360          * server_auth field.
361         */
362         if(  ctx->message_type != FKO_CLIENT_TIMEOUT_ACCESS_MSG
363           && ctx->message_type != FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
364           && ctx->message_type != FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
365         {
366             strlcpy(tbuf, ndx, t_size+1);
367
368             ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */
369             if(ctx->server_auth == NULL)
370             {
371                 free(tbuf);
372                 return(FKO_ERROR_MEMORY_ALLOCATION);
373             }
374
375             b64_decode(tbuf, (unsigned char*)ctx->server_auth);
376
377             /* At this point we should be done.
378             */
379             free(tbuf);
380
381             /* Call the context initialized.
382             */
383             ctx->initval = FKO_CTX_INITIALIZED;
384             FKO_SET_CTX_INITIALIZED(ctx);
385
386             return(FKO_SUCCESS);
387         }
388
389         /* If we are here then we may still have a server_auth string,
390          * or a timeout, or both. So we look for a ':' delimiter.  If
391          * it is there we have both, if not we check the message_type
392          * again.
393         */
394         if(strchr(ndx, ':'))
395         {
396             t_size = strcspn(ndx, ":");
397
398             if (t_size > MAX_SPA_MESSAGE_SIZE)
399             {
400                 free(tbuf);
401                 return(FKO_ERROR_INVALID_DATA);
402             }
403
404             /* Looks like we have both, so assume this is the 
405             */
406             strlcpy(tbuf, ndx, t_size+1);
407
408             ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */
409             if(ctx->server_auth == NULL)
410             {
411                 free(tbuf);
412                 return(FKO_ERROR_MEMORY_ALLOCATION);
413             }
414
415             b64_decode(tbuf, (unsigned char*)ctx->server_auth);
416
417             ndx += t_size + 1;
418         }
419
420         /* Now we look for a timeout value if one is supposed to be there.
421         */
422         if(  ctx->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG
423           || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
424           || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
425         {
426             if((t_size = strlen(ndx)) < 1)
427             {
428                 free(tbuf);
429                 return(FKO_ERROR_INVALID_DATA);
430             }
431             if (t_size > MAX_SPA_MESSAGE_SIZE)
432             {
433                 free(tbuf);
434                 return(FKO_ERROR_INVALID_DATA);
435             }
436
437             /* Should be a number only.
438             */
439             if(strspn(ndx, "0123456789") != t_size)
440             {
441                 free(tbuf);
442                 return(FKO_ERROR_INVALID_DATA);
443             }
444
445             ctx->client_timeout = (unsigned int)atoi(ndx);
446         }
447     }
448
449     /* Done with the tmp buffer.
450     */
451     free(tbuf);
452
453     /* Call the context initialized.
454     */
455     ctx->initval = FKO_CTX_INITIALIZED;
456     FKO_SET_CTX_INITIALIZED(ctx);
457
458     return(FKO_SUCCESS);
459 }
460
461 /***EOF***/