annotate src/c/openid.c @ 10:194577b60771

Call user-specified function after authentication
author Adam Chlipala <adam@chlipala.net>
date Wed, 29 Dec 2010 14:38:56 -0500
parents 870d99055dd1
children e637249abfd2
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@7 7 #include <openssl/hmac.h>
adam@8 8 #include <openssl/dh.h>
adam@0 9 #include <curl/curl.h>
adam@1 10 #include <expat.h>
adam@0 11
adam@1 12 #include <openid.h>
adam@0 13
adam@3 14 #define BUF_MAX 10240
adam@3 15 #define BUF_INIT 1024
adam@3 16
adam@8 17 #define PRIME_LEN 64
adam@8 18 #define GENERATOR DH_GENERATOR_5
adam@2 19
adam@2 20 uw_Basis_string uw_OpenidFfi_endpoint(uw_context ctx, uw_OpenidFfi_discovery d) {
adam@8 21 return d.endpoint;
adam@2 22 }
adam@2 23
adam@2 24 uw_Basis_string uw_OpenidFfi_localId(uw_context ctx, uw_OpenidFfi_discovery d) {
adam@8 25 return d.localId;
adam@2 26 }
adam@2 27
adam@0 28 uw_unit uw_OpenidFfi_init(uw_context ctx) {
adam@0 29 curl_global_init(CURL_GLOBAL_ALL);
adam@0 30
adam@0 31 return uw_unit_v;
adam@0 32 }
adam@1 33
adam@1 34 static CURL *curl(uw_context ctx) {
adam@1 35 CURL *r;
adam@1 36
adam@1 37 if (!(r = uw_get_global(ctx, "curl"))) {
adam@1 38 r = curl_easy_init();
adam@1 39 if (r)
adam@1 40 uw_set_global(ctx, "curl", r, curl_easy_cleanup);
adam@1 41 }
adam@1 42
adam@1 43 return r;
adam@1 44 }
adam@1 45
adam@1 46 typedef struct {
adam@1 47 uw_context ctx;
adam@8 48 uw_OpenidFfi_discovery *d;
adam@1 49 } endpoint;
adam@1 50
adam@1 51 static void XMLCALL startElement(void *userData, const XML_Char *name, const XML_Char **atts) {
adam@1 52 endpoint *ep = userData;
adam@1 53
adam@1 54 if (!strcmp(name, "link")) {
adam@1 55 const XML_Char **attp;
adam@1 56 int found = 0;
adam@1 57
adam@1 58 for (attp = atts; *attp; attp += 2) {
adam@1 59 if (!strcmp(attp[0], "rel") && !strcmp(attp[1], "openid2.provider")) {
adam@1 60 found = 1;
adam@1 61 break;
adam@1 62 }
adam@1 63 }
adam@1 64
adam@1 65 if (found) {
adam@1 66 for (attp = atts; *attp; attp += 2) {
adam@1 67 if (!strcmp(attp[0], "href")) {
adam@2 68 ep->d->endpoint = uw_strdup(ep->ctx, attp[1]);
adam@1 69 return;
adam@1 70 }
adam@1 71 }
adam@1 72 }
adam@1 73 }
adam@1 74 }
adam@1 75
adam@1 76 typedef struct {
adam@1 77 XML_Parser parser;
adam@1 78 int any_errors;
adam@3 79 } curl_discovery_data;
adam@1 80
adam@3 81 static size_t write_discovery_data(void *buffer, size_t size, size_t nmemb, void *userp) {
adam@3 82 curl_discovery_data *d = userp;
adam@1 83
adam@1 84 if (!XML_Parse(d->parser, buffer, size * nmemb, 0))
adam@1 85 d->any_errors = 1;
adam@1 86
adam@1 87 return size * nmemb;
adam@1 88 }
adam@1 89
adam@2 90 uw_OpenidFfi_discovery *uw_OpenidFfi_discover(uw_context ctx, uw_Basis_string id) {
adam@1 91 char *s;
adam@1 92 CURL *c = curl(ctx);
adam@3 93 curl_discovery_data cd = {};
adam@8 94 uw_OpenidFfi_discovery *dy = uw_malloc(ctx, sizeof(uw_OpenidFfi_discovery));
adam@2 95 endpoint ep = {ctx, dy};
adam@1 96 CURLcode code;
adam@1 97
adam@2 98 dy->endpoint = dy->localId = NULL;
adam@2 99
adam@1 100 if (!strchr(id, ':')) {
adam@1 101 id = uw_Basis_strcat(ctx, "http://", id);
adam@1 102 if ((s = strchr(id, '#')) != NULL)
adam@1 103 *s = 0;
adam@1 104 } else if ((s = strchr(id, '#')) != NULL) {
adam@1 105 char *id2 = uw_malloc(ctx, s - id + 1);
adam@1 106 memcpy(id2, s, s - id);
adam@1 107 id2[s - id] = 0;
adam@1 108 id = id2;
adam@1 109 }
adam@1 110
adam@1 111 cd.parser = XML_ParserCreate(NULL);
adam@1 112 XML_SetUserData(cd.parser, &ep);
adam@1 113 uw_push_cleanup(ctx, (void (*)(void *))XML_ParserFree, cd.parser);
adam@7 114 XML_SetStartElementHandler(cd.parser, startElement);
adam@1 115
adam@1 116 curl_easy_setopt(c, CURLOPT_URL, id);
adam@3 117 curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_discovery_data);
adam@1 118 curl_easy_setopt(c, CURLOPT_WRITEDATA, &cd);
adam@1 119
adam@1 120 code = curl_easy_perform(c);
adam@1 121 uw_pop_cleanup(ctx);
adam@1 122
adam@8 123 if (code || !dy->endpoint)
adam@1 124 return NULL;
adam@8 125 else
adam@8 126 return dy;
adam@1 127 }
adam@3 128
adam@3 129 uw_OpenidFfi_inputs uw_OpenidFfi_createInputs(uw_context ctx) {
adam@3 130 uw_buffer *r = uw_malloc(ctx, sizeof(uw_buffer));
adam@3 131 uw_buffer_init(BUF_MAX, r, BUF_INIT);
adam@3 132 return r;
adam@3 133 }
adam@3 134
adam@8 135 static void postify(uw_OpenidFfi_inputs buf, uw_Basis_string s) {
adam@8 136 for (; *s; ++s) {
adam@8 137 switch (*s) {
adam@8 138 case '=':
adam@8 139 uw_buffer_append(buf, "%3D", 3);
adam@8 140 break;
adam@8 141 case '&':
adam@8 142 uw_buffer_append(buf, "%26", 3);
adam@8 143 break;
adam@8 144 default:
adam@8 145 uw_buffer_append(buf, s, 1);
adam@8 146 }
adam@8 147 }
adam@3 148 }
adam@3 149
adam@3 150 uw_unit uw_OpenidFfi_addInput(uw_context ctx, uw_OpenidFfi_inputs buf, uw_Basis_string key, uw_Basis_string value) {
adam@3 151 if (uw_buffer_used(buf) > 0)
adam@3 152 uw_buffer_append(buf, "&", 1);
adam@3 153
adam@8 154 postify(buf, key);
adam@3 155 uw_buffer_append(buf, "=", 1);
adam@8 156 postify(buf, value);
adam@3 157
adam@3 158 return uw_unit_v;
adam@3 159 }
adam@3 160
adam@3 161 uw_Basis_string uw_OpenidFfi_getOutput(uw_context ctx, uw_OpenidFfi_outputs buf, uw_Basis_string key) {
adam@3 162 char *s = buf->start;
adam@3 163
adam@3 164 for (; *s; s = strchr(strchr(s, 0)+1, 0)+1)
adam@3 165 if (!strcmp(key, s))
adam@3 166 return strchr(s, 0)+1;
adam@3 167
adam@3 168 return NULL;
adam@3 169 }
adam@3 170
adam@3 171 static size_t write_buffer_data(void *buffer, size_t size, size_t nmemb, void *userp) {
adam@3 172 uw_buffer *buf = userp;
adam@3 173
adam@3 174 uw_buffer_append(buf, buffer, size * nmemb);
adam@3 175
adam@3 176 return size * nmemb;
adam@3 177 }
adam@3 178
adam@3 179 const char curl_failure[] = "error\0Error fetching URL";
adam@3 180
adam@4 181 uw_OpenidFfi_outputs uw_OpenidFfi_direct(uw_context ctx, uw_Basis_string url, uw_OpenidFfi_inputs inps) {
adam@3 182 uw_buffer *buf = uw_malloc(ctx, sizeof(uw_buffer));
adam@3 183 CURL *c = curl(ctx);
adam@3 184 CURLcode code;
adam@3 185
adam@3 186 uw_buffer_init(BUF_MAX, buf, BUF_INIT);
adam@3 187
adam@3 188 uw_buffer_append(inps, "", 1);
adam@3 189
adam@3 190 curl_easy_setopt(c, CURLOPT_URL, url);
adam@3 191 curl_easy_setopt(c, CURLOPT_POSTFIELDS, inps->start);
adam@3 192 curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_buffer_data);
adam@3 193 curl_easy_setopt(c, CURLOPT_WRITEDATA, buf);
adam@3 194
adam@3 195 code = curl_easy_perform(c);
adam@3 196
adam@3 197 uw_buffer_append(buf, "", 1);
adam@3 198
adam@3 199 if (code) {
adam@3 200 uw_buffer_reset(buf);
adam@3 201 uw_buffer_append(buf, curl_failure, sizeof curl_failure);
adam@3 202 } else {
adam@3 203 char *s;
adam@3 204
adam@8 205 printf("Result: %s\n", buf->start);
adam@8 206
adam@3 207 s = buf->start;
adam@3 208 while (*s) {
adam@3 209 char *colon = strchr(s, ':'), *newline;
adam@3 210
adam@3 211 if (!colon) {
adam@3 212 *s = 0;
adam@3 213 break;
adam@3 214 }
adam@3 215
adam@7 216 *colon = 0;
adam@7 217
adam@3 218 newline = strchr(colon+1, '\n');
adam@3 219
adam@7 220 if (!newline)
adam@3 221 break;
adam@3 222
adam@3 223 *newline = 0;
adam@3 224 s = newline+1;
adam@3 225 }
adam@3 226 }
adam@3 227
adam@3 228 return buf;
adam@3 229 }
adam@4 230
adam@4 231 static uw_Basis_string deurl(uw_context ctx, uw_Basis_string s) {
adam@4 232 uw_Basis_string r = uw_malloc(ctx, strlen(s)), s2 = r;
adam@4 233
adam@4 234 for (; *s; ++s) {
adam@4 235 if (s[0] == '%' && s[1] && s[2]) {
adam@4 236 unsigned u;
adam@4 237
adam@4 238 sscanf(s+1, "%02x", &u);
adam@4 239 *s2++ = u;
adam@4 240 s += 2;
adam@4 241 } else
adam@4 242 *s2++ = *s;
adam@4 243 }
adam@4 244
adam@4 245 *s2 = 0;
adam@4 246 return r;
adam@4 247 }
adam@4 248
adam@4 249 uw_OpenidFfi_outputs uw_OpenidFfi_indirect(uw_context ctx, uw_Basis_string fields) {
adam@4 250 uw_OpenidFfi_outputs b = malloc(sizeof(uw_buffer));
adam@4 251
adam@4 252 uw_buffer_init(BUF_MAX, b, BUF_INIT);
adam@4 253
adam@6 254 fields = uw_strdup(ctx, fields);
adam@6 255
adam@4 256 while (*fields) {
adam@4 257 char *equal = strchr(fields, '='), *and, *s;
adam@4 258
adam@4 259 if (!equal)
adam@4 260 break;
adam@4 261
adam@4 262 *equal = 0;
adam@4 263 s = deurl(ctx, fields);
adam@4 264 uw_buffer_append(b, s, strlen(s));
adam@4 265 uw_buffer_append(b, "", 1);
adam@4 266
adam@4 267 and = strchr(equal+1, '&');
adam@4 268 if (and) {
adam@4 269 *and = 0;
adam@4 270 fields = and+1;
adam@4 271 } else
adam@4 272 fields = and = strchr(equal+1, 0);
adam@4 273 s = deurl(ctx, equal+1);
adam@4 274 uw_buffer_append(b, s, strlen(s));
adam@4 275 uw_buffer_append(b, "", 1);
adam@4 276 }
adam@4 277
adam@4 278 uw_buffer_append(b, "", 1);
adam@4 279 return b;
adam@4 280 }
adam@6 281
adam@6 282 static uw_Basis_string base64(uw_context ctx, unsigned char *input, int length) {
adam@6 283 BIO *bmem, *b64;
adam@6 284
adam@6 285 b64 = BIO_new(BIO_f_base64());
adam@7 286 BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
adam@6 287 bmem = BIO_new(BIO_s_mem());
adam@7 288 BIO_push(b64, bmem);
adam@6 289 BIO_write(b64, input, length);
adam@6 290 (void)BIO_flush(b64);
adam@6 291
adam@7 292 int len = BIO_ctrl_pending(bmem);
adam@7 293 char *buff = uw_malloc(ctx, len+1);
adam@7 294 BIO_read(bmem, buff, len);
adam@7 295 buff[len] = 0;
adam@6 296
adam@6 297 BIO_free_all(b64);
adam@6 298
adam@6 299 return buff;
adam@6 300 }
adam@6 301
adam@8 302 static int unbase64(unsigned char *input, int length, unsigned char *buffer, int bufferLength)
adam@7 303 {
adam@7 304 BIO *b64, *bmem;
adam@8 305 int n;
adam@6 306
adam@7 307 b64 = BIO_new(BIO_f_base64());
adam@7 308 BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
adam@7 309 bmem = BIO_new_mem_buf(input, length);
adam@7 310 BIO_push(b64, bmem);
adam@8 311 n = BIO_read(b64, buffer, bufferLength);
adam@6 312
adam@7 313 BIO_free_all(bmem);
adam@8 314
adam@8 315 return n;
adam@8 316 }
adam@8 317
adam@8 318 uw_Basis_string uw_OpenidFfi_sha1(uw_context ctx, uw_Basis_string key, uw_Basis_string data) {
adam@8 319 unsigned char keyBin[SHA_DIGEST_LENGTH], out[EVP_MAX_MD_SIZE];
adam@8 320 unsigned outLen;
adam@8 321
adam@8 322 unbase64((unsigned char *)key, strlen(key), keyBin, sizeof keyBin);
adam@8 323
adam@8 324 HMAC(EVP_sha1(), keyBin, sizeof keyBin, (unsigned char *)data, strlen(data), out, &outLen);
adam@8 325 return base64(ctx, out, outLen);
adam@6 326 }
adam@7 327
adam@7 328 uw_Basis_string uw_OpenidFfi_sha256(uw_context ctx, uw_Basis_string key, uw_Basis_string data) {
adam@7 329 unsigned char keyBin[SHA256_DIGEST_LENGTH], out[EVP_MAX_MD_SIZE];
adam@7 330 unsigned outLen;
adam@7 331
adam@7 332 unbase64((unsigned char *)key, strlen(key), keyBin, sizeof keyBin);
adam@7 333
adam@7 334 HMAC(EVP_sha256(), keyBin, sizeof keyBin, (unsigned char *)data, strlen(data), out, &outLen);
adam@7 335 return base64(ctx, out, outLen);
adam@7 336 }
adam@8 337
adam@8 338 static uw_Basis_string btwoc(uw_context ctx, const BIGNUM *n) {
adam@8 339 int len = BN_num_bytes(n), i;
adam@8 340 unsigned char bytes[len+1];
adam@8 341
adam@8 342 bytes[0] = 0;
adam@8 343 BN_bn2bin(n, bytes+1);
adam@8 344
adam@8 345 for (i = 1; i <= len; ++i)
adam@8 346 if (bytes[i]) {
adam@8 347 if (bytes[i] & 0x80)
adam@8 348 --i;
adam@8 349 break;
adam@8 350 }
adam@8 351
adam@8 352 if (i > len)
adam@8 353 i = len;
adam@8 354
adam@8 355 return base64(ctx, bytes+i, len+1-i);
adam@8 356 }
adam@8 357
adam@8 358 static BIGNUM *unbtwoc(uw_context ctx, uw_Basis_string s) {
adam@8 359 unsigned char bytes[1024];
adam@8 360 int len;
adam@8 361
adam@8 362 len = unbase64((unsigned char *)s, strlen(s), bytes, sizeof bytes);
adam@8 363 return BN_bin2bn(bytes, len, NULL);
adam@8 364 }
adam@8 365
adam@8 366 uw_Basis_string uw_OpenidFfi_modulus(uw_context ctx, uw_OpenidFfi_dh dh) {
adam@8 367 return btwoc(ctx, dh->p);
adam@8 368 }
adam@8 369
adam@8 370 uw_Basis_string uw_OpenidFfi_generator(uw_context ctx, uw_OpenidFfi_dh dh) {
adam@8 371 return btwoc(ctx, dh->g);
adam@8 372 }
adam@8 373
adam@8 374 uw_Basis_string uw_OpenidFfi_public(uw_context ctx, uw_OpenidFfi_dh dh) {
adam@8 375 return btwoc(ctx, dh->pub_key);
adam@8 376 }
adam@8 377
adam@8 378 static void free_DH(void *data, int will_retry) {
adam@8 379 DH *dh = data;
adam@8 380 DH_free(dh);
adam@8 381 }
adam@8 382
adam@8 383 uw_OpenidFfi_dh uw_OpenidFfi_generate(uw_context ctx) {
adam@8 384 DH *dh = DH_new();
adam@8 385
adam@8 386 uw_register_transactional(ctx, dh, NULL, NULL, free_DH);
adam@8 387
adam@8 388 DH_generate_parameters_ex(dh, PRIME_LEN, GENERATOR, NULL);
adam@8 389
adam@8 390 if (DH_generate_key(dh) != 1)
adam@8 391 uw_error(ctx, FATAL, "Diffie-Hellman key generation failed");
adam@8 392
adam@8 393 return dh;
adam@8 394 }
adam@8 395
adam@8 396 uw_Basis_string uw_OpenidFfi_compute(uw_context ctx, uw_OpenidFfi_dh dh, uw_Basis_string server_pub) {
adam@8 397 BIGNUM *bn = unbtwoc(ctx, server_pub);
adam@8 398 unsigned char secret[DH_size(dh)];
adam@8 399 int size;
adam@8 400
adam@8 401 uw_push_cleanup(ctx, (void (*)(void *))BN_free, bn);
adam@8 402
adam@8 403 size = DH_compute_key(secret, bn, dh);
adam@8 404 if (size == -1)
adam@8 405 uw_error(ctx, FATAL, "Diffie-Hellman key computation failed");
adam@8 406
adam@8 407 uw_pop_cleanup(ctx);
adam@8 408
adam@8 409 return base64(ctx, secret, size);
adam@8 410 }