Webserver+3d print
cyclone_tcp/web_socket/web_socket_auth.c
- Committer:
- Sergunb
- Date:
- 2017-02-04
- Revision:
- 0:8918a71cdbe9
File content as of revision 0:8918a71cdbe9:
/** * @file web_socket_auth.c * @brief HTTP authentication for WebSockets * * @section License * * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. * * This file is part of CycloneTCP Open. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * @author Oryx Embedded SARL (www.oryx-embedded.com) * @version 1.7.6 **/ //Switch to the appropriate trace level #define TRACE_LEVEL WEB_SOCKET_TRACE_LEVEL //Dependencies #include <stdlib.h> #include "core/net.h" #include "web_socket/web_socket.h" #include "web_socket/web_socket_auth.h" #include "base64.h" #include "md5.h" #include "str.h" #include "debug.h" //Check TCP/IP stack configuration #if (WEB_SOCKET_SUPPORT == ENABLED) /** * @brief Parse WWW-Authenticate header field * @param[in] webSocket Handle to a WebSocket * @param[in] value NULL-terminated string that contains the value of header field * @return Error code **/ error_t webSocketParseAuthenticateField(WebSocket *webSocket, char_t *value) { #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) size_t n; char_t *p; char_t *token; char_t *separator; char_t *name; WebSocketAuthContext *authContext; //Point to the handshake context authContext = &webSocket->authContext; //Retrieve the authentication scheme token = strtok_r(value, " \t", &p); //Any parsing error? if(token == NULL) return ERROR_INVALID_SYNTAX; //Basic access authentication? if(!strcmp(token, "Basic")) { //Basic authentication is required by the WebSocket server authContext->requiredAuthMode = WS_AUTH_MODE_BASIC; } //Digest access authentication? else if(!strcasecmp(token, "Digest")) { //Digest authentication is required by the WebSocket server authContext->requiredAuthMode = WS_AUTH_MODE_DIGEST; } //Unknown authentication scheme? else { //Report an error return ERROR_INVALID_SYNTAX; } //Get the first parameter token = strtok_r(NULL, ",", &p); //Parse the WWW-Authenticate field while(token != NULL) { //Check whether a separator is present separator = strchr(token, '='); //Separator found? if(separator != NULL) { //Split the string *separator = '\0'; //Get field name and value name = strTrimWhitespace(token); value = strTrimWhitespace(separator + 1); //Retrieve the length of the value field n = strlen(value); //Discard the surrounding quotes if(n > 0 && value[n - 1] == '\"') value[n - 1] = '\0'; if(value[0] == '\"') value++; //Check parameter name if(!strcasecmp(name, "realm")) { //Save realm strSafeCopy(authContext->realm, value, WEB_SOCKET_REALM_MAX_LEN); } #if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) else if(!strcasecmp(name, "nonce")) { //Save nonce strSafeCopy(authContext->nonce, value, WEB_SOCKET_NONCE_MAX_LEN + 1); } else if(!strcasecmp(name, "opaque")) { //Save nonce strSafeCopy(authContext->opaque, value, WEB_SOCKET_OPAQUE_MAX_LEN + 1); } else if(!strcasecmp(name, "stale")) { //Save stale flag if(!strcasecmp(value, "true")) authContext->stale = TRUE; else authContext->stale = FALSE; } #endif //Get next parameter token = strtok_r(NULL, ",", &p); } } #endif //Successful processing return NO_ERROR; } /** * @brief Format Authorization header field * @param[in] webSocket Handle to a WebSocket * @param[out] output Buffer where to format the header field * @return Total length of the header field **/ size_t webSocketAddAuthorizationField(WebSocket *webSocket, char_t *output) { size_t n; #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) WebSocketAuthContext *authContext; //Point to the handshake context authContext = &webSocket->authContext; #endif #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED) //Basic authentication scheme? if(authContext->selectedAuthMode == WS_AUTH_MODE_BASIC) { size_t k; char_t *temp; //Temporary buffer temp = (char_t *) webSocket->rxContext.buffer; //Format Authorization header field n = sprintf(output, "Authorization: Basic "); //The client sends the userid and password, separated by a single colon //character, within a Base64 encoded string in the credentials k = sprintf(temp, "%s:%s", authContext->username, authContext->password); //Encode the resulting string using Base64 base64Encode(temp, k, output + n, &k); //Update the total length of the header field n += k; //Properly terminate the Authorization header field n += sprintf(output + n, "\r\n"); } else #endif #if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) //Digest authentication scheme? if(authContext->selectedAuthMode == WS_AUTH_MODE_DIGEST) { Md5Context md5Context; char_t ha1[2 * MD5_DIGEST_SIZE + 1]; char_t ha2[2 * MD5_DIGEST_SIZE + 1]; char_t nc[9]; //Count of the number of requests (including the current request) //that the client has sent with the nonce value in this request authContext->nc++; //Convert the value to hex string sprintf(nc, "%08x", authContext->nc); //Compute HA1 = MD5(username : realm : password) md5Init(&md5Context); md5Update(&md5Context, authContext->username, strlen(authContext->username)); md5Update(&md5Context, ":", 1); md5Update(&md5Context, authContext->realm, strlen(authContext->realm)); md5Update(&md5Context, ":", 1); md5Update(&md5Context, authContext->password, strlen(authContext->password)); md5Final(&md5Context, NULL); //Convert MD5 hash to hex string webSocketConvertArrayToHexString(md5Context.digest, MD5_DIGEST_SIZE, ha1); //Debug message TRACE_DEBUG(" HA1: %s\r\n", ha1); //Compute HA2 = MD5(method : uri) md5Init(&md5Context); md5Update(&md5Context, "GET", 3); md5Update(&md5Context, ":", 1); md5Update(&md5Context, webSocket->uri, strlen(webSocket->uri)); md5Final(&md5Context, NULL); //Convert MD5 hash to hex string webSocketConvertArrayToHexString(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, authContext->nonce, strlen(authContext->nonce)); md5Update(&md5Context, ":", 1); md5Update(&md5Context, nc, strlen(nc)); md5Update(&md5Context, ":", 1); md5Update(&md5Context, authContext->cnonce, strlen(authContext->cnonce)); md5Update(&md5Context, ":", 1); md5Update(&md5Context, "auth", 4); md5Update(&md5Context, ":", 1); md5Update(&md5Context, ha2, strlen(ha2)); md5Final(&md5Context, NULL); //Convert MD5 hash to hex string webSocketConvertArrayToHexString(md5Context.digest, MD5_DIGEST_SIZE, ha1); //Debug message TRACE_DEBUG(" response: %s\r\n", ha1); //Format Authorization header field n = sprintf(output, "Authorization: Digest\r\n"); //Username n += sprintf(output + n, " username=\"%s\",\r\n", authContext->username); //Realm n += sprintf(output + n, " realm=\"%s\",\r\n", authContext->realm); //Nonce value n += sprintf(output + n, " nonce=\"%s\",\r\n", authContext->nonce); //URI n += sprintf(output + n, " uri=\"%s\",\r\n", webSocket->uri); //Quality of protection n += sprintf(output + n, " qop=\"auth\",\r\n"); //Nonce count n += sprintf(output + n, " nc=\"%08x\",\r\n", authContext->nc); //Cnonce value n += sprintf(output + n, " cnonce=\"%s\",\r\n", authContext->cnonce); //Response n += sprintf(output + n, " response=\"%s\",\r\n", ha1); //Opaque parameter n += sprintf(output + n, " opaque=\"%s\",\r\n", authContext->opaque); } else #endif //Unknown authentication scheme? { //No need to add the Authorization header field n = 0; } //Return the total length of the Authorization header field return n; } /** * @brief Convert byte array to hex string * @param[in] input Point to the byte array * @param[in] inputLength Length of the byte array * @param[out] output NULL-terminated string resulting from the conversion * @return Error code **/ void webSocketConvertArrayToHexString(const uint8_t *input, size_t inputLength, char_t *output) { size_t i; //Hex conversion table static const char_t hexDigit[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; //Process byte array for(i = 0; i < inputLength; i++) { //Convert upper nibble output[i * 2] = hexDigit[(input[i] >> 4) & 0x0F]; //Then convert lower nibble output[i * 2 + 1] = hexDigit[input[i] & 0x0F]; } //Properly terminate the string with a NULL character output[i * 2] = '\0'; } #endif