bool_t httpCheckPassword(HttpConnection *connection, const char_t *password, HttpAuthMode mode) { //This flag tells whether the password is valid bool_t status = FALSE; //Debug message TRACE_DEBUG("Password verification...\r\n"); #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED) //Basic authentication scheme? if(mode == HTTP_AUTH_MODE_BASIC) { //Point to the authentication credentials HttpAuthorizationHeader *auth = &connection->request.auth; //Make sure authentication credentials have been found if(auth->found && auth->mode == HTTP_AUTH_MODE_BASIC) { //Sanity check if(auth->password != NULL) { //Check whether the password is valid if(!strcmp(password, auth->password)) status = TRUE; } } } #endif #if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) //Digest authentication scheme? if(mode == HTTP_AUTH_MODE_DIGEST) { //Point to the authentication credentials HttpAuthorizationHeader *auth = &connection->request.auth; //Make sure authentication credentials have been found if(auth->found && auth->mode == HTTP_AUTH_MODE_DIGEST) { //Sanity check if(auth->realm != NULL && auth->realm != NULL && auth->nonce != NULL && auth->qop != NULL && auth->nc != NULL && auth->cnonce != NULL && auth->response != NULL) { error_t error; Md5Context *md5Context; char_t ha1[2 * MD5_DIGEST_SIZE + 1]; char_t ha2[2 * MD5_DIGEST_SIZE + 1]; //Allocate a memory buffer to hold the MD5 context md5Context = osAllocMem(sizeof(Md5Context)); //MD5 context successfully allocated? if(md5Context != NULL) { //Compute HA1 = MD5(username : realm : password) md5Init(md5Context); md5Update(md5Context, auth->user, strlen(auth->user)); md5Update(md5Context, ":", 1); md5Update(md5Context, auth->realm, strlen(auth->realm)); md5Update(md5Context, ":", 1); md5Update(md5Context, password, strlen(password)); md5Final(md5Context, NULL); //Convert MD5 hash to hex string httpConvertArrayToHexString(md5Context->digest, MD5_DIGEST_SIZE, ha1); //Debug message TRACE_DEBUG(" HA1: %s\r\n", ha1); //Compute HA2 = MD5(method : uri) md5Init(md5Context); md5Update(md5Context, connection->request.method, strlen(connection->request.method)); md5Update(md5Context, ":", 1); md5Update(md5Context, auth->uri, strlen(auth->uri)); md5Final(md5Context, NULL); //Convert MD5 hash to hex string httpConvertArrayToHexString(md5Context->digest, MD5_DIGEST_SIZE, ha2); //Debug message TRACE_DEBUG(" HA2: %s\r\n", ha2); //Compute MD5(HA1 : nonce : nc : cnonce : qop : HA1) md5Init(md5Context); md5Update(md5Context, ha1, strlen(ha1)); md5Update(md5Context, ":", 1); md5Update(md5Context, auth->nonce, strlen(auth->nonce)); md5Update(md5Context, ":", 1); md5Update(md5Context, auth->nc, strlen(auth->nc)); md5Update(md5Context, ":", 1); md5Update(md5Context, auth->cnonce, strlen(auth->cnonce)); md5Update(md5Context, ":", 1); md5Update(md5Context, auth->qop, strlen(auth->qop)); md5Update(md5Context, ":", 1); md5Update(md5Context, ha2, strlen(ha2)); md5Final(md5Context, NULL); //Convert MD5 hash to hex string httpConvertArrayToHexString(md5Context->digest, MD5_DIGEST_SIZE, ha1); //Debug message TRACE_DEBUG(" response: %s\r\n", ha1); //Release MD5 context osFreeMem(md5Context); //Check response if(!strcasecmp(auth->response, ha1)) { //Perform nonce verification error = httpVerifyNonce(connection->serverContext, auth->nonce, auth->nc); //Valid nonce? if(!error) { //Access to the resource is granted status = TRUE; } else { //The client may wish to simply retry the request with a //new encrypted response, without re-prompting the user //for a new username and password connection->response.auth.stale = TRUE; } } } } } } #endif //Return TRUE is the password is valid, else FALSE return status; }
error_t httpWriteHeader(HttpConnection *connection) { error_t error; uint_t i; char_t *p; //HTTP version 0.9? if(connection->response.version == HTTP_VERSION_0_9) { //Enforce default parameters connection->response.keepAlive = FALSE; connection->response.chunkedEncoding = FALSE; //The size of the response body is not limited connection->response.byteCount = UINT_MAX; //We are done since HTTP 0.9 does not support Full-Response format return NO_ERROR; } //When generating dynamic web pages with HTTP 1.0, the only way to //signal the end of the body is to close the connection if(connection->response.version == HTTP_VERSION_1_0 && connection->response.chunkedEncoding) { //Make the connection non persistent connection->response.keepAlive = FALSE; connection->response.chunkedEncoding = FALSE; //The size of the response body is not limited connection->response.byteCount = UINT_MAX; } else { //Limit the size of the response body connection->response.byteCount = connection->response.contentLength; } //Point to the beginning of the buffer p = connection->buffer; //The first line of a response message is the Status-Line, consisting //of the protocol version followed by a numeric status code and its //associated textual phrase p += sprintf(p, "HTTP/%u.%u %u ", MSB(connection->response.version), LSB(connection->response.version), connection->response.statusCode); //Retrieve the Reason-Phrase that corresponds to the Status-Code for(i = 0; i < arraysize(statusCodeList); i++) { //Check the status code if(statusCodeList[i].value == connection->response.statusCode) { //Append the textual phrase to the Status-Line p += sprintf(p, statusCodeList[i].message); //Break the loop and continue processing break; } } //Properly terminate the Status-Line p += sprintf(p, "\r\n"); //The Server response-header field contains information about the //software used by the origin server to handle the request p += sprintf(p, "Server: Oryx Embedded HTTP Server\r\n"); //Format Location field? if(connection->response.location != NULL) { //Set Location field p += sprintf(p, "Location: %s\r\n", connection->response.location); } //Persistent connection? if(connection->response.keepAlive) { //Set Connection field p += sprintf(p, "Connection: keep-alive\r\n"); //Set Keep-Alive field p += sprintf(p, "Keep-Alive: timeout=%u, max=%u\r\n", HTTP_SERVER_IDLE_TIMEOUT / 1000, HTTP_SERVER_MAX_REQUESTS); } else { //Set Connection field p += sprintf(p, "Connection: close\r\n"); } //Prevent the client from using cache? if(connection->response.noCache) { //Set Pragma field p += sprintf(p, "Pragma: no-cache\r\n"); //Set Cache-Control field p += sprintf(p, "Cache-Control: no-store, no-cache, must-revalidate\r\n"); p += sprintf(p, "Cache-Control: post-check=0, pre-check=0\r\n"); } #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED) //Use basic access authentication? if(connection->response.auth.mode == HTTP_AUTH_MODE_BASIC) { //Set WWW-Authenticate field p += sprintf(p, "WWW-Authenticate: Basic realm=\"Protected Area\"\r\n"); } #endif #if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) //Use digest access authentication? if(connection->response.auth.mode == HTTP_AUTH_MODE_DIGEST) { size_t n; uint8_t opaque[16]; //Set WWW-Authenticate field p += sprintf(p, "WWW-Authenticate: Digest\r\n"); p += sprintf(p, " realm=\"Protected Area\",\r\n"); p += sprintf(p, " qop=\"auth\",\r\n"); p += sprintf(p, " nonce=\""); //A server-specified data string which should be uniquely generated //each time a 401 response is made error = httpGenerateNonce(connection->serverContext, p, &n); //Any error to report? if(error) return error; //Advance pointer p += n; //Properly terminate the nonce string p += sprintf(p, "\",\r\n"); //Format opaque parameter p += sprintf(p, " opaque=\""); //Generate a random value error = connection->settings->prngAlgo->read( connection->settings->prngContext, opaque, 16); //Random number generation failed? if(error) return error; //Convert the byte array to hex string httpConvertArrayToHexString(opaque, 16, p); //Advance pointer p += 32; //Properly terminate the opaque string p += sprintf(p, "\""); //The STALE flag indicates that the previous request from the client //was rejected because the nonce value was stale if(connection->response.auth.stale) p += sprintf(p, ",\r\n stale=TRUE"); //Properly terminate the WWW-Authenticate field p += sprintf(p, "\r\n"); } #endif //Content type p += sprintf(p, "Content-Type: %s\r\n", connection->response.contentType); //Use chunked encoding transfer? if(connection->response.chunkedEncoding) { //Set Transfer-Encoding field p += sprintf(p, "Transfer-Encoding: chunked\r\n"); } //Persistent connection? else if(connection->response.keepAlive) { //Set Content-Length field p += sprintf(p, "Content-Length: %" PRIuSIZE "\r\n", connection->response.contentLength); } //Terminate the header with an empty line p += sprintf(p, "\r\n"); //Debug message TRACE_DEBUG("HTTP response header:\r\n%s", connection->buffer); //Send HTTP response header to the client error = httpSend(connection, connection->buffer, strlen(connection->buffer), HTTP_FLAG_DELAY); //Return status code return error; }
error_t httpGenerateNonce(HttpServerContext *context, char_t *output, size_t *length) { #if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) error_t error; uint_t i; HttpNonceCacheEntry *entry; HttpNonceCacheEntry *oldestEntry; uint8_t nonce[HTTP_SERVER_NONCE_SIZE]; //Acquire exclusive access to the nonce cache osAcquireMutex(&context->nonceCacheMutex); //Keep track of the oldest entry oldestEntry = &context->nonceCache[0]; //Loop through nonce cache entries for(i = 0; i < HTTP_SERVER_NONCE_CACHE_SIZE; i++) { //Point to the current entry entry = &context->nonceCache[i]; //Check whether the entry is currently in used or not if(!entry->count) break; //Keep track of the oldest entry in the table if(timeCompare(entry->timestamp, oldestEntry->timestamp) < 0) oldestEntry = entry; } //The oldest entry is removed whenever the table runs out of space if(i >= HTTP_SERVER_NONCE_CACHE_SIZE) entry = oldestEntry; //Generate a new nonce error = context->settings.prngAlgo->read(context->settings.prngContext, nonce, HTTP_SERVER_NONCE_SIZE); //Check status code if(!error) { //Convert the byte array to hex string httpConvertArrayToHexString(nonce, HTTP_SERVER_NONCE_SIZE, entry->nonce); //Clear nonce count entry->count = 1; //Save the time at which the nonce was generated entry->timestamp = osGetSystemTime(); //Copy the nonce to the output buffer strcpy(output, entry->nonce); //Return the length of the nonce excluding the NULL character *length = HTTP_SERVER_NONCE_SIZE * 2; } else { //Random number generation failed memset(entry, 0, sizeof(HttpNonceCacheEntry)); } //Release exclusive access to the nonce cache osReleaseMutex(&context->nonceCacheMutex); //Return status code return error; #else //Not implemented return ERROR_NOT_IMPLEMENTED; #endif }