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

Dependents:   heating gps

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:
Fri May 17 15:02:00 2019 +0000
Revision:
125:772948168e4f
Parent:
123:06de83222fda
Updated net library

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 125:772948168e4f 59 static void handleRequest(char* pStateData, int size, char* pRequestStream, uint32_t positionInRequestStream)
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 125:772948168e4f 119 static int pollSomethingToSend(char* pStateData, bool clientFinished) //returns 0 no; +1 yes; -1 finished;
andrewboyson 109:3e82f62c7e1f 120 {
andrewboyson 122:cd3f391ac8aa 121 struct state* pState = (struct state*)pStateData;
andrewboyson 122:cd3f391ac8aa 122
andrewboyson 125:772948168e4f 123 if (!pState->toDo)
andrewboyson 125:772948168e4f 124 {
andrewboyson 125:772948168e4f 125 if (clientFinished) return -1; //The client hasn't requested anything and never will so finish
andrewboyson 125:772948168e4f 126 else return 0; //The client hasn't requested anything yet but still could
andrewboyson 125:772948168e4f 127 }
andrewboyson 125:772948168e4f 128 if (!pState->postComplete ) return 0; //Wait for the request (usually a POST) to finish
andrewboyson 125:772948168e4f 129 if (!MsTimerAbsolute(pState->delayUntil)) return 0; //Wait a while (usually after a LOGIN attempt)
andrewboyson 125:772948168e4f 130
andrewboyson 125:772948168e4f 131 return 1; //Yep, set up anything (just TSL at the moment) then call sendReply
andrewboyson 125:772948168e4f 132 }
andrewboyson 125:772948168e4f 133
andrewboyson 125:772948168e4f 134 static bool sendReply(char* pStateData)
andrewboyson 125:772948168e4f 135 {
andrewboyson 125:772948168e4f 136 struct state* pState = (struct state*)pStateData;
andrewboyson 122:cd3f391ac8aa 137
andrewboyson 122:cd3f391ac8aa 138 int todo = pState->toDo; //Make a copy so that we don't modify todo in the state
andrewboyson 122:cd3f391ac8aa 139
andrewboyson 109:3e82f62c7e1f 140 //Check if todo includes the need to send a cookie
andrewboyson 109:3e82f62c7e1f 141 if (todo >= DO_SEND_SESSION_ID)
andrewboyson 109:3e82f62c7e1f 142 {
andrewboyson 109:3e82f62c7e1f 143 HttpOkCookieName = WebLoginSessionNameGet();
andrewboyson 109:3e82f62c7e1f 144 HttpOkCookieValue = WebLoginSessionIdGet();
andrewboyson 109:3e82f62c7e1f 145 HttpOkCookieMaxAge = WebLoginSessionNameLife();
andrewboyson 109:3e82f62c7e1f 146 todo -= DO_SEND_SESSION_ID;
andrewboyson 109:3e82f62c7e1f 147 }
andrewboyson 109:3e82f62c7e1f 148 else
andrewboyson 109:3e82f62c7e1f 149 {
andrewboyson 109:3e82f62c7e1f 150 HttpOkCookieName = NULL;
andrewboyson 109:3e82f62c7e1f 151 HttpOkCookieValue = NULL;
andrewboyson 109:3e82f62c7e1f 152 HttpOkCookieMaxAge = -1;
andrewboyson 109:3e82f62c7e1f 153 }
andrewboyson 109:3e82f62c7e1f 154
andrewboyson 110:8ab752842d25 155 reply(todo);
andrewboyson 122:cd3f391ac8aa 156
andrewboyson 123:06de83222fda 157 return !HttpBufFilled(); //If we haven't used a full buffer then we have finished
andrewboyson 109:3e82f62c7e1f 158 }
andrewboyson 109:3e82f62c7e1f 159
andrewboyson 109:3e82f62c7e1f 160 int WebInit()
andrewboyson 109:3e82f62c7e1f 161 {
andrewboyson 109:3e82f62c7e1f 162 HttpRequestFunction = handleRequest;
andrewboyson 125:772948168e4f 163 HttpPollFunction = pollSomethingToSend;
andrewboyson 125:772948168e4f 164 HttpReplyFunction = sendReply;
andrewboyson 109:3e82f62c7e1f 165
andrewboyson 109:3e82f62c7e1f 166 WebLoginInit();
andrewboyson 109:3e82f62c7e1f 167
andrewboyson 109:3e82f62c7e1f 168 return 0;
andrewboyson 109:3e82f62c7e1f 169 }