]> code.citadel.org Git - citadel.git/blobdiff - citadel/modules/openid/serv_openid_rp.c
Fetch assoc_handles and use them in checkid_setup requests
[citadel.git] / citadel / modules / openid / serv_openid_rp.c
index 488345dec5d5283cb162812e962dccee26e0fab4..f12dddfd18ff9947b1c23915dc56db7c96243376 100644 (file)
 #include "ctdl_module.h"
 
 
+struct associate_handle {
+       char claimed_id[256];
+       char assoc_type[32];
+       time_t expiration_time;
+       char assoc_handle[128];
+       char mac_key[128];
+};
+
+HashList *HL = NULL;           // hash table of assoc_handle
+
 /* 
  * Locate a <link> tag and, given its 'rel=' parameter, return its 'href' parameter
  */
@@ -178,6 +188,120 @@ int fetch_http(char *url, char *target_buf, int maxbytes)
 }
 
 
+#define ASSOCIATE_RESPONSE_SIZE        4096
+
+/*
+ * libcurl callback function for prepare_openid_associate_request()
+ */
+size_t associate_callback(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+       char *response = (char *) stream;
+       int got_bytes = (size * nmemb);
+       int len = strlen(response);
+
+       if ((len + got_bytes + 1) < ASSOCIATE_RESPONSE_SIZE) {
+               memcpy(&response[len], ptr, got_bytes);
+               response[len+got_bytes] = 0;
+       }
+
+       return got_bytes;
+}
+
+
+/*
+ * Process the response from an "associate" request
+ */
+struct associate_handle *process_associate_response(char *claimed_id, char *associate_response)
+{
+       struct associate_handle *h = NULL;
+       char *ptr = associate_response;
+       char thisline[256];
+       char thiskey[256];
+       char thisdata[256];
+
+       h = (struct associate_handle *) malloc(sizeof(struct associate_handle));
+       safestrncpy(h->claimed_id, claimed_id, sizeof h->claimed_id);
+
+       do {
+               ptr = memreadline(ptr, thisline, sizeof thisline);
+               extract_token(thiskey, thisline, 0, ':', sizeof thiskey);
+               extract_token(thisdata, thisline, 1, ':', sizeof thisdata);
+
+               if (!strcasecmp(thiskey, "assoc_type")) {
+                       safestrncpy(h->assoc_type, thisdata, sizeof h->assoc_type);
+               }
+               else if (!strcasecmp(thiskey, "expires_in")) {
+                       h->expiration_time = time(NULL) + atol(thisdata);
+               }
+               else if (!strcasecmp(thiskey, "assoc_handle")) {
+                       safestrncpy(h->assoc_handle, thisdata, sizeof h->assoc_handle);
+               }
+               else if (!strcasecmp(thiskey, "mac_key")) {
+                       safestrncpy(h->mac_key, thisdata, sizeof h->mac_key);
+               }
+
+       } while (*ptr);
+
+       // FIXME add this data structure into a hash table
+
+       // FIXME periodically purge the hash table of expired handles
+
+       return h;
+}
+
+
+
+/*
+ * Establish a shared secret with an OpenID Identity Provider by sending
+ * an "associate" request.
+ */
+struct associate_handle *prepare_openid_associate_request(
+               char *claimed_id, char *openid_server, char *openid_delegate)
+{
+       CURL *curl;
+       CURLcode res;
+       struct curl_httppost *formpost=NULL;
+       struct curl_httppost *lastptr=NULL;
+       char associate_response[ASSOCIATE_RESPONSE_SIZE];
+       struct associate_handle *h = NULL;
+
+       memset(associate_response, 0, ASSOCIATE_RESPONSE_SIZE);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME,      "openid.mode",
+                       CURLFORM_COPYCONTENTS,  "associate",
+                       CURLFORM_END
+       );
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME,      "openid.session_type",
+                       CURLFORM_COPYCONTENTS,  "",
+                       CURLFORM_END
+       );
+
+       curl = curl_easy_init();
+       if (curl) {
+               curl_easy_setopt(curl, CURLOPT_URL, openid_server);
+               curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
+               curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
+               curl_easy_setopt(curl, CURLOPT_WRITEDATA, associate_response);
+               curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, associate_callback);
+               curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+                       
+               curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
+               res = curl_easy_perform(curl);
+               h = process_associate_response(claimed_id, associate_response);
+               curl_easy_cleanup(curl);
+       }
+       curl_formfree(formpost);
+
+       return h;
+}
+
+
+
 
 
 /*
@@ -189,6 +313,7 @@ void cmd_oid1(char *argbuf) {
        char trust_root[1024];
        int i;
        char buf[SIZ];
+       struct associate_handle *h = NULL;
 
        if (CC->logged_in) {
                cprintf("%d Already logged in.\n", ERROR + ALREADY_LOGGED_IN);
@@ -201,7 +326,6 @@ void cmd_oid1(char *argbuf) {
 
        i = fetch_http(openid_url, buf, sizeof buf - 1);
        buf[sizeof buf - 1] = 0;
-       CtdlLogPrintf(CTDL_DEBUG, "fetch got %d bytes:\n", i);
        if (i > 0) {
                char openid_server[1024];
                char openid_delegate[1024];
@@ -219,25 +343,42 @@ void cmd_oid1(char *argbuf) {
                        safestrncpy(openid_delegate, openid_url, sizeof openid_delegate);
                }
 
-               /* Now we know where to redirect to. */
+               /*
+                * Prepare an "associate" request.  This contacts the IdP and fetches
+                * a data structure containing an assoc_handle plus a shared secret.
+                */
+               h = prepare_openid_associate_request(openid_url, openid_server, openid_delegate);
 
+               /* Assemble a URL to which the user-agent will be redirected. */
                char redirect_string[4096];
                char escaped_identity[1024];
                char escaped_return_to[1024];
                char escaped_trust_root[1024];
+               char escaped_sreg_optional[256];
+               char escaped_assoc_handle[256];
 
                urlesc(escaped_identity, sizeof escaped_identity, openid_delegate);
+               urlesc(escaped_assoc_handle, sizeof escaped_assoc_handle, h->assoc_handle);
                urlesc(escaped_return_to, sizeof escaped_return_to, return_to);
                urlesc(escaped_trust_root, sizeof escaped_trust_root, trust_root);
+               urlesc(escaped_sreg_optional, sizeof escaped_sreg_optional,
+                       "nickname,email,fullname,postcode,country");
 
                snprintf(redirect_string, sizeof redirect_string,
                        "%s"
                        "?openid.mode=checkid_setup"
-                       "&openid_identity=%s"
+                       "&openid.identity=%s"
+                       "&openid.assoc_handle=%s"
                        "&openid.return_to=%s"
                        "&openid.trust_root=%s"
+                       "&openid.sreg.optional=%s"
                        ,
-                       openid_server, escaped_identity, escaped_return_to, escaped_trust_root
+                       openid_server,
+                       escaped_identity,
+                       escaped_assoc_handle,
+                       escaped_return_to,
+                       escaped_trust_root,
+                       escaped_sreg_optional
                );
                cprintf("%d %s\n", CIT_OK, redirect_string);
                return;
@@ -248,16 +389,15 @@ void cmd_oid1(char *argbuf) {
 
 
 
-
-
-/* To insert this module into the server activate the next block by changing the #if 0 to #if 1 */
 CTDL_MODULE_INIT(openid_rp)
 {
        if (!threading)
        {
+               curl_global_init(CURL_GLOBAL_ALL);
+               HL = NewHash(1, NULL);
                CtdlRegisterProtoHook(cmd_oid1, "OID1", "Begin OpenID checkid_setup operation");
        }
 
-   /* return our Subversion id for the Log */
-   return "$Id$";
+       /* return our Subversion id for the Log */
+       return "$Id$";
 }