annotate src/c/openid.c @ 6:99496175078b

Added preliminary versions of all the authentication verification steps
author Adam Chlipala <adam@chlipala.net>
date Mon, 27 Dec 2010 13:18:02 -0500
parents 2d409aff8800
children 976121190b2d
rev   line source
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 }