adam@1
|
1 #include <string.h>
|
adam@1
|
2
|
adam@6
|
3 #include <openssl/bio.h>
|
adam@6
|
4 #include <openssl/evp.h>
|
adam@6
|
5 #include <openssl/buffer.h>
|
adam@0
|
6 #include <openssl/sha.h>
|
adam@0
|
7 #include <curl/curl.h>
|
adam@1
|
8 #include <expat.h>
|
adam@0
|
9
|
adam@1
|
10 #include <openid.h>
|
adam@0
|
11
|
adam@3
|
12 #define BUF_MAX 10240
|
adam@3
|
13 #define BUF_INIT 1024
|
adam@3
|
14
|
adam@2
|
15 struct uw_OpenidFfi_discovery {
|
adam@2
|
16 uw_Basis_string endpoint, localId;
|
adam@2
|
17 };
|
adam@2
|
18
|
adam@2
|
19 uw_Basis_string uw_OpenidFfi_endpoint(uw_context ctx, uw_OpenidFfi_discovery d) {
|
adam@2
|
20 return d->endpoint;
|
adam@2
|
21 }
|
adam@2
|
22
|
adam@2
|
23 uw_Basis_string uw_OpenidFfi_localId(uw_context ctx, uw_OpenidFfi_discovery d) {
|
adam@2
|
24 return d->localId;
|
adam@2
|
25 }
|
adam@2
|
26
|
adam@0
|
27 uw_unit uw_OpenidFfi_init(uw_context ctx) {
|
adam@4
|
28
|
adam@4
|
29
|
adam@0
|
30 curl_global_init(CURL_GLOBAL_ALL);
|
adam@0
|
31
|
adam@0
|
32 return uw_unit_v;
|
adam@0
|
33 }
|
adam@1
|
34
|
adam@1
|
35 static CURL *curl(uw_context ctx) {
|
adam@1
|
36 CURL *r;
|
adam@1
|
37
|
adam@1
|
38 if (!(r = uw_get_global(ctx, "curl"))) {
|
adam@1
|
39 r = curl_easy_init();
|
adam@1
|
40 if (r)
|
adam@1
|
41 uw_set_global(ctx, "curl", r, curl_easy_cleanup);
|
adam@1
|
42 }
|
adam@1
|
43
|
adam@1
|
44 return r;
|
adam@1
|
45 }
|
adam@1
|
46
|
adam@1
|
47 typedef struct {
|
adam@1
|
48 uw_context ctx;
|
adam@2
|
49 uw_OpenidFfi_discovery d;
|
adam@1
|
50 } endpoint;
|
adam@1
|
51
|
adam@1
|
52 static void XMLCALL startElement(void *userData, const XML_Char *name, const XML_Char **atts) {
|
adam@1
|
53 endpoint *ep = userData;
|
adam@1
|
54
|
adam@1
|
55 if (!strcmp(name, "link")) {
|
adam@1
|
56 const XML_Char **attp;
|
adam@1
|
57 int found = 0;
|
adam@1
|
58
|
adam@1
|
59 for (attp = atts; *attp; attp += 2) {
|
adam@1
|
60 if (!strcmp(attp[0], "rel") && !strcmp(attp[1], "openid2.provider")) {
|
adam@1
|
61 found = 1;
|
adam@1
|
62 break;
|
adam@1
|
63 }
|
adam@1
|
64 }
|
adam@1
|
65
|
adam@1
|
66 if (found) {
|
adam@1
|
67 for (attp = atts; *attp; attp += 2) {
|
adam@1
|
68 if (!strcmp(attp[0], "href")) {
|
adam@2
|
69 ep->d->endpoint = uw_strdup(ep->ctx, attp[1]);
|
adam@1
|
70 return;
|
adam@1
|
71 }
|
adam@1
|
72 }
|
adam@1
|
73 }
|
adam@1
|
74 }
|
adam@1
|
75 }
|
adam@1
|
76
|
adam@1
|
77 static void XMLCALL endElement(void *userData, const XML_Char *name) {
|
adam@1
|
78 }
|
adam@1
|
79
|
adam@1
|
80 typedef struct {
|
adam@1
|
81 XML_Parser parser;
|
adam@1
|
82 int any_errors;
|
adam@3
|
83 } curl_discovery_data;
|
adam@1
|
84
|
adam@3
|
85 static size_t write_discovery_data(void *buffer, size_t size, size_t nmemb, void *userp) {
|
adam@3
|
86 curl_discovery_data *d = userp;
|
adam@1
|
87
|
adam@1
|
88 if (!XML_Parse(d->parser, buffer, size * nmemb, 0))
|
adam@1
|
89 d->any_errors = 1;
|
adam@1
|
90
|
adam@1
|
91 return size * nmemb;
|
adam@1
|
92 }
|
adam@1
|
93
|
adam@2
|
94 uw_OpenidFfi_discovery *uw_OpenidFfi_discover(uw_context ctx, uw_Basis_string id) {
|
adam@1
|
95 char *s;
|
adam@1
|
96 CURL *c = curl(ctx);
|
adam@3
|
97 curl_discovery_data cd = {};
|
adam@2
|
98 uw_OpenidFfi_discovery dy = uw_malloc(ctx, sizeof(struct uw_OpenidFfi_discovery));
|
adam@2
|
99 endpoint ep = {ctx, dy};
|
adam@1
|
100 CURLcode code;
|
adam@1
|
101
|
adam@2
|
102 dy->endpoint = dy->localId = NULL;
|
adam@2
|
103
|
adam@1
|
104 if (!strchr(id, ':')) {
|
adam@1
|
105 id = uw_Basis_strcat(ctx, "http://", id);
|
adam@1
|
106 if ((s = strchr(id, '#')) != NULL)
|
adam@1
|
107 *s = 0;
|
adam@1
|
108 } else if ((s = strchr(id, '#')) != NULL) {
|
adam@1
|
109 char *id2 = uw_malloc(ctx, s - id + 1);
|
adam@1
|
110 memcpy(id2, s, s - id);
|
adam@1
|
111 id2[s - id] = 0;
|
adam@1
|
112 id = id2;
|
adam@1
|
113 }
|
adam@1
|
114
|
adam@1
|
115 cd.parser = XML_ParserCreate(NULL);
|
adam@1
|
116 XML_SetUserData(cd.parser, &ep);
|
adam@1
|
117 uw_push_cleanup(ctx, (void (*)(void *))XML_ParserFree, cd.parser);
|
adam@1
|
118 XML_SetElementHandler(cd.parser, startElement, endElement);
|
adam@1
|
119
|
adam@1
|
120 curl_easy_setopt(c, CURLOPT_URL, id);
|
adam@3
|
121 curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_discovery_data);
|
adam@1
|
122 curl_easy_setopt(c, CURLOPT_WRITEDATA, &cd);
|
adam@1
|
123
|
adam@1
|
124 code = curl_easy_perform(c);
|
adam@1
|
125 uw_pop_cleanup(ctx);
|
adam@1
|
126
|
adam@2
|
127 if (code || !ep.d->endpoint)
|
adam@1
|
128 return NULL;
|
adam@2
|
129 else {
|
adam@2
|
130 uw_OpenidFfi_discovery *dyp = malloc(sizeof(uw_OpenidFfi_discovery));
|
adam@2
|
131 *dyp = ep.d;
|
adam@2
|
132 return dyp;
|
adam@2
|
133 }
|
adam@1
|
134 }
|
adam@3
|
135
|
adam@3
|
136 uw_OpenidFfi_inputs uw_OpenidFfi_createInputs(uw_context ctx) {
|
adam@3
|
137 uw_buffer *r = uw_malloc(ctx, sizeof(uw_buffer));
|
adam@3
|
138 uw_buffer_init(BUF_MAX, r, BUF_INIT);
|
adam@3
|
139 return r;
|
adam@3
|
140 }
|
adam@3
|
141
|
adam@3
|
142 static int okForPost(const char *s) {
|
adam@3
|
143 for (; *s; ++s)
|
adam@3
|
144 if (*s == '=' || *s == '&')
|
adam@3
|
145 return 0;
|
adam@3
|
146 return 1;
|
adam@3
|
147 }
|
adam@3
|
148
|
adam@3
|
149 uw_unit uw_OpenidFfi_addInput(uw_context ctx, uw_OpenidFfi_inputs buf, uw_Basis_string key, uw_Basis_string value) {
|
adam@3
|
150 if (!okForPost(key))
|
adam@3
|
151 uw_error(ctx, FATAL, "Invalid key for OpenID inputs");
|
adam@3
|
152 if (!okForPost(value))
|
adam@3
|
153 uw_error(ctx, FATAL, "Invalid value for OpenID inputs");
|
adam@3
|
154
|
adam@3
|
155 if (uw_buffer_used(buf) > 0)
|
adam@3
|
156 uw_buffer_append(buf, "&", 1);
|
adam@3
|
157
|
adam@3
|
158 uw_buffer_append(buf, key, strlen(key));
|
adam@3
|
159 uw_buffer_append(buf, "=", 1);
|
adam@3
|
160 uw_buffer_append(buf, value, strlen(value));
|
adam@3
|
161
|
adam@3
|
162 return uw_unit_v;
|
adam@3
|
163 }
|
adam@3
|
164
|
adam@3
|
165 uw_Basis_string uw_OpenidFfi_getOutput(uw_context ctx, uw_OpenidFfi_outputs buf, uw_Basis_string key) {
|
adam@3
|
166 char *s = buf->start;
|
adam@3
|
167
|
adam@3
|
168 for (; *s; s = strchr(strchr(s, 0)+1, 0)+1)
|
adam@3
|
169 if (!strcmp(key, s))
|
adam@3
|
170 return strchr(s, 0)+1;
|
adam@3
|
171
|
adam@3
|
172 return NULL;
|
adam@3
|
173 }
|
adam@3
|
174
|
adam@3
|
175 static size_t write_buffer_data(void *buffer, size_t size, size_t nmemb, void *userp) {
|
adam@3
|
176 uw_buffer *buf = userp;
|
adam@3
|
177
|
adam@3
|
178 uw_buffer_append(buf, buffer, size * nmemb);
|
adam@3
|
179
|
adam@3
|
180 return size * nmemb;
|
adam@3
|
181 }
|
adam@3
|
182
|
adam@3
|
183 const char curl_failure[] = "error\0Error fetching URL";
|
adam@3
|
184
|
adam@4
|
185 uw_OpenidFfi_outputs uw_OpenidFfi_direct(uw_context ctx, uw_Basis_string url, uw_OpenidFfi_inputs inps) {
|
adam@3
|
186 uw_buffer *buf = uw_malloc(ctx, sizeof(uw_buffer));
|
adam@3
|
187 CURL *c = curl(ctx);
|
adam@3
|
188 CURLcode code;
|
adam@3
|
189
|
adam@3
|
190 uw_buffer_init(BUF_MAX, buf, BUF_INIT);
|
adam@3
|
191
|
adam@3
|
192 uw_buffer_append(inps, "", 1);
|
adam@3
|
193
|
adam@3
|
194 curl_easy_setopt(c, CURLOPT_URL, url);
|
adam@3
|
195 curl_easy_setopt(c, CURLOPT_POSTFIELDS, inps->start);
|
adam@3
|
196 curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_buffer_data);
|
adam@3
|
197 curl_easy_setopt(c, CURLOPT_WRITEDATA, buf);
|
adam@3
|
198
|
adam@3
|
199 code = curl_easy_perform(c);
|
adam@3
|
200
|
adam@3
|
201 uw_buffer_append(buf, "", 1);
|
adam@3
|
202
|
adam@3
|
203 if (code) {
|
adam@3
|
204 uw_buffer_reset(buf);
|
adam@3
|
205 uw_buffer_append(buf, curl_failure, sizeof curl_failure);
|
adam@3
|
206 } else {
|
adam@3
|
207 char *s;
|
adam@3
|
208
|
adam@3
|
209 s = buf->start;
|
adam@3
|
210 while (*s) {
|
adam@3
|
211 char *colon = strchr(s, ':'), *newline;
|
adam@3
|
212
|
adam@3
|
213 if (!colon) {
|
adam@3
|
214 *s = 0;
|
adam@3
|
215 break;
|
adam@3
|
216 }
|
adam@3
|
217
|
adam@3
|
218 newline = strchr(colon+1, '\n');
|
adam@3
|
219
|
adam@3
|
220 if (!newline) {
|
adam@3
|
221 *s = 0;
|
adam@3
|
222 break;
|
adam@3
|
223 }
|
adam@3
|
224
|
adam@3
|
225 *colon = 0;
|
adam@3
|
226 *newline = 0;
|
adam@3
|
227 s = newline+1;
|
adam@3
|
228 }
|
adam@3
|
229 }
|
adam@3
|
230
|
adam@3
|
231 return buf;
|
adam@3
|
232 }
|
adam@4
|
233
|
adam@4
|
234 static uw_Basis_string deurl(uw_context ctx, uw_Basis_string s) {
|
adam@4
|
235 uw_Basis_string r = uw_malloc(ctx, strlen(s)), s2 = r;
|
adam@4
|
236
|
adam@4
|
237 for (; *s; ++s) {
|
adam@4
|
238 if (s[0] == '%' && s[1] && s[2]) {
|
adam@4
|
239 unsigned u;
|
adam@4
|
240
|
adam@4
|
241 sscanf(s+1, "%02x", &u);
|
adam@4
|
242 *s2++ = u;
|
adam@4
|
243 s += 2;
|
adam@4
|
244 } else
|
adam@4
|
245 *s2++ = *s;
|
adam@4
|
246 }
|
adam@4
|
247
|
adam@4
|
248 *s2 = 0;
|
adam@4
|
249 return r;
|
adam@4
|
250 }
|
adam@4
|
251
|
adam@4
|
252 uw_OpenidFfi_outputs uw_OpenidFfi_indirect(uw_context ctx, uw_Basis_string fields) {
|
adam@4
|
253 uw_OpenidFfi_outputs b = malloc(sizeof(uw_buffer));
|
adam@4
|
254
|
adam@4
|
255 uw_buffer_init(BUF_MAX, b, BUF_INIT);
|
adam@4
|
256
|
adam@6
|
257 fields = uw_strdup(ctx, fields);
|
adam@6
|
258
|
adam@4
|
259 while (*fields) {
|
adam@4
|
260 char *equal = strchr(fields, '='), *and, *s;
|
adam@4
|
261
|
adam@4
|
262 if (!equal)
|
adam@4
|
263 break;
|
adam@4
|
264
|
adam@4
|
265 *equal = 0;
|
adam@4
|
266 s = deurl(ctx, fields);
|
adam@4
|
267 uw_buffer_append(b, s, strlen(s));
|
adam@4
|
268 uw_buffer_append(b, "", 1);
|
adam@4
|
269
|
adam@4
|
270 and = strchr(equal+1, '&');
|
adam@4
|
271 if (and) {
|
adam@4
|
272 *and = 0;
|
adam@4
|
273 fields = and+1;
|
adam@4
|
274 } else
|
adam@4
|
275 fields = and = strchr(equal+1, 0);
|
adam@4
|
276 s = deurl(ctx, equal+1);
|
adam@4
|
277 uw_buffer_append(b, s, strlen(s));
|
adam@4
|
278 uw_buffer_append(b, "", 1);
|
adam@4
|
279 }
|
adam@4
|
280
|
adam@4
|
281 uw_buffer_append(b, "", 1);
|
adam@4
|
282 return b;
|
adam@4
|
283 }
|
adam@6
|
284
|
adam@6
|
285 static uw_Basis_string base64(uw_context ctx, unsigned char *input, int length) {
|
adam@6
|
286 BIO *bmem, *b64;
|
adam@6
|
287 BUF_MEM *bptr;
|
adam@6
|
288
|
adam@6
|
289 b64 = BIO_new(BIO_f_base64());
|
adam@6
|
290 bmem = BIO_new(BIO_s_mem());
|
adam@6
|
291 b64 = BIO_push(b64, bmem);
|
adam@6
|
292 BIO_write(b64, input, length);
|
adam@6
|
293 (void)BIO_flush(b64);
|
adam@6
|
294 BIO_get_mem_ptr(b64, &bptr);
|
adam@6
|
295
|
adam@6
|
296 char *buff = uw_malloc(ctx, bptr->length);
|
adam@6
|
297 memcpy(buff, bptr->data, bptr->length-1);
|
adam@6
|
298 buff[bptr->length-1] = 0;
|
adam@6
|
299
|
adam@6
|
300 BIO_free_all(b64);
|
adam@6
|
301
|
adam@6
|
302 return buff;
|
adam@6
|
303 }
|
adam@6
|
304
|
adam@6
|
305 uw_Basis_string uw_OpenidFfi_sha256(uw_context ctx, uw_Basis_string s) {
|
adam@6
|
306 unsigned char out[SHA256_DIGEST_LENGTH];
|
adam@6
|
307
|
adam@6
|
308 SHA256((unsigned char *)s, strlen(s), out);
|
adam@6
|
309
|
adam@6
|
310 return base64(ctx, out, sizeof out);
|
adam@6
|
311 }
|