gcc warning fix fox: fko_decode.c:43:17: warning: variable ‘edata_size’ set but not...
[fwknop.git] / lib / fko_decode.c
1 /*
2  *****************************************************************************
3  *
4  * File:    fko_decode.c
5  *
6  * Author:  Damien S. Stuart
7  *
8  * Purpose: Decrypt and decode an FKO SPA message.
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 /* Decrypt the encoded SPA data.
38 */
39 int
40 fko_decode_spa_data(fko_ctx_t ctx)
41 {
42     char       *tbuf, *ndx;
43     int         t_size;
44
45     /* Check for required data.
46     */
47     if(ctx->encoded_msg == NULL
48       || strlen(ctx->encoded_msg) < MIN_SPA_ENCODED_MSG_SIZE)
49         return(FKO_ERROR_INVALID_DATA);
50
51     /* Move the Digest to its place in the context.
52     */
53     ndx = strrchr(ctx->encoded_msg, ':'); /* Find the last : in the data */
54     if(ndx == NULL)
55         return(FKO_ERROR_INVALID_DATA);
56
57     ndx++;
58
59     t_size = strlen(ndx);
60
61     switch(t_size)
62     {
63         case MD5_B64_LENGTH:
64             ctx->digest_type = FKO_DIGEST_MD5;
65             break;
66
67         case SHA1_B64_LENGTH:
68             ctx->digest_type = FKO_DIGEST_SHA1;
69             break;
70
71         case SHA256_B64_LENGTH:
72             ctx->digest_type = FKO_DIGEST_SHA256;
73             break;
74
75         case SHA384_B64_LENGTH:
76             ctx->digest_type = FKO_DIGEST_SHA384;
77             break;
78
79         case SHA512_B64_LENGTH:
80             ctx->digest_type = FKO_DIGEST_SHA512;
81             break;
82
83         default: /* Invalid or unsupported digest */
84             return(FKO_ERROR_INVALID_DIGEST_TYPE);
85     }
86
87     /* Copy the digest into the context and terminate the encoded data
88      * at that point so the original digest is not part of the
89      * encoded string.
90     */
91     ctx->digest = strdup(ndx);
92     if(ctx->digest == NULL)
93         return(FKO_ERROR_MEMORY_ALLOCATION);
94
95     /* Zero out the rest of the encoded_msg bucket...
96     */
97     bzero((ndx-1), t_size);
98
99     /* Make a tmp bucket for processing base64 encoded data and
100      * other general use.
101     */
102     tbuf = malloc(FKO_ENCODE_TMP_BUF_SIZE);
103     if(tbuf == NULL)
104         return(FKO_ERROR_MEMORY_ALLOCATION);
105
106     /* Can now verify the digest.
107     */
108     switch(ctx->digest_type)
109     {
110         case FKO_DIGEST_MD5:
111             md5_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
112             break;
113
114         case FKO_DIGEST_SHA1:
115             sha1_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
116             break;
117
118         case FKO_DIGEST_SHA256:
119             sha256_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
120             break;
121
122         case FKO_DIGEST_SHA384:
123             sha384_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
124             break;
125
126         case FKO_DIGEST_SHA512:
127             sha512_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
128             break;
129
130     }
131
132     /* We give up here if the computed digest does not match the
133      * digest in the message data.
134     */
135     if(strcmp(ctx->digest, tbuf))
136     {
137         free(tbuf);
138         return(FKO_ERROR_DIGEST_VERIFICATION_FAILED);
139     }
140
141     /* Now we will work through the encoded data and extract (and base64-
142      * decode where necessary), the SPA data fields and populate the context.
143     */
144     ndx = ctx->encoded_msg;
145
146     /* The rand val data */
147     if((t_size = strcspn(ndx, ":")) < FKO_RAND_VAL_SIZE)
148     {
149         free(tbuf);
150         return(FKO_ERROR_INVALID_DATA);
151     }
152
153     ctx->rand_val = calloc(1, FKO_RAND_VAL_SIZE+1);
154     if(ctx->rand_val == NULL)
155     {
156         free(tbuf);
157         return(FKO_ERROR_MEMORY_ALLOCATION);
158     }
159     ctx->rand_val = strncpy(ctx->rand_val, ndx, FKO_RAND_VAL_SIZE);
160
161     /* Jump to the next field (username).  We need to use the temp buffer
162      * for the base64 decode step.
163     */
164     ndx += t_size + 1;
165     if((t_size = strcspn(ndx, ":")) < 1)
166     {
167         free(tbuf);
168         return(FKO_ERROR_INVALID_DATA);
169     }
170
171     strlcpy(tbuf, ndx, t_size+1);
172
173     ctx->username = malloc(t_size+1); /* Yes, more than we need */
174     if(ctx->username == NULL)
175     {
176         free(tbuf);
177         return(FKO_ERROR_MEMORY_ALLOCATION);
178     }
179
180     b64_decode(tbuf, (unsigned char*)ctx->username);
181
182     /* Extract the timestamp value.
183     */
184     ndx += t_size + 1;
185     if((t_size = strcspn(ndx, ":")) < 1)
186     {
187         free(tbuf);
188         return(FKO_ERROR_INVALID_DATA);
189     }
190
191     strlcpy(tbuf, ndx, t_size+1);
192
193     ctx->timestamp = (unsigned int)atoi(tbuf);
194
195     /* Extract the version string.
196     */
197     ndx += t_size + 1;
198     if((t_size = strcspn(ndx, ":")) < 1)
199     {
200         free(tbuf);
201         return(FKO_ERROR_INVALID_DATA);
202     }
203
204     ctx->version = malloc(t_size+1);
205     if(ctx->version == NULL)
206     {
207         free(tbuf);
208         return(FKO_ERROR_MEMORY_ALLOCATION);
209     }
210
211     strlcpy(ctx->version, ndx, t_size+1);
212
213     /* Extract the message type value.
214     */
215     ndx += t_size + 1;
216     if((t_size = strcspn(ndx, ":")) < 1)
217     {
218         free(tbuf);
219         return(FKO_ERROR_INVALID_DATA);
220     }
221
222     strlcpy(tbuf, ndx, t_size+1);
223
224     ctx->message_type = (unsigned int)atoi(tbuf);
225
226     /* Extract the SPA message string.
227     */
228     ndx += t_size + 1;
229     if((t_size = strcspn(ndx, ":")) < 1)
230     {
231         free(tbuf);
232         return(FKO_ERROR_INVALID_DATA);
233     }
234
235     strlcpy(tbuf, ndx, t_size+1);
236
237     ctx->message = malloc(t_size+1); /* Yes, more than we need */
238     if(ctx->message == NULL)
239     {
240         free(tbuf);
241         return(FKO_ERROR_MEMORY_ALLOCATION);
242     }
243
244     b64_decode(tbuf, (unsigned char*)ctx->message);
245
246     /* Extract nat_access string if the message_type indicates so.
247     */
248     if(  ctx->message_type == FKO_NAT_ACCESS_MSG
249       || ctx->message_type == FKO_LOCAL_NAT_ACCESS_MSG
250       || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
251       || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
252     {
253         ndx += t_size + 1;
254         if((t_size = strcspn(ndx, ":")) < 1)
255         {
256             free(tbuf);
257             return(FKO_ERROR_INVALID_DATA);
258         }
259
260         strlcpy(tbuf, ndx, t_size+1);
261
262         ctx->nat_access = malloc(t_size+1); /* Yes, more than we need */
263         if(ctx->nat_access == NULL)
264         {
265             free(tbuf);
266             return(FKO_ERROR_MEMORY_ALLOCATION);
267         }
268
269         b64_decode(tbuf, (unsigned char*)ctx->nat_access);
270     }
271
272     /* Now look for a server_auth string.
273     */
274     ndx += t_size + 1;
275     if((t_size = strlen(ndx)) > 0)
276     {
277         /* There is data, but what is it?
278          * If the message_type does not have a timeout, assume it is a
279          * server_auth field.
280         */
281         if(  ctx->message_type != FKO_CLIENT_TIMEOUT_ACCESS_MSG
282           && ctx->message_type != FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
283           && ctx->message_type != FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
284         {
285             strlcpy(tbuf, ndx, t_size+1);
286
287             ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */
288             if(ctx->server_auth == NULL)
289             {
290                 free(tbuf);
291                 return(FKO_ERROR_MEMORY_ALLOCATION);
292             }
293
294             b64_decode(tbuf, (unsigned char*)ctx->server_auth);
295
296             /* At this point we should be done.
297             */
298             free(tbuf);
299
300             /* Call the context initialized.
301             */
302             ctx->initval = FKO_CTX_INITIALIZED;
303             FKO_SET_CTX_INITIALIZED(ctx);
304
305             return(FKO_SUCCESS);
306         }
307
308         /* If we are here then we may still have a server_auth string,
309          * or a timeout, or both. So we look for a ':' delimiter.  If
310          * it is there we have both, if not we check the message_type
311          * again.
312         */
313         if(strchr(ndx, ':'))
314         {
315             t_size = strcspn(ndx, ":");
316
317             /* Looks like we have both, so assume this is the 
318             */
319             strlcpy(tbuf, ndx, t_size+1);
320
321             ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */
322             if(ctx->server_auth == NULL)
323             {
324                 free(tbuf);
325                 return(FKO_ERROR_MEMORY_ALLOCATION);
326             }
327
328             b64_decode(tbuf, (unsigned char*)ctx->server_auth);
329
330             ndx += t_size + 1;
331         }
332
333         /* Now we look for a timeout value if one is supposed to be there.
334         */
335         if(  ctx->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG
336           || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
337           || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
338         {
339             if((t_size = strlen(ndx)) < 1)
340             {
341                 free(tbuf);
342                 return(FKO_ERROR_INVALID_DATA);
343             }
344
345             /* Should be a number only.
346             */
347             if(strspn(ndx, "0123456789") != t_size)
348             {
349                 free(tbuf);
350                 return(FKO_ERROR_INVALID_DATA);
351             }
352
353             ctx->client_timeout = (unsigned int)atoi(ndx);
354         }
355     }
356
357     /* Done with the tmp buffer.
358     */
359     free(tbuf);
360
361     /* Call the context initialized.
362     */
363     ctx->initval = FKO_CTX_INITIALIZED;
364     FKO_SET_CTX_INITIALIZED(ctx);
365
366     return(FKO_SUCCESS);
367 }
368
369 /***EOF***/