1 /* scramplus.c --- Test the SCRAM-SHA-1-PLUS 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     case GSASL_SCRAM_SALTED_PASSWORD:
97       if (i & 0x04 && i & 0x08)	/* Only works with fixed salt. */
98 	{
99 	  const char *str[] = {
100 	    "06bfd2d70a0fa425c20473722a93700df39f3cbd",
101 	    "f1e6c0e5a207367176ac42c7799b67ae3e097d7e",
102 	  };
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       gsasl_property_set (sctx, prop, "Zm5vcmQ=");
116       rc = GSASL_OK;
117       break;
118 
119     default:
120       fail ("Unknown callback property %u\n", prop);
121       break;
122     }
123 
124   return rc;
125 }
126 
127 void
doit(void)128 doit (void)
129 {
130   Gsasl *ctx = NULL;
131   Gsasl_session *server = NULL, *client = NULL;
132   char *s1, *s2;
133   size_t s1len, s2len;
134   int res;
135 
136   res = gsasl_init (&ctx);
137   if (res != GSASL_OK)
138     {
139       fail ("gsasl_init() failed (%d):\n%s\n", res, gsasl_strerror (res));
140       return;
141     }
142 
143   if (!gsasl_client_support_p (ctx, "SCRAM-SHA-1-PLUS")
144       || !gsasl_server_support_p (ctx, "SCRAM-SHA-1-PLUS"))
145     {
146       gsasl_done (ctx);
147       fail ("No support for SCRAM-SHA-1-PLUS.\n");
148       exit (77);
149     }
150 
151   gsasl_callback_set (ctx, callback);
152 
153   for (i = 0; i <= 21; i++)
154     {
155       bool server_first = (i % 2) == 0;
156 
157       if (debug)
158 	printf ("Iteration %d ...\n", i);
159 
160       res = gsasl_server_start (ctx, "SCRAM-SHA-1-PLUS", &server);
161       if (res != GSASL_OK)
162 	{
163 	  fail ("gsasl_server_start() failed (%d):\n%s\n",
164 		res, gsasl_strerror (res));
165 	  return;
166 	}
167       res = gsasl_client_start (ctx, "SCRAM-SHA-1-PLUS", &client);
168       if (res != GSASL_OK)
169 	{
170 	  fail ("gsasl_client_start() failed (%d):\n%s\n",
171 		res, gsasl_strerror (res));
172 	  return;
173 	}
174 
175       if (server_first)
176 	{
177 	  res = gsasl_step (server, NULL, 0, &s1, &s1len);
178 	  if (res != GSASL_NEEDS_MORE)
179 	    {
180 	      fail ("gsasl_step[%d](0) failed (%d):\n%s\n", i, res,
181 		    gsasl_strerror (res));
182 	      return;
183 	    }
184 
185 	  if (s1len != 0)
186 	    fail ("dummy initial server step produced output?!\n");
187 
188 	  if (debug)
189 	    printf ("S: %.*s [%c]\n", (int) s1len,
190 		    s1, res == GSASL_OK ? 'O' : 'N');
191 	}
192       else
193 	{
194 	  s1 = NULL;
195 	  s1len = 0;
196 	}
197 
198       /* Client first... */
199 
200       res = gsasl_step (client, s1, s1len, &s1, &s1len);
201       if (res != GSASL_NEEDS_MORE)
202 	{
203 	  fail ("gsasl_step[%d](1) failed (%d):\n%s\n", i, res,
204 		gsasl_strerror (res));
205 	  return;
206 	}
207 
208       if (debug)
209 	printf ("C: %.*s [%c]\n", (int) s1len,
210 		s1, res == GSASL_OK ? 'O' : 'N');
211 
212       /* Server first... */
213 
214       res = gsasl_step (server, s1, s1len, &s2, &s2len);
215       gsasl_free (s1);
216       if (res != GSASL_NEEDS_MORE)
217 	{
218 	  fail ("gsasl_step[%d](2) failed (%d):\n%s\n", i, res,
219 		gsasl_strerror (res));
220 	  return;
221 	}
222 
223       if (debug)
224 	printf ("S: %.*s [%c]\n", (int) s2len,
225 		s2, res == GSASL_OK ? 'O' : 'N');
226 
227       /* Client final... */
228 
229       res = gsasl_step (client, s2, s2len, &s1, &s1len);
230       gsasl_free (s2);
231       if (res != GSASL_NEEDS_MORE)
232 	{
233 	  fail ("gsasl_step[%d](3) failed (%d):\n%s\n", i, res,
234 		gsasl_strerror (res));
235 	  return;
236 	}
237 
238       /* Shorten length of cbdata. */
239       if (i == 17)
240 	s1[41] = '=';
241 
242       /* Increase length of cbdata. */
243       if (i == 18)
244 	{
245 	  s1[28] = 'B';
246 	  s1[29] = 'C';
247 	}
248 
249       /* Modify cbdata. */
250       if (i == 19)
251 	s1[30] = 'B';
252 
253       if (debug)
254 	printf ("C: %.*s [%c]\n", (int) s1len,
255 		s1, res == GSASL_OK ? 'O' : 'N');
256 
257       /* Server final... */
258 
259       res = gsasl_step (server, s1, s1len, &s2, &s2len);
260       gsasl_free (s1);
261       if (i >= 17 && i <= 19)
262 	{
263 	  if (res == GSASL_AUTHENTICATION_ERROR)
264 	    {
265 	      if (debug)
266 		success ("Authentication failed expectedly\n");
267 	      goto done;
268 	    }
269 	  else
270 	    res = GSASL_AUTHENTICATION_ERROR;
271 	}
272       if (res != GSASL_OK)
273 	{
274 	  fail ("gsasl_step[%d](4) failed (%d):\n%s\n", i, res,
275 		gsasl_strerror (res));
276 	  return;
277 	}
278 
279       if (debug)
280 	printf ("S: %.*s [%c]\n", (int) s2len,
281 		s2, res == GSASL_OK ? 'O' : 'N');
282 
283       /* Let client parse server final... */
284 
285       res = gsasl_step (client, s2, s2len, &s1, &s1len);
286       gsasl_free (s2);
287       if (res != GSASL_OK)
288 	{
289 	  fail ("gsasl_step[%d](5) failed (%d):\n%s\n", i, res,
290 		gsasl_strerror (res));
291 	  return;
292 	}
293 
294       if (s1len != 0)
295 	fail ("dummy final client step produced output?!\n");
296 
297       {
298 	const char *p = gsasl_property_fast (server, GSASL_AUTHID);
299 	if (p && strcmp (p, AUTHID[i % N_AUTHID]) != 0)
300 	  fail ("Bad authid? %s != %s\n", p, AUTHID[i % N_AUTHID]);
301 	if (i & 0x01 && !p)
302 	  fail ("Expected authid? %d/%s\n", i, AUTHID[i % N_AUTHID]);
303       }
304 
305       {
306 	const char *p = gsasl_property_fast (server, GSASL_AUTHZID);
307 	if (p && strcmp (p, AUTHZID[i % N_AUTHZID]) != 0)
308 	  fail ("Bad authzid? %s != %s\n", p, AUTHZID[i % N_AUTHZID]);
309 	if (i & 0x01 && !p)
310 	  fail ("Expected authzid? %d/%s\n", i, AUTHZID[i % N_AUTHZID]);
311       }
312 
313     done:
314       if (debug)
315 	printf ("\n");
316 
317       gsasl_finish (client);
318       gsasl_finish (server);
319     }
320 
321   gsasl_done (ctx);
322 }
323