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