From: Art Cancro Date: Mon, 19 May 2008 03:33:03 +0000 (+0000) Subject: Moved all the OpenID Relying Party code that I've written so far X-Git-Tag: v7.86~2239 X-Git-Url: https://code.citadel.org/?p=citadel.git;a=commitdiff_plain;h=582470a6adda00d5fd5ec218ea28ad44c66ce508 Moved all the OpenID Relying Party code that I've written so far into the Citadel server, with only glue code in WebCit. This will allow Relying Party support to be implemented without requiring a highly trusted webcit client, and it also eliminates the need to link libcurl into webcit. --- diff --git a/citadel/modules/openid/serv_openid_rp.c b/citadel/modules/openid/serv_openid_rp.c new file mode 100644 index 000000000..488345dec --- /dev/null +++ b/citadel/modules/openid/serv_openid_rp.c @@ -0,0 +1,263 @@ +/* + * $Id$ + * + * OpenID 1.1 "relying party" implementation + * + */ + +#include "sysdep.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include +#include +#include +#include +#include "ctdl_module.h" + + +/* + * Locate a tag and, given its 'rel=' parameter, return its 'href' parameter + */ +void extract_link(char *target_buf, int target_size, char *rel, char *source_buf) +{ + char *ptr = source_buf; + + if (!target_buf) return; + if (!rel) return; + if (!source_buf) return; + + target_buf[0] = 0; + + while (ptr = bmstrcasestr(ptr, "'); + rel_tag[0] = 0; + href_tag[0] = 0; + + if ((link_tag_end) && (link_tag_end > link_tag_start)) { + int len; + len = link_tag_end - link_tag_start; + if (len > sizeof work_buffer) len = sizeof work_buffer; + memcpy(work_buffer, link_tag_start, len); + + char *rel_start = NULL; + char *rel_end = NULL; + rel_start = bmstrcasestr(work_buffer, "rel="); + if (rel_start) { + rel_start = strchr(rel_start, '\"'); + if (rel_start) { + ++rel_start; + rel_end = strchr(rel_start, '\"'); + if ((rel_end) && (rel_end > rel_start)) { + safestrncpy(rel_tag, rel_start, rel_end - rel_start + 1); + } + } + } + + char *href_start = NULL; + char *href_end = NULL; + href_start = bmstrcasestr(work_buffer, "href="); + if (href_start) { + href_start = strchr(href_start, '\"'); + if (href_start) { + ++href_start; + href_end = strchr(href_start, '\"'); + if ((href_end) && (href_end > href_start)) { + safestrncpy(href_tag, href_start, href_end - href_start + 1); + } + } + } + + if (!strcasecmp(rel, rel_tag)) { + safestrncpy(target_buf, href_tag, target_size); + return; + } + + } + + ++ptr; + } + + +} + + + +struct fh_data { + char *buf; + int total_bytes_received; + int maxbytes; +}; + + +size_t fh_callback(void *ptr, size_t size, size_t nmemb, void *stream) +{ + struct fh_data *fh = (struct fh_data *) stream; + int got_bytes = (size * nmemb); + + if (fh->total_bytes_received + got_bytes > fh->maxbytes) { + got_bytes = fh->maxbytes - fh->total_bytes_received; + } + if (got_bytes > 0) { + memcpy(&fh->buf[fh->total_bytes_received], ptr, got_bytes); + fh->total_bytes_received += got_bytes; + } + + return got_bytes; +} + + + +/* + * Begin an HTTP fetch (returns number of bytes actually fetched, or -1 for error) + * We first try 'curl' or 'wget' because they have more robust HTTP handling, and also + * support HTTPS. If neither one works, we fall back to a built in mini HTTP client. + */ +int fetch_http(char *url, char *target_buf, int maxbytes) +{ + CURL *curl; + CURLcode res; + char errmsg[1024] = ""; + struct fh_data fh = { + target_buf, + 0, + maxbytes + }; + + if (!url) return(-1); + if (!target_buf) return(-1); + memset(target_buf, 0, maxbytes); + + curl = curl_easy_init(); + if (!curl) { + CtdlLogPrintf(CTDL_ALERT, "Unable to initialize libcurl.\n"); + return(-1); + } + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &fh); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fh_callback); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errmsg); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + + res = curl_easy_perform(curl); + if (res) { + CtdlLogPrintf(CTDL_ALERT, "libcurl error %d: %s\n", res, errmsg); + } + + curl_easy_cleanup(curl); + return fh.total_bytes_received; +} + + + + +/* + * Begin the first portion of an OpenID checkid_setup operation. + */ +void cmd_oid1(char *argbuf) { + char openid_url[1024]; + char return_to[1024]; + char trust_root[1024]; + int i; + char buf[SIZ]; + + if (CC->logged_in) { + cprintf("%d Already logged in.\n", ERROR + ALREADY_LOGGED_IN); + return; + } + + extract_token(openid_url, argbuf, 0, '|', sizeof openid_url); + extract_token(return_to, argbuf, 1, '|', sizeof return_to); + extract_token(trust_root, argbuf, 2, '|', sizeof trust_root); + + 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]; + + extract_link(openid_server, sizeof openid_server, "openid.server", buf); + extract_link(openid_delegate, sizeof openid_delegate, "openid.delegate", buf); + + if (IsEmptyStr(openid_server)) { + cprintf("%d There is no OpenID identity provider at this URL.\n", ERROR); + return; + } + + /* Empty delegate is legal; we just use the openid_url instead */ + if (IsEmptyStr(openid_delegate)) { + safestrncpy(openid_delegate, openid_url, sizeof openid_delegate); + } + + /* Now we know where to redirect to. */ + + char redirect_string[4096]; + char escaped_identity[1024]; + char escaped_return_to[1024]; + char escaped_trust_root[1024]; + + urlesc(escaped_identity, sizeof escaped_identity, openid_delegate); + urlesc(escaped_return_to, sizeof escaped_return_to, return_to); + urlesc(escaped_trust_root, sizeof escaped_trust_root, trust_root); + + snprintf(redirect_string, sizeof redirect_string, + "%s" + "?openid.mode=checkid_setup" + "&openid_identity=%s" + "&openid.return_to=%s" + "&openid.trust_root=%s" + , + openid_server, escaped_identity, escaped_return_to, escaped_trust_root + ); + cprintf("%d %s\n", CIT_OK, redirect_string); + return; + } + + cprintf("%d Unable to fetch OpenID URL\n", ERROR); +} + + + + + +/* 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) + { + CtdlRegisterProtoHook(cmd_oid1, "OID1", "Begin OpenID checkid_setup operation"); + } + + /* return our Subversion id for the Log */ + return "$Id$"; +} diff --git a/citadel/modules/rssclient/serv_rssclient.c b/citadel/modules/rssclient/serv_rssclient.c index fdf509f2e..f176ae7c5 100644 --- a/citadel/modules/rssclient/serv_rssclient.c +++ b/citadel/modules/rssclient/serv_rssclient.c @@ -352,6 +352,9 @@ void rss_do_fetching(char *url, char *rooms) { CURL *curl; CURLcode res; + char errmsg[1024] = ""; + + CtdlLogPrintf(CTDL_DEBUG, "Fetching RSS feed <%s>\n", url); curl = curl_easy_init(); if (!curl) { @@ -371,6 +374,8 @@ void rss_do_fetching(char *url, char *rooms) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); curl_easy_setopt(curl, CURLOPT_WRITEDATA, xp); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, rss_libcurl_callback); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errmsg); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); memset(&ri, 0, sizeof(struct rss_item)); ri.roomlist = rooms; @@ -388,10 +393,10 @@ void rss_do_fetching(char *url, char *rooms) { if (CtdlThreadCheckStop()) goto shutdown ; - res = curl_easy_perform(curl); - //while got bytes - //XML_Parse(xp, buf, got_bytes, 0); + if (res) { + CtdlLogPrintf(CTDL_ALERT, "libcurl error %d: %s\n", res, errmsg); + } if (CtdlThreadCheckStop()) goto shutdown ; diff --git a/webcit/Makefile.in b/webcit/Makefile.in index cdb62ab34..8d5bdc9f4 100644 --- a/webcit/Makefile.in +++ b/webcit/Makefile.in @@ -51,7 +51,7 @@ webcit: webserver.o context_loop.o ical_dezonify.o \ groupdav_main.o groupdav_get.o groupdav_propfind.o fmt_date.o \ groupdav_options.o autocompletion.o gettext.o tabs.o sieve.o \ groupdav_delete.o groupdav_put.o http_datestring.o setup_wizard.o \ - downloads.o addressbook_popup.o pushemail.o sysdep.o httpfetch.o \ + downloads.o addressbook_popup.o pushemail.o sysdep.o \ $(LIBOBJS) $(CC) webserver.o context_loop.o cookie_conversion.o \ webcit.o auth.o tcp_sockets.o mainmenu.o serv_func.o who.o listsub.o \ @@ -63,7 +63,7 @@ webcit: webserver.o context_loop.o ical_dezonify.o \ groupdav_main.o groupdav_get.o groupdav_propfind.o groupdav_delete.o \ groupdav_options.o autocompletion.o tabs.o smtpqueue.o sieve.o \ groupdav_put.o http_datestring.o setup_wizard.o fmt_date.o \ - gettext.o downloads.o addressbook_popup.o pushemail.o sysdep.o httpfetch.o \ + gettext.o downloads.o addressbook_popup.o pushemail.o sysdep.o \ $(LIBOBJS) $(LIBS) $(LDFLAGS) -o webcit .c.o: diff --git a/webcit/auth.c b/webcit/auth.c index f0eed0e6a..94abedfbe 100644 --- a/webcit/auth.c +++ b/webcit/auth.c @@ -259,88 +259,12 @@ void do_login(void) } -/* - * Locate a tag and, given its 'rel=' parameter, return its 'href' parameter - */ -void extract_link(char *target_buf, int target_size, char *rel, char *source_buf) -{ - char *ptr = source_buf; - - if (!target_buf) return; - if (!rel) return; - if (!source_buf) return; - - target_buf[0] = 0; - - while (ptr = bmstrcasestr(ptr, "'); - rel_tag[0] = 0; - href_tag[0] = 0; - - if ((link_tag_end) && (link_tag_end > link_tag_start)) { - int len; - len = link_tag_end - link_tag_start; - if (len > sizeof work_buffer) len = sizeof work_buffer; - memcpy(work_buffer, link_tag_start, len); - - char *rel_start = NULL; - char *rel_end = NULL; - rel_start = bmstrcasestr(work_buffer, "rel="); - if (rel_start) { - rel_start = strchr(rel_start, '\"'); - if (rel_start) { - ++rel_start; - rel_end = strchr(rel_start, '\"'); - if ((rel_end) && (rel_end > rel_start)) { - safestrncpy(rel_tag, rel_start, rel_end - rel_start + 1); - } - } - } - - char *href_start = NULL; - char *href_end = NULL; - href_start = bmstrcasestr(work_buffer, "href="); - if (href_start) { - href_start = strchr(href_start, '\"'); - if (href_start) { - ++href_start; - href_end = strchr(href_start, '\"'); - if ((href_end) && (href_end > href_start)) { - safestrncpy(href_tag, href_start, href_end - href_start + 1); - } - } - } - - if (!strcasecmp(rel, rel_tag)) { - safestrncpy(target_buf, href_tag, target_size); - return; - } - - } - - ++ptr; - } - - -} - - /* * Perform authentication using OpenID * assemble the checkid_setup request and then redirect to the user's identity provider */ void do_openid_login(void) { - int i; char buf[4096]; if (havebstr("language")) { @@ -353,48 +277,22 @@ void do_openid_login(void) return; } if (havebstr("login_action")) { - i = fetch_http(bstr("openid_url"), buf, sizeof buf - 1); - buf[sizeof buf - 1] = 0; - if (i > 0) { - char openid_server[1024]; - char openid_delegate[1024]; - - extract_link(openid_server, sizeof openid_server, "openid.server", buf); - extract_link(openid_delegate, sizeof openid_delegate, "openid.delegate", buf); - - /* Empty delegate is legal; we just use the openid_url instead */ - if (IsEmptyStr(openid_delegate)) { - safestrncpy(openid_delegate, bstr("openid_url"), sizeof openid_delegate); - } + snprintf(buf, sizeof buf, + "OID1 %s|%s://%s/finish_openid_login|%s://%s", + bstr("openid_url"), + (is_https ? "https" : "http"), WC->http_host, + (is_https ? "https" : "http"), WC->http_host + ); - /* Now we know where to redirect to. */ - - char redirect_string[4096]; - char escaped_identity[1024]; - char escaped_return_to[1024]; - char escaped_trust_root[1024]; - - stresc(escaped_identity, sizeof escaped_identity, openid_delegate, 0, 1); - - snprintf(buf, sizeof buf, "%s://%s/finish_openid_login", - (is_https ? "https" : "http"), WC->http_host); - stresc(escaped_return_to, sizeof escaped_identity, buf, 0, 1); - - snprintf(buf, sizeof buf, "%s://%s", - (is_https ? "https" : "http"), WC->http_host); - stresc(escaped_trust_root, sizeof escaped_identity, buf, 0, 1); - - snprintf(redirect_string, sizeof redirect_string, - "%s" - "?openid.mode=checkid_setup" - "&openid_identity=%s" - "&openid.return_to=%s" - "&openid.trust_root=%s" - , - openid_server, escaped_identity, escaped_return_to, escaped_trust_root - ); - lprintf(CTDL_DEBUG, "OpenID server contacted; redirecting to %s\n", redirect_string); - http_redirect(redirect_string); + serv_puts(buf); + serv_getln(buf, sizeof buf); + if (buf[0] == '2') { + lprintf(CTDL_DEBUG, "OpenID server contacted; redirecting to %s\n", &buf[4]); + http_redirect(&buf[4]); + return; + } + else { + display_openid_login(&buf[4]); return; } } diff --git a/webcit/httpfetch.c b/webcit/httpfetch.c deleted file mode 100644 index e4c48af72..000000000 --- a/webcit/httpfetch.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * mini http client - */ - - -#include "webcit.h" -#include "webserver.h" -#define CLIENT_TIMEOUT 15 -#define sock_close(sock) close(sock) - -int sock_connect(char *host, char *service, char *protocol) -{ - struct hostent *phe; - struct servent *pse; - struct protoent *ppe; - struct sockaddr_in sin; - struct sockaddr_in egress_sin; - int s, type; - - if ((host == NULL) || IsEmptyStr(host)) - return(-1); - if ((service == NULL) || IsEmptyStr(service)) - return(-1); - if ((protocol == NULL) || IsEmptyStr(protocol)) - return(-1); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - - pse = getservbyname(service, protocol); - if (pse) { - sin.sin_port = pse->s_port; - } else if ((sin.sin_port = htons((u_short) atoi(service))) == 0) { - lprintf(CTDL_CRIT, "Can't get %s service entry: %s\n", - service, strerror(errno)); - return(-1); - } - phe = gethostbyname(host); - if (phe) { - memcpy(&sin.sin_addr, phe->h_addr, phe->h_length); - } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) { - lprintf(CTDL_ERR, "Can't get %s host entry: %s\n", - host, strerror(errno)); - return(-1); - } - if ((ppe = getprotobyname(protocol)) == 0) { - lprintf(CTDL_CRIT, "Can't get %s protocol entry: %s\n", - protocol, strerror(errno)); - return(-1); - } - if (!strcmp(protocol, "udp")) { - type = SOCK_DGRAM; - } else { - type = SOCK_STREAM; - } - - s = socket(PF_INET, type, ppe->p_proto); - if (s < 0) { - lprintf(CTDL_CRIT, "Can't create socket: %s\n", strerror(errno)); - return(-1); - } - - /* If citserver is bound to a specific IP address on the host, make - * sure we use that address for outbound connections. FIXME make this work in webcit - */ - memset(&egress_sin, 0, sizeof(egress_sin)); - egress_sin.sin_family = AF_INET; - // if (!IsEmptyStr(config.c_ip_addr)) { - // egress_sin.sin_addr.s_addr = inet_addr(config.c_ip_addr); - // if (egress_sin.sin_addr.s_addr == !INADDR_ANY) { - // egress_sin.sin_addr.s_addr = INADDR_ANY; - // } - - /* If this bind fails, no problem; we can still use INADDR_ANY */ - bind(s, (struct sockaddr *)&egress_sin, sizeof(egress_sin)); - // } - - /* Now try to connect to the remote host. */ - if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { - lprintf(CTDL_ERR, "Can't connect to %s:%s: %s\n", - host, service, strerror(errno)); - close(s); - return(-1); - } - - return (s); -} - - - -/* - * sock_read_to() - input binary data from socket, with a settable timeout. - * Returns the number of bytes read, or -1 for error. - * If keep_reading_until_full is nonzero, we keep reading until we get the number of requested bytes - */ -int sock_read_to(int sock, char *buf, int bytes, int timeout, int keep_reading_until_full) -{ - int len,rlen; - fd_set rfds; - struct timeval tv; - int retval; - - len = 0; - while (len 0) - && ( (buf[i - 1]==13) - || ( buf[i - 1]==10)) ) { - i--; - buf[i] = 0; - } - return(i); -} - - - -/* - * sock_puts() - send line to server - implemented in terms of serv_write() - * Returns the number of bytes written, or -1 for error. - */ -int sock_puts(int sock, char *buf) -{ - int i, j; - - i = sock_write(sock, buf, strlen(buf)); - if (i<0) return(i); - j = sock_write(sock, "\n", 1); - if (j<0) return(j); - return(i+j); -} - - - - - - -/* - * Fallback handler for fetch_http() that uses our built-in mini client - */ -int fetch_http_using_mini_client(char *url, char *target_buf, int maxbytes) -{ - char buf[1024]; - char httphost[1024]; - int httpport = 80; - char httpurl[1024]; - int sock = (-1); - int got_bytes = (-1); - int redirect_count = 0; - int total_bytes_received = 0; - int i = 0; - - /* Parse the URL */ - snprintf(buf, (sizeof buf)-1, "%s", url); - i = parse_url(buf, httphost, &httpport, httpurl); - if (i == 1) { - snprintf(buf, (sizeof buf)-1, "http://%s", url); - i = parse_url(buf, httphost, &httpport, httpurl); - } - if (i == 4) { - strcat(buf, "/"); - i = parse_url(buf, httphost, &httpport, httpurl); - } - if (i != 0) { - lprintf(CTDL_ALERT, "Invalid URL: %s (%d)\n", url, i); - return(-1); - } - -retry: lprintf(CTDL_NOTICE, "Connecting to <%s>\n", httphost); - sprintf(buf, "%d", httpport); - sock = sock_connect(httphost, buf, "tcp"); - if (sock >= 0) { - lprintf(CTDL_DEBUG, "Connected!\n"); - - snprintf(buf, sizeof buf, "GET %s HTTP/1.0", httpurl); - lprintf(CTDL_DEBUG, "<%s\n", buf); - sock_puts(sock, buf); - - snprintf(buf, sizeof buf, "Host: %s", httphost); - lprintf(CTDL_DEBUG, "<%s\n", buf); - sock_puts(sock, buf); - - snprintf(buf, sizeof buf, "User-Agent: WebCit"); - lprintf(CTDL_DEBUG, "<%s\n", buf); - sock_puts(sock, buf); - - snprintf(buf, sizeof buf, "Accept: */*"); - lprintf(CTDL_DEBUG, "<%s\n", buf); - sock_puts(sock, buf); - - sock_puts(sock, ""); - - if (sock_getln(sock, buf, sizeof buf) >= 0) { - lprintf(CTDL_DEBUG, ">%s\n", buf); - remove_token(buf, 0, ' '); - - /* 200 OK */ - if (buf[0] == '2') { - - while (got_bytes = sock_getln(sock, buf, sizeof buf), - (got_bytes >= 0 && (strcmp(buf, "")) && (strcmp(buf, "\r"))) ) { - /* discard headers */ - } - - while (got_bytes = sock_read_to(sock, buf, sizeof buf, CLIENT_TIMEOUT, 0), - (got_bytes>0) ) { - - if (total_bytes_received + got_bytes > maxbytes) { - got_bytes = maxbytes - total_bytes_received; - } - if (got_bytes > 0) { - memcpy(&target_buf[total_bytes_received], buf, got_bytes); - total_bytes_received += got_bytes; - } - - } - } - - /* 30X redirect */ - else if ( (!strncmp(buf, "30", 2)) && (redirect_count < 16) ) { - while (got_bytes = sock_getln(sock, buf, sizeof buf), - (got_bytes >= 0 && (strcmp(buf, "")) && (strcmp(buf, "\r"))) ) { - if (!strncasecmp(buf, "Location:", 9)) { - ++redirect_count; - strcpy(buf, &buf[9]); - striplt(buf); - if (parse_url(buf, httphost, &httpport, httpurl) == 0) { - sock_close(sock); - goto retry; - } - else { - lprintf(CTDL_ALERT, "Invalid URL: %s\n", buf); - } - } - } - } - - } - sock_close(sock); - } - else { - lprintf(CTDL_ERR, "Could not connect: %s\n", strerror(errno)); - } - - return total_bytes_received; -} - - - -/* - * Begin an HTTP fetch (returns number of bytes actually fetched, or -1 for error) - * We first try 'curl' or 'wget' because they have more robust HTTP handling, and also - * support HTTPS. If neither one works, we fall back to a built in mini HTTP client. - */ -int fetch_http(char *url, char *target_buf, int maxbytes) -{ - FILE *fp; - char cmd[1024]; - int bytes_received = 0; - char ch; - - memset(target_buf, 0, maxbytes); - - /* First try curl */ - snprintf(cmd, sizeof cmd, "curl -L %s /dev/null", url); - fp = popen(cmd, "r"); - - /* Then try wget */ - if (!fp) { - snprintf(cmd, sizeof cmd, "wget -q -O - %s /dev/null", url); - fp = popen(cmd, "r"); - } - - if (fp) { - while ( (!feof(fp)) && (bytes_received < maxbytes) ) { - ch = fgetc(fp); - if (ch != EOF) { - target_buf[bytes_received++] = ch; - } - } - pclose(fp); - return bytes_received; - } - - /* Fall back to the built-in mini handler */ - return fetch_http_using_mini_client(url, target_buf, maxbytes); -}