Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Vends temporary credentials by sending a `CreateSession` request to the Roles An

Note that if more than one certificate matches the `--cert-selector` parameter within the OS-specific secure store, the `credential-process` command will fail. To find the list of certificates that match a given `--cert-selector` parameter, you can use the same flag with the `read-certificate-data` command.

Also note that in Windows, if you would like the credential helper to search a system certificate store other than "MY" ("MY" will be the default) in the `CERT_SYSTEM_STORE_CURRENT_USER` context, you can specify the name of the certificate store through the `--system-store-name` flag. It's not possible for the credential helper to search multiple Windows system certificate stores at once currently. But it will indirectly search certificate stores in the `CERT_SYSTEM_STORE_LOCAL_MACHINE` context since all current user certificate stores will inherit contents of local machine certificate stores. The only exception to this rule is the Current User/Personal ("MY") store. Please see the [Microsoft documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/install/local-machine-and-current-user-certificate-stores?source=recommendations) for more details.
Also note that in Windows, if you would like the credential helper to search a system certificate store other than "MY" ("MY" will be the default), you can specify the name of the certificate store through the `--system-store-name` flag. You can also specify the store location through the `--system-store-location` flag, which accepts either `CurrentUser` (default) or `LocalMachine`. It's not possible for the credential helper to search multiple Windows system certificate stores at once currently. Note that when searching within the `CurrentUser` location, the credential helper will indirectly search the `LocalMachine` stores as well, since current user certificate stores inherit contents of local machine certificate stores. The only exception to this rule is the Current User/Personal ("MY") store. Please see the [Microsoft documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/install/local-machine-and-current-user-certificate-stores?source=recommendations) for more details.

When `credential-process` is used, AWS SDKs store the returned AWS credentials in memory. AWS SDKs will keep track of the credential expiration and generate new AWS session credentials via the credential process, provided the certificate has not expired or been revoked.

Expand Down
18 changes: 15 additions & 3 deletions aws_signing_helper/cert_store_signer_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ var (
ErrRequiresUI = errors.New("provider requries UI to operate")
)

// systemStoreLocationMap maps system store location names to their corresponding
// high word of the dwFlags used in CertOpenStore.
var systemStoreLocationMap = map[string]uint32{
"CurrentUser": windows.CERT_SYSTEM_STORE_CURRENT_USER,
"LocalMachine": windows.CERT_SYSTEM_STORE_LOCAL_MACHINE,
}

// Error codes for Windows APIs - implements the error interface.
// Error codes are maintained on a per-thread basis. In order to
// get the last error code, C.GetLastError needs to be called (called
Expand Down Expand Up @@ -143,16 +150,21 @@ func (secStatus securityStatus) Error() string {
return fmt.Sprintf("SECURITY_STATUS %d", int(secStatus))
}

// Gets the certificates that match the given CertIdentifier within the user's specified system
// certificate store. By default, that is "MY".
// Gets the certificates that match the given CertIdentifier within the specified system
// certificate store and store location. By default, that is "MY" in "CurrentUser".
// If there is only a single matching certificate, then its chain will be returned too
func GetMatchingCertsAndChain(certIdentifier CertIdentifier) (store windows.Handle, certCtxs []*windows.CertContext, certChains [][]*x509.Certificate, certContainers []CertificateContainer, err error) {
storeName, err := windows.UTF16PtrFromString(certIdentifier.SystemStoreName)
if err != nil {
return 0, nil, nil, nil, errors.New("unable to UTF-16 encode personal certificate store name")
}

store, err = windows.CertOpenStore(windows.CERT_STORE_PROV_SYSTEM_W, 0, 0, windows.CERT_SYSTEM_STORE_CURRENT_USER, uintptr(unsafe.Pointer(storeName)))
dwFlags, ok := systemStoreLocationMap[certIdentifier.SystemStoreLocation]
if !ok {
return 0, nil, nil, nil, fmt.Errorf("unsupported system store location: %s", certIdentifier.SystemStoreLocation)
}

store, err = windows.CertOpenStore(windows.CERT_STORE_PROV_SYSTEM_W, 0, 0, dwFlags, uintptr(unsafe.Pointer(storeName)))
if err != nil {
return 0, nil, nil, nil, errors.New("failed to open system cert store")
}
Expand Down
15 changes: 11 additions & 4 deletions aws_signing_helper/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ type SignerParams struct {
}

type CertIdentifier struct {
Subject string
Issuer string
SerialNumber *big.Int
SystemStoreName string // Only relevant in the case of Windows
Subject string
Issuer string
SerialNumber *big.Int
SystemStoreName string // Only relevant in the case of Windows
SystemStoreLocation string // Only relevant in the case of Windows
}

var (
Expand All @@ -64,6 +65,12 @@ var (
"CA",
}

// Predefined system store locations.
SystemStoreLocations = []string{
"CurrentUser",
"LocalMachine",
}

// Signing name for the IAM Roles Anywhere service
ROLESANYWHERE_SIGNING_NAME = "rolesanywhere"
)
Expand Down
26 changes: 22 additions & 4 deletions cmd/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var (

certSelector string
systemStoreName string
systemStoreLocation string
useLatestExpiringCertificate bool

libPkcs11 string
Expand Down Expand Up @@ -80,8 +81,10 @@ func initCredentialsSubCommand(subCmd *cobra.Command) {
subCmd.PersistentFlags().StringVar(&certificateBundleId, "intermediates", "", "Path to intermediate certificate bundle file")
subCmd.PersistentFlags().StringVar(&certSelector, "cert-selector", "", "JSON structure to identify a certificate from a certificate store. "+
"Can be passed in either as string or a file name (prefixed by \"file://\")")
subCmd.PersistentFlags().StringVar(&systemStoreName, "system-store-name", "MY", "Name of the system store to search for within the "+
"CERT_SYSTEM_STORE_CURRENT_USER context. Note that this flag is only relevant for Windows certificate stores and will be ignored otherwise")
subCmd.PersistentFlags().StringVar(&systemStoreName, "system-store-name", "MY", "Name of the system store to search for. "+
"Note that this flag is only relevant for Windows certificate stores and will be ignored otherwise")
subCmd.PersistentFlags().StringVar(&systemStoreLocation, "system-store-location", "CurrentUser", "Location of the system store to search for. "+
"Can be either \"CurrentUser\" or \"LocalMachine\". Note that this flag is only relevant for Windows certificate stores and will be ignored otherwise")
subCmd.PersistentFlags().BoolVar(&useLatestExpiringCertificate, "use-latest-expiring-certificate", false, "If multiple certificates match "+
"a given certificate selector, the one that expires the latest will be chosen (if more than one still fits this criteria, an arbitrary "+
"one is chosen from those that meet the criteria)")
Expand All @@ -97,14 +100,17 @@ func initCredentialsSubCommand(subCmd *cobra.Command) {

subCmd.MarkFlagsMutuallyExclusive("certificate", "cert-selector")
subCmd.MarkFlagsMutuallyExclusive("certificate", "system-store-name")
subCmd.MarkFlagsMutuallyExclusive("certificate", "system-store-location")
subCmd.MarkFlagsMutuallyExclusive("private-key", "cert-selector")
subCmd.MarkFlagsMutuallyExclusive("private-key", "system-store-name")
subCmd.MarkFlagsMutuallyExclusive("private-key", "system-store-location")
subCmd.MarkFlagsMutuallyExclusive("private-key", "use-latest-expiring-certificate")
subCmd.MarkFlagsMutuallyExclusive("use-latest-expiring-certificate", "intermediates")
subCmd.MarkFlagsMutuallyExclusive("use-latest-expiring-certificate", "reuse-pin")
subCmd.MarkFlagsMutuallyExclusive("cert-selector", "intermediates")
subCmd.MarkFlagsMutuallyExclusive("cert-selector", "reuse-pin")
subCmd.MarkFlagsMutuallyExclusive("system-store-name", "reuse-pin")
subCmd.MarkFlagsMutuallyExclusive("system-store-location", "reuse-pin")
subCmd.MarkFlagsMutuallyExclusive("tpm-key-password", "cert-selector")
subCmd.MarkFlagsMutuallyExclusive("tpm-key-password", "reuse-pin")
subCmd.MarkFlagsMutuallyExclusive("no-tpm-key-password", "cert-selector")
Expand Down Expand Up @@ -230,7 +236,7 @@ func PopulateCertIdentifierFromCertSelectorStr(certSelectorStr string) (helper.C

// Populates a CertIdentifier using a cert selector
// Note that this method can take in a file name as a the cert selector
func PopulateCertIdentifier(certSelector string, systemStoreName string) (helper.CertIdentifier, error) {
func PopulateCertIdentifier(certSelector string, systemStoreName string, systemStoreLocation string) (helper.CertIdentifier, error) {
var (
certIdentifier helper.CertIdentifier
err error
Expand Down Expand Up @@ -265,12 +271,24 @@ func PopulateCertIdentifier(certSelector string, systemStoreName string) (helper
certIdentifier.SystemStoreName = systemStoreName
}

matchedSystemStoreLocation := false
for _, predefinedLocation := range helper.SystemStoreLocations {
if strings.EqualFold(systemStoreLocation, predefinedLocation) {
certIdentifier.SystemStoreLocation = predefinedLocation
matchedSystemStoreLocation = true
break
}
}
if !matchedSystemStoreLocation {
return helper.CertIdentifier{}, fmt.Errorf("unsupported system store location: %s", systemStoreLocation)
}

return certIdentifier, err
}

// Populate CredentialsOpts that is used to aggregate all the information required to call CreateSession
func PopulateCredentialsOptions() error {
certIdentifier, err := PopulateCertIdentifier(certSelector, systemStoreName)
certIdentifier, err := PopulateCertIdentifier(certSelector, systemStoreName, systemStoreLocation)
if err != nil {
return err
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestValidSelectorParsing(t *testing.T) {
" \n Key=x509Subject,Value=CN=CN With Trailing Spaces ,O=O With Spaces \t Key=x509Issuer,Value=CN=Issuer \n",
}
for _, fixture := range fixtures {
_, err := PopulateCertIdentifier(fixture, "MY")
_, err := PopulateCertIdentifier(fixture, "MY", "CurrentUser")
if err != nil {
t.Log("Unable to populate cert identifier from selector")
t.Fail()
Expand All @@ -44,7 +44,7 @@ func TestInvalidSelectorParsing(t *testing.T) {
"Key=x509Subject,Value=Test Key=x509Subject,Value=Test2",
}
for _, fixture := range fixtures {
_, err := PopulateCertIdentifier(fixture, "MY")
_, err := PopulateCertIdentifier(fixture, "MY", "CurrentUser")
if err == nil {
t.Log("Expected parsing failure, but received none")
t.Fail()
Expand All @@ -55,7 +55,7 @@ func TestInvalidSelectorParsing(t *testing.T) {
func TestValidSelectorParsingResults(t *testing.T) {
fixture := " \n Key=x509Subject,Value=CN=CN With Trailing Spaces ,O=O With Spaces \t Key=x509Issuer,Value=CN=Issuer \n "

certIdentifier, err := PopulateCertIdentifier(fixture, "MY")
certIdentifier, err := PopulateCertIdentifier(fixture, "MY", "CurrentUser")
if err != nil {
t.Log("Unable to populate cert identifier from selector")
t.Fail()
Expand Down
9 changes: 6 additions & 3 deletions cmd/read_certificate_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ func init() {
readCertificateDataCmd.PersistentFlags().StringVar(&certificateId, "certificate", "", "Path to certificate file")
readCertificateDataCmd.PersistentFlags().StringVar(&certSelector, "cert-selector", "", "JSON structure to identify a certificate from a certificate store."+
" Can be passed in either as string or a file name (prefixed by \"file://\")")
readCertificateDataCmd.PersistentFlags().StringVar(&systemStoreName, "system-store-name", "MY", "Name of the system store to search for within the "+
"CERT_SYSTEM_STORE_CURRENT_USER context. Note that this flag is only relevant for Windows certificate stores and will be ignored otherwise")
readCertificateDataCmd.PersistentFlags().StringVar(&systemStoreName, "system-store-name", "MY", "Name of the system store to search for. "+
"Note that this flag is only relevant for Windows certificate stores and will be ignored otherwise")
readCertificateDataCmd.PersistentFlags().StringVar(&systemStoreLocation, "system-store-location", "CurrentUser", "Location of the system store to search for. "+
"Can be either \"CurrentUser\" or \"LocalMachine\". Note that this flag is only relevant for Windows certificate stores and will be ignored otherwise")
readCertificateDataCmd.PersistentFlags().StringVar(&libPkcs11, "pkcs11-lib", "", "Library for smart card / cryptographic device (OpenSC or vendor specific)")
readCertificateDataCmd.PersistentFlags().BoolVar(&debug, "debug", false, "To print debug output")

readCertificateDataCmd.MarkFlagsMutuallyExclusive("certificate", "cert-selector")
readCertificateDataCmd.MarkFlagsMutuallyExclusive("certificate", "system-store-name")
readCertificateDataCmd.MarkFlagsMutuallyExclusive("certificate", "system-store-location")
}

type PrintCertificate func(int, helper.CertificateContainer)
Expand All @@ -38,7 +41,7 @@ var readCertificateDataCmd = &cobra.Command{
Long: `Diagnostic command to read certificate data, either from files or
from a certificate store`,
Run: func(cmd *cobra.Command, args []string) {
certIdentifier, err := PopulateCertIdentifier(certSelector, systemStoreName)
certIdentifier, err := PopulateCertIdentifier(certSelector, systemStoreName, systemStoreLocation)
if err != nil {
log.Println(err)
os.Exit(1)
Expand Down
9 changes: 7 additions & 2 deletions cmd/sign_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@ func init() {
signStringCmd.PersistentFlags().BoolVar(&debug, "debug", false, "To print debug output")
signStringCmd.PersistentFlags().StringVar(&certSelector, "cert-selector", "", "JSON structure to identify a certificate from a certificate store. "+
"Can be passed in either as string or a file name (prefixed by \"file://\")")
signStringCmd.PersistentFlags().StringVar(&systemStoreName, "system-store-name", "MY", "Name of the system store to search for within the "+
"CERT_SYSTEM_STORE_CURRENT_USER context. Note that this flag is only relevant for Windows certificate stores and will be ignored otherwise")
signStringCmd.PersistentFlags().StringVar(&systemStoreName, "system-store-name", "MY", "Name of the system store to search for. "+
"Note that this flag is only relevant for Windows certificate stores and will be ignored otherwise")
signStringCmd.PersistentFlags().StringVar(&systemStoreLocation, "system-store-location", "CurrentUser", "Location of the system store to search for. "+
"Can be either \"CurrentUser\" or \"LocalMachine\". Note that this flag is only relevant for Windows certificate stores and will be ignored otherwise")
signStringCmd.PersistentFlags().BoolVar(&useLatestExpiringCertificate, "use-latest-expiring-certificate", false, "If multiple certificates match "+
"a given certificate selector, the one that expires the latest will be chosen (if more than one still fits this criteria, an arbitrary "+
"one is chosen from those that meet the criteria)")
Expand All @@ -96,12 +98,15 @@ func init() {

signStringCmd.MarkFlagsMutuallyExclusive("certificate", "cert-selector")
signStringCmd.MarkFlagsMutuallyExclusive("certificate", "system-store-name")
signStringCmd.MarkFlagsMutuallyExclusive("certificate", "system-store-location")
signStringCmd.MarkFlagsMutuallyExclusive("private-key", "cert-selector")
signStringCmd.MarkFlagsMutuallyExclusive("private-key", "system-store-name")
signStringCmd.MarkFlagsMutuallyExclusive("private-key", "system-store-location")
signStringCmd.MarkFlagsMutuallyExclusive("private-key", "use-latest-expiring-certificate")
signStringCmd.MarkFlagsMutuallyExclusive("use-latest-expiring-certificate", "reuse-pin")
signStringCmd.MarkFlagsMutuallyExclusive("cert-selector", "reuse-pin")
signStringCmd.MarkFlagsMutuallyExclusive("system-store-name", "reuse-pin")
signStringCmd.MarkFlagsMutuallyExclusive("system-store-location", "reuse-pin")
signStringCmd.MarkFlagsMutuallyExclusive("tpm-key-password", "cert-selector")
signStringCmd.MarkFlagsMutuallyExclusive("tpm-key-password", "reuse-pin")
signStringCmd.MarkFlagsMutuallyExclusive("no-tpm-key-password", "cert-selector")
Expand Down