go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/tokenserver/appengine/impl/machinetoken/rpc_inspect_machine_token.go (about) 1 // Copyright 2016 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package machinetoken 16 17 import ( 18 "context" 19 "fmt" 20 "math/big" 21 22 "google.golang.org/grpc/codes" 23 "google.golang.org/grpc/status" 24 25 "go.chromium.org/luci/common/retry/transient" 26 "go.chromium.org/luci/server/auth/signing" 27 28 tokenserver "go.chromium.org/luci/tokenserver/api" 29 admin "go.chromium.org/luci/tokenserver/api/admin/v1" 30 31 "go.chromium.org/luci/tokenserver/appengine/impl/certchecker" 32 "go.chromium.org/luci/tokenserver/appengine/impl/certconfig" 33 ) 34 35 // InspectMachineTokenRPC implements Admin.InspectMachineToken API method. 36 // 37 // It assumes authorization has happened already. 38 type InspectMachineTokenRPC struct { 39 // Signer is mocked in tests. 40 // 41 // In prod it is the default server signer that uses server's service account. 42 Signer signing.Signer 43 } 44 45 // InspectMachineToken decodes a machine token and verifies it is valid. 46 func (r *InspectMachineTokenRPC) InspectMachineToken(c context.Context, req *admin.InspectMachineTokenRequest) (*admin.InspectMachineTokenResponse, error) { 47 // Defaults. 48 if req.TokenType == 0 { 49 req.TokenType = tokenserver.MachineTokenType_LUCI_MACHINE_TOKEN 50 } 51 52 // Only LUCI_MACHINE_TOKEN is supported currently. 53 switch req.TokenType { 54 case tokenserver.MachineTokenType_LUCI_MACHINE_TOKEN: 55 // supported 56 default: 57 return nil, status.Errorf(codes.InvalidArgument, "unsupported token type %s", req.TokenType) 58 } 59 60 // Deserialize the token, check its signature. 61 inspection, err := InspectToken(c, r.Signer, req.Token) 62 if err != nil { 63 return nil, status.Errorf(codes.Internal, "%s", err) 64 } 65 resp := &admin.InspectMachineTokenResponse{ 66 Signed: inspection.Signed, 67 NonExpired: inspection.NonExpired, 68 InvalidityReason: inspection.InvalidityReason, 69 } 70 71 // Grab the signing key from the envelope, if managed to deserialize it. 72 if env, _ := inspection.Envelope.(*tokenserver.MachineTokenEnvelope); env != nil { 73 resp.SigningKeyId = env.KeyId 74 } 75 76 // If we could not deserialize the body, that's all checks we could do. 77 body, _ := inspection.Body.(*tokenserver.MachineTokenBody) 78 if body == nil { 79 return resp, nil 80 } 81 resp.TokenType = &admin.InspectMachineTokenResponse_LuciMachineToken{ 82 LuciMachineToken: body, 83 } 84 85 addReason := func(r string) { 86 if resp.InvalidityReason == "" { 87 resp.InvalidityReason = r 88 } else { 89 resp.InvalidityReason += "; " + r 90 } 91 } 92 93 // Check revocation status. Find CA name that signed the certificate used when 94 // minting the token. 95 caName, err := certconfig.GetCAByUniqueID(c, body.CaId) 96 switch { 97 case err != nil: 98 return nil, status.Errorf(codes.Internal, "can't resolve ca_id to CA name - %s", err) 99 case caName == "": 100 addReason("no CA with given ID") 101 return resp, nil 102 } 103 resp.CertCaName = caName 104 105 // Grab CertChecker for this CA. It has CRL cached. 106 certChecker, err := certchecker.GetCertChecker(c, caName) 107 switch { 108 case transient.Tag.In(err): 109 return nil, status.Errorf(codes.Internal, "can't fetch CRL - %s", err) 110 case err != nil: 111 addReason(fmt.Sprintf("can't fetch CRL - %s", err)) 112 return resp, nil 113 } 114 115 // Check that certificate SN is not in the revocation list. 116 sn := big.NewInt(0).SetBytes(body.CertSn) 117 revoked, err := certChecker.CRL.IsRevokedSN(c, sn) 118 if err != nil { 119 return nil, status.Errorf(codes.Internal, "can't check CRL - %s", err) 120 } 121 resp.NonRevoked = !revoked 122 123 // Note: if Signed or NonExpired is false, InvalidityReason is already set. 124 if resp.Signed && resp.NonExpired { 125 if resp.NonRevoked { 126 resp.Valid = true 127 } else { 128 addReason("corresponding cert was revoked") 129 } 130 } 131 132 return resp, nil 133 }