A Port of TI's Webserver for the CC3000

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HttpAuth.cpp Source File

HttpAuth.cpp

00001 /*****************************************************************************
00002 *
00003 *  HttpAuth.c
00004 *  Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
00005 *
00006 *  Redistribution and use in source and binary forms, with or without
00007 *  modification, are permitted provided that the following conditions
00008 *  are met:
00009 *
00010 *    Redistributions of source code must retain the above copyright
00011 *    notice, this list of conditions and the following disclaimer.
00012 *
00013 *    Redistributions in binary form must reproduce the above copyright
00014 *    notice, this list of conditions and the following disclaimer in the
00015 *    documentation and/or other materials provided with the   
00016 *    distribution.
00017 *
00018 *    Neither the name of Texas Instruments Incorporated nor the names of
00019 *    its contributors may be used to endorse or promote products derived
00020 *    from this software without specific prior written permission.
00021 *
00022 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
00023 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
00024 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00025 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
00026 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
00027 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
00028 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00029 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00030 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
00031 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
00032 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 *
00034 *****************************************************************************/
00035 #include "HttpAuth.h"
00036 
00037 #ifdef HTTP_CORE_ENABLE_AUTH
00038 
00039 #include "md5.h"
00040 #include <string.h>
00041 #include "HttpString.h"
00042 /** 
00043  * @addtogroup HttpAuth
00044  * @{
00045  */
00046 
00047 #define DIGEST_AUTHENTICATION_BUFFER_SIZE (32)
00048 
00049 /**
00050  * This structure holds the HTTP digest-access authentication state
00051  */
00052 struct HttpAuthState
00053 {
00054     /// Last-generated nonce
00055     uint8 nonce[DIGEST_AUTHENTICATION_BUFFER_SIZE];
00056     /// Last-generated opaque
00057     uint8 opaque[DIGEST_AUTHENTICATION_BUFFER_SIZE];
00058     /// The hash of the username, realm, and password
00059     uint8 ha1[DIGEST_AUTHENTICATION_BUFFER_SIZE];
00060 };
00061 
00062 
00063 
00064 /// The global state for digest-access authentication
00065 
00066 static struct HttpAuthState g_authState; // = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, 
00067 //                                            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, 
00068 //                                            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
00069 
00070 ///
00071 MD5_CTX MD5state;
00072 
00073 char    HTTP_AUTH_REALM[]                     =  "mbed web server";
00074 
00075 /// Header strings to be used for response headers
00076 char    HTTP_AUTHENTICATE_RESPONSE_REALM[]    =  "WWW-Authenticate: Digest realm=\"";
00077 char    HTTP_AUTHENTICATE_RESPONSE_NONCE[]    =  "\",qop=\"auth\",nonce=\"";
00078 char    HTTP_AUTHENTICATE_RESPONSE_OPAQUE[]   =  "\",opaque=\"";
00079 char    HTTP_AUTHENTICATE_RESPONSE_EOH[]      =  "\"\r\n"; 
00080 
00081 /// Authenticate header tokens
00082 char    HTTP_AUTHENTICATE_REALM[]      =  "realm";
00083 char    HTTP_AUTHENTICATE_QOP[]        =  "qop";
00084 char    HTTP_AUTHENTICATE_AUTH[]       =  "auth";
00085 char    HTTP_AUTHENTICATE_NONCE[]      =  "nonce";
00086 char    HTTP_AUTHENTICATE_OPAQUE[]     =  "opaque";
00087 char    HTTP_AUTHENTICATE_DIGEST[]     =  "digest";
00088 char    HTTP_AUTHENTICATE_URI[]        =  "uri";
00089 char    HTTP_AUTHENTICATE_NC[]         =  "nc=";
00090 char    HTTP_AUTHENTICATE_CNONCE[]     =  "cnonce";
00091 char    HTTP_AUTHENTICATE_RESPONSE[]   =  "response";
00092 char    HTTP_AUTHENTICATE_USERNAME[]   =  "username";
00093 char    HTTP_DELIMITER_QUOTE[]         =  "\"";
00094 char    HTTP_DELIMITER_COMMA[]         =  ",";
00095 
00096 
00097 /// The length of the response authentication header
00098 /// The two 32 numbers represent nonce and opaque strings and the -5 is to compensate for the sizeof() calls
00099 uint16  HTTP_AUTHENTICATE_RESPONSE_HEADER_LENGTH = 
00100                 sizeof(HTTP_AUTHENTICATE_RESPONSE_REALM) +
00101                 sizeof(HTTP_AUTHENTICATE_RESPONSE_NONCE) +
00102                 sizeof(HTTP_AUTHENTICATE_RESPONSE_OPAQUE) +
00103                 sizeof(HTTP_AUTHENTICATE_RESPONSE_EOH) +
00104                 sizeof(HTTP_AUTH_REALM) +
00105                 DIGEST_AUTHENTICATION_BUFFER_SIZE + DIGEST_AUTHENTICATION_BUFFER_SIZE - 5;
00106 
00107 
00108 /**
00109  * Simple random generator
00110  * To improve randomness the initial seed has to be dynamic
00111  */
00112 static uint32 GetRandomUint()
00113 {
00114     // set static seed
00115     static uint32 m_z = 1234;
00116     static uint32 m_w = 98877;
00117 
00118     m_z = 36969 * (m_z & 65535) + (m_z >> 16);
00119     m_w = 18000 * (m_w & 65535) + (m_w >> 16);
00120     return (m_z << 16) + m_w;
00121 }
00122 
00123 static void MD5_FinalToString(uint8* str, MD5_CTX *md5stat)
00124 {
00125     uint8 tmp[16];
00126     uint8 i;
00127     struct HttpBlob location;
00128     location.uLength = 2;
00129     location.pData = str;
00130     MD5_Final(tmp, md5stat);
00131     for (i=0; i<16; i++, location.pData += 2)
00132         HttpString_htoa(tmp[i], &location, 1);
00133 }
00134 
00135 
00136 /**
00137  * This function will generate random 16 bytes to be used for Nonce and opaque strings
00138  */
00139 static void Generate32BytesRandomString(uint8 *str)
00140 {
00141     struct HttpBlob str1;
00142     str1.uLength = 8;
00143     str1.pData = str;
00144     HttpString_htoa(GetRandomUint(), &str1, 1);
00145     str1.pData = str + 8;
00146     HttpString_htoa(GetRandomUint(), &str1, 1);
00147     str1.pData = str + 16;
00148     HttpString_htoa(GetRandomUint(), &str1, 1);
00149     str1.pData = str + 24;
00150     HttpString_htoa(GetRandomUint(), &str1, 1);
00151 }
00152 
00153 void HttpAuth_Init(struct HttpBlob username, struct HttpBlob password)
00154 {
00155     MD5_Init(&MD5state);
00156     MD5_Update(&MD5state, username.pData, username.uLength);
00157     MD5_Update(&MD5state, (unsigned char*)":", 1);
00158     MD5_Update(&MD5state, HTTP_AUTH_REALM, sizeof(HTTP_AUTH_REALM)-1);
00159     MD5_Update(&MD5state, (unsigned char*)":", 1);
00160     MD5_Update(&MD5state, password.pData, password.uLength);
00161     MD5_FinalToString(g_authState.ha1, &MD5state);
00162 }
00163 
00164 static void AddStringToBlob(struct HttpBlob * trgt, char *str, uint16 length)
00165 {
00166     memcpy(trgt->pData + trgt->uLength, str, length);
00167     trgt->uLength += length;
00168 }
00169 
00170 void HttpAuth_ResponseAuthenticate(struct HttpRequest* pRequest, struct HttpBlob* pWWWAuthenticate)
00171 {
00172     struct HttpBlob headerBlob;
00173 
00174     if (pWWWAuthenticate->uLength < HTTP_AUTHENTICATE_RESPONSE_HEADER_LENGTH)
00175     {
00176         pWWWAuthenticate->uLength = 0;
00177         return;
00178     }
00179     // There is enough space to add the authenticate header
00180 
00181     headerBlob.pData = pWWWAuthenticate->pData;
00182     headerBlob.uLength = 0;
00183 
00184     // Generate new Nonce and opaque
00185     Generate32BytesRandomString(g_authState.nonce);
00186     Generate32BytesRandomString(g_authState.opaque);
00187 
00188     // Build response header
00189     AddStringToBlob(&headerBlob, HTTP_AUTHENTICATE_RESPONSE_REALM, sizeof(HTTP_AUTHENTICATE_RESPONSE_REALM)-1);
00190     AddStringToBlob(&headerBlob, HTTP_AUTH_REALM, sizeof(HTTP_AUTH_REALM) -1);
00191     AddStringToBlob(&headerBlob, HTTP_AUTHENTICATE_RESPONSE_NONCE, sizeof(HTTP_AUTHENTICATE_RESPONSE_NONCE) -1);
00192     AddStringToBlob(&headerBlob, (char*)g_authState.nonce, sizeof(g_authState.nonce));
00193     AddStringToBlob(&headerBlob, HTTP_AUTHENTICATE_RESPONSE_OPAQUE, sizeof(HTTP_AUTHENTICATE_RESPONSE_OPAQUE) -1);
00194     AddStringToBlob(&headerBlob, (char*)g_authState.opaque, sizeof(g_authState.opaque));
00195     AddStringToBlob(&headerBlob, HTTP_AUTHENTICATE_RESPONSE_EOH, sizeof(HTTP_AUTHENTICATE_RESPONSE_EOH) - 1);
00196 
00197     pWWWAuthenticate->uLength = headerBlob.uLength;
00198 }
00199 
00200 /**
00201  * Find/verify name value pair in the input blob
00202  * 
00203  * After return the location is stays in the same place since the order of the name value pairs in not constant
00204  * Returns:     if header not found return 0
00205  *              if value token is NULL - return pointer to start of value length of value till the \" delimiter
00206  *              if value token is not NULL - return 1 if values match
00207  */
00208 static uint16 HttpAuth_VerifyHeaderNameValue(struct HttpBlob *location, char* nameToken, uint8 tokenlenLen, char *value, uint8 valuelen, char** outValue)
00209 {
00210     uint8 * found;
00211     struct HttpBlob originalLocation;
00212     originalLocation.uLength = location->uLength;
00213     originalLocation.pData = location->pData;
00214 
00215     found = HttpString_nextToken(nameToken, tokenlenLen, *location);
00216 
00217     // Missing header name
00218     if (found == 0)
00219         return 0;
00220     else
00221     {
00222         location->uLength = originalLocation.uLength - (uint16)(found - originalLocation.pData) - (tokenlenLen + 2);
00223         location->pData = found + tokenlenLen + 2;
00224         // Return the value pointer and size
00225         if (value == NULL)
00226         {
00227             *outValue= (char*)location->pData;
00228             nameToken = (char *)location->pData;
00229             found = HttpString_nextToken(HTTP_DELIMITER_QUOTE, sizeof(HTTP_DELIMITER_QUOTE) - 1, *location);
00230             if (found==0)
00231                 found = HttpString_nextToken(HTTP_DELIMITER_COMMA, sizeof(HTTP_DELIMITER_COMMA) - 1, *location);
00232 
00233             // Restore current location 
00234             location->uLength = originalLocation.uLength;
00235             location->pData = originalLocation.pData;
00236             return (uint16)((char *)found - nameToken);
00237         }
00238 
00239         found = HttpString_nextToken(value, valuelen, *location);
00240         // Value does not match - restore location
00241         if (found == 0)
00242         {
00243             location->uLength = originalLocation.uLength;
00244             location->pData = originalLocation.pData;
00245             return 0;
00246         }
00247         
00248         // Restore location
00249         location->uLength = originalLocation.uLength;
00250         location->pData = originalLocation.pData;
00251         return 1;
00252     }
00253 }
00254 
00255 
00256 void HttpAuth_RequestAuthenticate(struct HttpRequest* pRequest, struct HttpBlob authorization)
00257 {
00258     uint8 ha2[DIGEST_AUTHENTICATION_BUFFER_SIZE], correctResponse[DIGEST_AUTHENTICATION_BUFFER_SIZE];
00259     struct HttpBlob currentLocation, blob;
00260     // HA1 was not copmuted
00261     if ((uint32)*g_authState.ha1 == 0)
00262     {
00263         return;
00264     }
00265 
00266     // Parse the header - find relevant tokens and handle
00267     currentLocation.pData = authorization.pData;
00268     currentLocation.uLength = authorization.uLength;
00269     
00270     // Verify the mandatory tokens, whose content we ignore are present
00271     
00272     // verify we are in degest authentication method, any other is not supported
00273     if (HttpString_nextToken(HTTP_AUTHENTICATE_DIGEST, sizeof(HTTP_AUTHENTICATE_DIGEST)-1, currentLocation) == 0)
00274         return;
00275 
00276     // verify username exists
00277     if (HttpString_nextToken(HTTP_AUTHENTICATE_USERNAME, sizeof(HTTP_AUTHENTICATE_USERNAME)-1, currentLocation) == 0)
00278         return;
00279 
00280     // Verify realm 
00281     if (HttpAuth_VerifyHeaderNameValue(&currentLocation, HTTP_AUTHENTICATE_REALM, sizeof(HTTP_AUTHENTICATE_REALM)-1, HTTP_AUTH_REALM, sizeof(HTTP_AUTH_REALM)-1, 0) != 1)
00282         return;
00283 
00284     // Verify correct nonce
00285     if (HttpAuth_VerifyHeaderNameValue(&currentLocation, HTTP_AUTHENTICATE_NONCE, sizeof(HTTP_AUTHENTICATE_NONCE)-1, (char *)g_authState.nonce, DIGEST_AUTHENTICATION_BUFFER_SIZE, 0) != 1)
00286         return;
00287 
00288     // Verify correct opaque
00289     if (HttpAuth_VerifyHeaderNameValue(&currentLocation, HTTP_AUTHENTICATE_OPAQUE, sizeof(HTTP_AUTHENTICATE_OPAQUE)-1, (char *)g_authState.opaque, DIGEST_AUTHENTICATION_BUFFER_SIZE, 0) != 1)
00290         return;
00291 
00292     // Find neccessary tokents and compute HA2 if some tokens are not found - return
00293     blob.pData = NULL;
00294     blob.uLength = HttpAuth_VerifyHeaderNameValue(&currentLocation, HTTP_AUTHENTICATE_URI, sizeof(HTTP_AUTHENTICATE_URI)-1, 0, 0, (char**)&blob.pData);
00295     // Uri is missing
00296     if (blob.uLength == 0)
00297         return;
00298 
00299     MD5_Init(&MD5state);
00300     if ((pRequest->uFlags & HTTP_REQUEST_FLAG_METHOD_POST) != 0)
00301         MD5_Update(&MD5state, (unsigned char*)"POST", 4);
00302     else
00303         MD5_Update(&MD5state, (unsigned char*)"GET", 3);
00304     MD5_Update(&MD5state, (unsigned char*)":", 1);
00305     MD5_Update(&MD5state, blob.pData, blob.uLength);
00306     MD5_FinalToString(ha2, &MD5state);
00307 
00308     // Find tokens to compute correct response
00309     blob.pData = HttpString_nextToken(HTTP_AUTHENTICATE_NC, sizeof(HTTP_AUTHENTICATE_NC)-1, currentLocation);
00310     // Ncount is missing
00311     if (blob.pData == 0)
00312         return;
00313     blob.pData += sizeof(HTTP_AUTHENTICATE_NC) - 1;
00314     blob.uLength = 8;
00315 
00316     MD5_Init(&MD5state);
00317     MD5_Update(&MD5state, g_authState.ha1, DIGEST_AUTHENTICATION_BUFFER_SIZE);
00318     MD5_Update(&MD5state, (unsigned char*)":", 1);
00319     MD5_Update(&MD5state, g_authState.nonce, DIGEST_AUTHENTICATION_BUFFER_SIZE);
00320     MD5_Update(&MD5state, (unsigned char*)":", 1);
00321     MD5_Update(&MD5state, blob.pData, blob.uLength);
00322 
00323     blob.pData = NULL;
00324     blob.uLength = HttpAuth_VerifyHeaderNameValue(&currentLocation, HTTP_AUTHENTICATE_CNONCE, sizeof(HTTP_AUTHENTICATE_CNONCE)-1, 0, 0, (char **)&blob.pData);
00325     // Cnonce is missing
00326     if (blob.uLength == 0)
00327         return;
00328     MD5_Update(&MD5state, (unsigned char*)":", 1);
00329     MD5_Update(&MD5state, blob.pData, blob.uLength);
00330     MD5_Update(&MD5state, (unsigned char*)":auth:", 6);
00331     MD5_Update(&MD5state, ha2, DIGEST_AUTHENTICATION_BUFFER_SIZE);
00332     MD5_FinalToString(correctResponse, &MD5state);
00333 
00334     // Compare received response to the one computed locally - if equal then authorize
00335     blob.pData = NULL;
00336     blob.uLength = HttpAuth_VerifyHeaderNameValue(&currentLocation, HTTP_AUTHENTICATE_RESPONSE, sizeof(HTTP_AUTHENTICATE_RESPONSE)-1, 0, 0, (char **)&blob.pData);
00337     // Response is missing
00338     if (blob.uLength != DIGEST_AUTHENTICATION_BUFFER_SIZE)
00339         return;
00340 
00341     currentLocation.pData = correctResponse;
00342     currentLocation.uLength = DIGEST_AUTHENTICATION_BUFFER_SIZE;
00343     // if the responses are equal
00344     if (HttpString_strcmp(blob, currentLocation) == 0)
00345         pRequest->uFlags |= HTTP_REQUEST_FLAG_AUTHENTICATED;
00346 
00347 }
00348 #endif
00349