1 | /* parser.c --- SCRAM parser. |
2 | * Copyright (C) 2009-2012 Simon Josefsson |
3 | * |
4 | * This file is part of GNU SASL Library. |
5 | * |
6 | * GNU SASL Library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public License |
8 | * as published by the Free Software Foundation; either version 2.1 of |
9 | * the License, or (at your option) any later version. |
10 | * |
11 | * GNU SASL Library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with GNU SASL Library; if not, write to the Free |
18 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | * |
21 | */ |
22 | |
23 | #ifdef HAVE_CONFIG_H |
24 | #include "config.h" |
25 | #endif |
26 | |
27 | /* Get prototypes. */ |
28 | #include "parser.h" |
29 | |
30 | /* Get malloc, free. */ |
31 | #include <stdlib.h> |
32 | |
33 | /* Get memcpy, strlen. */ |
34 | #include <string.h> |
35 | |
36 | /* Get validator. */ |
37 | #include "validate.h" |
38 | |
39 | /* Get c_isalpha. */ |
40 | #include "c-ctype.h" |
41 | |
42 | static char * |
43 | unescape (const char *str, size_t len) |
44 | { |
45 | char *out = malloc (len + 1); |
46 | char *p = out; |
47 | |
48 | if (!out) |
49 | return NULL; |
50 | |
51 | while (len > 0 && *str) |
52 | { |
53 | if (len >= 3 && str[0] == '=' && str[1] == '2' && str[2] == 'C') |
54 | { |
55 | *p++ = ','; |
56 | str += 3; |
57 | len -= 3; |
58 | } |
59 | else if (len >= 3 && str[0] == '=' && str[1] == '3' && str[2] == 'D') |
60 | { |
61 | *p++ = '='; |
62 | str += 3; |
63 | len -= 3; |
64 | } |
65 | else |
66 | { |
67 | *p++ = *str; |
68 | str++; |
69 | len--; |
70 | } |
71 | } |
72 | *p = '\0'; |
73 | |
74 | return out; |
75 | } |
76 | |
77 | int |
78 | scram_parse_client_first (const char *str, size_t len, |
79 | struct scram_client_first *cf) |
80 | { |
81 | /* Minimum client first string is 'n,,n=a,r=b'. */ |
82 | if (strnlen (str, len) < 10) |
83 | return -1; |
84 | |
85 | if (len == 0 || (*str != 'n' && *str != 'y' && *str != 'p')) |
86 | return -1; |
87 | cf->cbflag = *str; |
88 | str++, len--; |
89 | |
90 | if (cf->cbflag == 'p') |
91 | { |
92 | const char *p; |
93 | |
94 | if (len == 0 || *str != '=') |
95 | return -1; |
96 | str++, len--; |
97 | |
98 | p = memchr (str, ',', len); |
99 | if (!p) |
100 | return -1; |
101 | cf->cbname = malloc (p - str + 1); |
102 | if (!cf->cbname) |
103 | return -1; |
104 | memcpy (cf->cbname, str, p - str); |
105 | cf->cbname[p - str] = '\0'; |
106 | len -= (p - str); |
107 | str += (p - str); |
108 | } |
109 | |
110 | if (len == 0 || *str != ',') |
111 | return -1; |
112 | str++, len--; |
113 | |
114 | if (len == 0) |
115 | return -1; |
116 | if (*str == 'a') |
117 | { |
118 | const char *p; |
119 | size_t l; |
120 | |
121 | str++, len--; |
122 | if (len == 0 || *str != '=') |
123 | return -1; |
124 | str++, len--; |
125 | |
126 | p = memchr (str, ',', len); |
127 | if (!p) |
128 | return -1; |
129 | |
130 | l = p - str; |
131 | if (len < l) |
132 | return -1; |
133 | |
134 | cf->authzid = unescape (str, l); |
135 | if (!cf->authzid) |
136 | return -1; |
137 | |
138 | str = p; |
139 | len -= l; |
140 | } |
141 | |
142 | if (len == 0 || *str != ',') |
143 | return -1; |
144 | str++, len--; |
145 | |
146 | if (len == 0 || *str != 'n') |
147 | return -1; |
148 | str++, len--; |
149 | |
150 | if (len == 0 || *str != '=') |
151 | return -1; |
152 | str++, len--; |
153 | |
154 | { |
155 | const char *p; |
156 | size_t l; |
157 | |
158 | p = memchr (str, ',', len); |
159 | if (!p) |
160 | return -1; |
161 | |
162 | l = p - str; |
163 | if (len < l) |
164 | return -1; |
165 | |
166 | cf->username = unescape (str, l); |
167 | if (!cf->username) |
168 | return -1; |
169 | |
170 | str = p; |
171 | len -= l; |
172 | } |
173 | |
174 | if (len == 0 || *str != ',') |
175 | return -1; |
176 | str++, len--; |
177 | |
178 | if (len == 0 || *str != 'r') |
179 | return -1; |
180 | str++, len--; |
181 | |
182 | if (len == 0 || *str != '=') |
183 | return -1; |
184 | str++, len--; |
185 | |
186 | { |
187 | const char *p; |
188 | size_t l; |
189 | |
190 | p = memchr (str, ',', len); |
191 | if (!p) |
192 | p = str + len; |
193 | if (!p) |
194 | return -1; |
195 | |
196 | l = p - str; |
197 | if (len < l) |
198 | return -1; |
199 | |
200 | cf->client_nonce = malloc (l + 1); |
201 | if (!cf->client_nonce) |
202 | return -1; |
203 | |
204 | memcpy (cf->client_nonce, str, l); |
205 | cf->client_nonce[l] = '\0'; |
206 | |
207 | str = p; |
208 | len -= l; |
209 | } |
210 | |
211 | /* FIXME check that any extension fields follow valid syntax. */ |
212 | |
213 | if (scram_valid_client_first (cf) < 0) |
214 | return -1; |
215 | |
216 | return 0; |
217 | } |
218 | |
219 | int |
220 | scram_parse_server_first (const char *str, size_t len, |
221 | struct scram_server_first *sf) |
222 | { |
223 | /* Minimum server first string is 'r=ab,s=biws,i=1'. */ |
224 | if (strnlen (str, len) < 15) |
225 | return -1; |
226 | |
227 | if (len == 0 || *str != 'r') |
228 | return -1; |
229 | str++, len--; |
230 | |
231 | if (len == 0 || *str != '=') |
232 | return -1; |
233 | str++, len--; |
234 | |
235 | { |
236 | const char *p; |
237 | size_t l; |
238 | |
239 | p = memchr (str, ',', len); |
240 | if (!p) |
241 | return -1; |
242 | |
243 | l = p - str; |
244 | if (len < l) |
245 | return -1; |
246 | |
247 | sf->nonce = malloc (l + 1); |
248 | if (!sf->nonce) |
249 | return -1; |
250 | |
251 | memcpy (sf->nonce, str, l); |
252 | sf->nonce[l] = '\0'; |
253 | |
254 | str = p; |
255 | len -= l; |
256 | } |
257 | |
258 | if (len == 0 || *str != ',') |
259 | return -1; |
260 | str++, len--; |
261 | |
262 | if (len == 0 || *str != 's') |
263 | return -1; |
264 | str++, len--; |
265 | |
266 | if (len == 0 || *str != '=') |
267 | return -1; |
268 | str++, len--; |
269 | |
270 | { |
271 | const char *p; |
272 | size_t l; |
273 | |
274 | p = memchr (str, ',', len); |
275 | if (!p) |
276 | return -1; |
277 | |
278 | l = p - str; |
279 | if (len < l) |
280 | return -1; |
281 | |
282 | sf->salt = malloc (l + 1); |
283 | if (!sf->salt) |
284 | return -1; |
285 | |
286 | memcpy (sf->salt, str, l); |
287 | sf->salt[l] = '\0'; |
288 | |
289 | str = p; |
290 | len -= l; |
291 | } |
292 | |
293 | if (len == 0 || *str != ',') |
294 | return -1; |
295 | str++, len--; |
296 | |
297 | if (len == 0 || *str != 'i') |
298 | return -1; |
299 | str++, len--; |
300 | |
301 | if (len == 0 || *str != '=') |
302 | return -1; |
303 | str++, len--; |
304 | |
305 | sf->iter = 0; |
306 | for (; len > 0 && *str >= '0' && *str <= '9'; str++, len--) |
307 | { |
308 | size_t last_iter = sf->iter; |
309 | |
310 | sf->iter = sf->iter * 10 + (*str - '0'); |
311 | |
312 | /* Protect against wrap arounds. */ |
313 | if (sf->iter < last_iter) |
314 | return -1; |
315 | } |
316 | |
317 | if (len > 0 && *str != ',') |
318 | return -1; |
319 | |
320 | /* FIXME check that any extension fields follow valid syntax. */ |
321 | |
322 | if (scram_valid_server_first (sf) < 0) |
323 | return -1; |
324 | |
325 | return 0; |
326 | } |
327 | |
328 | int |
329 | scram_parse_client_final (const char *str, size_t len, |
330 | struct scram_client_final *cl) |
331 | { |
332 | /* Minimum client final string is 'c=biws,r=ab,p=ab=='. */ |
333 | if (strnlen (str, len) < 18) |
334 | return -1; |
335 | |
336 | if (len == 0 || *str != 'c') |
337 | return -1; |
338 | str++, len--; |
339 | |
340 | if (len == 0 || *str != '=') |
341 | return -1; |
342 | str++, len--; |
343 | |
344 | { |
345 | const char *p; |
346 | size_t l; |
347 | |
348 | p = memchr (str, ',', len); |
349 | if (!p) |
350 | return -1; |
351 | |
352 | l = p - str; |
353 | if (len < l) |
354 | return -1; |
355 | |
356 | cl->cbind = malloc (l + 1); |
357 | if (!cl->cbind) |
358 | return -1; |
359 | |
360 | memcpy (cl->cbind, str, l); |
361 | cl->cbind[l] = '\0'; |
362 | |
363 | str = p; |
364 | len -= l; |
365 | } |
366 | |
367 | if (len == 0 || *str != ',') |
368 | return -1; |
369 | str++, len--; |
370 | |
371 | if (len == 0 || *str != 'r') |
372 | return -1; |
373 | str++, len--; |
374 | |
375 | if (len == 0 || *str != '=') |
376 | return -1; |
377 | str++, len--; |
378 | |
379 | { |
380 | const char *p; |
381 | size_t l; |
382 | |
383 | p = memchr (str, ',', len); |
384 | if (!p) |
385 | return -1; |
386 | |
387 | l = p - str; |
388 | if (len < l) |
389 | return -1; |
390 | |
391 | cl->nonce = malloc (l + 1); |
392 | if (!cl->nonce) |
393 | return -1; |
394 | |
395 | memcpy (cl->nonce, str, l); |
396 | cl->nonce[l] = '\0'; |
397 | |
398 | str = p; |
399 | len -= l; |
400 | } |
401 | |
402 | if (len == 0 || *str != ',') |
403 | return -1; |
404 | str++, len--; |
405 | |
406 | /* Ignore extensions. */ |
407 | while (len > 0 && c_isalpha (*str) && *str != 'p') |
408 | { |
409 | const char *p; |
410 | size_t l; |
411 | |
412 | str++, len--; |
413 | |
414 | if (len == 0 || *str != '=') |
415 | return -1; |
416 | str++, len--; |
417 | |
418 | p = memchr (str, ',', len); |
419 | if (!p) |
420 | return -1; |
421 | p++; |
422 | |
423 | l = p - str; |
424 | if (len < l) |
425 | return -1; |
426 | |
427 | str = p; |
428 | len -= l; |
429 | } |
430 | |
431 | if (len == 0 || *str != 'p') |
432 | return -1; |
433 | str++, len--; |
434 | |
435 | if (len == 0 || *str != '=') |
436 | return -1; |
437 | str++, len--; |
438 | |
439 | /* Sanity check proof. */ |
440 | if (memchr (str, '\0', len)) |
441 | return -1; |
442 | |
443 | cl->proof = malloc (len + 1); |
444 | if (!cl->proof) |
445 | return -1; |
446 | |
447 | memcpy (cl->proof, str, len); |
448 | cl->proof[len] = '\0'; |
449 | |
450 | if (scram_valid_client_final (cl) < 0) |
451 | return -1; |
452 | |
453 | return 0; |
454 | } |
455 | |
456 | int |
457 | scram_parse_server_final (const char *str, size_t len, |
458 | struct scram_server_final *sl) |
459 | { |
460 | /* Minimum client final string is 'v=ab=='. */ |
461 | if (strnlen (str, len) < 6) |
462 | return -1; |
463 | |
464 | if (len == 0 || *str != 'v') |
465 | return -1; |
466 | str++, len--; |
467 | |
468 | if (len == 0 || *str != '=') |
469 | return -1; |
470 | str++, len--; |
471 | |
472 | /* Sanity check proof. */ |
473 | if (memchr (str, '\0', len)) |
474 | return -1; |
475 | |
476 | sl->verifier = malloc (len + 1); |
477 | if (!sl->verifier) |
478 | return -1; |
479 | |
480 | memcpy (sl->verifier, str, len); |
481 | sl->verifier[len] = '\0'; |
482 | |
483 | if (scram_valid_server_final (sl) < 0) |
484 | return -1; |
485 | |
486 | return 0; |
487 | } |
488 | |