Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
web_socket_auth.c
00001 /** 00002 * @file web_socket_auth.c 00003 * @brief HTTP authentication for WebSockets 00004 * 00005 * @section License 00006 * 00007 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. 00008 * 00009 * This file is part of CycloneTCP Open. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU General Public License 00013 * as published by the Free Software Foundation; either version 2 00014 * of the License, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software Foundation, 00023 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 * 00025 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00026 * @version 1.7.6 00027 **/ 00028 00029 //Switch to the appropriate trace level 00030 #define TRACE_LEVEL WEB_SOCKET_TRACE_LEVEL 00031 00032 //Dependencies 00033 #include <stdlib.h> 00034 #include "core/net.h" 00035 #include "web_socket/web_socket.h" 00036 #include "web_socket/web_socket_auth.h" 00037 #include "base64.h" 00038 #include "md5.h" 00039 #include "str.h" 00040 #include "debug.h" 00041 00042 //Check TCP/IP stack configuration 00043 #if (WEB_SOCKET_SUPPORT == ENABLED) 00044 00045 00046 /** 00047 * @brief Parse WWW-Authenticate header field 00048 * @param[in] webSocket Handle to a WebSocket 00049 * @param[in] value NULL-terminated string that contains the value of header field 00050 * @return Error code 00051 **/ 00052 00053 error_t webSocketParseAuthenticateField(WebSocket *webSocket, char_t *value) 00054 { 00055 #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) 00056 size_t n; 00057 char_t *p; 00058 char_t *token; 00059 char_t *separator; 00060 char_t *name; 00061 WebSocketAuthContext *authContext; 00062 00063 //Point to the handshake context 00064 authContext = &webSocket->authContext; 00065 00066 //Retrieve the authentication scheme 00067 token = strtok_r(value, " \t", &p); 00068 00069 //Any parsing error? 00070 if(token == NULL) 00071 return ERROR_INVALID_SYNTAX; 00072 00073 //Basic access authentication? 00074 if(!strcmp(token, "Basic")) 00075 { 00076 //Basic authentication is required by the WebSocket server 00077 authContext->requiredAuthMode = WS_AUTH_MODE_BASIC; 00078 } 00079 //Digest access authentication? 00080 else if(!strcasecmp(token, "Digest")) 00081 { 00082 //Digest authentication is required by the WebSocket server 00083 authContext->requiredAuthMode = WS_AUTH_MODE_DIGEST; 00084 } 00085 //Unknown authentication scheme? 00086 else 00087 { 00088 //Report an error 00089 return ERROR_INVALID_SYNTAX; 00090 } 00091 00092 //Get the first parameter 00093 token = strtok_r(NULL, ",", &p); 00094 00095 //Parse the WWW-Authenticate field 00096 while(token != NULL) 00097 { 00098 //Check whether a separator is present 00099 separator = strchr(token, '='); 00100 00101 //Separator found? 00102 if(separator != NULL) 00103 { 00104 //Split the string 00105 *separator = '\0'; 00106 00107 //Get field name and value 00108 name = strTrimWhitespace(token); 00109 value = strTrimWhitespace(separator + 1); 00110 00111 //Retrieve the length of the value field 00112 n = strlen(value); 00113 00114 //Discard the surrounding quotes 00115 if(n > 0 && value[n - 1] == '\"') 00116 value[n - 1] = '\0'; 00117 if(value[0] == '\"') 00118 value++; 00119 00120 //Check parameter name 00121 if(!strcasecmp(name, "realm")) 00122 { 00123 //Save realm 00124 strSafeCopy(authContext->realm, value, WEB_SOCKET_REALM_MAX_LEN); 00125 } 00126 #if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) 00127 else if(!strcasecmp(name, "nonce")) 00128 { 00129 //Save nonce 00130 strSafeCopy(authContext->nonce, value, WEB_SOCKET_NONCE_MAX_LEN + 1); 00131 } 00132 else if(!strcasecmp(name, "opaque")) 00133 { 00134 //Save nonce 00135 strSafeCopy(authContext->opaque, value, WEB_SOCKET_OPAQUE_MAX_LEN + 1); 00136 } 00137 else if(!strcasecmp(name, "stale")) 00138 { 00139 //Save stale flag 00140 if(!strcasecmp(value, "true")) 00141 authContext->stale = TRUE; 00142 else 00143 authContext->stale = FALSE; 00144 } 00145 #endif 00146 00147 //Get next parameter 00148 token = strtok_r(NULL, ",", &p); 00149 } 00150 } 00151 #endif 00152 00153 //Successful processing 00154 return NO_ERROR; 00155 } 00156 00157 00158 /** 00159 * @brief Format Authorization header field 00160 * @param[in] webSocket Handle to a WebSocket 00161 * @param[out] output Buffer where to format the header field 00162 * @return Total length of the header field 00163 **/ 00164 00165 size_t webSocketAddAuthorizationField(WebSocket *webSocket, char_t *output) 00166 { 00167 size_t n; 00168 00169 #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) 00170 WebSocketAuthContext *authContext; 00171 00172 //Point to the handshake context 00173 authContext = &webSocket->authContext; 00174 #endif 00175 00176 #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED) 00177 //Basic authentication scheme? 00178 if(authContext->selectedAuthMode == WS_AUTH_MODE_BASIC) 00179 { 00180 size_t k; 00181 char_t *temp; 00182 00183 //Temporary buffer 00184 temp = (char_t *) webSocket->rxContext.buffer; 00185 00186 //Format Authorization header field 00187 n = sprintf(output, "Authorization: Basic "); 00188 00189 //The client sends the userid and password, separated by a single colon 00190 //character, within a Base64 encoded string in the credentials 00191 k = sprintf(temp, "%s:%s", authContext->username, 00192 authContext->password); 00193 00194 //Encode the resulting string using Base64 00195 base64Encode(temp, k, output + n, &k); 00196 //Update the total length of the header field 00197 n += k; 00198 00199 //Properly terminate the Authorization header field 00200 n += sprintf(output + n, "\r\n"); 00201 } 00202 else 00203 #endif 00204 #if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) 00205 //Digest authentication scheme? 00206 if(authContext->selectedAuthMode == WS_AUTH_MODE_DIGEST) 00207 { 00208 Md5Context md5Context; 00209 char_t ha1[2 * MD5_DIGEST_SIZE + 1]; 00210 char_t ha2[2 * MD5_DIGEST_SIZE + 1]; 00211 char_t nc[9]; 00212 00213 //Count of the number of requests (including the current request) 00214 //that the client has sent with the nonce value in this request 00215 authContext->nc++; 00216 00217 //Convert the value to hex string 00218 sprintf(nc, "%08x", authContext->nc); 00219 00220 //Compute HA1 = MD5(username : realm : password) 00221 md5Init(&md5Context); 00222 md5Update(&md5Context, authContext->username, strlen(authContext->username)); 00223 md5Update(&md5Context, ":", 1); 00224 md5Update(&md5Context, authContext->realm, strlen(authContext->realm)); 00225 md5Update(&md5Context, ":", 1); 00226 md5Update(&md5Context, authContext->password, strlen(authContext->password)); 00227 md5Final(&md5Context, NULL); 00228 00229 //Convert MD5 hash to hex string 00230 webSocketConvertArrayToHexString(md5Context.digest, MD5_DIGEST_SIZE, ha1); 00231 //Debug message 00232 TRACE_DEBUG(" HA1: %s\r\n", ha1); 00233 00234 //Compute HA2 = MD5(method : uri) 00235 md5Init(&md5Context); 00236 md5Update(&md5Context, "GET", 3); 00237 md5Update(&md5Context, ":", 1); 00238 md5Update(&md5Context, webSocket->uri, strlen(webSocket->uri)); 00239 md5Final(&md5Context, NULL); 00240 00241 //Convert MD5 hash to hex string 00242 webSocketConvertArrayToHexString(md5Context.digest, MD5_DIGEST_SIZE, ha2); 00243 //Debug message 00244 TRACE_DEBUG(" HA2: %s\r\n", ha2); 00245 00246 //Compute MD5(HA1 : nonce : nc : cnonce : qop : HA1) 00247 md5Init(&md5Context); 00248 md5Update(&md5Context, ha1, strlen(ha1)); 00249 md5Update(&md5Context, ":", 1); 00250 md5Update(&md5Context, authContext->nonce, strlen(authContext->nonce)); 00251 md5Update(&md5Context, ":", 1); 00252 md5Update(&md5Context, nc, strlen(nc)); 00253 md5Update(&md5Context, ":", 1); 00254 md5Update(&md5Context, authContext->cnonce, strlen(authContext->cnonce)); 00255 md5Update(&md5Context, ":", 1); 00256 md5Update(&md5Context, "auth", 4); 00257 md5Update(&md5Context, ":", 1); 00258 md5Update(&md5Context, ha2, strlen(ha2)); 00259 md5Final(&md5Context, NULL); 00260 00261 //Convert MD5 hash to hex string 00262 webSocketConvertArrayToHexString(md5Context.digest, MD5_DIGEST_SIZE, ha1); 00263 //Debug message 00264 TRACE_DEBUG(" response: %s\r\n", ha1); 00265 00266 //Format Authorization header field 00267 n = sprintf(output, "Authorization: Digest\r\n"); 00268 00269 //Username 00270 n += sprintf(output + n, " username=\"%s\",\r\n", authContext->username); 00271 //Realm 00272 n += sprintf(output + n, " realm=\"%s\",\r\n", authContext->realm); 00273 //Nonce value 00274 n += sprintf(output + n, " nonce=\"%s\",\r\n", authContext->nonce); 00275 //URI 00276 n += sprintf(output + n, " uri=\"%s\",\r\n", webSocket->uri); 00277 //Quality of protection 00278 n += sprintf(output + n, " qop=\"auth\",\r\n"); 00279 //Nonce count 00280 n += sprintf(output + n, " nc=\"%08x\",\r\n", authContext->nc); 00281 //Cnonce value 00282 n += sprintf(output + n, " cnonce=\"%s\",\r\n", authContext->cnonce); 00283 //Response 00284 n += sprintf(output + n, " response=\"%s\",\r\n", ha1); 00285 //Opaque parameter 00286 n += sprintf(output + n, " opaque=\"%s\",\r\n", authContext->opaque); 00287 } 00288 else 00289 #endif 00290 //Unknown authentication scheme? 00291 { 00292 //No need to add the Authorization header field 00293 n = 0; 00294 } 00295 00296 //Return the total length of the Authorization header field 00297 return n; 00298 } 00299 00300 00301 /** 00302 * @brief Convert byte array to hex string 00303 * @param[in] input Point to the byte array 00304 * @param[in] inputLength Length of the byte array 00305 * @param[out] output NULL-terminated string resulting from the conversion 00306 * @return Error code 00307 **/ 00308 00309 void webSocketConvertArrayToHexString(const uint8_t *input, 00310 size_t inputLength, char_t *output) 00311 { 00312 size_t i; 00313 00314 //Hex conversion table 00315 static const char_t hexDigit[] = 00316 { 00317 '0', '1', '2', '3', '4', '5', '6', '7', 00318 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 00319 }; 00320 00321 //Process byte array 00322 for(i = 0; i < inputLength; i++) 00323 { 00324 //Convert upper nibble 00325 output[i * 2] = hexDigit[(input[i] >> 4) & 0x0F]; 00326 //Then convert lower nibble 00327 output[i * 2 + 1] = hexDigit[input[i] & 0x0F]; 00328 } 00329 00330 //Properly terminate the string with a NULL character 00331 output[i * 2] = '\0'; 00332 } 00333 00334 #endif 00335
Generated on Tue Jul 12 2022 17:10:17 by
