Common stuff for all my devices' web server pages: css, login, log, ipv4, ipv6, firmware update, clock, reset info etc.

Dependents:   oldheating gps motorhome heating

Security

A password has to be set whenever there has been a software reset. Resets following faults or power on do not require a new password as the hash is restored from the RTC GPREG register.

The password is not saved on the device; instead a 32 bit hash of the password is saved. It would take 2^31 attempts to brute force the password: this could be done in under a month if an attempt were possible every millisecond. To prevent this a 200 ms delay is introduced in the reply to the login form, that gives a more reasonable 13 years to brute force the password.

Once the password is accepted a random session id is created. This is 36 bit to give six base 64 characters but without an extra delay. If an attempt could be made every ms then this would still take over a year to brute force.

The most likely attack would to use a dictionary with, say, 10 million entries against the password which would still take 20 days to do.

Committer:
andrewboyson
Date:
Sun May 12 17:18:19 2019 +0000
Revision:
122:cd3f391ac8aa
Parent:
112:f29bb9b99059
Child:
123:06de83222fda
Updated http

Who changed what in which revision?

UserRevisionLine numberNew contents of line
andrewboyson 109:3e82f62c7e1f 1 #include "http.h"
andrewboyson 109:3e82f62c7e1f 2 #include "web-server-base.h"
andrewboyson 112:f29bb9b99059 3 #include "web-server-this.h"
andrewboyson 110:8ab752842d25 4 #include "web.h"
andrewboyson 110:8ab752842d25 5 #include "web-pages-base.h"
andrewboyson 109:3e82f62c7e1f 6 #include "mstimer.h"
andrewboyson 109:3e82f62c7e1f 7
andrewboyson 109:3e82f62c7e1f 8 #define LOGIN_DELAY_MS 200
andrewboyson 109:3e82f62c7e1f 9
andrewboyson 110:8ab752842d25 10 #define DO_LOGIN DO_SERVER + 0
andrewboyson 110:8ab752842d25 11
andrewboyson 122:cd3f391ac8aa 12 struct state
andrewboyson 122:cd3f391ac8aa 13 {
andrewboyson 122:cd3f391ac8aa 14 int toDo;
andrewboyson 122:cd3f391ac8aa 15 bool postComplete;
andrewboyson 122:cd3f391ac8aa 16 uint32_t delayUntil;
andrewboyson 122:cd3f391ac8aa 17 };
andrewboyson 122:cd3f391ac8aa 18
andrewboyson 110:8ab752842d25 19 static int decideWhatToDo(char *pPath, char* pLastModified)
andrewboyson 110:8ab752842d25 20 {
andrewboyson 110:8ab752842d25 21 if (HttpSameStr(pPath, "/login")) return DO_LOGIN;
andrewboyson 110:8ab752842d25 22
andrewboyson 110:8ab752842d25 23 int todo;
andrewboyson 112:f29bb9b99059 24 todo = WebServerBaseDecideWhatToDo(pPath, pLastModified); if (todo != DO_NOT_FOUND) return todo;
andrewboyson 112:f29bb9b99059 25 todo = WebServerThisDecideWhatToDo(pPath, pLastModified); if (todo != DO_NOT_FOUND) return todo;
andrewboyson 110:8ab752842d25 26 return DO_NOT_FOUND;
andrewboyson 110:8ab752842d25 27 }
andrewboyson 110:8ab752842d25 28 static void handleQuery(int todo, char* pQuery)
andrewboyson 110:8ab752842d25 29 {
andrewboyson 110:8ab752842d25 30 switch (todo)
andrewboyson 110:8ab752842d25 31 {
andrewboyson 110:8ab752842d25 32 case DO_LOGIN: WebLoginQuery (pQuery); return;
andrewboyson 110:8ab752842d25 33 }
andrewboyson 112:f29bb9b99059 34 if (WebServerBaseHandleQuery(todo, pQuery)) return;
andrewboyson 112:f29bb9b99059 35 if (WebServerThisHandleQuery(todo, pQuery)) return;
andrewboyson 110:8ab752842d25 36 }
andrewboyson 110:8ab752842d25 37 static void handlePost(int todo, int contentLength, int contentStart, int size, char* pRequestStream, uint32_t positionInRequestStream, bool* pComplete)
andrewboyson 110:8ab752842d25 38 {
andrewboyson 112:f29bb9b99059 39 if (WebServerBasePost(todo, contentLength, contentStart, size, pRequestStream, positionInRequestStream, pComplete)) return;
andrewboyson 112:f29bb9b99059 40 if (WebServerThisPost(todo, contentLength, contentStart, size, pRequestStream, positionInRequestStream, pComplete)) return;
andrewboyson 111:aaa858678e34 41 *pComplete = true;
andrewboyson 110:8ab752842d25 42 }
andrewboyson 110:8ab752842d25 43
andrewboyson 110:8ab752842d25 44 static void reply(int todo)
andrewboyson 110:8ab752842d25 45 {
andrewboyson 110:8ab752842d25 46 //Try all the base modules
andrewboyson 110:8ab752842d25 47 switch (todo)
andrewboyson 110:8ab752842d25 48 {
andrewboyson 110:8ab752842d25 49 case DO_LOGIN: WebLoginHtml (); return;
andrewboyson 110:8ab752842d25 50 case DO_NOT_FOUND: HttpNotFound (); return;
andrewboyson 110:8ab752842d25 51 case DO_NOT_MODIFIED: HttpNotModified (); return;
andrewboyson 110:8ab752842d25 52 }
andrewboyson 110:8ab752842d25 53
andrewboyson 110:8ab752842d25 54 //If not called then call the derived (child) module
andrewboyson 112:f29bb9b99059 55 if (WebServerBaseReply(todo)) return;
andrewboyson 112:f29bb9b99059 56 if (WebServerThisReply(todo)) return;
andrewboyson 110:8ab752842d25 57 }
andrewboyson 110:8ab752842d25 58
andrewboyson 122:cd3f391ac8aa 59 static void handleRequest(int size, char* pRequestStream, uint32_t positionInRequestStream, char* pStateData)
andrewboyson 109:3e82f62c7e1f 60 {
andrewboyson 122:cd3f391ac8aa 61 struct state* pState = (struct state*)pStateData;
andrewboyson 122:cd3f391ac8aa 62
andrewboyson 122:cd3f391ac8aa 63 pState->delayUntil = MsTimerCount; //Default to no delay unless modified;
andrewboyson 122:cd3f391ac8aa 64
andrewboyson 109:3e82f62c7e1f 65 //Handle request for the first packet of data received but leave todo the same after that.
andrewboyson 109:3e82f62c7e1f 66 int contentLength = 0;
andrewboyson 109:3e82f62c7e1f 67 int contentStart = 0;
andrewboyson 109:3e82f62c7e1f 68 if (size && positionInRequestStream == 0)
andrewboyson 109:3e82f62c7e1f 69 {
andrewboyson 109:3e82f62c7e1f 70 //Read the headers
andrewboyson 109:3e82f62c7e1f 71 char* pMethod;
andrewboyson 109:3e82f62c7e1f 72 char* pPath;
andrewboyson 109:3e82f62c7e1f 73 char* pQuery;
andrewboyson 109:3e82f62c7e1f 74 char* pLastModified;
andrewboyson 109:3e82f62c7e1f 75 char* pCookies;
andrewboyson 109:3e82f62c7e1f 76 contentStart = HttpRequestRead(pRequestStream, size, &pMethod, &pPath, &pQuery, &pLastModified, &pCookies, &contentLength);
andrewboyson 109:3e82f62c7e1f 77
andrewboyson 109:3e82f62c7e1f 78 //Ask the web server what to do
andrewboyson 122:cd3f391ac8aa 79 pState->toDo = decideWhatToDo(pPath, pLastModified);
andrewboyson 109:3e82f62c7e1f 80
andrewboyson 109:3e82f62c7e1f 81 //If what to do is NOTHING, NOT_FOUND or NOT_MODIFIED then no query or post will be valid so stop now
andrewboyson 122:cd3f391ac8aa 82 if (pState->toDo < DO_LOGIN) { pState->postComplete = true; return; }
andrewboyson 109:3e82f62c7e1f 83
andrewboyson 109:3e82f62c7e1f 84 //If what to do is LOGIN then the user has just returned the login form
andrewboyson 122:cd3f391ac8aa 85 if (pState->toDo == DO_LOGIN)
andrewboyson 109:3e82f62c7e1f 86 {
andrewboyson 122:cd3f391ac8aa 87 handleQuery(pState->toDo, pQuery); //Read the password and the original location
andrewboyson 109:3e82f62c7e1f 88 if (WebLoginQueryPasswordOk)
andrewboyson 109:3e82f62c7e1f 89 {
andrewboyson 109:3e82f62c7e1f 90 if (!WebLoginSessionIdIsSet()) //If there isn't a session id already
andrewboyson 109:3e82f62c7e1f 91 {
andrewboyson 109:3e82f62c7e1f 92 WebLoginSessionIdNew(); //Create a new session id
andrewboyson 109:3e82f62c7e1f 93 }
andrewboyson 122:cd3f391ac8aa 94 pState->toDo = WebLoginOriginalToDo; //Load the original todo and SEND_SESSION_ID
andrewboyson 122:cd3f391ac8aa 95 pState->toDo += DO_SEND_SESSION_ID;
andrewboyson 109:3e82f62c7e1f 96 }
andrewboyson 122:cd3f391ac8aa 97 pState->delayUntil = MsTimerCount + LOGIN_DELAY_MS; //To prevent brute forcing the hash delay the reply to the login
andrewboyson 122:cd3f391ac8aa 98 pState->postComplete = true;
andrewboyson 109:3e82f62c7e1f 99 return; //Either way no query or post will be valid
andrewboyson 109:3e82f62c7e1f 100 }
andrewboyson 109:3e82f62c7e1f 101
andrewboyson 109:3e82f62c7e1f 102 //Have a normal request so authenticate
andrewboyson 109:3e82f62c7e1f 103 if (!WebLoginCookiesContainValidSessionId(pCookies))
andrewboyson 109:3e82f62c7e1f 104 {
andrewboyson 122:cd3f391ac8aa 105 WebLoginOriginalToDo = pState->toDo; //Record the original destination for redirection
andrewboyson 122:cd3f391ac8aa 106 pState->toDo = DO_LOGIN;
andrewboyson 122:cd3f391ac8aa 107 pState->postComplete = true;
andrewboyson 109:3e82f62c7e1f 108 return; //Ignore any query or post as the user is not authenticated
andrewboyson 109:3e82f62c7e1f 109 }
andrewboyson 109:3e82f62c7e1f 110
andrewboyson 109:3e82f62c7e1f 111 //Handle the query
andrewboyson 122:cd3f391ac8aa 112 handleQuery(pState->toDo, pQuery);
andrewboyson 109:3e82f62c7e1f 113 }
andrewboyson 109:3e82f62c7e1f 114
andrewboyson 109:3e82f62c7e1f 115 //Do the upload of anything that needs it. Todos it doesn't understand are ignored.
andrewboyson 122:cd3f391ac8aa 116 if (!pState->postComplete) handlePost(pState->toDo, contentLength, contentStart, size, pRequestStream, positionInRequestStream, &pState->postComplete);
andrewboyson 109:3e82f62c7e1f 117 }
andrewboyson 109:3e82f62c7e1f 118
andrewboyson 122:cd3f391ac8aa 119 static int sendReply(char* pStateData)
andrewboyson 109:3e82f62c7e1f 120 {
andrewboyson 122:cd3f391ac8aa 121 struct state* pState = (struct state*)pStateData;
andrewboyson 122:cd3f391ac8aa 122
andrewboyson 122:cd3f391ac8aa 123 //0: not started; +1: started; -1: wait
andrewboyson 122:cd3f391ac8aa 124 if (!pState->toDo ) return 0; //return not started
andrewboyson 122:cd3f391ac8aa 125 if (!pState->postComplete ) return -1; //return started but without adding any data
andrewboyson 122:cd3f391ac8aa 126 if (!MsTimerAbsolute(pState->delayUntil)) return -1; //return started but without adding any data
andrewboyson 122:cd3f391ac8aa 127
andrewboyson 122:cd3f391ac8aa 128 int todo = pState->toDo; //Make a copy so that we don't modify todo in the state
andrewboyson 122:cd3f391ac8aa 129
andrewboyson 109:3e82f62c7e1f 130 //Check if todo includes the need to send a cookie
andrewboyson 109:3e82f62c7e1f 131 if (todo >= DO_SEND_SESSION_ID)
andrewboyson 109:3e82f62c7e1f 132 {
andrewboyson 109:3e82f62c7e1f 133 HttpOkCookieName = WebLoginSessionNameGet();
andrewboyson 109:3e82f62c7e1f 134 HttpOkCookieValue = WebLoginSessionIdGet();
andrewboyson 109:3e82f62c7e1f 135 HttpOkCookieMaxAge = WebLoginSessionNameLife();
andrewboyson 109:3e82f62c7e1f 136 todo -= DO_SEND_SESSION_ID;
andrewboyson 109:3e82f62c7e1f 137 }
andrewboyson 109:3e82f62c7e1f 138 else
andrewboyson 109:3e82f62c7e1f 139 {
andrewboyson 109:3e82f62c7e1f 140 HttpOkCookieName = NULL;
andrewboyson 109:3e82f62c7e1f 141 HttpOkCookieValue = NULL;
andrewboyson 109:3e82f62c7e1f 142 HttpOkCookieMaxAge = -1;
andrewboyson 109:3e82f62c7e1f 143 }
andrewboyson 109:3e82f62c7e1f 144
andrewboyson 110:8ab752842d25 145 reply(todo);
andrewboyson 122:cd3f391ac8aa 146
andrewboyson 122:cd3f391ac8aa 147 return +1; //return started after adding data
andrewboyson 109:3e82f62c7e1f 148 }
andrewboyson 109:3e82f62c7e1f 149
andrewboyson 109:3e82f62c7e1f 150 int WebInit()
andrewboyson 109:3e82f62c7e1f 151 {
andrewboyson 109:3e82f62c7e1f 152 HttpRequestFunction = handleRequest;
andrewboyson 109:3e82f62c7e1f 153 HttpReplyFunction = sendReply;
andrewboyson 109:3e82f62c7e1f 154
andrewboyson 109:3e82f62c7e1f 155 WebLoginInit();
andrewboyson 109:3e82f62c7e1f 156
andrewboyson 109:3e82f62c7e1f 157 return 0;
andrewboyson 109:3e82f62c7e1f 158 }