From 40c784169ecaef20b626c00772b9f075e1a08de7 Mon Sep 17 00:00:00 2001 From: Jeremy Tan Date: Thu, 24 Oct 2013 19:17:27 +0800 Subject: [PATCH] Add POST handling and URLDecode POST/GET request data. Switch Login to use POST instead of GET. Hopefully this doesn't introduce any new bugs. --- server/fastcgi.c | 63 ++++++++++++++++++++++------ server/fastcgi.h | 1 + server/login.c | 7 +++- server/sensor.c | 2 +- testing/fastcgi-approach/post/post.c | 53 +++++++++++++++++++++++ 5 files changed, 110 insertions(+), 16 deletions(-) create mode 100644 testing/fastcgi-approach/post/post.c diff --git a/server/fastcgi.c b/server/fastcgi.c index 76ce61f..19d2869 100644 --- a/server/fastcgi.c +++ b/server/fastcgi.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "common.h" #include "sensor.h" @@ -537,6 +538,40 @@ char *FCGI_EscapeText(char *buf) return buf; } +/** + * Unescapes a URL encoded string in-place. The string + * must be NULL terminated. + * (e.g this%2d+string --> this- string) + * @param buf The buffer to decode. Will be modified in-place. + * @return The same buffer. + */ +char *FCGI_URLDecode(char *buf) +{ + char *head = buf, *tail = buf; + char hex[3] = {0}; + + while (*tail) { + if (*tail == '%') { //%hh hex to char + tail++; + if (isxdigit(*tail) && isxdigit(*(tail+1))) { + hex[0] = *tail++; + hex[1] = *tail++; + *head++ = (char)strtol(hex, NULL, 16); + } else { //Not valid format; keep original + head++; + } + } else if (*tail == '+') { //Plus to space + tail++; + *head++ = ' '; + } else { //Anything else + *head++ = *tail++; + } + } + *head = 0; //NULL-terminate at new end point + + return buf; +} + /** * Main FCGI request loop that receives/responds to client requests. * @param data Reserved. @@ -555,7 +590,13 @@ void * FCGI_RequestLoop (void *data) //strncpy doesn't zero-truncate properly snprintf(module, BUFSIZ, "%s", getenv("DOCUMENT_URI_LOCAL")); - snprintf(params, BUFSIZ, "%s", getenv("QUERY_STRING")); + + //Read from post body. If not empty, try GET instead. + if (fgets(params, BUFSIZ, stdin) == NULL || *params == '\0') { + snprintf(params, BUFSIZ, "%s", getenv("QUERY_STRING")); + } + //URL decode the parameters + FCGI_URLDecode(params); FCGI_GetControlCookie(context.received_key); Log(LOGDEBUG, "Got request #%d - Module %s, params %s", context.response_number, module, params); @@ -592,26 +633,23 @@ void * FCGI_RequestLoop (void *data) context.current_module = module; context.response_number++; - if (module_handler) - { - if (module_handler != Login_Handler && module_handler != IdentifyHandler && module_handler) - //if (false) // Testing - { + if (module_handler) { + if (module_handler == IdentifyHandler) { + FCGI_EscapeText(params); + } else if (module_handler != Login_Handler) { if (!FCGI_HasControl(&context)) { - if (g_options.auth_method == AUTH_NONE) - { //:( + if (g_options.auth_method == AUTH_NONE) { //:( Log(LOGWARN, "Locking control (no auth!)"); FCGI_LockControl(&context, NOAUTH_USERNAME, USER_ADMIN); FCGI_SendControlCookie(&context, true); } - else - { + else { FCGI_RejectJSON(&context, "Please login. Invalid control key."); continue; } } - + //Escape all special characters. //Don't escape for login (password may have special chars?) FCGI_EscapeText(params); @@ -619,8 +657,7 @@ void * FCGI_RequestLoop (void *data) module_handler(&context, params); } - else - { + else { FCGI_RejectJSON(&context, "Unhandled module"); } } diff --git a/server/fastcgi.h b/server/fastcgi.h index 5826ece..a417a43 100644 --- a/server/fastcgi.h +++ b/server/fastcgi.h @@ -85,6 +85,7 @@ extern void FCGI_JSONKey(const char *key); extern void FCGI_PrintRaw(const char *format, ...); extern void FCGI_EndJSON(); extern void FCGI_RejectJSONEx(FCGIContext *context, StatusCodes status, const char *description); +extern char *FCGI_URLDecode(char *buf); extern char *FCGI_EscapeText(char *buf); extern void *FCGI_RequestLoop (void *data); diff --git a/server/login.c b/server/login.c index 4d3d7fd..d6d7748 100644 --- a/server/login.c +++ b/server/login.c @@ -178,7 +178,10 @@ void Logout_Handler(FCGIContext * context, char * params) /** * Handle a Login Request * @param context - The context - * @param params - Parameter string, should contain username and password + * @param params - Parameter string, should contain username and password. + * NOTE: Care should be taken when using params, as it is + * completely unescaped. Do not log or use it without + * suitable escaping. */ void Login_Handler(FCGIContext * context, char * params) { @@ -220,7 +223,7 @@ void Login_Handler(FCGIContext * context, char * params) case AUTH_LDAP: { - if (strlen(pass) <= 0) + if (*pass == '\0') { FCGI_RejectJSON(context, "No password supplied."); return; diff --git a/server/sensor.c b/server/sensor.c index d146905..53b3f03 100644 --- a/server/sensor.c +++ b/server/sensor.c @@ -47,7 +47,7 @@ int Sensor_Add(const char * name, int user_id, ReadFn read, InitFn init, CleanFn s->init = init; // Set init function // Start by averaging values taken over a second - DOUBLE_TO_TIMEVAL(1e-3, &(s->sample_time)); + DOUBLE_TO_TIMEVAL(1, &(s->sample_time)); s->averages = 1; s->num_read = 0; diff --git a/testing/fastcgi-approach/post/post.c b/testing/fastcgi-approach/post/post.c new file mode 100644 index 0000000..7cd803d --- /dev/null +++ b/testing/fastcgi-approach/post/post.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +char *FCGI_URLDecode(char *buf); + +int main() { + while (FCGI_Accept() >= 0) { + char buf[BUFSIZ]; + printf("Content-type: text/plain\r\n\r\n"); + + + while(fgets(buf, BUFSIZ, stdin)) { + printf("POST (raw):\r\n"); + printf("%s", buf); + printf("\r\nPOST (decoded):\r\n"); + printf("%s", FCGI_URLDecode(buf)); + } + + snprintf(buf, BUFSIZ, "%s", getenv("QUERY_STRING")); + printf("\r\nGET (raw):\r\n"); + printf("%s", getenv("QUERY_STRING")); + + printf("\r\nGET (decoded):\r\n"); + printf("%s", FCGI_URLDecode(buf)); + } + return 0; + +} + +char *FCGI_URLDecode(char *buf) { + char *head = buf, *tail = buf; + char hex[3] = {0}; + while (*tail) { + if (*tail == '%') { + tail++; + if (isxdigit(*tail) && isxdigit(*(tail+1))) { + hex[0] = *tail++; + hex[1] = *tail++; + *head++ = (char)strtol(hex, NULL, 16); + } else { + head++; + } + } else if (*tail == '+') { + tail++; + *head++ = ' '; + } else { + *head++ = *tail++; + } + } + *head = 0; + return buf; +} -- 2.20.1