Tuesday, December 10, 2013

DD-WRT, Afraid.org, and truncating base64 hashes

I've noticed that since 2010 that DD-WRT has had problems with correctly implementing Dynamic DNS authentication with Afraid.org.   You enter your username and password, but the authentication fails.   To get around this issue, you need to follow this workaround: http://www.dd-wrt.com/phpBB2/viewtopic.php?p=781615

I decided to poke into the DD-WRT source code to understand why this nuisance continues to persist to this day.  What did I find?  Inside router/httpd/validate.c, DD-WRT makes a call to http://freedns.afraid.org/api/?action=getdyndns&sha=[SHA] where [SHA] represents the SHA1 function of "username|password" (as an example of how this authentication process works, see https://github.com/atdt/afraid/blob/master/afraid/__init__.py#L94)).  The response, assuming you aren't using the Curl command-line, comes in this form:

DYNDNS_HOST|IP_ADDRESS|http://freedns.afraid.org/dynamic/update.php?HASH_VALUE

Afraid.org needs this hash value when DD-WRT reports its DNS location. The problem is that DD-WRT only assumes there are only 36 characters in the hash. The offending section is here: https://github.com/mirror/dd-wrt/blob/master/src/router/httpd/validate/webs.c#L3438

This pull request should fix this issue:

https://github.com/mirror/dd-wrt/pull/5/files

How did I verify?  Well, I saved the response from making an authenticated call to Afraid.org:

>>> open("/tmp/hash", "w").write(urllib.urlopen("http://freedns.afraid.org/api/?action=getdyndns&sha=xxxx").read())

Then I created and compiled this source code to verify:
#include <stdio.h>
#include <malloc.h> char *main() { int i; FILE *in = fopen("hash", "rb"); while (getc(in) != '?' && feof(in) == 0) ; i = 0; char *hash = malloc(64); if (feof(in)) { free(hash); return NULL; } for (i = 0; i < 63 && feof(in) == 0; i++) { hash[i] = getc(in); if (hash[i] == EOF) break; } fclose(in); hash[i] = 0; printf("%s", hash); return hash; }
Note in the bolded text that we have to break out of the loop if the character is EOF, since feof() will only exit after one extra loop.  We could also set the string terminator to be i-1 too (see Stack Overflow article about the challenges feof() presents)

No comments:

Post a Comment