1/* server.c --- OPENID20 mechanism, server side.
2 * Copyright (C) 2011-2012 Simon Josefsson
3 *
4 * This file is part of GNU SASL Library.
5 *
6 * GNU SASL Library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
10 *
11 * GNU SASL Library 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 GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with GNU SASL Library; if not, write to the Free
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27/* Get specification. */
28#include "openid20.h"
29
30/* Get strdup, strlen. */
31#include <string.h>
32
33/* Get calloc, free. */
34#include <stdlib.h>
35
36/* Get _gsasl_parse_gs2_header. */
37#include "mechtools.h"
38
39struct openid20_server_state
40{
41 int step;
42 int allow_error_step;
43};
44
45int
46_gsasl_openid20_server_start (Gsasl_session * sctx, void **mech_data)
47{
48 struct openid20_server_state *state;
49
50 state = (struct openid20_server_state *) calloc (sizeof (*state), 1);
51 if (state == NULL)
52 return GSASL_MALLOC_ERROR;
53
54 *mech_data = state;
55
56 return GSASL_OK;
57}
58
59int
60_gsasl_openid20_server_step (Gsasl_session * sctx,
61 void *mech_data,
62 const char *input, size_t input_len,
63 char **output, size_t * output_len)
64{
65 struct openid20_server_state *state = mech_data;
66 int res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
67
68 *output_len = 0;
69 *output = NULL;
70
71 switch (state->step)
72 {
73 case 0:
74 {
75 const char *p;
76 char *authzid;
77 size_t headerlen;
78
79 if (input_len == 0)
80 return GSASL_NEEDS_MORE;
81
82 res = _gsasl_parse_gs2_header (input, input_len,
83 &authzid, &headerlen);
84 if (res != GSASL_OK)
85 return res;
86
87 if (authzid)
88 {
89 gsasl_property_set (sctx, GSASL_AUTHZID, authzid);
90 free (authzid);
91 }
92
93 input += headerlen;
94 input_len -= headerlen;
95
96 gsasl_property_set_raw (sctx, GSASL_AUTHID, input, input_len);
97
98 p = gsasl_property_get (sctx, GSASL_OPENID20_REDIRECT_URL);
99 if (!p || !*p)
100 return GSASL_NO_OPENID20_REDIRECT_URL;
101
102 *output_len = strlen (p);
103 *output = malloc (*output_len);
104 if (!*output)
105 return GSASL_MALLOC_ERROR;
106
107 memcpy (*output, p, *output_len);
108
109 res = GSASL_NEEDS_MORE;
110 state->step++;
111 break;
112 }
113
114 case 1:
115 {
116 const char *outcome_data;
117
118 if (!(input_len == 1 && *input == '='))
119 return GSASL_MECHANISM_PARSE_ERROR;
120
121 res = gsasl_callback (NULL, sctx, GSASL_VALIDATE_OPENID20);
122 if (res != GSASL_OK)
123 {
124 *output = strdup ("openid.error=fail");
125 if (!*output)
126 return GSASL_MALLOC_ERROR;
127 *output_len = strlen (*output);
128
129 /* [RFC4422] Section 3.6 explicitly prohibits additional
130 information in an unsuccessful authentication outcome.
131 Therefore, the openid.error and openid.error_code are
132 to be sent as an additional challenge in the event of
133 an unsuccessful outcome. In this case, as the protocol
134 is lock step, the client will follow with an additional
135 exchange containing "=", after which the server will
136 respond with an application-level outcome. */
137
138 state->allow_error_step = 1;
139 state->step++;
140 return GSASL_NEEDS_MORE;
141 }
142
143 outcome_data = gsasl_property_get (sctx, GSASL_OPENID20_OUTCOME_DATA);
144 if (outcome_data)
145 {
146 *output = strdup (outcome_data);
147 if (!*output)
148 return GSASL_MALLOC_ERROR;
149 *output_len = strlen (*output);
150 }
151 else
152 {
153 *output = NULL;
154 *output_len = 0;
155 }
156
157 res = GSASL_OK;
158 state->step++;
159 }
160 break;
161
162 case 2:
163 {
164 /* We only get here when the previous step signalled an error
165 to the client. */
166
167 if (!state->allow_error_step)
168 return GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
169
170 if (!(input_len == 1 && *input == '='))
171 return GSASL_MECHANISM_PARSE_ERROR;
172
173 res = GSASL_AUTHENTICATION_ERROR;
174 state->step++;
175 }
176 break;
177
178 default:
179 break;
180 }
181
182 return res;
183}
184
185void
186_gsasl_openid20_server_finish (Gsasl_session * sctx, void *mech_data)
187{
188 struct openid20_server_state *state = mech_data;
189
190 if (!state)
191 return;
192
193 free (state);
194}
195