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
42static char *
43unescape (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
77int
78scram_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
219int
220scram_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
328int
329scram_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
456int
457scram_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