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:
Wed Aug 28 07:12:39 2019 +0000
Revision:
128:fc9708e1d17c
Parent:
127:bd6dd135009d
Child:
129:6d9bffc72676
Added connection status

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