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.
Fork of azure_c_shared_utility by
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: [The string "&skn=" is appended to result.]*/ 00272 /*Codes_SRS_SASTOKEN_06_023: [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 (STRING_concat(result, "&skn=") != 0) || 00283 (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: [If keyName is NULL then SASToken_Create shall return NULL.]*/ 00312 if ((key == NULL) || 00313 (scope == NULL) || 00314 (keyName == NULL)) 00315 { 00316 LogError("Invalid Parameter to SASToken_Create. handle key: %p, handle scope: %p, handle keyName: %p", key, scope, keyName); 00317 result = NULL; 00318 } 00319 else 00320 { 00321 const char* string_key = STRING_c_str(key); 00322 const char* string_scope = STRING_c_str(scope); 00323 const char* string_name = STRING_c_str(keyName); 00324 result = construct_sas_token(string_key, string_scope, string_name, expiry); 00325 } 00326 return result; 00327 } 00328 00329 STRING_HANDLE SASToken_CreateString(const char* key, const char* scope, const char* keyName, size_t expiry) 00330 { 00331 STRING_HANDLE result; 00332 00333 /*Codes_SRS_SASTOKEN_06_001: [If key is NULL then SASToken_Create shall return NULL.]*/ 00334 /*Codes_SRS_SASTOKEN_06_003: [If scope is NULL then SASToken_Create shall return NULL.]*/ 00335 /*Codes_SRS_SASTOKEN_06_007: [If keyName is NULL then SASToken_Create shall return NULL.]*/ 00336 if ((key == NULL) || 00337 (scope == NULL) || 00338 (keyName == NULL)) 00339 { 00340 LogError("Invalid Parameter to SASToken_Create. handle key: %p, handle scope: %p, handle keyName: %p", key, scope, keyName); 00341 result = NULL; 00342 } 00343 else 00344 { 00345 result = construct_sas_token(key, scope, keyName, expiry); 00346 } 00347 return result; 00348 }
Generated on Tue Jul 12 2022 19:14:38 by
1.7.2
