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  }