1 /* scram.c --- Test the SCRAM mechanism.
2 * Copyright (C) 2009-2021 Simon Josefsson
3 *
4 * This file is part of GNU SASL.
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdbool.h>
30
31 #include "utils.h"
32
33 #define PASSWORD "Open, Sesame"
34
35 #define N_AUTHID 4
36 static const char *AUTHID[N_AUTHID] = {
37 "Ali Baba", "BAB,ABA", ",=,=", "="
38 /* "Ali " "\xC2\xAD" "Bab" "\xC2\xAA" */
39 /* "Al\xC2\xAA""dd\xC2\xAD""in\xC2\xAE" */
40 };
41
42 #define N_AUTHZID 4
43 static const char *AUTHZID[N_AUTHZID] = {
44 "jas", "BAB,ABA", ",=,=", "="
45 };
46
47 int i;
48
49 static int
callback(Gsasl * ctx,Gsasl_session * sctx,Gsasl_property prop)50 callback (Gsasl * ctx, Gsasl_session * sctx, Gsasl_property prop)
51 {
52 int rc = GSASL_NO_CALLBACK;
53
54 /* Get user info from user. */
55
56 switch (prop)
57 {
58 case GSASL_PASSWORD:
59 gsasl_property_set (sctx, prop, PASSWORD);
60 rc = GSASL_OK;
61 break;
62
63 case GSASL_AUTHID:
64 gsasl_property_set (sctx, prop, AUTHID[i % N_AUTHID]);
65 rc = GSASL_OK;
66 break;
67
68 case GSASL_AUTHZID:
69 if (i & 0x01)
70 {
71 gsasl_property_set (sctx, prop, AUTHZID[i % N_AUTHZID]);
72 rc = GSASL_OK;
73 }
74 break;
75
76 case GSASL_SCRAM_ITER:
77 if (strcmp (gsasl_property_fast (sctx, GSASL_AUTHID),
78 AUTHID[i % N_AUTHID]) != 0)
79 fail ("Username mismatch: %s",
80 gsasl_property_fast (sctx, GSASL_AUTHID));
81 if (i & 0x02)
82 {
83 gsasl_property_set (sctx, prop, "1234");
84 rc = GSASL_OK;
85 }
86 break;
87
88 case GSASL_SCRAM_SALT:
89 if (i & 0x04)
90 {
91 gsasl_property_set (sctx, prop, "c2FsdA==");
92 rc = GSASL_OK;
93 }
94 break;
95
96 #define SP1 "06bfd2d70a0fa425c20473722a93700df39f3cbd"
97 #define SP2 "f1e6c0e5a207367176ac42c7799b67ae3e097d7e"
98
99 case GSASL_SCRAM_SALTED_PASSWORD:
100 if (i & 0x04 && i & 0x08) /* Only works with fixed salt. */
101 {
102 const char *str[] = { SP1, SP2 };
103 /* >>1 to mask out authzid. */
104 size_t pos = (i & ~0x04 & ~0x08) >> 1;
105 gsasl_property_set (sctx, prop, str[pos]);
106 rc = GSASL_OK;
107 }
108 break;
109
110 case GSASL_SCRAM_SERVERKEY:
111 case GSASL_SCRAM_STOREDKEY:
112 break;
113
114 case GSASL_CB_TLS_UNIQUE:
115 break;
116
117 default:
118 fail ("Unknown callback property %u\n", prop);
119 break;
120 }
121
122 return rc;
123 }
124
125 void
doit(void)126 doit (void)
127 {
128 Gsasl *ctx = NULL;
129 Gsasl_session *server = NULL, *client = NULL;
130 char *s1, *s2;
131 size_t s1len, s2len;
132 int res;
133
134 res = gsasl_init (&ctx);
135 if (res != GSASL_OK)
136 {
137 fail ("gsasl_init() failed (%d):\n%s\n", res, gsasl_strerror (res));
138 return;
139 }
140
141 if (!gsasl_client_support_p (ctx, "SCRAM-SHA-1")
142 || !gsasl_server_support_p (ctx, "SCRAM-SHA-1"))
143 {
144 gsasl_done (ctx);
145 fail ("No support for SCRAM-SHA-1.\n");
146 exit (77);
147 }
148
149 gsasl_callback_set (ctx, callback);
150
151 for (i = 0; i <= 21; i++)
152 {
153 bool server_first = (i % 2) == 0;
154
155 if (debug)
156 printf ("Iteration %d ...\n", i);
157
158 res = gsasl_server_start (ctx, "SCRAM-SHA-1", &server);
159 if (res != GSASL_OK)
160 {
161 fail ("gsasl_server_start() failed (%d):\n%s\n",
162 res, gsasl_strerror (res));
163 return;
164 }
165 res = gsasl_client_start (ctx, "SCRAM-SHA-1", &client);
166 if (res != GSASL_OK)
167 {
168 fail ("gsasl_client_start() failed (%d):\n%s\n",
169 res, gsasl_strerror (res));
170 return;
171 }
172
173 if (server_first)
174 {
175 res = gsasl_step (server, NULL, 0, &s1, &s1len);
176 if (res != GSASL_NEEDS_MORE)
177 {
178 fail ("gsasl_step[%d](0) failed (%d):\n%s\n", i, res,
179 gsasl_strerror (res));
180 return;
181 }
182
183 if (s1len != 0)
184 fail ("dummy initial server step produced output?!\n");
185
186 if (debug)
187 printf ("S: %.*s [%c]\n", (int) s1len,
188 s1, res == GSASL_OK ? 'O' : 'N');
189 }
190 else
191 {
192 s1 = NULL;
193 s1len = 0;
194 }
195
196 /* Client first... */
197
198 res = gsasl_step (client, s1, s1len, &s1, &s1len);
199 if (res != GSASL_NEEDS_MORE)
200 {
201 fail ("gsasl_step[%d](1) failed (%d):\n%s\n", i, res,
202 gsasl_strerror (res));
203 return;
204 }
205
206 if (i == 16 || i == 17)
207 s1[0] = 'y';
208
209 if (i == 18)
210 {
211 char *s;
212 int l;
213
214 l = asprintf (&s, "%s,a=b", s1);
215 if (l < 0)
216 {
217 fail ("malloc");
218 return;
219 }
220 gsasl_free (s1);
221 s1 = s;
222 s1len = strlen (s);
223 }
224
225 if (i == 20)
226 {
227 char *s;
228 int l;
229
230 l = asprintf (&s, "%s,a=b,b=c,c=d", s1);
231 if (l < 0)
232 {
233 fail ("malloc");
234 return;
235 }
236 gsasl_free (s1);
237 s1 = s;
238 s1len = strlen (s);
239 }
240
241 if (debug)
242 printf ("C: %.*s [%c]\n", (int) s1len,
243 s1, res == GSASL_OK ? 'O' : 'N');
244
245 /* Server first... */
246
247 res = gsasl_step (server, s1, s1len, &s2, &s2len);
248 gsasl_free (s1);
249 if (res != GSASL_NEEDS_MORE)
250 {
251 fail ("gsasl_step[%d](2) failed (%d):\n%s\n", i, res,
252 gsasl_strerror (res));
253 return;
254 }
255
256 if (debug)
257 printf ("S: %.*s [%c]\n", (int) s2len,
258 s2, res == GSASL_OK ? 'O' : 'N');
259
260 /* Client final... */
261
262 res = gsasl_step (client, s2, s2len, &s1, &s1len);
263 gsasl_free (s2);
264 if (res != GSASL_NEEDS_MORE)
265 {
266 fail ("gsasl_step[%d](3) failed (%d):\n%s\n", i, res,
267 gsasl_strerror (res));
268 return;
269 }
270
271 if (i == 17)
272 memcpy (s1 + 2, "eS", 2);
273
274 if (i == 19 && s1len > 31)
275 {
276 char *s;
277 int l;
278
279 l = asprintf (&s, "%.*s,a=b,%s", (int) (s1len - 31),
280 s1, s1 + s1len - 31 + 1);
281 if (l < 0)
282 {
283 fail ("malloc");
284 return;
285 }
286 gsasl_free (s1);
287 s1 = s;
288 s1len = strlen (s);
289 }
290
291 if (i == 21 && s1len > 31)
292 {
293 char *s;
294 int l;
295
296 l = asprintf (&s, "%.*s,a=b,b=c,c=d,%s", (int) (s1len - 31),
297 s1, s1 + s1len - 31 + 1);
298 if (l < 0)
299 {
300 fail ("malloc");
301 return;
302 }
303 gsasl_free (s1);
304 s1 = s;
305 s1len = strlen (s);
306 }
307
308 if (debug)
309 printf ("C: %.*s [%c]\n", (int) s1len,
310 s1, res == GSASL_OK ? 'O' : 'N');
311
312 /* Server final... */
313
314 res = gsasl_step (server, s1, s1len, &s2, &s2len);
315 gsasl_free (s1);
316 if (i >= 16 && i <= 21)
317 {
318 if (res == GSASL_AUTHENTICATION_ERROR)
319 {
320 if (debug)
321 success ("Authentication failed expectedly\n");
322 goto done;
323 }
324 else
325 res = GSASL_AUTHENTICATION_ERROR;
326 }
327 if (res != GSASL_OK)
328 {
329 fail ("gsasl_step[%d](4) failed (%d):\n%s\n", i, res,
330 gsasl_strerror (res));
331 return;
332 }
333
334 if (debug)
335 printf ("S: %.*s [%c]\n", (int) s2len,
336 s2, res == GSASL_OK ? 'O' : 'N');
337
338 /* Let client parse server final... */
339
340 res = gsasl_step (client, s2, s2len, &s1, &s1len);
341 gsasl_free (s2);
342 if (res != GSASL_OK)
343 {
344 fail ("gsasl_step[%d](5) failed (%d):\n%s\n", i, res,
345 gsasl_strerror (res));
346 return;
347 }
348
349 if (s1len != 0)
350 fail ("dummy final client step produced output?!\n");
351
352 {
353 const char *p = gsasl_property_fast (server, GSASL_AUTHID);
354 if (p && strcmp (p, AUTHID[i % N_AUTHID]) != 0)
355 fail ("Bad authid? %s != %s\n", p, AUTHID[i % N_AUTHID]);
356 if (i & 0x01 && !p)
357 fail ("Expected authid? %d/%s\n", i, AUTHID[i % N_AUTHID]);
358 }
359
360 {
361 const char *p = gsasl_property_fast (server, GSASL_AUTHZID);
362 if (p && strcmp (p, AUTHZID[i % N_AUTHZID]) != 0)
363 fail ("Bad authzid? %s != %s\n", p, AUTHZID[i % N_AUTHZID]);
364 if (i & 0x01 && !p)
365 fail ("Expected authzid? %d/%s\n", i, AUTHZID[i % N_AUTHZID]);
366 }
367
368 {
369 const char *ci = gsasl_property_fast (client, GSASL_SCRAM_ITER);
370 const char *si = gsasl_property_fast (server, GSASL_SCRAM_ITER);
371 if (debug)
372 {
373 printf ("GSASL_SCRAM_ITER (client): %s\n", ci);
374 printf ("GSASL_SCRAM_ITER (server): %s\n", si);
375 }
376 if (!ci || !si || strcmp (ci, si) != 0)
377 fail ("scram iter mismatch\n");
378 }
379
380 {
381 const char *cs = gsasl_property_fast (client, GSASL_SCRAM_SALT);
382 const char *ss = gsasl_property_fast (server, GSASL_SCRAM_SALT);
383 if (debug)
384 {
385 printf ("GSASL_SCRAM_ITER (client): %s\n", cs);
386 printf ("GSASL_SCRAM_ITER (server): %s\n", ss);
387 }
388 if (!cs || !ss || strcmp (cs, ss) != 0)
389 fail ("scram salt mismatch\n");
390 }
391
392 {
393 const char *csp =
394 gsasl_property_fast (client, GSASL_SCRAM_SALTED_PASSWORD);
395 const char *ssp =
396 gsasl_property_fast (server, GSASL_SCRAM_SALTED_PASSWORD);
397
398 if (debug)
399 {
400 printf ("GSASL_SCRAM_SALTED_PASSWORD (client): %s\n", csp);
401 printf ("GSASL_SCRAM_SALTED_PASSWORD (server): %s\n", ssp);
402 }
403 if (!csp || !ssp || strcmp (csp, ssp) != 0)
404 fail ("scram salted password mismatch\n");
405 }
406
407
408 {
409 const char *sek = gsasl_property_fast (server, GSASL_SCRAM_SERVERKEY);
410 const char *stk = gsasl_property_fast (server, GSASL_SCRAM_STOREDKEY);
411 const char *ssp =
412 gsasl_property_fast (server, GSASL_SCRAM_SALTED_PASSWORD);
413
414 if (debug)
415 {
416 printf ("GSASL_SCRAM_SERVERKEY: %s\n", sek);
417 printf ("GSASL_SCRAM_STOREDKEY: %s\n", stk);
418 }
419
420 if (!sek)
421 fail ("missing ServerKey\n");
422 if (!stk)
423 fail ("missing StoredKey\n");
424 if ((strcmp (ssp, SP2) == 0)
425 && (strcmp (sek, "m05YwQmkiYtfagsfqHyTaFbylTI=") != 0))
426 fail ("incorrect ServerKey\n");
427 if ((strcmp (ssp, SP2) == 0)
428 && (strcmp (stk, "bgzByAo2e3aFvfezFeqJy5OJSUs=") != 0))
429 fail ("incorrect StoredKey\n");
430 }
431
432 done:
433 if (debug)
434 printf ("\n");
435
436 gsasl_finish (client);
437 gsasl_finish (server);
438 }
439
440 gsasl_done (ctx);
441 }
442