1 /* openid20.c --- Test the OPENID20 mechanism.
2  * Copyright (C) 2010-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 #include <config.h>
22 
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "utils.h"
29 
30 const char *authzid = NULL;
31 const char *sreg = NULL;
32 int validation_res = GSASL_OK;
33 int expect_server_res = GSASL_OK;
34 int expect_client_res = GSASL_OK;
35 int expect_server2_res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
36 
37 static int
client_callback(Gsasl * ctx,Gsasl_session * sctx,Gsasl_property prop)38 client_callback (Gsasl * ctx, Gsasl_session * sctx, Gsasl_property prop)
39 {
40   int rc = GSASL_NO_CALLBACK;
41 
42   switch (prop)
43     {
44     case GSASL_AUTHZID:
45       if (authzid)
46 	gsasl_property_set (sctx, prop, authzid);
47       rc = GSASL_OK;
48       break;
49 
50     case GSASL_AUTHID:
51       gsasl_property_set (sctx, prop, "http://user.example.org/");
52       rc = GSASL_OK;
53       break;
54 
55     case GSASL_OPENID20_AUTHENTICATE_IN_BROWSER:
56       rc = GSASL_OK;
57       break;
58 
59     default:
60       fail ("Unknown client callback property %u\n", prop);
61       break;
62     }
63 
64   return rc;
65 }
66 
67 static int
server_callback(Gsasl * ctx,Gsasl_session * sctx,Gsasl_property prop)68 server_callback (Gsasl * ctx, Gsasl_session * sctx, Gsasl_property prop)
69 {
70   int rc = GSASL_NO_CALLBACK;
71 
72   switch (prop)
73     {
74     case GSASL_OPENID20_REDIRECT_URL:
75       gsasl_property_set (sctx, prop,
76 			  "http://idp.example/NONCE/?openid.foo=bar");
77       rc = GSASL_OK;
78       break;
79 
80     case GSASL_VALIDATE_OPENID20:
81       rc = validation_res;
82       break;
83 
84     case GSASL_OPENID20_OUTCOME_DATA:
85       if (sreg)
86 	gsasl_property_set (sctx, prop, sreg);
87       rc = GSASL_OK;
88       break;
89 
90     default:
91       fail ("Unknown server callback property %u\n", prop);
92       break;
93     }
94 
95   return rc;
96 }
97 
98 static void
openid20(Gsasl * c,Gsasl * s)99 openid20 (Gsasl * c, Gsasl * s)
100 {
101   Gsasl_session *client, *server;
102   char *s1, *s2;
103   int res;
104 
105   /* Simple client */
106 
107   res = gsasl_client_start (c, "OPENID20", &client);
108   if (res != GSASL_OK)
109     {
110       fail ("gsasl_client_start (%d):\n%s\n", res, gsasl_strerror (res));
111       return;
112     }
113 
114   res = gsasl_server_start (s, "OPENID20", &server);
115   if (res != GSASL_OK)
116     {
117       fail ("gsasl_server_start (%d):\n%s\n", res, gsasl_strerror (res));
118       return;
119     }
120 
121   /* OPENID20 is client-first.  Check that server just waits. */
122 
123   res = gsasl_step64 (server, NULL, &s2);
124   if (res != GSASL_NEEDS_MORE)
125     {
126       fail ("gsasl_step server0 (%d):\n%s\n", res, gsasl_strerror (res));
127       return;
128     }
129 
130   if (debug)
131     printf ("S: `%s' (%d) %s\n", s2 ? s2 : "", (int) strlen (s2),
132 	    gsasl_strerror_name (res));
133 
134   /* The client should send the OpenID URL. */
135 
136   res = gsasl_step64 (client, s2, &s1);
137   gsasl_free (s2);
138   if (res != GSASL_NEEDS_MORE)
139     {
140       fail ("gsasl_step client1 (%d):\n%s\n", res, gsasl_strerror (res));
141       return;
142     }
143 
144   if (debug)
145     printf ("C: `%s' (%d) %s\n", s1 ? s1 : "", (int) strlen (s1),
146 	    gsasl_strerror_name (res));
147 
148   /* The server should send the redirect URL. */
149 
150   res = gsasl_step64 (server, s1, &s2);
151   gsasl_free (s1);
152   if (res != GSASL_NEEDS_MORE)
153     {
154       fail ("gsasl_step server1 (%d):\n%s\n", res, gsasl_strerror (res));
155       return;
156     }
157 
158   if (debug)
159     printf ("S: `%s' (%d) %s\n", s2 ? s2 : "", (int) strlen (s2),
160 	    gsasl_strerror_name (res));
161 
162   /* The client sends '='. */
163 
164   res = gsasl_step64 (client, s2, &s1);
165   gsasl_free (s2);
166   if (res != GSASL_OK)
167     {
168       fail ("gsasl_step client2 (%d):\n%s\n", res, gsasl_strerror (res));
169       return;
170     }
171 
172   if (debug)
173     printf ("C: `%s' (%d) %s\n", s1 ? s1 : "", (int) strlen (s1),
174 	    gsasl_strerror_name (res));
175 
176   /* Now the server sends the outcome_data */
177 
178   res = gsasl_step64 (server, s1, &s2);
179   gsasl_free (s1);
180   if (res != expect_server_res)
181     {
182       fail ("gsasl_step server2 (%d):\n%s\n", res, gsasl_strerror (res));
183       return;
184     }
185 
186   if (res == GSASL_OK || res == GSASL_NEEDS_MORE)
187     {
188       if (debug)
189 	printf ("S: `%s' (%d) %s\n", s2 ? s2 : "", (int) strlen (s2),
190 		gsasl_strerror_name (res));
191     }
192 
193   /* The client receives the outcome data and sends a empty packet. */
194 
195   res = gsasl_step64 (client, s2, &s1);
196   gsasl_free (s2);
197   if (res != expect_client_res)
198     {
199       fail ("gsasl_step client3 (%d):\n%s\n", res, gsasl_strerror (res));
200       return;
201     }
202 
203   if (res == GSASL_OK || res == GSASL_NEEDS_MORE)
204     {
205       if (debug)
206 	printf ("C: `%s' (%d) %s\n", s1 ? s1 : "", (int) strlen (s1),
207 		gsasl_strerror_name (res));
208     }
209   else if (debug)
210     {
211       printf ("C: %s\n", gsasl_strerror_name (res));
212       s1 = NULL;
213     }
214 
215   /* The server should reject authentication at this point */
216 
217   res = gsasl_step64 (server, s1, &s2);
218   gsasl_free (s1);
219   if (res != expect_server2_res)
220     {
221       fail ("gsasl_step server3 (%d):\n%s\n", res, gsasl_strerror (res));
222       return;
223     }
224 
225   if (res == GSASL_OK || res == GSASL_NEEDS_MORE)
226     {
227       if (debug)
228 	printf ("S: `%s' (%d) %s\n", s2 ? s2 : "", (int) strlen (s2),
229 		gsasl_strerror_name (res));
230     }
231   else if (debug)
232     {
233       printf ("S: %s\n", gsasl_strerror_name (res));
234       s2 = NULL;
235     }
236 
237   /* The client should be called too many times now */
238 
239   res = gsasl_step64 (client, s2, &s1);
240   gsasl_free (s2);
241   if (res != GSASL_MECHANISM_CALLED_TOO_MANY_TIMES)
242     {
243       fail ("gsasl_step client4 (%d):\n%s\n", res, gsasl_strerror (res));
244       return;
245     }
246 
247   if (debug)
248     printf ("C: %s\n", gsasl_strerror_name (res));
249 
250   if (authzid == NULL && gsasl_property_fast (server, GSASL_AUTHZID) == NULL)
251     success ("expected and got no authzid\n");
252   else if (!authzid && gsasl_property_fast (server, GSASL_AUTHZID))
253     fail ("got unexpected authzid? %s\n",
254 	  gsasl_property_fast (server, GSASL_AUTHZID));
255   else if (authzid && !gsasl_property_fast (server, GSASL_AUTHZID))
256     fail ("did not get authzid? %s\n", authzid);
257   else if (strcmp (authzid, gsasl_property_fast (server, GSASL_AUTHZID)) != 0)
258     fail ("authzid comparison failed: got %s expected %s\n",
259 	  gsasl_property_fast (server, GSASL_AUTHZID), authzid);
260 
261   gsasl_finish (client);
262   gsasl_finish (server);
263 }
264 
265 void
doit(void)266 doit (void)
267 {
268   Gsasl *c = NULL, *s = NULL;
269   int res;
270 
271   res = gsasl_init (&c);
272   if (res != GSASL_OK)
273     {
274       fail ("gsasl_init() failed (%d):\n%s\n", res, gsasl_strerror (res));
275       return;
276     }
277 
278   res = gsasl_init (&s);
279   if (res != GSASL_OK)
280     {
281       fail ("gsasl_init() failed (%d):\n%s\n", res, gsasl_strerror (res));
282       return;
283     }
284 
285   if (!gsasl_client_support_p (c, "OPENID20"))
286     {
287       gsasl_done (c);
288       fail ("No support for OPENID20 clients.\n");
289       exit (77);
290     }
291 
292   if (!gsasl_server_support_p (s, "OPENID20"))
293     {
294       gsasl_done (s);
295       fail ("No support for OPENID20 servers.\n");
296       exit (77);
297     }
298 
299   gsasl_callback_set (c, client_callback);
300   gsasl_callback_set (s, server_callback);
301 
302   printf ("Running successful authentication without SREG.\n");
303   openid20 (c, s);
304 
305   printf ("Running successful authentication with SREG.\n");
306   sreg = "nickname=jas";
307   openid20 (c, s);
308 
309   authzid = "user";
310   printf ("Running successful authentication without SREG with authzid.\n");
311   openid20 (c, s);
312 
313   printf ("Running successful authentication with SREG with authzid.\n");
314   sreg = "nickname=jas";
315   openid20 (c, s);
316 
317   printf ("Running failed authentication.\n");
318   validation_res = GSASL_AUTHENTICATION_ERROR;
319   expect_server_res = GSASL_NEEDS_MORE;
320   expect_client_res = GSASL_NEEDS_MORE;
321   expect_server2_res = GSASL_AUTHENTICATION_ERROR;
322   openid20 (c, s);
323 
324   gsasl_done (c);
325   gsasl_done (s);
326 }
327