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