Beispiel #1
0
static hFILE * s3_rewrite(const char *s3url, const char *mode, va_list *argsp)
{
    const char *bucket, *path;
    char *header_list[4], **header = header_list;

    kstring_t url = { 0, 0, NULL };
    kstring_t profile = { 0, 0, NULL };
    kstring_t host_base = { 0, 0, NULL };
    kstring_t token_hdr = { 0, 0, NULL };

    s3_auth_data *ad = calloc(1, sizeof(*ad));

    if (!ad)
        return NULL;
    ad->mode = strchr(mode, 'r') ? 'r' : 'w';

    // Our S3 URL format is s3[+SCHEME]://[ID[:SECRET[:TOKEN]]@]BUCKET/PATH

    if (s3url[2] == '+') {
        bucket = strchr(s3url, ':') + 1;
        kputsn(&s3url[3], bucket - &s3url[3], &url);
    }
    else {
        kputs("https:", &url);
        bucket = &s3url[3];
    }
    while (*bucket == '/') kputc(*bucket++, &url);

    path = bucket + strcspn(bucket, "/?#@");
    if (*path == '@') {
        const char *colon = strpbrk(bucket, ":@");
        if (*colon != ':') {
            urldecode_kput(bucket, colon - bucket, &profile);
        }
        else {
            const char *colon2 = strpbrk(&colon[1], ":@");
            urldecode_kput(bucket, colon - bucket, &ad->id);
            urldecode_kput(&colon[1], colon2 - &colon[1], &ad->secret);
            if (*colon2 == ':')
                urldecode_kput(&colon2[1], path - &colon2[1], &ad->token);
        }

        bucket = &path[1];
        path = bucket + strcspn(bucket, "/?#");
    }
    else {
        // If the URL has no ID[:SECRET]@, consider environment variables.
        const char *v;
        if ((v = getenv("AWS_ACCESS_KEY_ID")) != NULL) kputs(v, &ad->id);
        if ((v = getenv("AWS_SECRET_ACCESS_KEY")) != NULL) kputs(v, &ad->secret);
        if ((v = getenv("AWS_SESSION_TOKEN")) != NULL) kputs(v, &ad->token);

        if ((v = getenv("AWS_DEFAULT_PROFILE")) != NULL) kputs(v, &profile);
        else if ((v = getenv("AWS_PROFILE")) != NULL) kputs(v, &profile);
        else kputs("default", &profile);
    }

    if (ad->id.l == 0) {
        const char *v = getenv("AWS_SHARED_CREDENTIALS_FILE");
        parse_ini(v? v : "~/.aws/credentials", profile.s,
                  "aws_access_key_id", &ad->id,
                  "aws_secret_access_key", &ad->secret,
                  "aws_session_token", &ad->token, NULL);
    }
    if (ad->id.l == 0)
        parse_ini("~/.s3cfg", profile.s, "access_key", &ad->id,
                  "secret_key", &ad->secret, "access_token", &ad->token,
                  "host_base", &host_base, NULL);
    if (ad->id.l == 0)
        parse_simple("~/.awssecret", &ad->id, &ad->secret);

    if (host_base.l == 0)
        kputs("s3.amazonaws.com", &host_base);
    // Use virtual hosted-style access if possible, otherwise path-style.
    if (is_dns_compliant(bucket, path)) {
        kputsn(bucket, path - bucket, &url);
        kputc('.', &url);
        kputs(host_base.s, &url);
    }
    else {
        kputs(host_base.s, &url);
        kputc('/', &url);
        kputsn(bucket, path - bucket, &url);
    }
    kputs(path, &url);

    if (ad->token.l > 0) {
        kputs("X-Amz-Security-Token: ", &token_hdr);
        kputs(ad->token.s, &token_hdr);
        *header++ = token_hdr.s;
    }

    ad->bucket = strdup(bucket);
    if (!ad->bucket)
        goto fail;

    *header = NULL;
    hFILE *fp = hopen(url.s, mode, "va_list", argsp, "httphdr:v", header_list,
                      "httphdr_callback", auth_header_callback,
                      "httphdr_callback_data", ad, NULL);
    if (!fp) goto fail;

    free(url.s);
    free(profile.s);
    free(host_base.s);
    free(token_hdr.s);
    return fp;

 fail:
    free(url.s);
    free(profile.s);
    free(host_base.s);
    free(token_hdr.s);
    free_auth_data(ad);
    return NULL;
}
Beispiel #2
0
static int
add_s3_settings(hFILE_libcurl *fp, const char *s3url, kstring_t *message)
{
    int ret, save;
    const char *bucket, *path;
    char date_hdr[40];
    CURLcode err;

    kstring_t url = { 0, 0, NULL };
    kstring_t profile = { 0, 0, NULL };
    kstring_t id = { 0, 0, NULL };
    kstring_t secret = { 0, 0, NULL };
    kstring_t token = { 0, 0, NULL };
    kstring_t token_hdr = { 0, 0, NULL };
    kstring_t auth_hdr = { 0, 0, NULL };

    time_t now = time(NULL);
#ifdef HAVE_GMTIME_R
    struct tm tm_buffer;
    struct tm *tm = gmtime_r(&now, &tm_buffer);
#else
    struct tm *tm = gmtime(&now);
#endif

    strftime(date_hdr, sizeof date_hdr, "Date: %a, %d %b %Y %H:%M:%S GMT", tm);
    if (add_header(fp, date_hdr) < 0) goto error;
    kputs(&date_hdr[6], message);
    kputc('\n', message);

    // Our S3 URL format is s3[+SCHEME]://[ID[:SECRET[:TOKEN]]@]BUCKET/PATH

    if (s3url[2] == '+') {
        bucket = strchr(s3url, ':') + 1;
        kputsn(&s3url[3], bucket - &s3url[3], &url);
    }
    else {
        kputs("https:", &url);
        bucket = &s3url[3];
    }
    while (*bucket == '/') kputc(*bucket++, &url);

    path = bucket + strcspn(bucket, "/?#@");
    if (*path == '@') {
        const char *colon = strpbrk(bucket, ":@");
        if (*colon != ':') {
            urldecode_kput(bucket, colon - bucket, fp, &profile);
        }
        else {
            const char *colon2 = strpbrk(&colon[1], ":@");
            urldecode_kput(bucket, colon - bucket, fp, &id);
            urldecode_kput(&colon[1], colon2 - &colon[1], fp, &secret);
            if (*colon2 == ':')
                urldecode_kput(&colon2[1], path - &colon2[1], fp, &token);
        }

        bucket = &path[1];
        path = bucket + strcspn(bucket, "/?#");
    }
    else {
        // If the URL has no ID[:SECRET]@, consider environment variables.
        const char *v;
        if ((v = getenv("AWS_ACCESS_KEY_ID")) != NULL) kputs(v, &id);
        if ((v = getenv("AWS_SECRET_ACCESS_KEY")) != NULL) kputs(v, &secret);
        if ((v = getenv("AWS_SESSION_TOKEN")) != NULL) kputs(v, &token);

        if ((v = getenv("AWS_DEFAULT_PROFILE")) != NULL) kputs(v, &profile);
        else if ((v = getenv("AWS_PROFILE")) != NULL) kputs(v, &profile);
        else kputs("default", &profile);
    }

    // Use virtual hosted-style access if possible, otherwise path-style.
    if (is_dns_compliant(bucket, path)) {
        kputsn(bucket, path - bucket, &url);
        kputs(".s3.amazonaws.com", &url);
    }
    else {
        kputs("s3.amazonaws.com/", &url);
        kputsn(bucket, path - bucket, &url);
    }
    kputs(path, &url);

    if (id.l == 0) {
        const char *v = getenv("AWS_SHARED_CREDENTIALS_FILE");
        parse_ini(v? v : "~/.aws/credentials", profile.s,
                  "aws_access_key_id", &id, "aws_secret_access_key", &secret,
                  "aws_session_token", &token, NULL);
    }
    if (id.l == 0)
        parse_ini("~/.s3cfg", profile.s, "access_key", &id,
                  "secret_key", &secret, "access_token", &token, NULL);
    if (id.l == 0)
        parse_simple("~/.awssecret", &id, &secret);

    if (token.l > 0) {
        kputs("x-amz-security-token:", message);
        kputs(token.s, message);
        kputc('\n', message);

        kputs("X-Amz-Security-Token: ", &token_hdr);
        kputs(token.s, &token_hdr);
        if (add_header(fp, token_hdr.s) < 0) goto error;
    }

    kputc('/', message);
    kputs(bucket, message); // CanonicalizedResource is '/' + bucket + path

    err = curl_easy_setopt(fp->easy, CURLOPT_URL, url.s);
    if (err != CURLE_OK) { errno = easy_errno(fp->easy, err); goto error; }

    // If we have no id/secret, we can't sign the request but will
    // still be able to access public data sets.
    if (id.l > 0 && secret.l > 0) {
        unsigned char digest[DIGEST_BUFSIZ];
        size_t digest_len = s3_sign(digest, &secret, message);

        kputs("Authorization: AWS ", &auth_hdr);
        kputs(id.s, &auth_hdr);
        kputc(':', &auth_hdr);
        base64_kput(digest, digest_len, &auth_hdr);

        if (add_header(fp, auth_hdr.s) < 0) goto error;
    }

    ret = 0;
    goto free_and_return;

error:
    ret = -1;

free_and_return:
    save = errno;
    free(url.s);
    free(profile.s);
    free(id.s);
    free(secret.s);
    free(token.s);
    free(token_hdr.s);
    free(auth_hdr.s);
    free(message->s);
    errno = save;
    return ret;
}