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