diff --git a/README.md b/README.md index 6a2deff..f83c391 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# AWS SDK for Zig (zig-native branch) +# AWS SDK for Zig (zig native branch) [![Build Status](https://drone.lerch.org/api/badges/lobo/aws-sdk-for-zig/status.svg?ref=refs/heads/master)](https://drone.lerch.org/api/badges/lobo/aws-sdk-for-zig/) @@ -10,9 +10,19 @@ services only support XML, and zig 0.8.0 and master both trigger compile errors while incorporating the XML parser. S3 also requires some plumbing tweaks in the signature calculation. Examples of usage are in src/main.zig. -Current executable size for the demo is 868k after compiling with -Drelease-safe -and [stripping the executable after compilation](https://github.com/ziglang/zig/issues/351). -This is for x86_linux, (which is all that's tested at the moment). +Current executable size for the demo is 953k (90k of which is the AWS PEM file) +after compiling with -Drelease-safe and +[stripping the executable after compilation](https://github.com/ziglang/zig/issues/351). +This is for x86_linux. Tested targets: + +* x86_64-linux +* riscv64-linux +* aarch64-linux +* x86_64 Windows + +Tested/not working: + +* arm-linux ## Building @@ -51,6 +61,7 @@ Only environment variable based credentials can be used at the moment. TODO List: * Add STS key support +* Add option to cache signature keys * Implement credentials provider * Implement jitter/exponential backoff * Implement timeouts and other TODO's in the code diff --git a/src/aws_authentication.zig b/src/aws_authentication.zig index 580c93d..1be1ef3 100644 --- a/src/aws_authentication.zig +++ b/src/aws_authentication.zig @@ -1,6 +1,33 @@ +const std = @import("std"); + pub const Credentials = struct { access_key: []const u8, - secret_key: []const u8, + secret_key: []u8, session_token: ?[]const u8, // uint64_t expiration_timepoint_seconds); + + allocator: std.mem.Allocator, + + const Self = @This(); + + pub fn init( + allocator: std.mem.Allocator, + access_key: []const u8, + secret_key: []u8, + session_token: ?[]const u8, + ) Self { + return .{ + .access_key = access_key, + .secret_key = secret_key, + .session_token = session_token, + + .allocator = allocator, + }; + } + pub fn deinit(self: Self) void { + for (self.secret_key) |_, i| self.secret_key[i] = 0; + self.allocator.free(self.access_key); + self.allocator.free(self.secret_key); + if (self.session_token) |t| self.allocator.free(t); + } }; diff --git a/src/aws_credentials.zig b/src/aws_credentials.zig index f7c1497..1a73910 100644 --- a/src/aws_credentials.zig +++ b/src/aws_credentials.zig @@ -5,19 +5,31 @@ //! 4. ECS Container credentials, using AWS_CONTAINER_CREDENTIALS_RELATIVE_URI //! 5. EC2 instance profile credentials const std = @import("std"); +const builtin = @import("builtin"); const auth = @import("aws_authentication.zig"); pub fn getCredentials(allocator: std.mem.Allocator) !auth.Credentials { - _ = allocator; - if (getEnvironmentCredentials()) |cred| return cred; + if (try getEnvironmentCredentials(allocator)) |cred| return cred; // TODO: 2-5 return error.NotImplemented; } -fn getEnvironmentCredentials() ?auth.Credentials { - return auth.Credentials{ - .access_key = std.os.getenv("AWS_ACCESS_KEY_ID") orelse return null, - .secret_key = std.os.getenv("AWS_SECRET_ACCESS_KEY") orelse return null, - .session_token = std.os.getenv("AWS_SESSION_TOKEN"), +fn getEnvironmentCredentials(allocator: std.mem.Allocator) !?auth.Credentials { + const secret_key = (try getEnvironmentVariable(allocator, "AWS_SECRET_ACCESS_KEY")) orelse return null; + defer allocator.free(secret_key); //yes, we're not zeroing. But then, the secret key is in an environment var anyway + const mutable_key = try allocator.dupe(u8, secret_key); + // Use cross-platform API (requires allocation) + return auth.Credentials.init( + allocator, + (try getEnvironmentVariable(allocator, "AWS_ACCESS_KEY_ID")) orelse return null, + mutable_key, + try getEnvironmentVariable(allocator, "AWS_SESSION_TOKEN"), + ); +} + +fn getEnvironmentVariable(allocator: std.mem.Allocator, key: []const u8) !?[]const u8 { + return std.process.getEnvVarOwned(allocator, key) catch |e| switch (e) { + std.process.GetEnvVarOwnedError.EnvironmentVariableNotFound => return null, + else => return e, }; } diff --git a/src/aws_http.zig b/src/aws_http.zig index 8de5cbf..b3953c5 100644 --- a/src/aws_http.zig +++ b/src/aws_http.zig @@ -98,7 +98,7 @@ pub const AwsHttp = struct { defer endpoint.deinit(); log.debug("Calling endpoint {s}", .{endpoint.uri}); const creds = try credentials.getCredentials(self.allocator); - // defer allocator.free(), except sometimes we don't need freeing... + defer creds.deinit(); const signing_config: signing.Config = .{ .region = options.region, .service = options.sigv4_service_name orelse service, @@ -217,8 +217,15 @@ fn addHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(base.Header) return null; } +fn getEnvironmentVariable(allocator: std.mem.Allocator, key: []const u8) !?[]const u8 { + return std.process.getEnvVarOwned(allocator, key) catch |e| switch (e) { + std.process.GetEnvVarOwnedError.EnvironmentVariableNotFound => return null, + else => return e, + }; +} + fn regionSubDomain(allocator: std.mem.Allocator, service: []const u8, region: []const u8, useDualStack: bool) !EndPoint { - const environment_override = std.os.getenv("AWS_ENDPOINT_URL"); + const environment_override = try getEnvironmentVariable(allocator, "AWS_ENDPOINT_URL"); if (environment_override) |override| { const uri = try allocator.dupeZ(u8, override); return endPointFromUri(allocator, uri);