Azure IoT / azure_c_shared_utility

Dependents:   STM32F746_iothub_client_sample_mqtt f767zi_mqtt iothub_client_sample_amqp iothub_client_sample_http ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers sastoken.c Source File

sastoken.c

00001 // Copyright (c) Microsoft. All rights reserved.
00002 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
00003 
00004 #include <stdlib.h>
00005 #include <stdio.h>
00006 #include <string.h>
00007 #include "azure_c_shared_utility/gballoc.h"
00008 #include "azure_c_shared_utility/sastoken.h"
00009 #include "azure_c_shared_utility/urlencode.h"
00010 #include "azure_c_shared_utility/hmacsha256.h"
00011 #include "azure_c_shared_utility/base64.h"
00012 #include "azure_c_shared_utility/agenttime.h"
00013 #include "azure_c_shared_utility/strings.h"
00014 #include "azure_c_shared_utility/buffer_.h"
00015 #include "azure_c_shared_utility/xlogging.h"
00016 #include "azure_c_shared_utility/crt_abstractions.h"
00017 
00018 static double getExpiryValue(const char* expiryASCII)
00019 {
00020     double value = 0;
00021     size_t i = 0;
00022     for (i = 0; expiryASCII[i] != '\0'; i++)
00023     {
00024         if (expiryASCII[i] >= '0' && expiryASCII[i] <= '9')
00025         {
00026             value = value * 10 + (expiryASCII[i] - '0');
00027         }
00028         else
00029         {
00030             value = 0;
00031             break;
00032         }
00033     }
00034     return value;
00035 }
00036 
00037 bool SASToken_Validate(STRING_HANDLE sasToken)
00038 {
00039     bool result;
00040     /*Codes_SRS_SASTOKEN_25_025: [**SASToken_Validate shall get the SASToken value by invoking STRING_c_str on the handle.**]***/
00041     const char* sasTokenArray = STRING_c_str(sasToken);
00042 
00043     /* Codes_SRS_SASTOKEN_25_024: [**If handle is NULL then SASToken_Validate shall return false.**] */
00044     /* Codes_SRS_SASTOKEN_25_026: [**If STRING_c_str on handle return NULL then SASToken_Validate shall return false.**] */
00045     if (sasToken == NULL || sasTokenArray == NULL)
00046     {
00047         result = false;
00048     }
00049     else
00050     {
00051         int seStart = -1, seStop = -1;
00052         int srStart = -1, srStop = -1;
00053         int sigStart = -1, sigStop = -1;
00054         int tokenLength = (int)STRING_length(sasToken);
00055         int i;
00056         for (i = 0; i < tokenLength; i++)
00057         {
00058             if (sasTokenArray[i] == 's' && sasTokenArray[i + 1] == 'e' && sasTokenArray[i + 2] == '=') // Look for se=
00059             {
00060                 seStart = i + 3;
00061                 if (srStart > 0 && srStop < 0)
00062                 {
00063                     if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') // look for either & or space
00064                         srStop = i - 1;
00065                     else if (sasTokenArray[i - 1] == '&')
00066                         srStop = i - 2;
00067                     else
00068                         seStart = -1; // as the format is not either "&se=" or " se="
00069                 }
00070                 else if (sigStart > 0 && sigStop < 0)
00071                 {
00072                     if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ')
00073                         sigStop = i - 1;
00074                     else if (sasTokenArray[i - 1] == '&')
00075                         sigStop = i - 2;
00076                     else
00077                         seStart = -1;
00078                 }
00079             }
00080             else if (sasTokenArray[i] == 's' && sasTokenArray[i + 1] == 'r' && sasTokenArray[i + 2] == '=') // Look for sr=
00081             {
00082                 srStart = i + 3;
00083                 if (seStart > 0 && seStop < 0)
00084                 {
00085                     if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ')
00086                         seStop = i - 1;
00087                     else if (sasTokenArray[i - 1] == '&')
00088                         seStop = i - 2;
00089                     else
00090                         srStart = -1;
00091                 }
00092                 else if (sigStart > 0 && sigStop < 0)
00093                 {
00094                     if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ')
00095                         sigStop = i - 1;
00096                     else if (sasTokenArray[i - 1] == '&')
00097                         sigStop = i - 2;
00098                     else
00099                         srStart = -1;
00100                 }
00101             }
00102             else if (sasTokenArray[i] == 's' && sasTokenArray[i + 1] == 'i' && sasTokenArray[i + 2] == 'g' && sasTokenArray[i + 3] == '=') // Look for sig=
00103             {
00104                 sigStart = i + 4;
00105                 if (srStart > 0 && srStop < 0)
00106                 {
00107                     if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ')
00108                         srStop = i - 1;
00109                     else if (sasTokenArray[i - 1] == '&')
00110                         srStop = i - 2;
00111                     else
00112                         sigStart = -1;
00113                 }
00114                 else if (seStart > 0 && seStop < 0)
00115                 {
00116                     if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ')
00117                         seStop = i - 1;
00118                     else if (sasTokenArray[i - 1] == '&')
00119                         seStop = i - 2;
00120                     else
00121                         sigStart = -1;
00122                 }
00123             }
00124         }
00125 
00126         /*Codes_SRS_SASTOKEN_25_027: [**If SASTOKEN does not obey the SASToken format then SASToken_Validate shall return false.**]***/
00127         /*Codes_SRS_SASTOKEN_25_028: [**SASToken_validate shall check for the presence of sr, se and sig from the token and return false if not found**]***/
00128         if (seStart < 0 || srStart < 0 || sigStart < 0)
00129         {
00130             result = false;
00131         }
00132         else
00133         {
00134             if (seStop < 0)
00135             {
00136                 seStop = tokenLength;
00137             }
00138             else if (srStop < 0)
00139             {
00140                 srStop = tokenLength;
00141             }
00142             else if (sigStop < 0)
00143             {
00144                 sigStop = tokenLength;
00145             }
00146 
00147             if ((seStop <= seStart) ||
00148                 (srStop <= srStart) ||
00149                 (sigStop <= sigStart))
00150             {
00151                 result = false;
00152             }
00153             else
00154             {
00155                 char* expiryASCII = (char*)malloc(seStop - seStart + 1);
00156                 /*Codes_SRS_SASTOKEN_25_031: [**If malloc fails during validation then SASToken_Validate shall return false.**]***/
00157                 if (expiryASCII == NULL)
00158                 {
00159                     result = false;
00160                 }
00161                 else
00162                 {
00163                     double expiry;
00164                     // Add the Null terminator here
00165                     memset(expiryASCII, 0, seStop - seStart + 1);
00166                     for (i = seStart; i < seStop; i++)
00167                     {
00168                         // The se contains the expiration values, if a & token is encountered then
00169                         // the se field is complete.
00170                         if (sasTokenArray[i] == '&')
00171                         {
00172                             break;
00173                         }
00174                         expiryASCII[i - seStart] = sasTokenArray[i];
00175                     }
00176                     expiry = getExpiryValue(expiryASCII);
00177                     /*Codes_SRS_SASTOKEN_25_029: [**SASToken_validate shall check for expiry time from token and if token has expired then would return false **]***/
00178                     if (expiry <= 0)
00179                     {
00180                         result = false;
00181                     }
00182                     else
00183                     {
00184                         double secSinceEpoch = get_difftime(get_time(NULL), (time_t)0);
00185                         if (expiry < secSinceEpoch)
00186                         {
00187                             /*Codes_SRS_SASTOKEN_25_029: [**SASToken_validate shall check for expiry time from token and if token has expired then would return false **]***/
00188                             result = false;
00189                         }
00190                         else
00191                         {
00192                             /*Codes_SRS_SASTOKEN_25_030: [**SASToken_validate shall return true only if the format is obeyed and the token has not yet expired **]***/
00193                             result = true;
00194                         }
00195                     }
00196                     free(expiryASCII);
00197                 }
00198             }
00199         }
00200     }
00201 
00202     return result;
00203 }
00204 
00205 static STRING_HANDLE construct_sas_token(const char* key, const char* scope, const char* keyname, size_t expiry)
00206 {
00207     STRING_HANDLE result;
00208 
00209     char tokenExpirationTime[32] = { 0 };
00210 
00211     BUFFER_HANDLE decodedKey;
00212 
00213     /*Codes_SRS_SASTOKEN_06_029: [The key parameter is decoded from base64.]*/
00214     if ((decodedKey = Base64_Decoder(key)) == NULL)
00215     {
00216         /*Codes_SRS_SASTOKEN_06_030: [If there is an error in the decoding then SASToken_Create shall return NULL.]*/
00217         LogError("Unable to decode the key for generating the SAS.");
00218         result = NULL;
00219     }
00220     else
00221     {
00222         /*Codes_SRS_SASTOKEN_06_026: [If the conversion to string form fails for any reason then SASToken_Create shall return NULL.]*/
00223         if (size_tToString(tokenExpirationTime, sizeof(tokenExpirationTime), expiry) != 0)
00224         {
00225             LogError("For some reason converting seconds to a string failed.  No SAS can be generated.");
00226             result = NULL;
00227         }
00228         else
00229         {
00230             STRING_HANDLE toBeHashed = NULL;
00231             BUFFER_HANDLE hash = NULL;
00232             if (((hash = BUFFER_new()) == NULL) ||
00233                 ((toBeHashed = STRING_new()) == NULL) ||
00234                 ((result = STRING_new()) == NULL))
00235             {
00236                 LogError("Unable to allocate memory to prepare SAS token.");
00237                 result = NULL;
00238             }
00239             else
00240             {
00241                 /*Codes_SRS_SASTOKEN_06_009: [The scope is the basis for creating a STRING_HANDLE.]*/
00242                 /*Codes_SRS_SASTOKEN_06_010: [A "\n" is appended to that string.]*/
00243                 /*Codes_SRS_SASTOKEN_06_011: [tokenExpirationTime is appended to that string.]*/
00244                 if ((STRING_concat(toBeHashed, scope) != 0) ||
00245                     (STRING_concat(toBeHashed, "\n") != 0) ||
00246                     (STRING_concat(toBeHashed, tokenExpirationTime) != 0))
00247                 {
00248                     LogError("Unable to build the input to the HMAC to prepare SAS token.");
00249                     STRING_delete(result);
00250                     result = NULL;
00251                 }
00252                 else
00253                 {
00254                     STRING_HANDLE base64Signature = NULL;
00255                     STRING_HANDLE urlEncodedSignature = NULL;
00256                     size_t inLen = STRING_length(toBeHashed);
00257                     const unsigned char* inBuf = (const unsigned char*)STRING_c_str(toBeHashed);
00258                     size_t outLen = BUFFER_length(decodedKey);
00259                     unsigned char* outBuf = BUFFER_u_char(decodedKey);
00260                     /*Codes_SRS_SASTOKEN_06_013: [If an error is returned from the HMAC256 function then NULL is returned from SASToken_Create.]*/
00261                     /*Codes_SRS_SASTOKEN_06_012: [An HMAC256 hash is calculated using the decodedKey, over toBeHashed.]*/
00262                     /*Codes_SRS_SASTOKEN_06_014: [If there are any errors from the following operations then NULL shall be returned.]*/
00263                     /*Codes_SRS_SASTOKEN_06_015: [The hash is base 64 encoded.]*/
00264                     /*Codes_SRS_SASTOKEN_06_028: [base64Signature shall be url encoded.]*/
00265                     /*Codes_SRS_SASTOKEN_06_016: [The string "SharedAccessSignature sr=" is the first part of the result of SASToken_Create.]*/
00266                     /*Codes_SRS_SASTOKEN_06_017: [The scope parameter is appended to result.]*/
00267                     /*Codes_SRS_SASTOKEN_06_018: [The string "&sig=" is appended to result.]*/
00268                     /*Codes_SRS_SASTOKEN_06_019: [The string urlEncodedSignature shall be appended to result.]*/
00269                     /*Codes_SRS_SASTOKEN_06_020: [The string "&se=" shall be appended to result.]*/
00270                     /*Codes_SRS_SASTOKEN_06_021: [tokenExpirationTime is appended to result.]*/
00271                     /*Codes_SRS_SASTOKEN_06_022: [If keyName is non-NULL, the string "&skn=" is appended to result.]*/
00272                     /*Codes_SRS_SASTOKEN_06_023: [If keyName is non-NULL, the argument keyName is appended to result.]*/
00273                     if ((HMACSHA256_ComputeHash(outBuf, outLen, inBuf, inLen, hash) != HMACSHA256_OK) ||
00274                         ((base64Signature = Base64_Encoder(hash)) == NULL) ||
00275                         ((urlEncodedSignature = URL_Encode(base64Signature)) == NULL) ||
00276                         (STRING_copy(result, "SharedAccessSignature sr=") != 0) ||
00277                         (STRING_concat(result, scope) != 0) ||
00278                         (STRING_concat(result, "&sig=") != 0) ||
00279                         (STRING_concat_with_STRING(result, urlEncodedSignature) != 0) ||
00280                         (STRING_concat(result, "&se=") != 0) ||
00281                         (STRING_concat(result, tokenExpirationTime) != 0) ||
00282                         ((keyname != NULL) && (STRING_concat(result, "&skn=") != 0)) ||
00283                         ((keyname != NULL) && (STRING_concat(result, keyname) != 0)))
00284                     {
00285                         LogError("Unable to build the SAS token.");
00286                         STRING_delete(result);
00287                         result = NULL;
00288                     }
00289                     else
00290                     {
00291                         /* everything OK */
00292                     }
00293                     STRING_delete(base64Signature);
00294                     STRING_delete(urlEncodedSignature);
00295                 }
00296             }
00297             STRING_delete(toBeHashed);
00298             BUFFER_delete(hash);
00299         }
00300         BUFFER_delete(decodedKey);
00301     }
00302     return result;
00303 }
00304 
00305 STRING_HANDLE SASToken_Create(STRING_HANDLE key, STRING_HANDLE scope, STRING_HANDLE keyName, size_t expiry)
00306 {
00307     STRING_HANDLE result;
00308 
00309     /*Codes_SRS_SASTOKEN_06_001: [If key is NULL then SASToken_Create shall return NULL.]*/
00310     /*Codes_SRS_SASTOKEN_06_003: [If scope is NULL then SASToken_Create shall return NULL.]*/
00311     /*Codes_SRS_SASTOKEN_06_007: [keyName is optional and can be set to NULL.]*/
00312     if ((key == NULL) ||
00313         (scope == NULL))
00314     {
00315         LogError("Invalid Parameter to SASToken_Create. handle key: %p, handle scope: %p, handle keyName: %p", key, scope, keyName);
00316         result = NULL;
00317     }
00318     else
00319     {
00320         const char* string_key = STRING_c_str(key);
00321         const char* string_scope = STRING_c_str(scope);
00322         const char* string_name = STRING_c_str(keyName);
00323         result = construct_sas_token(string_key, string_scope, string_name, expiry);
00324     }
00325     return result;
00326 }
00327 
00328 STRING_HANDLE SASToken_CreateString(const char* key, const char* scope, const char* keyName, size_t expiry)
00329 {
00330     STRING_HANDLE result;
00331 
00332     /*Codes_SRS_SASTOKEN_06_001: [If key is NULL then SASToken_Create shall return NULL.]*/
00333     /*Codes_SRS_SASTOKEN_06_003: [If scope is NULL then SASToken_Create shall return NULL.]*/
00334     /*Codes_SRS_SASTOKEN_06_007: [keyName is optional and can be set to NULL.]*/
00335     if ((key == NULL) ||
00336         (scope == NULL))
00337     {
00338         LogError("Invalid Parameter to SASToken_Create. handle key: %p, handle scope: %p, handle keyName: %p", key, scope, keyName);
00339         result = NULL;
00340     }
00341     else
00342     {
00343         result = construct_sas_token(key, scope, keyName, expiry);
00344     }
00345     return result;
00346 }