2 *****************************************************************************
6 * Author: Damien S. Stuart
8 * Purpose: Decode an FKO SPA message after decryption.
10 * Copyright 2009-2010 Damien Stuart (dstuart@dstuart.org)
12 * License (GNU Public License):
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.
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.
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
29 *****************************************************************************
31 #include "fko_common.h"
33 #include "cipher_funcs.h"
37 /* Decode the encoded SPA data.
40 fko_decode_spa_data(fko_ctx_t ctx)
42 char *tbuf, *ndx, *tmp;
45 /* Check for required data.
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);
51 if(strnlen(ctx->encoded_msg,
52 MAX_SPA_ENCODED_MSG_SIZE) == MAX_SPA_ENCODED_MSG_SIZE)
53 return(FKO_ERROR_INVALID_DATA);
55 /* Make sure there are enough fields in the SPA packet
56 * delimited with ':' chars
58 ndx = ctx->encoded_msg;
59 for (i=0; i < MAX_SPA_FIELDS; i++)
61 if ((tmp = strchr(ndx, ':')) == NULL)
68 if (i < MIN_SPA_FIELDS)
69 return(FKO_ERROR_INVALID_DATA);
76 ctx->digest_type = FKO_DIGEST_MD5;
80 ctx->digest_type = FKO_DIGEST_SHA1;
83 case SHA256_B64_LENGTH:
84 ctx->digest_type = FKO_DIGEST_SHA256;
87 case SHA384_B64_LENGTH:
88 ctx->digest_type = FKO_DIGEST_SHA384;
91 case SHA512_B64_LENGTH:
92 ctx->digest_type = FKO_DIGEST_SHA512;
95 default: /* Invalid or unsupported digest */
96 return(FKO_ERROR_INVALID_DIGEST_TYPE);
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
103 ctx->digest = strdup(ndx);
104 if(ctx->digest == NULL)
105 return(FKO_ERROR_MEMORY_ALLOCATION);
107 /* Zero out the rest of the encoded_msg bucket...
109 bzero((ndx-1), t_size);
111 /* Make a tmp bucket for processing base64 encoded data and
114 tbuf = malloc(FKO_ENCODE_TMP_BUF_SIZE);
116 return(FKO_ERROR_MEMORY_ALLOCATION);
118 /* Can now verify the digest.
120 switch(ctx->digest_type)
123 md5_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
126 case FKO_DIGEST_SHA1:
127 sha1_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
130 case FKO_DIGEST_SHA256:
131 sha256_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
134 case FKO_DIGEST_SHA384:
135 sha384_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
138 case FKO_DIGEST_SHA512:
139 sha512_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
144 /* We give up here if the computed digest does not match the
145 * digest in the message data.
147 if(strncmp(ctx->digest, tbuf, t_size))
150 return(FKO_ERROR_DIGEST_VERIFICATION_FAILED);
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.
156 ndx = ctx->encoded_msg;
158 /* The rand val data */
159 if((t_size = strcspn(ndx, ":")) < FKO_RAND_VAL_SIZE)
162 return(FKO_ERROR_INVALID_DATA);
165 ctx->rand_val = calloc(1, FKO_RAND_VAL_SIZE+1);
166 if(ctx->rand_val == NULL)
169 return(FKO_ERROR_MEMORY_ALLOCATION);
171 ctx->rand_val = strncpy(ctx->rand_val, ndx, FKO_RAND_VAL_SIZE);
173 /* Jump to the next field (username). We need to use the temp buffer
174 * for the base64 decode step.
177 if((t_size = strcspn(ndx, ":")) < 1)
180 return(FKO_ERROR_INVALID_DATA);
183 if (t_size > MAX_SPA_USERNAME_SIZE)
186 return(FKO_ERROR_INVALID_DATA);
189 strlcpy(tbuf, ndx, t_size+1);
191 ctx->username = malloc(t_size+1); /* Yes, more than we need */
192 if(ctx->username == NULL)
195 return(FKO_ERROR_MEMORY_ALLOCATION);
198 b64_decode(tbuf, (unsigned char*)ctx->username);
200 /* Extract the timestamp value.
203 if((t_size = strcspn(ndx, ":")) < 1)
206 return(FKO_ERROR_INVALID_DATA);
209 if (t_size > MAX_SPA_TIMESTAMP_SIZE)
212 return(FKO_ERROR_INVALID_DATA);
215 strlcpy(tbuf, ndx, t_size+1);
217 ctx->timestamp = (unsigned int)atoi(tbuf);
219 /* Extract the version string.
222 if((t_size = strcspn(ndx, ":")) < 1)
225 return(FKO_ERROR_INVALID_DATA);
228 if (t_size > MAX_SPA_VERSION_SIZE)
231 return(FKO_ERROR_INVALID_DATA);
234 ctx->version = malloc(t_size+1);
235 if(ctx->version == NULL)
238 return(FKO_ERROR_MEMORY_ALLOCATION);
241 strlcpy(ctx->version, ndx, t_size+1);
243 /* Extract the message type value.
246 if((t_size = strcspn(ndx, ":")) < 1)
249 return(FKO_ERROR_INVALID_DATA);
252 if (t_size > MAX_SPA_MESSAGE_TYPE_SIZE)
255 return(FKO_ERROR_INVALID_DATA);
258 strlcpy(tbuf, ndx, t_size+1);
260 ctx->message_type = (unsigned int)atoi(tbuf);
262 /* Extract the SPA message string.
265 if((t_size = strcspn(ndx, ":")) < 1)
268 return(FKO_ERROR_INVALID_DATA);
271 if (t_size > MAX_SPA_MESSAGE_SIZE)
274 return(FKO_ERROR_INVALID_DATA);
277 strlcpy(tbuf, ndx, t_size+1);
279 ctx->message = malloc(t_size+1); /* Yes, more than we need */
280 if(ctx->message == NULL)
283 return(FKO_ERROR_MEMORY_ALLOCATION);
286 b64_decode(tbuf, (unsigned char*)ctx->message);
288 /* Require a message similar to: 1.2.3.4,tcp/22
290 if(validate_access_msg(ctx->message) != FKO_SUCCESS)
293 return(FKO_ERROR_INVALID_DATA);
296 /* Extract nat_access string if the message_type indicates so.
298 if( ctx->message_type == FKO_NAT_ACCESS_MSG
299 || ctx->message_type == FKO_LOCAL_NAT_ACCESS_MSG
300 || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
301 || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
304 if((t_size = strcspn(ndx, ":")) < 1)
307 return(FKO_ERROR_INVALID_DATA);
310 if (t_size > MAX_SPA_MESSAGE_SIZE)
313 return(FKO_ERROR_INVALID_DATA);
316 strlcpy(tbuf, ndx, t_size+1);
318 ctx->nat_access = malloc(t_size+1); /* Yes, more than we need */
319 if(ctx->nat_access == NULL)
322 return(FKO_ERROR_MEMORY_ALLOCATION);
325 b64_decode(tbuf, (unsigned char*)ctx->nat_access);
328 /* Now look for a server_auth string.
331 if((t_size = strlen(ndx)) > 0)
333 if (t_size > MAX_SPA_MESSAGE_SIZE)
336 return(FKO_ERROR_INVALID_DATA);
339 /* There is data, but what is it?
340 * If the message_type does not have a timeout, assume it is a
343 if( ctx->message_type != FKO_CLIENT_TIMEOUT_ACCESS_MSG
344 && ctx->message_type != FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
345 && ctx->message_type != FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
347 strlcpy(tbuf, ndx, t_size+1);
349 ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */
350 if(ctx->server_auth == NULL)
353 return(FKO_ERROR_MEMORY_ALLOCATION);
356 b64_decode(tbuf, (unsigned char*)ctx->server_auth);
358 /* At this point we should be done.
362 /* Call the context initialized.
364 ctx->initval = FKO_CTX_INITIALIZED;
365 FKO_SET_CTX_INITIALIZED(ctx);
370 /* If we are here then we may still have a server_auth string,
371 * or a timeout, or both. So we look for a ':' delimiter. If
372 * it is there we have both, if not we check the message_type
377 t_size = strcspn(ndx, ":");
379 if (t_size > MAX_SPA_MESSAGE_SIZE)
382 return(FKO_ERROR_INVALID_DATA);
385 /* Looks like we have both, so assume this is the
387 strlcpy(tbuf, ndx, t_size+1);
389 ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */
390 if(ctx->server_auth == NULL)
393 return(FKO_ERROR_MEMORY_ALLOCATION);
396 b64_decode(tbuf, (unsigned char*)ctx->server_auth);
401 /* Now we look for a timeout value if one is supposed to be there.
403 if( ctx->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG
404 || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
405 || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
407 if((t_size = strlen(ndx)) < 1)
410 return(FKO_ERROR_INVALID_DATA);
412 if (t_size > MAX_SPA_MESSAGE_SIZE)
415 return(FKO_ERROR_INVALID_DATA);
418 /* Should be a number only.
420 if(strspn(ndx, "0123456789") != t_size)
423 return(FKO_ERROR_INVALID_DATA);
426 ctx->client_timeout = (unsigned int)atoi(ndx);
430 /* Done with the tmp buffer.
434 /* Call the context initialized.
436 ctx->initval = FKO_CTX_INITIALIZED;
437 FKO_SET_CTX_INITIALIZED(ctx);