|
@@ -53,6 +53,10 @@
|
|
|
#define REQUEST_STACK_SIZE 32
|
|
|
#define SIGNATURE_SCOPE_SIZE 64
|
|
|
|
|
|
+#ifdef WINSCP
|
|
|
+#define SIGNATURE_DEBUG
|
|
|
+#endif
|
|
|
+
|
|
|
static char userAgentG[USER_AGENT_SIZE];
|
|
|
|
|
|
#ifndef WINSCP
|
|
@@ -130,7 +134,7 @@ typedef struct RequestComputedValues
|
|
|
char rangeHeader[128];
|
|
|
|
|
|
// Authorization header
|
|
|
- char authorizationHeader[1024];
|
|
|
+ char authorizationHeader[4096];
|
|
|
|
|
|
// Request date stamp
|
|
|
char requestDateISO8601[64];
|
|
@@ -184,15 +188,15 @@ static int neon_header_func(void * userdata, ne_request * NeonRequest, const ne_
|
|
|
Request *request = (Request *) userdata;
|
|
|
|
|
|
void * cursor = NULL;
|
|
|
- const char * header_name;
|
|
|
- const char * header_value;
|
|
|
- while ((cursor = ne_response_header_iterate(NeonRequest, cursor, &header_name, &header_value)) != NULL)
|
|
|
- {
|
|
|
- response_headers_handler_add
|
|
|
+ const char * header_name;
|
|
|
+ const char * header_value;
|
|
|
+ while ((cursor = ne_response_header_iterate(NeonRequest, cursor, &header_name, &header_value)) != NULL)
|
|
|
+ {
|
|
|
+ response_headers_handler_add
|
|
|
(&(request->responseHeadersHandler), header_name, header_value);
|
|
|
- }
|
|
|
-
|
|
|
- return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -249,15 +253,15 @@ static int neon_write_func(void * data, const char * buf, size_t len)
|
|
|
|
|
|
// WinSCP (ignore empty responses)
|
|
|
if (len == 0)
|
|
|
- {
|
|
|
- return 0;
|
|
|
+ {
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
request_headers_done(request);
|
|
|
|
|
|
if (request->requestContext->responseDataCallback != NULL)
|
|
|
{
|
|
|
- request->requestContext->responseDataCallback(buf, len, request->requestContext->responseDataCallbackData);
|
|
|
+ request->requestContext->responseDataCallback(buf, len, request->requestContext->responseDataCallbackData);
|
|
|
}
|
|
|
|
|
|
if (request->status != S3StatusOK) {
|
|
@@ -294,8 +298,9 @@ static S3Status append_amz_header(RequestComputedValues *values,
|
|
|
values->amzHeaders[values->amzHeadersCount++] = &(values->amzHeadersRaw[rawPos]);
|
|
|
|
|
|
const char *headerStr = headerName;
|
|
|
+
|
|
|
+ char headerNameWithPrefix[S3_MAX_METADATA_SIZE - sizeof(": v")];
|
|
|
if (addPrefix) {
|
|
|
- char headerNameWithPrefix[S3_MAX_METADATA_SIZE - sizeof(": v")];
|
|
|
snprintf(headerNameWithPrefix, sizeof(headerNameWithPrefix),
|
|
|
S3_METADATA_HEADER_NAME_PREFIX "%s", headerName);
|
|
|
headerStr = headerNameWithPrefix;
|
|
@@ -369,6 +374,9 @@ static S3Status compose_amz_headers(const RequestParams *params,
|
|
|
case S3CannedAclPublicReadWrite:
|
|
|
cannedAclString = "public-read-write";
|
|
|
break;
|
|
|
+ case S3CannedAclBucketOwnerFullControl:
|
|
|
+ cannedAclString = "bucket-owner-full-control";
|
|
|
+ break;
|
|
|
default: // S3CannedAclAuthenticatedRead
|
|
|
cannedAclString = "authenticated-read";
|
|
|
break;
|
|
@@ -766,13 +774,13 @@ static void canonicalize_signature_headers(RequestComputedValues *values)
|
|
|
// Canonicalizes the resource into params->canonicalizedResource
|
|
|
static void canonicalize_resource(const S3BucketContext *context,
|
|
|
const char *urlEncodedKey,
|
|
|
- char *buffer)
|
|
|
+ char *buffer, unsigned int buffer_max)
|
|
|
{
|
|
|
int len = 0;
|
|
|
|
|
|
*buffer = 0;
|
|
|
|
|
|
-#define append(str) len += sprintf(&(buffer[len]), "%s", str)
|
|
|
+#define append(str) len += snprintf(&(buffer[len]), buffer_max - len, "%s", str)
|
|
|
|
|
|
if (context->uriStyle == S3UriStylePath) {
|
|
|
if (context->bucketName && context->bucketName[0]) {
|
|
@@ -790,10 +798,12 @@ static void canonicalize_resource(const S3BucketContext *context,
|
|
|
#undef append
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-static void sort_query_string(const char *queryString, char *result)
|
|
|
+static void sort_query_string(const char *queryString, char *result,
|
|
|
+ unsigned int result_size)
|
|
|
{
|
|
|
+#ifdef SIGNATURE_DEBUG
|
|
|
ne_debug(NULL, NE_DBG_HTTPBODY, "\n--\nsort_and_urlencode\nqueryString: %s\n", queryString);
|
|
|
+#endif
|
|
|
|
|
|
unsigned int numParams = 1;
|
|
|
const char *tmp = queryString;
|
|
@@ -804,10 +814,10 @@ static void sort_query_string(const char *queryString, char *result)
|
|
|
|
|
|
const char** params = new const char*[numParams]; // WINSCP (heap allocation)
|
|
|
|
|
|
- char * tokenized = new char[strlen(queryString) + 1]; // WINSCP (heap allocation)
|
|
|
- strncpy(tokenized, queryString, strlen(queryString) + 1);
|
|
|
-
|
|
|
- char *tok = tokenized;
|
|
|
+ // Where did strdup go?!??
|
|
|
+ int queryStringLen = strlen(queryString);
|
|
|
+ char *tok = (char *) malloc(queryStringLen + 1);
|
|
|
+ strcpy(tok, queryString);
|
|
|
const char *token = NULL;
|
|
|
char *save = NULL;
|
|
|
unsigned int i = 0;
|
|
@@ -819,37 +829,47 @@ static void sort_query_string(const char *queryString, char *result)
|
|
|
|
|
|
kv_gnome_sort(params, numParams, '=');
|
|
|
|
|
|
+#ifdef SIGNATURE_DEBUG
|
|
|
for (i = 0; i < numParams; i++) {
|
|
|
ne_debug(NULL, NE_DBG_HTTPBODY, "%d: %s\n", i, params[i]);
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
+ // All params are urlEncoded
|
|
|
+#define append(str) len += snprintf(&(result[len]), result_size - len, "%s", str)
|
|
|
unsigned int pi = 0;
|
|
|
+ unsigned int len = 0;
|
|
|
for (; pi < numParams; pi++) {
|
|
|
- // All params are urlEncoded
|
|
|
- strncat(result, params[pi], strlen(params[pi]));
|
|
|
- strncat(result, "&", 1);
|
|
|
+ append(params[pi]);
|
|
|
+ append("&");
|
|
|
+ }
|
|
|
+ // Take off the extra '&'
|
|
|
+ if (len > 0) {
|
|
|
+ result[len - 1] = 0;
|
|
|
}
|
|
|
- result[strlen(result) - 1] = '\0';
|
|
|
+#undef append
|
|
|
|
|
|
delete[] params; // WINSCP (heap allocation)
|
|
|
- delete[] tokenized;
|
|
|
+ free(tok);
|
|
|
}
|
|
|
|
|
|
|
|
|
// Canonicalize the query string part of the request into a buffer
|
|
|
static void canonicalize_query_string(const char *queryParams,
|
|
|
- const char *subResource, char *buffer)
|
|
|
+ const char *subResource,
|
|
|
+ char *buffer, unsigned int buffer_size)
|
|
|
{
|
|
|
int len = 0;
|
|
|
|
|
|
*buffer = 0;
|
|
|
|
|
|
-#define append(str) len += sprintf(&(buffer[len]), "%s", str)
|
|
|
+#define append(str) len += snprintf(&(buffer[len]), buffer_size - len, "%s", str)
|
|
|
|
|
|
if (queryParams && queryParams[0]) {
|
|
|
- char * sorted = new char[strlen(queryParams) * 2]; // WINSCP (heap allocation)
|
|
|
+ int sortedLen = strlen(queryParams) * 2;
|
|
|
+ char * sorted = new char[sortedLen]; // WINSCP (heap allocation)
|
|
|
sorted[0] = '\0';
|
|
|
- sort_query_string(queryParams, sorted);
|
|
|
+ sort_query_string(queryParams, sorted, sortedLen);
|
|
|
append(sorted);
|
|
|
delete[] sorted; // WINSCP (heap allocation)
|
|
|
}
|
|
@@ -945,7 +965,9 @@ static S3Status compose_auth_header(const RequestParams *params,
|
|
|
|
|
|
buf_append(canonicalRequest, "%s", values->payloadHash);
|
|
|
|
|
|
+#ifdef SIGNATURE_DEBUG
|
|
|
ne_debug(NULL, NE_DBG_HTTPBODY, "--\nCanonical Request:\n%s\n", canonicalRequest);
|
|
|
+#endif
|
|
|
|
|
|
len = 0;
|
|
|
unsigned char canonicalRequestHash[S3_SHA256_DIGEST_LENGTH];
|
|
@@ -968,17 +990,20 @@ static S3Status compose_auth_header(const RequestParams *params,
|
|
|
if (params->bucketContext.authRegion) {
|
|
|
awsRegion = params->bucketContext.authRegion;
|
|
|
}
|
|
|
- char scope[SIGNATURE_SCOPE_SIZE + 1];
|
|
|
+ char scope[sizeof(values->requestDateISO8601) + sizeof(awsRegion) +
|
|
|
+ sizeof("//s3/aws4_request") + 1];
|
|
|
snprintf(scope, sizeof(scope), "%.8s/%s/s3/aws4_request",
|
|
|
values->requestDateISO8601, awsRegion);
|
|
|
|
|
|
- const int stringToSignLen = 17 + 17 + SIGNATURE_SCOPE_SIZE + 1
|
|
|
- + strlen(canonicalRequestHashHex); // WINSCP (heap allocation)
|
|
|
+ const int stringToSignLen = 17 + 17 + sizeof(values->requestDateISO8601) +
|
|
|
+ sizeof(scope) + sizeof(canonicalRequestHashHex) + 1; // WINSCP (heap allocation)
|
|
|
char * stringToSign = new char[stringToSignLen];
|
|
|
snprintf(stringToSign, stringToSignLen, "AWS4-HMAC-SHA256\n%s\n%s\n%s",
|
|
|
values->requestDateISO8601, scope, canonicalRequestHashHex);
|
|
|
|
|
|
+#ifdef SIGNATURE_DEBUG
|
|
|
ne_debug(NULL, NE_DBG_HTTPBODY, "--\nString to Sign:\n%s\n", stringToSign);
|
|
|
+#endif
|
|
|
|
|
|
const char *secretAccessKey = params->bucketContext.secretAccessKey;
|
|
|
const int accessKeyLen = strlen(secretAccessKey) + 5; // WINSCP (heap allocation)
|
|
@@ -1040,14 +1065,15 @@ static S3Status compose_auth_header(const RequestParams *params,
|
|
|
"%s/%.8s/%s/s3/aws4_request", params->bucketContext.accessKeyId,
|
|
|
values->requestDateISO8601, awsRegion);
|
|
|
|
|
|
- snprintf(
|
|
|
- values->authorizationHeader,
|
|
|
- sizeof(values->authorizationHeader),
|
|
|
- "Authorization: AWS4-HMAC-SHA256 Credential=%s,SignedHeaders=%s,Signature=%s",
|
|
|
- values->authCredential, values->signedHeaders,
|
|
|
- values->requestSignatureHex);
|
|
|
+ snprintf(values->authorizationHeader,
|
|
|
+ sizeof(values->authorizationHeader),
|
|
|
+ "Authorization: AWS4-HMAC-SHA256 Credential=%s,SignedHeaders=%s,Signature=%s",
|
|
|
+ values->authCredential, values->signedHeaders,
|
|
|
+ values->requestSignatureHex);
|
|
|
|
|
|
+#ifdef SIGNATURE_DEBUG
|
|
|
ne_debug(NULL, NE_DBG_HTTPBODY, "--\nAuthorization Header:\n%s\n", values->authorizationHeader);
|
|
|
+#endif
|
|
|
|
|
|
return S3StatusOK;
|
|
|
|
|
@@ -1129,7 +1155,7 @@ int neon_ssl_callback(void * user_data, int failures, const ne_ssl_certificate *
|
|
|
int result = NE_ERROR;
|
|
|
if (request->requestContext->sslCallback != NULL)
|
|
|
{
|
|
|
- result = request->requestContext->sslCallback(failures, certificate, request->requestContext->sslCallbackData);
|
|
|
+ result = request->requestContext->sslCallback(failures, certificate, request->requestContext->sslCallbackData);
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
@@ -1148,12 +1174,12 @@ static S3Status setup_neon(Request *request,
|
|
|
if (ne_uri_parse(request->uri, &uri) != 0)
|
|
|
{
|
|
|
return S3StatusFailedToInitializeRequest;
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
int port = uri.port;
|
|
|
if (port == 0)
|
|
|
- {
|
|
|
- port = ne_uri_defaultport(uri.scheme);
|
|
|
+ {
|
|
|
+ port = ne_uri_defaultport(uri.scheme);
|
|
|
}
|
|
|
request->NeonSession = ne_session_create(uri.scheme, uri.host, port);
|
|
|
if (request->requestContext->sessionCallback != NULL)
|
|
@@ -1185,9 +1211,9 @@ static S3Status setup_neon(Request *request,
|
|
|
ne_buffer * buf = ne_buffer_create();
|
|
|
ne_buffer_zappend(buf, uri.path);
|
|
|
if (uri.query != NULL)
|
|
|
- {
|
|
|
- ne_buffer_concat(buf, "?", uri.query, NULL);
|
|
|
- }
|
|
|
+ {
|
|
|
+ ne_buffer_concat(buf, "?", uri.query, NULL);
|
|
|
+ }
|
|
|
request->NeonRequest = ne_request_create(request->NeonSession, method, buf->data);
|
|
|
ne_buffer_destroy(buf);
|
|
|
ne_uri_free(&uri);
|
|
@@ -1224,8 +1250,8 @@ static S3Status setup_neon(Request *request,
|
|
|
|
|
|
if (params->timeoutMs > 0) {
|
|
|
ne_set_read_timeout(request->NeonSession, params->timeoutMs);
|
|
|
-
|
|
|
- ne_set_connect_timeout(request->NeonSession, params->timeoutMs);
|
|
|
+
|
|
|
+ ne_set_connect_timeout(request->NeonSession, params->timeoutMs);
|
|
|
}
|
|
|
|
|
|
#define do_add_header(header) \
|
|
@@ -1286,8 +1312,8 @@ static void request_deinitialize(Request *request)
|
|
|
|
|
|
|
|
|
static S3Status request_get(const RequestParams *params,
|
|
|
- S3RequestContext *context,
|
|
|
const RequestComputedValues *values,
|
|
|
+ S3RequestContext *context, // WINSCP (non-const)
|
|
|
Request **reqReturn)
|
|
|
{
|
|
|
Request *request = 0;
|
|
@@ -1349,6 +1375,17 @@ static S3Status request_get(const RequestParams *params,
|
|
|
return status;
|
|
|
}
|
|
|
|
|
|
+#ifndef WINSCP
|
|
|
+ if (context && context->setupCurlCallback &&
|
|
|
+ (status = context->setupCurlCallback(
|
|
|
+ context->curlm, request->curl,
|
|
|
+ context->setupCurlCallbackData)) != S3StatusOK) {
|
|
|
+ curl_easy_cleanup(request->curl);
|
|
|
+ free(request);
|
|
|
+ return status;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
request->propertiesCallback = params->propertiesCallback;
|
|
|
|
|
|
request->toS3Callback = params->toS3Callback;
|
|
@@ -1428,8 +1465,8 @@ S3Status request_api_initialize(const char *userAgentInfo, int flags,
|
|
|
userAgentInfo = "Unknown";
|
|
|
}
|
|
|
|
|
|
- char platform[96];
|
|
|
struct utsname utsn;
|
|
|
+ char platform[sizeof(utsn.sysname) + 1 + sizeof(utsn.machine) + 1];
|
|
|
if (uname(&utsn)) {
|
|
|
snprintf(platform, sizeof(platform), "Unknown");
|
|
|
}
|
|
@@ -1480,14 +1517,14 @@ static S3Status setup_request(const RequestParams *params,
|
|
|
struct tm gmt;
|
|
|
memset(&gmt, 0, sizeof(gmt));
|
|
|
gmt.tm_sec=st.wSecond;
|
|
|
- gmt.tm_min=st.wMinute;
|
|
|
- gmt.tm_hour=st.wHour;
|
|
|
- gmt.tm_mday=st.wDay;
|
|
|
- gmt.tm_mon=st.wMonth-1;
|
|
|
- gmt.tm_year=(st.wYear>=1900?st.wYear-1900:0);
|
|
|
- gmt.tm_wday=st.wDayOfWeek;
|
|
|
- gmt.tm_yday=-1; /* GetSystemTime doesn't tell us */
|
|
|
- gmt.tm_isdst=0; /* GetSystemTime doesn't tell us */
|
|
|
+ gmt.tm_min=st.wMinute;
|
|
|
+ gmt.tm_hour=st.wHour;
|
|
|
+ gmt.tm_mday=st.wDay;
|
|
|
+ gmt.tm_mon=st.wMonth-1;
|
|
|
+ gmt.tm_year=(st.wYear>=1900?st.wYear-1900:0);
|
|
|
+ gmt.tm_wday=st.wDayOfWeek;
|
|
|
+ gmt.tm_yday=-1; /* GetSystemTime doesn't tell us */
|
|
|
+ gmt.tm_isdst=0; /* GetSystemTime doesn't tell us */
|
|
|
strftime(computed->requestDateISO8601, sizeof(computed->requestDateISO8601),
|
|
|
"%Y%m%dT%H%M%SZ", &gmt);
|
|
|
|
|
@@ -1512,20 +1549,24 @@ static S3Status setup_request(const RequestParams *params,
|
|
|
|
|
|
// Compute the canonicalized resource
|
|
|
canonicalize_resource(¶ms->bucketContext, computed->urlEncodedKey,
|
|
|
- computed->canonicalURI);
|
|
|
+ computed->canonicalURI,
|
|
|
+ sizeof(computed->canonicalURI));
|
|
|
canonicalize_query_string(params->queryParams, params->subResource,
|
|
|
- computed->canonicalQueryString);
|
|
|
+ computed->canonicalQueryString,
|
|
|
+ sizeof(computed->canonicalQueryString));
|
|
|
|
|
|
// Compose Authorization header
|
|
|
if ((status = compose_auth_header(params, computed)) != S3StatusOK) {
|
|
|
return status;
|
|
|
}
|
|
|
|
|
|
+#ifdef SIGNATURE_DEBUG
|
|
|
int i = 0;
|
|
|
ne_debug(NULL, NE_DBG_HTTPBODY, "\n--\nAMZ Headers:\n");
|
|
|
for (; i < computed->amzHeadersCount; i++) {
|
|
|
ne_debug(NULL, NE_DBG_HTTPBODY, "%s\n", computed->amzHeaders[i]);
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
return status;
|
|
|
}
|
|
@@ -1547,7 +1588,7 @@ void request_perform(const RequestParams *params, S3RequestContext *context)
|
|
|
}
|
|
|
|
|
|
// Get an initialized Request structure now
|
|
|
- if ((status = request_get(params, context /*WINSCP*/, &computed, &request)) != S3StatusOK) {
|
|
|
+ if ((status = request_get(params, &computed, context, &request)) != S3StatusOK) {
|
|
|
return_status(status);
|
|
|
}
|
|
|
// WINSCP (we should always verify the peer)
|
|
@@ -1607,8 +1648,8 @@ void request_finish(Request *request, NeonCode code)
|
|
|
if (request->status == S3StatusOK) {
|
|
|
if (code != NE_OK) {
|
|
|
request->status = request_neon_code_to_status(code);
|
|
|
- if (request->errorParser.s3ErrorDetails.message == NULL) {
|
|
|
- const char * neonError = ne_get_error(request->NeonSession);
|
|
|
+ if (request->errorParser.s3ErrorDetails.message == NULL) {
|
|
|
+ const char * neonError = ne_get_error(request->NeonSession);
|
|
|
int allFit;
|
|
|
string_buffer_append(request->statusMessage, neonError, strlen(neonError), allFit);
|
|
|
request->errorParser.s3ErrorDetails.message = request->statusMessage;
|
|
@@ -1732,12 +1773,16 @@ S3Status S3_generate_authenticated_query_string
|
|
|
}
|
|
|
|
|
|
// Finally, compose the URI, with params
|
|
|
- char queryParams[sizeof("X-Amz-Algorithm=AWS4-HMAC-SHA256")
|
|
|
- + sizeof("&X-Amz-Credential=") + MAX_CREDENTIAL_SIZE
|
|
|
- + sizeof("&X-Amz-Date=") + 16 + sizeof("&X-Amz-Expires=") + 6
|
|
|
- + sizeof("&X-Amz-SignedHeaders=") + 128 + sizeof("&X-Amz-Signature=")
|
|
|
- + sizeof(computed.requestSignatureHex) + 1];
|
|
|
-
|
|
|
+ char queryParams[sizeof("X-Amz-Algorithm=AWS4-HMAC-SHA256") +
|
|
|
+ sizeof("&X-Amz-Credential=") +
|
|
|
+ sizeof(computed.authCredential) +
|
|
|
+ sizeof("&X-Amz-Date=") +
|
|
|
+ sizeof(computed.requestDateISO8601) +
|
|
|
+ sizeof("&X-Amz-Expires=") + 64 +
|
|
|
+ sizeof("&X-Amz-SignedHeaders=") +
|
|
|
+ sizeof(computed.signedHeaders) +
|
|
|
+ sizeof("&X-Amz-Signature=") +
|
|
|
+ sizeof(computed.requestSignatureHex) + 1];
|
|
|
snprintf(queryParams, sizeof(queryParams),
|
|
|
"X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=%s"
|
|
|
"&X-Amz-Date=%s&X-Amz-Expires=%d"
|