1 /* ykclient_server_response.c --- Server response parsing and validation.
2  *
3  * Written by Sebastien Martini <seb@dbzteam.org>.
4  * Copyright (c) 2011-2013 Yubico AB
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are
9  * met:
10  *
11  *    * Redistributions of source code must retain the above copyright
12  *      notice, this list of conditions and the following disclaimer.
13  *
14  *    * Redistributions in binary form must reproduce the above
15  *      copyright notice, this list of conditions and the following
16  *      disclaimer in the documentation and/or other materials provided
17  *      with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32 
33 #include "ykclient_server_response.h"
34 
35 #include "ykclient.h"
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include "sha.h"
42 #include "cdecode.h"
43 
44 
45 /* Parameters' manipulation functions */
46 
47 static void
parameter_free(ykclient_parameter_t * param)48 parameter_free (ykclient_parameter_t * param)
49 {
50   if (param == NULL)
51     return;
52 
53   if (param->key)
54     free (param->key);
55   if (param->value)
56     free (param->value);
57 
58   free (param);
59 }
60 
61 /* Inserts elem in front of params. */
62 static void
list_parameter_insert_front(ykclient_parameters_t ** params,ykclient_parameter_t * elem)63 list_parameter_insert_front (ykclient_parameters_t ** params,
64 			     ykclient_parameter_t * elem)
65 {
66   if (params == NULL || elem == NULL)
67     return;
68 
69   ykclient_parameters_t *new_node = malloc (sizeof (ykclient_parameters_t));
70   if (new_node == NULL)
71     return;
72   memset (new_node, 0, (sizeof (ykclient_parameters_t)));
73 
74   new_node->parameter = elem;
75   new_node->next = NULL;
76 
77   if (*params == NULL)
78     {
79       *params = new_node;
80       return;
81     }
82 
83   new_node->next = *params;
84   *params = new_node;
85 }
86 
87 /* Keys comparison function. It compares two keys, and returns 1 if
88    the first precedes the second. */
89 static int
alphanum_less_than(const ykclient_parameter_t * rhs,const ykclient_parameter_t * lhs)90 alphanum_less_than (const ykclient_parameter_t * rhs,
91 		    const ykclient_parameter_t * lhs)
92 {
93   if (rhs == NULL || lhs == NULL)
94     return -1;
95 
96   if (strcmp (rhs->key, lhs->key) < 0)
97     return 1;
98   return 0;
99 }
100 
101 /* Inserts elem into params. The position where elem must inserted is
102    determined by cmp_func. cmp_func must be a strict weak ordering binary
103    predicate. */
104 static void
list_parameter_insert_ord(ykclient_parameters_t ** params,ykclient_parameter_t * elem,int (* cmp_func)(const ykclient_parameter_t * rhs,const ykclient_parameter_t * lhs))105 list_parameter_insert_ord (ykclient_parameters_t ** params,
106 			   ykclient_parameter_t * elem,
107 			   int (*cmp_func) (const ykclient_parameter_t * rhs,
108 					    const ykclient_parameter_t * lhs))
109 {
110   if (elem == NULL)
111     return;
112 
113   ykclient_parameters_t *iter = *params;
114   ykclient_parameters_t *prev = NULL;
115 
116   for (; iter != NULL; iter = iter->next)
117     {
118       const int result = cmp_func (elem, iter->parameter);
119       if (result == -1)
120 	return;			/* error */
121       if (result == 1)
122 	break;
123       prev = iter;
124     }
125 
126   list_parameter_insert_front (&iter, elem);
127   if (prev != NULL)
128     prev->next = iter;
129   else
130     *params = iter;
131 }
132 
133 static void
list_parameter_free(ykclient_parameters_t * params)134 list_parameter_free (ykclient_parameters_t * params)
135 {
136   ykclient_parameters_t *iter = params;
137   while (iter != NULL)
138     {
139       parameter_free (iter->parameter);
140       ykclient_parameters_t *current = iter;
141       iter = iter->next;
142       free (current);
143     }
144 }
145 
146 
147 /* Server's response functions */
148 
149 ykclient_server_response_t *
ykclient_server_response_init(void)150 ykclient_server_response_init (void)
151 {
152   ykclient_server_response_t *serv_response =
153     malloc (sizeof (ykclient_server_response_t));
154   if (serv_response == NULL)
155     return NULL;
156   memset (serv_response, 0, (sizeof (ykclient_server_response_t)));
157   serv_response->signature = NULL;
158   serv_response->parameters = NULL;
159   return serv_response;
160 }
161 
162 void
ykclient_server_response_free(ykclient_server_response_t * response)163 ykclient_server_response_free (ykclient_server_response_t * response)
164 {
165   if (response == NULL)
166     return;
167   list_parameter_free (response->parameters);
168   parameter_free (response->signature);
169   free (response);
170 }
171 
172 
173 /* Server's response parsing functions */
174 
175 /* Returns 1 if c is a whitespace or a line break character, 0 otherwise. */
176 static int
is_ws_or_lb(char c)177 is_ws_or_lb (char c)
178 {
179   switch (c)
180     {
181       /* Line breaks */
182     case '\n':
183     case '\r':
184       /* Spaces */
185     case ' ':
186     case '\t':
187       return 1;
188     default:
189       return 0;
190     }
191   return 0;
192 }
193 
194 /* Trims leading whitespaces and line breaks. */
195 static void
trim_ws_and_lb(char ** s)196 trim_ws_and_lb (char **s)
197 {
198   if (s == NULL || *s == NULL)
199     return;
200 
201   char *pos = *s;
202   while (*pos != '\0' && is_ws_or_lb (*pos))
203     ++pos;
204   *s = pos;
205 }
206 
207 /* Parses and builds the next parameter param from s, moves response's pointer
208    to the immediate right character. Returns 0 if it succeeds. */
209 static ykclient_rc
parse_next_parameter(char ** s,ykclient_parameter_t * param)210 parse_next_parameter (char **s, ykclient_parameter_t * param)
211 {
212   if (s == NULL || *s == NULL || param == NULL)
213     return YKCLIENT_PARSE_ERROR;
214   char *pos = *s;
215   int index = 0;
216 
217   /* key parsing */
218   while (*(pos + index) != '\0' && *(pos + index) != '=')
219     ++index;
220   if (*(pos + index) == '\0')
221     return YKCLIENT_PARSE_ERROR;
222 
223   param->key = malloc (index + 1);
224   if (param->key == NULL)
225     {
226       return YKCLIENT_OUT_OF_MEMORY;
227     }
228   strncpy (param->key, pos, index);
229   param->key[index] = '\0';
230 
231   /* value parsing */
232   pos += index + 1;
233   index = 0;
234   while (*(pos + index) != '\0' && !is_ws_or_lb (*(pos + index)))
235     ++index;
236   if (*(pos + index) == '\0')
237     {
238       parameter_free (param);
239       return YKCLIENT_PARSE_ERROR;
240     }
241 
242   param->value = malloc (index + 1);
243   if (param->value == NULL)
244     {
245       parameter_free (param);
246       return YKCLIENT_OUT_OF_MEMORY;
247     }
248   strncpy (param->value, pos, index);
249   param->value[index] = '\0';
250 
251   pos += index;
252   *s = pos;
253   return 0;
254 }
255 
256 ykclient_rc
ykclient_server_response_parse(char * response,ykclient_server_response_t * serv_response)257 ykclient_server_response_parse (char *response,
258 				ykclient_server_response_t * serv_response)
259 {
260   if (response == NULL || serv_response == NULL)
261     return YKCLIENT_PARSE_ERROR;
262 
263   trim_ws_and_lb (&response);
264   while (*response != '\0')
265     {
266       ykclient_parameter_t *param = malloc (sizeof (ykclient_parameter_t));
267       if (param == NULL)
268 	return YKCLIENT_OUT_OF_MEMORY;
269       memset (param, 0, (sizeof (ykclient_parameter_t)));
270       int ret = parse_next_parameter (&response, param);
271       if (ret)
272 	return ret;
273 
274       if (!strcmp (param->key, "h"))
275 	serv_response->signature = param;
276       else
277 	/* Parameters are alphanumerically ordered. */
278 	list_parameter_insert_ord (&serv_response->parameters, param,
279 				   alphanum_less_than);
280       trim_ws_and_lb (&response);
281     }
282 
283   /* We expect at least one parameter along its mandatory signature. */
284   if (serv_response->signature == NULL)
285     return YKCLIENT_BAD_SERVER_SIGNATURE;
286   if (serv_response->parameters == NULL)
287     return YKCLIENT_PARSE_ERROR;
288   return 0;
289 }
290 
291 int
ykclient_server_response_verify_signature(const ykclient_server_response_t * serv_response,const char * key,int key_length)292 ykclient_server_response_verify_signature (const ykclient_server_response_t *
293 					   serv_response, const char *key,
294 					   int key_length)
295 {
296   if (serv_response == NULL || key == NULL || key_length < 0)
297     return 1;
298 
299   HMACContext ctx;
300   if (hmacReset (&ctx, SHA1, (const unsigned char *) key, key_length))
301     return 1;
302 
303   /* Iterate over parameters and feed the hmac. */
304   ykclient_parameters_t *iter = serv_response->parameters;
305   for (; iter != NULL; iter = iter->next)
306     {
307       if (hmacInput (&ctx, (unsigned char *) iter->parameter->key,
308 		     strlen (iter->parameter->key)))
309 	return 1;
310       if (hmacInput (&ctx, (const unsigned char *) "=", 1))
311 	return 1;
312       if (hmacInput (&ctx, (unsigned char *) iter->parameter->value,
313 		     strlen (iter->parameter->value)))
314 	return 1;
315       if (iter->next != NULL
316 	  && hmacInput (&ctx, (const unsigned char *) "&", 1))
317 	return 1;
318     }
319 
320   uint8_t digest[SHA1HashSize + 1];
321   if (hmacResult (&ctx, digest))
322     return 1;
323 
324   if (serv_response->signature == NULL ||
325       serv_response->signature->value == NULL)
326     return 1;
327 
328   char server_digest[SHA1HashSize + 1];
329   base64_decodestate b64;
330   base64_init_decodestate (&b64);
331   if (base64_decode_block (serv_response->signature->value,
332 			   strlen (serv_response->signature->value),
333 			   server_digest, &b64) != SHA1HashSize)
334     return 1;
335 
336   if (memcmp (server_digest, digest, SHA1HashSize) != 0)
337     return 1;
338   return 0;
339 }
340 
341 char *
ykclient_server_response_get(const ykclient_server_response_t * serv_response,const char * key)342 ykclient_server_response_get (const ykclient_server_response_t *
343 			      serv_response, const char *key)
344 {
345   if (serv_response == NULL || key == NULL)
346     return NULL;
347 
348   ykclient_parameters_t *iter = serv_response->parameters;
349   for (; iter != NULL; iter = iter->next)
350     if (!strcmp (iter->parameter->key, key))
351       return iter->parameter->value;
352   return NULL;
353 }
354