mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 18:17:55 +00:00 
			
		
		
		
	 fa13dbd381
			
		
	
	fa13dbd381
	
	
	
		
			
			* add gosimport to make fmt and run it * move installation to tools.sh * correct weird spacing issue * Update Makefile Co-authored-by: Nick Cabatoff <ncabatoff@hashicorp.com> * fix a weird issue --------- Co-authored-by: Nick Cabatoff <ncabatoff@hashicorp.com>
		
			
				
	
	
		
			304 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			304 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) HashiCorp, Inc.
 | |
| // SPDX-License-Identifier: BUSL-1.1
 | |
| 
 | |
| package command
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/ghodss/yaml"
 | |
| 	"github.com/hashicorp/vault/api"
 | |
| 	"github.com/ryanuber/columnize"
 | |
| )
 | |
| 
 | |
| type PKIListIntermediateCommand struct {
 | |
| 	*BaseCommand
 | |
| 
 | |
| 	flagConfig          string
 | |
| 	flagReturnIndicator string
 | |
| 	flagDefaultDisabled bool
 | |
| 	flagList            bool
 | |
| 
 | |
| 	flagUseNames bool
 | |
| 
 | |
| 	flagSignatureMatch    bool
 | |
| 	flagIndirectSignMatch bool
 | |
| 	flagKeyIdMatch        bool
 | |
| 	flagSubjectMatch      bool
 | |
| 	flagPathMatch         bool
 | |
| }
 | |
| 
 | |
| func (c *PKIListIntermediateCommand) Synopsis() string {
 | |
| 	return "Determine which of a list of certificates, were issued by a given parent certificate"
 | |
| }
 | |
| 
 | |
| func (c *PKIListIntermediateCommand) Help() string {
 | |
| 	helpText := `
 | |
| Usage: vault pki list-intermediates PARENT [CHILD] [CHILD] [CHILD] ...
 | |
| 
 | |
|   Lists the set of intermediate CAs issued by this parent issuer.
 | |
| 
 | |
|   PARENT is the certificate that might be the issuer that everything should
 | |
|   be verified against.
 | |
| 
 | |
|   CHILD is an optional list of paths to certificates to be compared to the
 | |
|   PARENT, or pki mounts to look for certificates on. If CHILD is omitted
 | |
|   entirely, the list will be constructed from all accessible pki mounts.
 | |
| 
 | |
|   This returns a list of issuing certificates, and whether they are a match.
 | |
|   By default, the type of match required is whether the PARENT has the
 | |
|   expected subject, key_id, and could have (directly) signed this issuer. 
 | |
|   The match criteria can be updated by changed the corresponding flag.
 | |
| 
 | |
| ` + c.Flags().Help()
 | |
| 	return strings.TrimSpace(helpText)
 | |
| }
 | |
| 
 | |
| func (c *PKIListIntermediateCommand) Flags() *FlagSets {
 | |
| 	set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
 | |
| 	f := set.NewFlagSet("Command Options")
 | |
| 
 | |
| 	f.BoolVar(&BoolVar{
 | |
| 		Name:    "subject_match",
 | |
| 		Target:  &c.flagSubjectMatch,
 | |
| 		Default: true,
 | |
| 		EnvVar:  "",
 | |
| 		Usage:   `Whether the subject name of the potential parent cert matches the issuer name of the child cert.`,
 | |
| 	})
 | |
| 
 | |
| 	f.BoolVar(&BoolVar{
 | |
| 		Name:    "key_id_match",
 | |
| 		Target:  &c.flagKeyIdMatch,
 | |
| 		Default: true,
 | |
| 		EnvVar:  "",
 | |
| 		Usage:   `Whether the subject key id (SKID) of the potential parent cert matches the authority key id (AKID) of the child cert.`,
 | |
| 	})
 | |
| 
 | |
| 	f.BoolVar(&BoolVar{
 | |
| 		Name:    "path_match",
 | |
| 		Target:  &c.flagPathMatch,
 | |
| 		Default: false,
 | |
| 		EnvVar:  "",
 | |
| 		Usage:   `Whether the potential parent appears in the certificate chain field (ca_chain) of the issued cert.`,
 | |
| 	})
 | |
| 
 | |
| 	f.BoolVar(&BoolVar{
 | |
| 		Name:    "direct_sign",
 | |
| 		Target:  &c.flagSignatureMatch,
 | |
| 		Default: true,
 | |
| 		EnvVar:  "",
 | |
| 		Usage:   `Whether the key of the potential parent directly signed this issued certificate.`,
 | |
| 	})
 | |
| 
 | |
| 	f.BoolVar(&BoolVar{
 | |
| 		Name:    "indirect_sign",
 | |
| 		Target:  &c.flagIndirectSignMatch,
 | |
| 		Default: true,
 | |
| 		EnvVar:  "",
 | |
| 		Usage:   `Whether trusting the parent certificate is sufficient to trust the child certificate.`,
 | |
| 	})
 | |
| 
 | |
| 	f.BoolVar(&BoolVar{
 | |
| 		Name:    "use_names",
 | |
| 		Target:  &c.flagUseNames,
 | |
| 		Default: false,
 | |
| 		EnvVar:  "",
 | |
| 		Usage:   `Whether the list of issuers returned is referred to by name (when it exists) rather than by uuid.`,
 | |
| 	})
 | |
| 
 | |
| 	return set
 | |
| }
 | |
| 
 | |
| func (c *PKIListIntermediateCommand) Run(args []string) int {
 | |
| 	f := c.Flags()
 | |
| 	if err := f.Parse(args); err != nil {
 | |
| 		c.UI.Error(err.Error())
 | |
| 		return 1
 | |
| 	}
 | |
| 
 | |
| 	args = f.Args()
 | |
| 
 | |
| 	if len(args) < 1 {
 | |
| 		c.UI.Error("Not enough arguments (expected potential parent, got nothing)")
 | |
| 		return 1
 | |
| 	} else if len(args) > 2 {
 | |
| 		for _, arg := range args {
 | |
| 			if strings.HasPrefix(arg, "-") {
 | |
| 				c.UI.Warn(fmt.Sprintf("Options (%v) must be specified before positional arguments (%v)", arg, args[0]))
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	client, err := c.Client()
 | |
| 	if err != nil {
 | |
| 		c.UI.Error(fmt.Sprintf("Failed to obtain client: %s", err))
 | |
| 		return 1
 | |
| 	}
 | |
| 
 | |
| 	issuer := sanitizePath(args[0])
 | |
| 	var issued []string
 | |
| 	if len(args) > 1 {
 | |
| 		for _, arg := range args[1:] {
 | |
| 			cleanPath := sanitizePath(arg)
 | |
| 			// Arg Might be a Fully Qualified Path
 | |
| 			if strings.Contains(cleanPath, "/issuer/") ||
 | |
| 				strings.Contains(cleanPath, "/certs/") ||
 | |
| 				strings.Contains(cleanPath, "/revoked/") {
 | |
| 				issued = append(issued, cleanPath)
 | |
| 			} else { // Or Arg Might be a Mount
 | |
| 				mountCaList, err := c.getIssuerListFromMount(client, arg)
 | |
| 				if err != nil {
 | |
| 					c.UI.Error(err.Error())
 | |
| 					return 1
 | |
| 				}
 | |
| 				issued = append(issued, mountCaList...)
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		mountListRaw, err := client.Logical().Read("/sys/mounts/")
 | |
| 		if err != nil {
 | |
| 			c.UI.Error(fmt.Sprintf("Failed to Read List of Mounts With Potential Issuers: %v", err))
 | |
| 			return 1
 | |
| 		}
 | |
| 		for path, rawValueMap := range mountListRaw.Data {
 | |
| 			valueMap := rawValueMap.(map[string]interface{})
 | |
| 			if valueMap["type"].(string) == "pki" {
 | |
| 				mountCaList, err := c.getIssuerListFromMount(client, sanitizePath(path))
 | |
| 				if err != nil {
 | |
| 					c.UI.Error(err.Error())
 | |
| 					return 1
 | |
| 				}
 | |
| 				issued = append(issued, mountCaList...)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	childrenMatches := make(map[string]bool)
 | |
| 
 | |
| 	constraintMap := map[string]bool{
 | |
| 		// This comparison isn't strictly correct, despite a standard ordering these are sets
 | |
| 		"subject_match":   c.flagSubjectMatch,
 | |
| 		"path_match":      c.flagPathMatch,
 | |
| 		"trust_match":     c.flagIndirectSignMatch,
 | |
| 		"key_id_match":    c.flagKeyIdMatch,
 | |
| 		"signature_match": c.flagSignatureMatch,
 | |
| 	}
 | |
| 
 | |
| 	issuerResp, err := readIssuer(client, issuer)
 | |
| 	if err != nil {
 | |
| 		c.UI.Error(fmt.Sprintf("Failed to read parent issuer on path %s: %s", issuer, err.Error()))
 | |
| 		return 1
 | |
| 	}
 | |
| 
 | |
| 	for _, child := range issued {
 | |
| 		path := sanitizePath(child)
 | |
| 		if path != "" {
 | |
| 			verifyResults, err := verifySignBetween(client, issuerResp, path)
 | |
| 			if err != nil {
 | |
| 				c.UI.Error(fmt.Sprintf("Failed to run verification on path %v: %v", path, err))
 | |
| 				return 1
 | |
| 			}
 | |
| 			childrenMatches[path] = checkIfResultsMatchFilters(verifyResults, constraintMap)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	err = c.outputResults(childrenMatches)
 | |
| 	if err != nil {
 | |
| 		c.UI.Error(err.Error())
 | |
| 		return 1
 | |
| 	}
 | |
| 
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| func (c *PKIListIntermediateCommand) getIssuerListFromMount(client *api.Client, mountString string) ([]string, error) {
 | |
| 	var issuerList []string
 | |
| 	issuerListEndpoint := sanitizePath(mountString) + "/issuers"
 | |
| 	rawIssuersResp, err := client.Logical().List(issuerListEndpoint)
 | |
| 	if err != nil {
 | |
| 		return issuerList, fmt.Errorf("failed to read list of issuers within mount %v: %v", mountString, err)
 | |
| 	}
 | |
| 	if rawIssuersResp == nil { // No Issuers (Empty Mount)
 | |
| 		return issuerList, nil
 | |
| 	}
 | |
| 	issuersMap := rawIssuersResp.Data["keys"]
 | |
| 	certList := issuersMap.([]interface{})
 | |
| 	for _, certId := range certList {
 | |
| 		identifier := certId.(string)
 | |
| 		if c.flagUseNames {
 | |
| 			issuerReadResp, err := client.Logical().Read(sanitizePath(mountString) + "/issuer/" + identifier)
 | |
| 			if err != nil {
 | |
| 				c.UI.Warn(fmt.Sprintf("Unable to Fetch Issuer to Recover Name at: %v", sanitizePath(mountString)+"/issuer/"+identifier))
 | |
| 			}
 | |
| 			if issuerReadResp != nil {
 | |
| 				issuerName := issuerReadResp.Data["issuer_name"].(string)
 | |
| 				if issuerName != "" {
 | |
| 					identifier = issuerName
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		issuerList = append(issuerList, sanitizePath(mountString)+"/issuer/"+identifier)
 | |
| 	}
 | |
| 	return issuerList, nil
 | |
| }
 | |
| 
 | |
| func checkIfResultsMatchFilters(verifyResults, constraintMap map[string]bool) bool {
 | |
| 	for key, required := range constraintMap {
 | |
| 		if required && !verifyResults[key] {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func (c *PKIListIntermediateCommand) outputResults(results map[string]bool) error {
 | |
| 	switch Format(c.UI) {
 | |
| 	case "", "table":
 | |
| 		return c.outputResultsTable(results)
 | |
| 	case "json":
 | |
| 		return c.outputResultsJSON(results)
 | |
| 	case "yaml":
 | |
| 		return c.outputResultsYAML(results)
 | |
| 	default:
 | |
| 		return fmt.Errorf("unknown output format: %v", Format(c.UI))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (c *PKIListIntermediateCommand) outputResultsTable(results map[string]bool) error {
 | |
| 	data := []string{"intermediate" + hopeDelim + "match?"}
 | |
| 	for field, finding := range results {
 | |
| 		row := field + hopeDelim + strconv.FormatBool(finding)
 | |
| 		data = append(data, row)
 | |
| 	}
 | |
| 	c.UI.Output(tableOutput(data, &columnize.Config{
 | |
| 		Delim: hopeDelim,
 | |
| 	}))
 | |
| 	c.UI.Output("\n")
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *PKIListIntermediateCommand) outputResultsJSON(results map[string]bool) error {
 | |
| 	bytes, err := json.MarshalIndent(results, "", "  ")
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	c.UI.Output(string(bytes))
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *PKIListIntermediateCommand) outputResultsYAML(results map[string]bool) error {
 | |
| 	bytes, err := yaml.Marshal(results)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	c.UI.Output(string(bytes))
 | |
| 	return nil
 | |
| }
 |