Guys:
Below is what I have now for 2-legged OAuth in iwhd. It actually works, with oauthtest2 from liboauth.
The patch is only a small piece, since it has no client, which we need to run the tests, has no documentation, and no user list. However, it retires most of the risk. We know that it can be done, this way. The biggest risk left is interoperability with Aeolus.
BTW, the OAuth header looks like this:
Authorization: OAuth realm="http://example.org/", oauth_consumer_key="chiaki", oauth_nonce="mitPzrhrCMHABJiYava_", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1317182031", oauth_version="1.0", oauth_signature="Ya36%2FkXTG5YATRx%2BWAXK9NXzZCg%3D"
-- Pete
diff --git a/configure.ac b/configure.ac index e23d1fa..8b115cb 100644 --- a/configure.ac +++ b/configure.ac @@ -70,6 +70,9 @@ AC_CHECK_LIB([uuid], [uuid_generate_random], [AC_MSG_ERROR([Missing required uuid lib])]) AC_SUBST([UUID_LIB])
+dnl How to check properly? +LIBS="$LIBS -loauth" + PKG_CHECK_MODULES([HAIL],[libhail >= 0.8]) AC_SUBST([HAIL_LIBS]) AC_SUBST([HAIL_CFLAGS]) @@ -97,6 +100,9 @@ AC_CACHE_CHECK([whether json_load_file takes 3 arguments], AC_DEFINE_UNQUOTED([JANSSON_LOAD_FLAG], $iw_cv_func_jansson_flag, [Define to "0," if json_load_file and json_loads require a flags arguments.])
+AC_CHECK_HEADER([oauth.h], , + [AC_MSG_ERROR([Missing OAuth development library: liboauth-devel])]) + # from http://www.gnu.org/software/autoconf-archive/ AX_BOOST_BASE AX_BOOST_SYSTEM diff --git a/rest.c b/rest.c index 92f36cb..fec602c 100644 --- a/rest.c +++ b/rest.c @@ -15,12 +15,14 @@
#include <config.h>
+#include <ctype.h> #include <error.h> #include <fcntl.h> #include <getopt.h> #include <poll.h> #include <pthread.h> #include <semaphore.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -33,6 +35,7 @@ #include <microhttpd.h> #include <hstor.h> /* only for ARRAY_SIZE at this point */ #include <curl/curl.h> +#include <oauth.h>
#include "configmake.h" /* for LOCALEDIR */ #include "dirname.h" @@ -113,6 +116,9 @@ static unsigned short my_port = MY_PORT; static LIST_HEAD(gc_list, _my_state) my_states; /* this keeps GC informed */ static pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER; static const char *log_name; +static char my_hostname[HOST_NAME_MAX+1]; +static bool oauth = false; +static const char *username, *userpass;
static const char *const (reserved_name[]) = {"_default", "_new", "_policy", "_query", NULL}; static const char *const (reserved_attr[]) = {"_attrs", "_bucket", "_date", "_etag", "_key", "_loc", "_size", NULL}; @@ -2091,6 +2097,454 @@ parse_url (const char *url, my_state *ms) return eindex; }
+/* TBD: evict this to "user.h" once -U is gone. */ +#define USERNAME_MAX 64 +#define USERPASS_MAX 64 +struct user { + char name[USERNAME_MAX+1]; + char pass[USERPASS_MAX+1]; +}; + +static struct user * +user_lookup (const char *user) +{ + struct user *ret; + + /* + * We do not have a real user table yet, so just compare with -U. + */ + if (username == NULL || strcmp(user, username) != 0) + return NULL; + + ret = malloc(sizeof(struct user)); + if (!ret) + return NULL; + memset(ret,0,sizeof(struct user)); + strcpy(ret->name, username); + strcpy(ret->pass, userpass); + return ret; +} + +static char * +rebuild_url(bool is_ssl, struct MHD_Connection *conn, const char *path) +{ + const char *val; + char *ret; + int rc; + + /* + * Luckily for us, this header includes the colon-port. + * It really should be called "Netloc", not "Host". + */ + val = MHD_lookup_connection_value(conn,MHD_HEADER_KIND,"Host"); + if (!val) { + if (my_hostname[0] == 0) { + if (gethostname(my_hostname, HOST_NAME_MAX) != 0) { + /* TBD: This may flood - user-driven. */ + error (0, errno, _("gethostname")); + return NULL; + } + my_hostname[HOST_NAME_MAX] = 0; + } + /* TBD: And port? But this is a fall-back anyway. */ + val = my_hostname; + } + + rc = asprintf(&ret, "%s://%s%s", is_ssl? "https": "http", val, path); + if (rc < 0) { + DPRINTF("No core"); + return NULL; + } + return ret; +} + +static const char * +auth_type_check (const char *val, const char *type) +{ + const char *p; + size_t tlen = strlen(type); + + p = val; + while (*p == ' ') { + if (*p == 0) { + return false; + } + p++; + } + if (strncmp(p, type, tlen) != 0) + return false; + p += tlen; + if (*p != ' ') + return false; + p++; + while (*p == ' ') { + if (*p == 0) { + return false; + } + p++; + } + return p; +} + +/* + * Split up an Authorization header. + * Note that this decodes the strings. + */ +static bool +auth_hdr_split (int *pargc, char ***pargv, const char *val) +{ + enum { ARGC_MAX = 20 }; /* better than 2-pass */ + enum { VAL_MAX = 512 }; /* a constant is not great, but... */ + enum { + ST_NONE, ST_ERR, ST_KEY, ST_EQ, ST_VAL, ST_Q2, ST_COM, + ST_PCENT1, ST_PCENT2 + } state; + int argc; + char **argv; + const char *p; + const char *key; + size_t klen = 0; + size_t vlen = 0; + char vbuf[VAL_MAX+1]; + unsigned int acc = 0, n; + char *arg; + + if (!(val = auth_type_check(val, "OAuth"))) + return false; + + argv = malloc((ARGC_MAX+1) * sizeof(char*)); + if (!argv) { + return false; + } + argc = 0; + + arg = NULL; + state = ST_NONE; + key = NULL; + for (p = val; *p != 0; p++) { + switch (state) { + case ST_NONE: + if (*p == ' ') { + ; + } + else if (!isascii(*p) || !isalpha(*p)) { + state = ST_ERR; + } + else { + state = ST_KEY; + key = p; + } + break; + case ST_KEY: + if (*p == '=') { + state = ST_EQ; + klen = p-key; + } + break; + case ST_EQ: + if (*p == '"') { + state = ST_VAL; + vlen = 0; + } + else { + /* + * A compiler writer would've had an ulcer + * from this, because obviously we are going + * to recover in a dirty way. But whatever, + * all we want is an error indication. + */ + state = ST_ERR; + } + break; + case ST_VAL: + if (*p == '"') { + vbuf[vlen] = 0; + state = ST_Q2; + } + else if (*p == '%') { + state = ST_PCENT1; + acc = 0; + } + else { + vbuf[vlen++] = *p; + } + break; + case ST_PCENT1: + if (isxdigit(*p)) { + if (*p <= '9') { + n = toupper(*p) - '0'; + } else { + n = toupper(*p) - 'A' + 10; + } + acc = (acc << 4) | (n & 0x0f); + state = ST_PCENT2; + } else { + state = ST_ERR; + } + break; + case ST_PCENT2: + if (isxdigit(*p)) { + if (*p <= '9') { + n = toupper(*p) - '0'; + } else { + n = toupper(*p) - 'A' + 10; + } + acc = (acc << 4) | (n & 0x0f); + if (iscntrl(acc)) + acc = '?'; + if (vlen >= VAL_MAX) { + state = ST_ERR; + break; + } + vbuf[vlen++] = acc; + state = ST_VAL; + } + else { + state = ST_ERR; + } + break; + case ST_Q2: + if (*p == ',') { + arg = malloc(klen+1+vlen+1); + if (!arg) { + state = ST_ERR; + break; + } + memcpy(arg, key, klen); + arg[klen] = '='; + memcpy(arg+klen+1, vbuf, vlen); + arg[klen+1+vlen] = 0; + + state = ST_COM; + key = NULL; + } + else { + state = ST_ERR; + } + break; + case ST_COM: + if (*p == ' ') { + if (!arg) { + state = ST_NONE; + key = NULL; + break; + } + if (argc >= ARGC_MAX) { + /* TBD: error out immediately */ + state = ST_ERR; + break; + } + argv[argc++] = arg; + arg = NULL; + state = ST_NONE; + key = NULL; + } + else { + state = ST_ERR; + } + default: // ST_ERR + if (*p == ',') { + state = ST_COM; + } + } + } + + if (arg) { + DPRINTF("auth_hdr_split ended with arg %s\n", arg); + free (arg); + } + + if (state == ST_Q2) { + if (key) { + arg = malloc(klen+1+vlen+1); + if (arg && argc < ARGC_MAX) { + memcpy(arg, key, klen); + arg[klen] = '='; + memcpy(arg+klen+1, vbuf, vlen); + arg[klen+1+vlen] = 0; + argv[argc++] = arg; + } + } + } + argv[argc] = NULL; + + *pargc = argc; + *pargv = argv; + return true; +} + +/* Extract the value from key="val". */ +static char * +oauth_param_val(const char *arg) { + char *val; + + val = strchr(arg, '='); + if (!val) + return NULL; + val++; + if (*val == 0) + return NULL; + return strdup(val); +} + +/* clone of oauth_param_exists with minimal changes */ +static int +oauth_param_find(char **argv, int argc, const char *key) { + int i; + size_t l= strlen(key); + for (i=0;i<argc;i++) + if (strlen(argv[i])>l && !strncmp(argv[i],key,l) && argv[i][l] == '=') return i; + return -1; +} + +static int +do_oauth (struct MHD_Connection *conn, my_state *ms, + const char *url, const char *method) +{ + const char *val; + struct MHD_Response *resp; + int argc; + char **argv; + int oargc; + char **oargv; + int x; + struct user *u; + char *sig_supplied, *sig_calculated; + + val = MHD_lookup_connection_value(conn,MHD_HEADER_KIND,"Authorization"); + if (!val) { + resp = MHD_create_response_from_data(0,NULL,MHD_NO,MHD_NO); + if (!resp) { + return -1; + } + MHD_add_response_header(resp,"WWW-Authenticate","OAuth"); + MHD_queue_response(conn,MHD_HTTP_UNAUTHORIZED,resp); + MHD_destroy_response(resp); + return 1; + } + + if (!auth_hdr_split(&argc, &argv, val)) { + do_resp(conn,NULL,MHD_HTTP_BAD_REQUEST,NULL,"400\r\n"); + return -1; + } + if (verbose >= 2) { + int i; + printf("OAuth supplied [%d]\n", argc); + for (i = 0; i < argc; i++) { + printf(" %s\n", argv[i]); + } + } + + x = oauth_param_find(argv,argc,"oauth_consumer_key"); + if (x == -1) { + do_resp(conn,NULL,MHD_HTTP_BAD_REQUEST,NULL,"400 no user\r\n"); + return -1; + } + + u = user_lookup(oauth_param_val(argv[x])); + if (!u) { + do_resp(conn,NULL,MHD_HTTP_FORBIDDEN,NULL,"403 bad user\r\n"); + return -1; + } + + /* + * Putting marshalling arguments for and extracting results from + * oauth_sign_array2_process makes you dirty, but this way offloads + * some tricks to liboauth, so there. + * XXX - verify HMAC or other type + */ + oargc = 1+argc; /* add a slot for URL */ + + /* + * If we just call malloc() here, we actually involve a function + * intercepted by GC. Then, glibc will abort inside liboauth + * oauth_sign_array2_process->xrealloc->free() => "invalid pointer". + * Solution: use a random liboauth function which allocates + * the memory we need. The result can be safely passed to liboauth. + */ + oargv = malloc((oargc+1)*sizeof(char*)); + + memset((void*)oargv, '0', (oargc+1)*sizeof(char*)); + ((char *)oargv)[(oargc+1)*sizeof(char*) - 1] = 0; + oargv = (char **) oauth_url_escape((char *)oargv); + + if (!oargv) { + do_resp(conn,NULL,MHD_HTTP_INTERNAL_SERVER_ERROR,NULL, + "500\r\n"); + return -1; + } + oargv[0] = rebuild_url(false, conn, url); /* TBD: SSL */ + if (!oargv[0]) { + do_resp(conn,NULL,MHD_HTTP_INTERNAL_SERVER_ERROR,NULL, + "500\r\n"); + return -1; + } + + // no good, must filter + // memcpy(&oargv[1], argv, (argc+1)*sizeof(char*)); + + oargc = 1; + + x = oauth_param_find(argv,argc,"oauth_nonce"); + if (x == -1) { + do_resp(conn,NULL,MHD_HTTP_BAD_REQUEST,NULL,"400 no nonce\r\n"); + return -1; + } + oargv[oargc++] = argv[x]; + + x = oauth_param_find(argv,argc,"oauth_timestamp"); + if (x == -1) { + do_resp(conn,NULL,MHD_HTTP_BAD_REQUEST,NULL, + "400 no timestamp\r\n"); + return -1; + } + oargv[oargc++] = argv[x]; + + oauth_sign_array2_process(&oargc, &oargv, NULL, OA_HMAC, method, + u->name, u->pass, NULL, NULL); + if (verbose >= 2) { + int i; + printf("OAuth calculated\n"); + for (i = 0; i < oargc; i++) { + printf(" %s\n", oargv[i]); + } + } + + /* + * We've got the signature, now verify. + */ + x = oauth_param_find(argv,argc,"oauth_signature"); + if (x == -1) { + do_resp(conn,NULL,MHD_HTTP_BAD_REQUEST,NULL, + "400 no signature\r\n"); + return -1; + } + sig_supplied = oauth_param_val(argv[x]); + + x = oauth_param_find(oargv,oargc,"oauth_signature"); + if (x == -1) { + do_resp(conn,NULL,MHD_HTTP_INTERNAL_SERVER_ERROR,NULL, + "500 no signature\r\n"); + return -1; + } + sig_calculated = oauth_param_val(oargv[x]); + + if (strcmp(sig_supplied, sig_calculated) != 0) { + DPRINTF("OAuth signature mismatch"); + return -1; + } + return 0; +} + +static int kv_iter_dump(void *cls, enum MHD_ValueKind kind, + const char *key, const char *value) +{ + /* ok to use printf when MHD_USE_THREAD_PER_CONNECTION maybe? */ + printf("%s: %s\n", key, value); + + return MHD_YES; +} + static int access_handler_0 (void *cctx, struct MHD_Connection *conn, const char *url, const char *method, const char *version, const char *data, @@ -2099,10 +2553,24 @@ access_handler_0 (void *cctx, struct MHD_Connection *conn, const char *url, unsigned int i; url_type utype; my_state *ms = *rctx; + int rc;
log_check(); + if (verbose >= 2) { + printf(">> HTTP\n"); + rc = MHD_get_connection_values(conn, MHD_HEADER_KIND, + kv_iter_dump, NULL); + printf(">> headers: %d\n", rc); + }
if (ms) { + if (oauth) { + rc = do_oauth(conn,ms,url,method); + if (rc < 0) + return MHD_NO; + if (rc > 0) + return MHD_YES; + } return ms->handler(cctx,conn,url,method,version, data,data_size,rctx); } @@ -2131,12 +2599,20 @@ access_handler_0 (void *cctx, struct MHD_Connection *conn, const char *url, ms->conn = conn; *rctx = ms; pthread_mutex_lock(&my_lock); - LIST_INSERT_HEAD(&my_states, ms, gc_link); + LIST_INSERT_HEAD(&my_states,ms,gc_link); pthread_mutex_unlock(&my_lock); + if (oauth) { + rc = do_oauth(conn,ms,url,method); + if (rc < 0) + return MHD_NO; + if (rc > 0) + return MHD_YES; + } return ms->handler(cctx,conn,url,method,version, data,data_size,rctx); }
+ /* This is *unauthenticated*. Remove altogether? TBD */ if (!strcmp(method,"QUIT")) { (void)sem_post((sem_t *)cctx); return MHD_NO; @@ -2194,7 +2670,9 @@ static const struct option my_options[] = { { "db", required_argument, NULL, 'd' }, { "logfile", required_argument, NULL, 'l' }, { "master", required_argument, NULL, 'm' }, + { "oauth", no_argument, NULL, 'o' }, { "port", required_argument, NULL, 'p' }, + { "usercred", required_argument, NULL, 'U' }, { "verbose", no_argument, NULL, 'v' }, { "version", no_argument, NULL, GETOPT_VERSION_CHAR }, { "help", no_argument, NULL, GETOPT_HELP_CHAR }, @@ -2222,7 +2700,9 @@ A configuration file must be specified.\n\ -d, --db=HOST_PORT database server as ip[:port]\n\ -l, --logfile=FILE logfile (default stdout/stderr)\n\ -m, --master=HOST_PORT master (upstream) server as ip[:port]\n\ + -o, --oauth enable OAuth\n\ -p, --port=PORT alternate listen port (default 9090)\n\ + -U, --usercred=U:P singleton pair of username:password\n\ -v, --verbose verbose/debug output\n\ \n\ --help display this help and exit\n\ @@ -2275,6 +2755,7 @@ main (int argc, char **argv) sem_t the_sem; bool autostart = false; char *cfg_file = NULL; + char *usercred = NULL;
set_program_name (argv[0]); setlocale (LC_ALL, ""); @@ -2287,7 +2768,8 @@ main (int argc, char **argv) db_host = xstrdup ("localhost");
for (;;) - switch (getopt_long(argc,argv,"ac:d:l:m:p:v",my_options,NULL)) { + switch (getopt_long(argc,argv,"ac:d:l:m:op:U:v",my_options,NULL)) + { case 'a': autostart = true; break; @@ -2308,10 +2790,16 @@ main (int argc, char **argv) free (master_host); master_host = NULL; extract_host_port (optarg, &master_host, &master_port); break; + case 'o': + oauth = true; + break; case 'p': assert (optarg); my_port = get_port (optarg); break; + case 'U': /* Uppercase because very temporary until -u appears. */ + usercred = optarg; + break; case 'v': ++verbose; break; @@ -2366,6 +2854,30 @@ args_done: usage (EXIT_FAILURE); }
+ if (usercred) { + const char *p = strchr(usercred,':'); + if (!p) { + error (0, 0, _("The -U argument must contain a colon")); + usage (EXIT_FAILURE); + } + if (p == usercred) { + error (0, 0, + _("The user part of -U argument is empty")); + usage (EXIT_FAILURE); + } + username = strndup(usercred,p-usercred); + userpass = strdup(p+1); + if (!username || !userpass) { + error (EXIT_FAILURE, 0, _("No core")); + } + if (strlen(username) > USERNAME_MAX) { + error (EXIT_FAILURE, 0, _("User name too long")); + } + if (strlen(userpass) > USERPASS_MAX) { + error (EXIT_FAILURE, 0, _("User password too long")); + } + } + char *t; char const *tmpdir = ((t = getenv ("TMPDIR")) ? t : "/tmp"); tmpfile_template = xasprintf ("%s/iwhd.XXXXXX", tmpdir); @@ -2382,6 +2894,9 @@ args_done: printf("db is at %s:%u\n",db_host,db_port); printf("will listen on port %u\n",my_port); printf("my location is "%s"\n",me); + if (oauth) { + printf("autheticated with OAuth\n"); + } if (fflush(stdout) || ferror(stdout)) error(EXIT_FAILURE, 0, _("write failed")); }
On Tue, Sep 27, 2011 at 09:58:06PM -0600, Pete Zaitcev wrote:
Guys:
Below is what I have now for 2-legged OAuth in iwhd. It actually works, with oauthtest2 from liboauth.
The patch is only a small piece, since it has no client, which we need to run the tests, has no documentation, and no user list. However, it retires most of the risk. We know that it can be done, this way. The biggest risk left is interoperability with Aeolus.
Great stuff!
If you're able to build RPMs from this and/or run it somewhere I can access it, I can work on putting a client together today and verifying interoperability.
-- Matt
On Wed, 28 Sep 2011 10:05:31 -0400 Matt Wagner matt.wagner@redhat.com wrote:
If you're able to build RPMs from this and/or run it somewhere I can access it, I can work on putting a client together today and verifying interoperability.
RPMs are available here: http://people.redhat.com/zaitcev/ftp/iwhd/iwhd-0.98.41-1.z1.el6.src.rpm http://people.redhat.com/zaitcev/ftp/iwhd/iwhd-0.98.41-1.z1.el6.x86_64.rpm
For now there is no user file, so only one user can be set, and it is set with -U flag. So, you would need to do something like this: echo 'IWHD_ARGS="-d localhost:27017 -l /var/log/iwhd.log \ -o -U user:password"' >> /etc/sysconfig/iwhd
The RPMs include the import timeout increased to 1 hour.
Please let me know how it goes.
-- Pete
On Mon, Oct 03, 2011 at 05:12:14PM -0600, Pete Zaitcev wrote:
RPMs are available here: http://people.redhat.com/zaitcev/ftp/iwhd/iwhd-0.98.41-1.z1.el6.src.rpm http://people.redhat.com/zaitcev/ftp/iwhd/iwhd-0.98.41-1.z1.el6.x86_64.rpm
For now there is no user file, so only one user can be set, and it is set with -U flag. So, you would need to do something like this: echo 'IWHD_ARGS="-d localhost:27017 -l /var/log/iwhd.log \ -o -U user:password"' >> /etc/sysconfig/iwhd
The RPMs include the import timeout increased to 1 hour.
Please let me know how it goes.
Some quick tests indicate that all's well from Ruby-land!
I used a basic client that I'd written, started iwhd in OAuth mode (per the example you included), and verified the following:
- Visit /images without any OAuth setup. I got a 401.
- Visit /images with a bogus username. I got a 403.
- Visit /images with the keys specified (I keep wanting to call them 'username' and 'password'). I got a 200 and the XML list of images.
The one thing that was a little weird is that if I used a correct consumer key but an incorrect consumer secret, Ruby raises an "EOFError: end of file reached" exception. I infer we're receiving something like an empty response as opposed to a reply indicating an authorization failure.
-- Matt
On Tue, Oct 04, 2011 at 03:36:54PM -0400, Matt Wagner wrote:
On Mon, Oct 03, 2011 at 05:12:14PM -0600, Pete Zaitcev wrote:
RPMs are available here: http://people.redhat.com/zaitcev/ftp/iwhd/iwhd-0.98.41-1.z1.el6.src.rpm http://people.redhat.com/zaitcev/ftp/iwhd/iwhd-0.98.41-1.z1.el6.x86_64.rpm
For now there is no user file, so only one user can be set, and it is set with -U flag. So, you would need to do something like this: echo 'IWHD_ARGS="-d localhost:27017 -l /var/log/iwhd.log \ -o -U user:password"' >> /etc/sysconfig/iwhd
The RPMs include the import timeout increased to 1 hour.
Please let me know how it goes.
Some quick tests indicate that all's well from Ruby-land!
I used a basic client that I'd written, started iwhd in OAuth mode (per the example you included), and verified the following:
Visit /images without any OAuth setup. I got a 401.
Visit /images with a bogus username. I got a 403.
Visit /images with the keys specified (I keep wanting to call them 'username' and 'password'). I got a 200 and the XML list of images.
The one thing that was a little weird is that if I used a correct consumer key but an incorrect consumer secret, Ruby raises an "EOFError: end of file reached" exception. I infer we're receiving something like an empty response as opposed to a reply indicating an authorization failure.
This is excellent news. Matt, Pete, you guys rock. Well done.
--Hugh
On Tue, 4 Oct 2011 15:36:54 -0400 Matt Wagner matt.wagner@redhat.com wrote:
The one thing that was a little weird is that if I used a correct consumer key but an incorrect consumer secret, Ruby raises an "EOFError: end of file reached" exception. I infer we're receiving something like an empty response as opposed to a reply indicating an authorization failure.
Indeed, sorry about that. It's my bug. The fix looks like this
diff --git a/rest.c b/rest.c index a7e9021..0b0ce8d 100644 --- a/rest.c +++ b/rest.c @@ -2800,7 +2800,9 @@ do_oauth (struct MHD_Connection *conn, my_state *ms,
if (strcmp(sig_supplied, sig_calculated) != 0) { DPRINTF("OAuth signature mismatch"); - return -1; + do_resp(conn,NULL,MHD_HTTP_FORBIDDEN,NULL, + "403 signature mismatch\r\n"); + return 1; } return 0; }
Thanks for the help!
-- pete
Pete Zaitcev wrote:
Guys:
Below is what I have now for 2-legged OAuth in iwhd. It actually works, with oauthtest2 from liboauth.
The patch is only a small piece, since it has no client, which we need to run the tests, has no documentation, and no user list. However, it retires most of the risk. We know that it can be done, this way. The biggest risk left is interoperability with Aeolus.
BTW, the OAuth header looks like this:
Authorization: OAuth realm="http://example.org/", oauth_consumer_key="chiaki", oauth_nonce="mitPzrhrCMHABJiYava_", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1317182031", oauth_version="1.0", oauth_signature="Ya36%2FkXTG5YATRx%2BWAXK9NXzZCg%3D"
-- Pete
diff --git a/configure.ac b/configure.ac index e23d1fa..8b115cb 100644 --- a/configure.ac +++ b/configure.ac @@ -70,6 +70,9 @@ AC_CHECK_LIB([uuid], [uuid_generate_random], [AC_MSG_ERROR([Missing required uuid lib])]) AC_SUBST([UUID_LIB])
+dnl How to check properly? +LIBS="$LIBS -loauth"
Thanks a lot, Pete. I've only just started looking through this, but will go ahead and send these suggested changes already. You are welcome to merge these into yours.
From 563a727b770f1a16390a3538492741278ebe31c4 Mon Sep 17 00:00:00 2001
From: Jim Meyering meyering@redhat.com Date: Thu, 29 Sep 2011 18:52:50 +0200 Subject: [PATCH 1/2] build-req
* iwhd.spec.in (BuildRequires): Add liboauth-devel --- iwhd.spec.in | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/iwhd.spec.in b/iwhd.spec.in index 24d97d6..6b9d1cd 100644 --- a/iwhd.spec.in +++ b/iwhd.spec.in @@ -21,6 +21,7 @@ BuildRequires: hail-devel BuildRequires: jansson-devel BuildRequires: libcurl-devel BuildRequires: libmicrohttpd-devel +BuildRequires: liboauth-devel BuildRequires: libxml2-devel BuildRequires: libuuid-devel BuildRequires: mongodb-devel -- 1.7.7.rc0.362.g5a14
From ce5aa582f9435dd6201bb0ff6732c15d0faa67b8 Mon Sep 17 00:00:00 2001
From: Jim Meyering meyering@redhat.com Date: Thu, 29 Sep 2011 19:01:41 +0200 Subject: [PATCH 2/2] add configure-time check for liboauth
* configure.ac (OAUTH_LIB): Check for liboauth. * Makefile.am (iwhd_LDADD): Use it. --- Makefile.am | 1 + configure.ac | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/Makefile.am b/Makefile.am index 7cf2e54..94510ed 100644 --- a/Makefile.am +++ b/Makefile.am @@ -154,6 +154,7 @@ iwhd_LDADD = \ $(BOOST_THREAD_LIB) \ $(CURL_LIB) \ $(JANSSON_LIB) \ + $(OAUTH_LIB) \ $(UHTTPD_LIB) \ $(PTHREAD_LIB) \ $(HAIL_LIBS) diff --git a/configure.ac b/configure.ac index 8b115cb..2cd667a 100644 --- a/configure.ac +++ b/configure.ac @@ -70,8 +70,10 @@ AC_CHECK_LIB([uuid], [uuid_generate_random], [AC_MSG_ERROR([Missing required uuid lib])]) AC_SUBST([UUID_LIB])
-dnl How to check properly? -LIBS="$LIBS -loauth" +AC_CHECK_LIB([oauth], [oauth_init_nss], + [OAUTH_LIB=-loauth], + [AC_MSG_ERROR([Missing required oauth lib])]) +AC_SUBST([OAUTH_LIB])
PKG_CHECK_MODULES([HAIL],[libhail >= 0.8]) AC_SUBST([HAIL_LIBS]) -- 1.7.7.rc0.362.g5a14
Pete Zaitcev wrote:
Below is what I have now for 2-legged OAuth in iwhd. It actually works, with oauthtest2 from liboauth.
The patch is only a small piece, since it has no client, which we need to run the tests, has no documentation, and no user list. However, it retires most of the risk. We know that it can be done, this way. The biggest risk left is interoperability with Aeolus.
BTW, the OAuth header looks like this:
Authorization: OAuth realm="http://example.org/", oauth_consumer_key="chiaki", oauth_nonce="mitPzrhrCMHABJiYava_", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1317182031", oauth_version="1.0", oauth_signature="Ya36%2FkXTG5YATRx%2BWAXK9NXzZCg%3D"
Hi Pete,
I noticed that sig_supplied and sig_calculated can both end up being NULL, either because they were absent or because strdup failed. Best not to dereference NULL ;-)
Here's one way to do it, just piggy-backing on the generic OAuth signature mismatch. It's probably better to diagnose things individually.
Actually, it would be better still to change the oauth_param function to let the caller distinguish between missing parameter and strdup failure. Those are so different that they deserve different diagnostics.
diff --git a/rest.c b/rest.c index 6d5a146..95d3138 100644 --- a/rest.c +++ b/rest.c @@ -2532,7 +2532,8 @@ do_oauth (struct MHD_Connection *conn, my_state *ms, } sig_calculated = oauth_param_val(oargv[x]);
- if (strcmp(sig_supplied, sig_calculated) != 0) { + if (sig_supplied == NULL || sig_calculated == NULL + || strcmp(sig_supplied, sig_calculated) != 0) { DPRINTF("OAuth signature mismatch"); return -1; }
aeolus-devel@lists.fedorahosted.org