Changeset 74070 in vbox for trunk/src/VBox/Runtime
- Timestamp:
- Sep 4, 2018 3:17:01 PM (6 years ago)
- Location:
- trunk/src/VBox/Runtime
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/common/rest/RTCRestClientApiBaseOci.cpp
r74060 r74070 32 32 #include <iprt/cpp/restclient.h> 33 33 34 #include <iprt/assert.h> 34 35 #include <iprt/base64.h> 35 36 #include <iprt/err.h> 36 37 #include <iprt/http.h> 37 38 #include <iprt/log.h> 39 #include <iprt/sha.h> 40 #include <iprt/time.h> 38 41 #include <iprt/uri.h> 39 #include <iprt/sha.h> 40 #include <iprt/crypto/digest.h> 41 #include <iprt/crypto/pkix.h> 42 42 43 43 44 44 45 /** 45 * Worker for ociSignRequestAddAllFields(). 46 * Ensures that we've got a 'Content-Length' header. 47 * 48 * @returns IPRT status code. 49 * @param hHttp The HTTP client handle. 50 * @param pvContent 46 51 */ 47 static int ociSignRequestAddField(RTCRDIGEST hDigest, RTCString *pStrAuth, const char *pszField, size_t cchField, 48 const char *pszValue, size_t cchValue) 52 static int ociSignRequestEnsureDateOrXDate(RTHTTP hHttp) 49 53 { 50 /* First? */ 51 bool const fFirst = pStrAuth->endsWith("\""); 54 if (RTHttpGetHeader(hHttp, RT_STR_TUPLE("x-date"))) 55 return VINF_SUCCESS; 56 if (RTHttpGetHeader(hHttp, RT_STR_TUPLE("date"))) 57 return VINF_SUCCESS; 52 58 53 /* Append the field to the list, in lowercased form: */ 54 int rc = VINF_SUCCESS; 55 if (!fFirst) 56 rc = pStrAuth->appendNoThrow(' '); 57 if (RT_SUCCESS(rc)) 58 { 59 size_t offStart = pStrAuth->length(); 60 rc = pStrAuth->appendNoThrow(pszField, cchField); 61 if (RT_SUCCESS(rc)) 62 { 63 RTStrToLower(pStrAuth->mutableRaw() + offStart); 59 RTTIMESPEC NowSpec; 60 RTTIME Now; 61 char szDate[RTTIME_RTC2822_LEN]; 62 ssize_t cch = RTTimeToRfc2822(RTTimeExplode(&Now, RTTimeNow(&NowSpec)), szDate, sizeof(szDate)); 63 AssertRCReturn((int)cch, (int)cch); 64 64 65 /* 2.3 (3) If not the first field, add newline separator. */ 66 rc = RTCrDigestUpdate(hDigest, "\n", 1); 67 68 69 /* Update the digest with the field name followed by ': ' */ 70 rc = RTCrDigestUpdate(hDigest, pStrAuth->c_str() + offStart, pStrAuth->length() - offStart); 71 if (RT_SUCCESS(rc)) 72 { 73 rc = RTCrDigestUpdate(hDigest, RT_STR_TUPLE(": ")); 74 if (RT_SUCCESS(rc)) 75 { 76 /* Update the digest with the field value: */ 77 if (cchValue == RTSTR_MAX) 78 cchValue = strlen(pszValue); 79 rc = RTCrDigestUpdate(hDigest, pszValue, cchValue); 80 } 81 } 82 } 83 } 84 return rc; 65 return RTHttpAddHeader(hHttp, "x-date", szDate, cch, RTHTTPADDHDR_F_BACK); 85 66 } 86 67 87 68 88 69 /** 89 * Worker for ociSignRequest(). 70 * Ensures that we've got a 'x-content-sha256' header. 71 * 72 * @returns IPRT status code. 73 * @param hHttp The HTTP client handle. 74 * @param pvContent 90 75 */ 91 static int ociSignRequestAddAllFields(RTHTTP a_hHttp, RTCString const &a_rStrFullUrl, RTHTTPMETHOD a_enmHttpMethod, 92 RTCString const &a_rStrXmitBody, uint32_t a_fFlags, 93 RTCRDIGEST hDigest, RTCString *pStrAuth) 76 static int ociSignRequestEnsureXContentSha256(RTHTTP hHttp, void const *pvContent, size_t cbContent) 94 77 { 95 char szTmp[256]; 78 if (RTHttpGetHeader(hHttp, RT_STR_TUPLE("x-content-sha256"))) 79 return VINF_SUCCESS; 96 80 97 /** @todo This is a little ugly. I think that instead of this uglyness, we should just 98 * ensure the presence of required fields and sign all headers. That way all parameters 99 * will be covered by the signature. */ 81 #ifdef RT_STRICT 82 const char *pszContentLength = RTHttpGetHeader(hHttp, RT_STR_TUPLE("Content-Length")); 83 Assert(pszContentLength); 84 AssertMsg(!pszContentLength || RTStrToUInt64(pszContentLength) == cbContent, ("'%s' vs %RU64\n", pszContentLength, cbContent)); 85 #endif 100 86 101 /*102 * (request-target) and host.103 */ 104 RTURIPARSED ParsedUrl;105 int rc = RT UriParse(a_rStrFullUrl.c_str(), &ParsedUrl);87 uint8_t abHash[RTSHA256_HASH_SIZE]; 88 RTSha256(pvContent, cbContent, abHash); 89 90 char szBase64[RTSHA256_DIGEST_LEN + 1]; /* (base64 should be shorter) */ 91 int rc = RTBase64EncodeEx(abHash, sizeof(abHash), RTBASE64_FLAGS_NO_LINE_BREAKS, szBase64, sizeof(szBase64), NULL); 106 92 AssertRCReturn(rc, rc); 107 93 108 const char *pszMethod = NULL; 109 switch (a_enmHttpMethod) 110 { 111 case RTHTTPMETHOD_GET: pszMethod = "get "; break; 112 case RTHTTPMETHOD_PUT: pszMethod = "put "; break; 113 case RTHTTPMETHOD_POST: pszMethod = "post "; break; 114 case RTHTTPMETHOD_PATCH: pszMethod = "patch "; break; 115 case RTHTTPMETHOD_DELETE: pszMethod = "delete "; break; 116 case RTHTTPMETHOD_HEAD: pszMethod = "head "; break; 117 case RTHTTPMETHOD_OPTIONS: pszMethod = "options "; break; 118 case RTHTTPMETHOD_TRACE: pszMethod = "trace "; break; 119 case RTHTTPMETHOD_END: 120 case RTHTTPMETHOD_INVALID: 121 case RTHTTPMETHOD_32BIT_HACK: 122 break; 123 } 124 AssertReturn(pszMethod, VERR_REST_INTERAL_ERROR_6); 125 rc = ociSignRequestAddField(hDigest, pStrAuth, RT_STR_TUPLE("(request-target)"), 126 pszMethod, strlen(pszMethod)); 127 AssertRCReturn(rc, rc); 128 const char *pszValue = a_rStrFullUrl.c_str() + ParsedUrl.offPath; /* Add the path. */ 129 rc = RTCrDigestUpdate(hDigest, pszValue, strlen(pszValue)); 94 return RTHttpAddHeader(hHttp, "x-content-sha256", szBase64, RTSTR_MAX, RTHTTPADDHDR_F_BACK); 95 } 130 96 131 rc = ociSignRequestAddField(hDigest,pStrAuth, RT_STR_TUPLE("host"), 132 a_rStrFullUrl.c_str() + ParsedUrl.offAuthorityHost, ParsedUrl.cchAuthorityHost); 97 98 /** 99 * Ensures that we've got a 'Content-Length' header. 100 * 101 * @returns IPRT status code. 102 * @param hHttp The HTTP client handle. 103 * @param cbContent The content length. 104 */ 105 static int ociSignRequestEnsureContentLength(RTHTTP hHttp, uint64_t cbContent) 106 { 107 if (RTHttpGetHeader(hHttp, RT_STR_TUPLE("Content-Length"))) 108 return VINF_SUCCESS; 109 char szValue[64]; 110 ssize_t cchValue = RTStrFormatU64(szValue, sizeof(szValue), cbContent, 10, 0, 0, 0); 111 AssertRCReturn((int)cchValue, (int)cchValue); 112 return RTHttpAddHeader(hHttp, "Content-Length", szValue, cchValue, RTHTTPADDHDR_F_BACK); 113 } 114 115 116 /** 117 * Ensures that we've got a host header. 118 * 119 * @returns IPRT status code. 120 * @param hHttp The HTTP client handle. 121 * @param pszUrl The URL. 122 */ 123 static int ociSignRequestEnsureHost(RTHTTP hHttp, const char *pszUrl) 124 { 125 if (RTHttpGetHeader(hHttp, RT_STR_TUPLE("host"))) 126 return VINF_SUCCESS; 127 128 RTURIPARSED ParsedUrl; 129 int rc = RTUriParse(pszUrl, &ParsedUrl); 133 130 AssertRCReturn(rc, rc); 134 131 135 /* 136 * Content-Length - required for POST and PUT. 137 * Note! We add it to the digest a little bit later to preserve documented order.. 138 */ 139 static char s_szContentLength[] = "content-length"; 140 const char *pszContentLength = RTHttpGetHeader(a_hHttp, RT_STR_TUPLE(s_szContentLength)); 141 if ( !pszContentLength 142 && ( a_rStrXmitBody.isNotEmpty() 143 || a_enmHttpMethod == RTHTTPMETHOD_POST 144 || a_enmHttpMethod == RTHTTPMETHOD_PUT)) 145 { 146 RTStrPrintf(szTmp, sizeof(szTmp), "%zu", a_rStrXmitBody.length()); 147 148 rc = RTHttpAddHeader(a_hHttp, s_szContentLength, szTmp, RTHTTPADDHDR_F_BACK); 149 AssertRCReturn(rc, rc); 150 pszContentLength = RTHttpGetHeader(a_hHttp, RT_STR_TUPLE(s_szContentLength)); 151 AssertPtrReturn(pszContentLength, VERR_REST_INTERAL_ERROR_4); 152 } 153 if (pszContentLength) 154 { 155 rc = ociSignRequestAddField(hDigest, pStrAuth, RT_STR_TUPLE(s_szContentLength), pszContentLength, RTSTR_MAX); 156 AssertRCReturn(rc, rc); 157 } 158 159 /* 160 * x-content-sha256 - required when there is a body. 161 */ 162 static char s_szXContentSha256[] = "x-content-sha256"; 163 pszValue = RTHttpGetHeader(a_hHttp, RT_STR_TUPLE(s_szXContentSha256)); 164 if ( !pszValue 165 && pszContentLength 166 && strcmp(pszContentLength, "0") != 0 167 && !(a_fFlags & RTCRestClientApiBase::kDoCall_OciReqSignExcludeBody) ) 168 { 169 #ifdef RT_STRICT 170 RTStrPrintf(szTmp, sizeof(szTmp), "%zu", a_rStrXmitBody.length()); 171 AssertMsgReturn(strcmp(szTmp, pszContentLength) == 0, ("szTmp=%s; pszContentLength=%s\n", szTmp, pszContentLength), 172 VERR_REST_INTERAL_ERROR_5); 173 #endif 174 175 uint8_t abHash[RTSHA256_HASH_SIZE]; 176 RTSha256(a_rStrXmitBody.c_str(), a_rStrXmitBody.length(), abHash); 177 rc = RTBase64EncodeEx(abHash, sizeof(abHash), RTBASE64_FLAGS_NO_LINE_BREAKS, szTmp, sizeof(szTmp), NULL); 178 179 rc = RTHttpAddHeader(a_hHttp, s_szXContentSha256, szTmp, RTHTTPADDHDR_F_BACK); 180 AssertRCReturn(rc, rc); 181 pszValue = RTHttpGetHeader(a_hHttp, RT_STR_TUPLE(s_szXContentSha256)); 182 AssertPtrReturn(pszValue, VERR_REST_INTERAL_ERROR_4); 183 } 184 if (pszValue) 185 { 186 rc = ociSignRequestAddField(hDigest, pStrAuth, RT_STR_TUPLE(s_szXContentSha256), pszValue, RTSTR_MAX); 187 AssertRCReturn(rc, rc); 188 } 189 190 /* 191 * Content-Type 192 */ 193 pszValue = RTHttpGetHeader(a_hHttp, RT_STR_TUPLE("content-type")); 194 Assert( pszValue 195 || !pszContentLength 196 || strcmp(pszContentLength, "0") == 0); 197 if (pszValue) 198 { 199 rc = ociSignRequestAddField(hDigest, pStrAuth, RT_STR_TUPLE("content-type"), pszValue, RTSTR_MAX); 200 AssertRCReturn(rc, rc); 201 } 202 203 /* 204 * x-date or/and date. 205 */ 206 static char const s_szDate[] = "date"; 207 pszValue = RTHttpGetHeader(a_hHttp, RT_STR_TUPLE(s_szDate)); 208 static char const s_szXDate[] = "x-date"; 209 const char *pszXDate = RTHttpGetHeader(a_hHttp, RT_STR_TUPLE(s_szXDate)); 210 if (!pszValue && !pszXDate) 211 { 212 RTTIMESPEC TimeSpec; 213 RTTIME Time; 214 RT_ZERO(Time); /* paranoia */ 215 RTTimeExplode(&Time, RTTimeNow(&TimeSpec)); 216 217 /* Date format: Tue, 16 Nov 2018 12:15:00 GMT */ 218 /** @todo make RTTimeXxx api that does exactly this (RFC-1123). */ 219 static const char * const s_apszWeekDays[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; 220 static const char * const s_apszMonths[] = { "000", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; 221 RTStrPrintf(szTmp, sizeof(szTmp), "%s, %u %s %u %u02:%u02:%u02 GMT", 222 s_apszWeekDays[Time.u8WeekDay], Time.u8MonthDay, s_apszMonths[Time.u8Month], Time.i32Year, 223 Time.u8Hour, Time.u8Minute, Time.u8Second); 224 225 rc = RTHttpAddHeader(a_hHttp, s_szXDate, szTmp, RTHTTPADDHDR_F_BACK); 226 AssertRCReturn(rc, rc); 227 pszXDate = RTHttpGetHeader(a_hHttp, RT_STR_TUPLE(s_szXDate)); 228 AssertPtrReturn(pszXDate, VERR_REST_INTERAL_ERROR_4); 229 } 230 if (pszXDate) 231 rc = ociSignRequestAddField(hDigest, pStrAuth, RT_STR_TUPLE(s_szXDate), pszXDate, RTSTR_MAX); 232 else 233 rc = ociSignRequestAddField(hDigest, pStrAuth, RT_STR_TUPLE(s_szDate), pszValue, RTSTR_MAX); 234 AssertRCReturn(rc, rc); 235 236 /* 237 * We probably should add all parameter fields ... 238 */ 239 /** @todo sign more header fields */ 240 return VINF_SUCCESS; 132 return RTHttpAddHeader(hHttp, "host", &pszUrl[ParsedUrl.offAuthorityHost], ParsedUrl.cchAuthorityHost, RTHTTPADDHDR_F_BACK); 241 133 } 242 134 … … 247 139 { 248 140 /* 249 * Start the signature.141 * First make sure required headers are present, adding them as needed. 250 142 */ 251 RTCString strAuth; 252 int rc = strAuth.printfNoThrow("Signature version=\"1\",keyId=\"%s\",algorithm=\"rsa-sha256\",headers=\"", 253 a_rStrKeyId.c_str()); 143 int rc = ociSignRequestEnsureHost(a_hHttp, a_rStrFullUrl.c_str()); 254 144 if (RT_SUCCESS(rc)) 255 145 { 256 RTCRDIGEST hDigest; 257 rc = RTCrDigestCreateByType(&hDigest, RTDIGESTTYPE_SHA256); 146 if ( a_rStrXmitBody.isNotEmpty() 147 || a_enmHttpMethod == RTHTTPMETHOD_POST 148 || a_enmHttpMethod == RTHTTPMETHOD_PUT) 149 rc = ociSignRequestEnsureContentLength(a_hHttp, a_rStrXmitBody.length()); 150 if ( RT_SUCCESS(rc) 151 && a_rStrXmitBody.isNotEmpty()) 152 rc = ociSignRequestEnsureXContentSha256(a_hHttp, a_rStrXmitBody.c_str(), a_rStrXmitBody.length()); 153 if (RT_SUCCESS(rc)) 154 rc = ociSignRequestEnsureDateOrXDate(a_hHttp); 258 155 if (RT_SUCCESS(rc)) 259 156 { 260 157 /* 261 * Call worker for adding the fields. Simpler to clean up hDigest this way.158 * Do the signing. 262 159 */ 263 rc = ociSignRequestAddAllFields(a_hHttp, a_rStrFullUrl, a_enmHttpMethod, a_rStrXmitBody, a_fFlags, hDigest, &strAuth); 264 if (RT_SUCCESS(rc)) 265 { 266 /* 267 * Do the signing. 268 */ 269 RTCRPKIXSIGNATURE hSigner; 270 rc = RTCrPkixSignatureCreateByObjIdString(&hSigner, RTCR_PKCS1_SHA256_WITH_RSA_OID, a_hKey, 271 NULL, true /*fSigning*/); 272 AssertRC(rc); 273 if (RT_SUCCESS(rc)) 274 { 275 /* Figure the signature size first. */ 276 size_t cbSignature = 0; 277 RTCrPkixSignatureSign(hSigner, hDigest, NULL, &cbSignature); 278 if (cbSignature == 0) 279 cbSignature = _32K; 280 size_t cbBase64Sign = RTBase64EncodedLengthEx(cbSignature, RTBASE64_FLAGS_NO_LINE_BREAKS) + 2; 281 282 /* Allocate temporary heap buffer and calc the signature. */ 283 uint8_t *pbSignature = (uint8_t *)RTMemTmpAllocZ(cbSignature + cbBase64Sign); 284 if (pbSignature) 285 { 286 size_t cbActual = cbSignature; 287 rc = RTCrPkixSignatureSign(hSigner, hDigest, pbSignature, &cbActual); 288 AssertRC(rc); 289 if (RT_SUCCESS(rc)) 290 { 291 /* 292 * Convert the signature to Base64 and add it to the auth value. 293 */ 294 char *pszBase64 = (char *)(pbSignature + cbSignature); 295 rc = RTBase64EncodeEx(pbSignature, cbActual, RTBASE64_FLAGS_NO_LINE_BREAKS, 296 pszBase64, cbBase64Sign, NULL); 297 AssertRC(rc); 298 if (RT_SUCCESS(rc)) 299 { 300 rc = strAuth.appendPrintfNoThrow("\",signature=\"Base64(RSA-SHA256(%s))\"", pszBase64); 301 if (RT_SUCCESS(rc)) 302 { 303 /* 304 * Finally, add the authorization header. 305 */ 306 rc = RTHttpAddHeader(a_hHttp, "Authorization", strAuth.c_str(), RTHTTPADDHDR_F_FRONT); 307 AssertRC(rc); 308 } 309 } 310 } 311 RT_BZERO(pbSignature, cbSignature + cbBase64Sign); 312 RTMemTmpFree(pbSignature); 313 } 314 else 315 rc = VERR_NO_TMP_MEMORY; 316 uint32_t cRefs = RTCrPkixSignatureRelease(hSigner); 317 Assert(cRefs == 0); NOREF(cRefs); 318 } 319 } 320 RTCrDigestRelease(hDigest); 160 rc = RTHttpSignHeaders(a_hHttp, a_enmHttpMethod, a_rStrFullUrl.c_str(), a_hKey, a_rStrKeyId.c_str(), 0 /*fFlags*/); 321 161 } 322 162 } 163 RT_NOREF_PV(a_fFlags); /* We don't need to use the kDoCall_OciReqSignExcludeBody flag it turns out. */ 323 164 return rc; 324 165 } -
trunk/src/VBox/Runtime/common/rest/RTCRestClientRequestBase.cpp
r74060 r74070 193 193 AssertRCReturn(rc, rc); 194 194 195 rc = RTHttpAddHeader(a_hHttp, a_paHeaderParams[i].pszName, strTmpVal.c_str(), RTHTTPADDHDR_F_BACK); 195 rc = RTHttpAddHeader(a_hHttp, a_paHeaderParams[i].pszName, strTmpVal.c_str(), strTmpVal.length(), 196 RTHTTPADDHDR_F_BACK); 196 197 AssertRCReturn(rc, rc); 197 198 } … … 217 218 AssertRCReturn(rc, rc); 218 219 219 rc = RTHttpAddHeader(a_hHttp, strTmpName.c_str(), strTmpVal.c_str(), RTHTTPADDHDR_F_BACK); 220 rc = RTHttpAddHeader(a_hHttp, strTmpName.c_str(), strTmpVal.c_str(), strTmpVal.length(), 221 RTHTTPADDHDR_F_BACK); 220 222 AssertRCReturn(rc, rc); 221 223 } -
trunk/src/VBox/Runtime/generic/http-curl.cpp
r74064 r74070 2153 2153 2154 2154 2155 RTR3DECL(int) RTHttpAddHeader(RTHTTP hHttp, const char *pszField, const char *pszValue, uint32_t fFlags)2155 RTR3DECL(int) RTHttpAddHeader(RTHTTP hHttp, const char *pszField, const char *pszValue, size_t cchValue, uint32_t fFlags) 2156 2156 { 2157 2157 /* … … 2175 2175 2176 2176 AssertPtr(pszValue); 2177 size_t const cchValue = strlen(pszValue); 2177 if (cchValue == RTSTR_MAX) 2178 cchValue = strlen(pszValue); 2178 2179 2179 2180 /*
Note:
See TracChangeset
for help on using the changeset viewer.