mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-17 11:50:18 +00:00
vendor: vendor fscrypt integration dependencies
Signed-off-by: Marcel Lauhoff <marcel.lauhoff@suse.com>
This commit is contained in:
parent
cfea8d7562
commit
f8faffac89
2
go.mod
2
go.mod
@ -14,8 +14,8 @@ require (
|
||||
github.com/csi-addons/spec v0.1.2-0.20220906123848-52ce69f90900
|
||||
github.com/gemalto/kmip-go v0.0.8-0.20220721195433-3fe83e2d3f26
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/google/fscrypt v0.3.3
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
||||
github.com/hashicorp/vault/api v1.7.2
|
||||
|
1
go.sum
1
go.sum
@ -1502,6 +1502,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||
|
202
vendor/github.com/google/fscrypt/LICENSE
generated
vendored
Normal file
202
vendor/github.com/google/fscrypt/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
132
vendor/github.com/google/fscrypt/actions/callback.go
generated
vendored
Normal file
132
vendor/github.com/google/fscrypt/actions/callback.go
generated
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* callback.go - defines how the caller of an action function passes along a key
|
||||
* to be used in this package.
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/google/fscrypt/crypto"
|
||||
"github.com/google/fscrypt/filesystem"
|
||||
"github.com/google/fscrypt/metadata"
|
||||
)
|
||||
|
||||
// ProtectorInfo is the information a caller will receive about a Protector
|
||||
// before they have to return the corresponding key. This is currently a
|
||||
// read-only view of metadata.ProtectorData.
|
||||
type ProtectorInfo struct {
|
||||
data *metadata.ProtectorData
|
||||
}
|
||||
|
||||
// Descriptor is the Protector's descriptor used to uniquely identify it.
|
||||
func (pi *ProtectorInfo) Descriptor() string { return pi.data.GetProtectorDescriptor() }
|
||||
|
||||
// Source indicates the type of the descriptor (how it should be unlocked).
|
||||
func (pi *ProtectorInfo) Source() metadata.SourceType { return pi.data.GetSource() }
|
||||
|
||||
// Name is used to describe custom passphrase and raw key descriptors.
|
||||
func (pi *ProtectorInfo) Name() string { return pi.data.GetName() }
|
||||
|
||||
// UID is used to identify the user for login passphrases.
|
||||
func (pi *ProtectorInfo) UID() int64 { return pi.data.GetUid() }
|
||||
|
||||
// KeyFunc is passed to a function that will require some type of key.
|
||||
// The info parameter is provided so the callback knows which key to provide.
|
||||
// The retry parameter indicates that a previous key provided by this callback
|
||||
// was incorrect (this allows for user feedback like "incorrect passphrase").
|
||||
//
|
||||
// For passphrase sources, the returned key should be a passphrase. For raw
|
||||
// sources, the returned key should be a 256-bit cryptographic key. Consumers
|
||||
// of the callback will wipe the returned key. An error returned by the callback
|
||||
// will be propagated back to the caller.
|
||||
type KeyFunc func(info ProtectorInfo, retry bool) (*crypto.Key, error)
|
||||
|
||||
// getWrappingKey uses the provided callback to get the wrapping key
|
||||
// corresponding to the ProtectorInfo. This runs the passphrase hash for
|
||||
// passphrase sources or just relays the callback for raw sources.
|
||||
func getWrappingKey(info ProtectorInfo, keyFn KeyFunc, retry bool) (*crypto.Key, error) {
|
||||
// For raw key sources, we can just use the key directly.
|
||||
if info.Source() == metadata.SourceType_raw_key {
|
||||
return keyFn(info, retry)
|
||||
}
|
||||
|
||||
// Run the passphrase hash for other sources.
|
||||
passphrase, err := keyFn(info, retry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer passphrase.Wipe()
|
||||
|
||||
log.Printf("running passphrase hash for protector %s", info.Descriptor())
|
||||
return crypto.PassphraseHash(passphrase, info.data.Salt, info.data.Costs)
|
||||
}
|
||||
|
||||
// unwrapProtectorKey uses the provided callback and ProtectorInfo to return
|
||||
// the unwrapped protector key. This will repeatedly call keyFn to get the
|
||||
// wrapping key until the correct key is returned by the callback or the
|
||||
// callback returns an error.
|
||||
func unwrapProtectorKey(info ProtectorInfo, keyFn KeyFunc) (*crypto.Key, error) {
|
||||
retry := false
|
||||
for {
|
||||
wrappingKey, err := getWrappingKey(info, keyFn, retry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
protectorKey, err := crypto.Unwrap(wrappingKey, info.data.WrappedKey)
|
||||
wrappingKey.Wipe()
|
||||
|
||||
switch errors.Cause(err) {
|
||||
case nil:
|
||||
log.Printf("valid wrapping key for protector %s", info.Descriptor())
|
||||
return protectorKey, nil
|
||||
case crypto.ErrBadAuth:
|
||||
// After the first failure, we let the callback know we are retrying.
|
||||
log.Printf("invalid wrapping key for protector %s", info.Descriptor())
|
||||
retry = true
|
||||
continue
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ProtectorOption is information about a protector relative to a Policy.
|
||||
type ProtectorOption struct {
|
||||
ProtectorInfo
|
||||
// LinkedMount is the mountpoint for a linked protector. It is nil if
|
||||
// the protector is not a linked protector (or there is a LoadError).
|
||||
LinkedMount *filesystem.Mount
|
||||
// LoadError is non-nil if there was an error in getting the data for
|
||||
// the protector.
|
||||
LoadError error
|
||||
}
|
||||
|
||||
// OptionFunc is passed to a function that needs to unlock a Policy.
|
||||
// The callback is used to specify which protector should be used to unlock a
|
||||
// Policy. The descriptor indicates which Policy we are using, while the options
|
||||
// correspond to the valid Protectors protecting the Policy.
|
||||
//
|
||||
// The OptionFunc should either return a valid index into options, which
|
||||
// corresponds to the desired protector, or an error (which will be propagated
|
||||
// back to the caller).
|
||||
type OptionFunc func(policyDescriptor string, options []*ProtectorOption) (int, error)
|
293
vendor/github.com/google/fscrypt/actions/config.go
generated
vendored
Normal file
293
vendor/github.com/google/fscrypt/actions/config.go
generated
vendored
Normal file
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* config.go - Actions for creating a new config file, which includes new
|
||||
* hashing costs and the config file's location.
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/google/fscrypt/crypto"
|
||||
"github.com/google/fscrypt/filesystem"
|
||||
"github.com/google/fscrypt/metadata"
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
// ConfigFileLocation is the location of fscrypt's global settings. This can be
|
||||
// overridden by the user of this package.
|
||||
var ConfigFileLocation = "/etc/fscrypt.conf"
|
||||
|
||||
// ErrBadConfig is an internal error that indicates that the config struct is invalid.
|
||||
type ErrBadConfig struct {
|
||||
Config *metadata.Config
|
||||
UnderlyingError error
|
||||
}
|
||||
|
||||
func (err *ErrBadConfig) Error() string {
|
||||
return fmt.Sprintf(`internal error: config is invalid: %s
|
||||
|
||||
The invalid config is %s`, err.UnderlyingError, err.Config)
|
||||
}
|
||||
|
||||
// ErrBadConfigFile indicates that the config file is invalid.
|
||||
type ErrBadConfigFile struct {
|
||||
Path string
|
||||
UnderlyingError error
|
||||
}
|
||||
|
||||
func (err *ErrBadConfigFile) Error() string {
|
||||
return fmt.Sprintf("%q is invalid: %s", err.Path, err.UnderlyingError)
|
||||
}
|
||||
|
||||
// ErrConfigFileExists indicates that the config file already exists.
|
||||
type ErrConfigFileExists struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func (err *ErrConfigFileExists) Error() string {
|
||||
return fmt.Sprintf("%q already exists", err.Path)
|
||||
}
|
||||
|
||||
// ErrNoConfigFile indicates that the config file doesn't exist.
|
||||
type ErrNoConfigFile struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func (err *ErrNoConfigFile) Error() string {
|
||||
return fmt.Sprintf("%q doesn't exist", err.Path)
|
||||
}
|
||||
|
||||
const (
|
||||
// Permissions of the config file (global readable)
|
||||
configPermissions = 0644
|
||||
// Config file should be created for writing and not already exist
|
||||
createFlags = os.O_CREATE | os.O_WRONLY | os.O_EXCL
|
||||
// 128 MiB is a large enough amount of memory to make the password hash
|
||||
// very difficult to brute force on specialized hardware, but small
|
||||
// enough to work on most GNU/Linux systems.
|
||||
maxMemoryBytes = 128 * 1024 * 1024
|
||||
)
|
||||
|
||||
var (
|
||||
timingPassphrase = []byte("I am a fake passphrase")
|
||||
timingSalt = bytes.Repeat([]byte{42}, metadata.SaltLen)
|
||||
)
|
||||
|
||||
// CreateConfigFile creates a new config file at the appropriate location with
|
||||
// the appropriate hashing costs and encryption parameters. The hashing will be
|
||||
// configured to take as long as the specified time target. In addition, the
|
||||
// version of encryption policy to use may be overridden from the default of v1.
|
||||
func CreateConfigFile(target time.Duration, policyVersion int64) error {
|
||||
// Create the config file before computing the hashing costs, so we fail
|
||||
// immediately if the program has insufficient permissions.
|
||||
configFile, err := filesystem.OpenFileOverridingUmask(ConfigFileLocation,
|
||||
createFlags, configPermissions)
|
||||
switch {
|
||||
case os.IsExist(err):
|
||||
return &ErrConfigFileExists{ConfigFileLocation}
|
||||
case err != nil:
|
||||
return err
|
||||
}
|
||||
defer configFile.Close()
|
||||
|
||||
config := &metadata.Config{
|
||||
Source: metadata.DefaultSource,
|
||||
Options: metadata.DefaultOptions,
|
||||
}
|
||||
|
||||
if policyVersion != 0 {
|
||||
config.Options.PolicyVersion = policyVersion
|
||||
}
|
||||
|
||||
if config.HashCosts, err = getHashingCosts(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Creating config at %q with %v\n", ConfigFileLocation, config)
|
||||
return metadata.WriteConfig(config, configFile)
|
||||
}
|
||||
|
||||
// getConfig returns the current configuration struct. Any fields not specified
|
||||
// in the config file use the system defaults. An error is returned if the
|
||||
// config file hasn't been setup with CreateConfigFile yet or the config
|
||||
// contains invalid data.
|
||||
func getConfig() (*metadata.Config, error) {
|
||||
configFile, err := os.Open(ConfigFileLocation)
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
return nil, &ErrNoConfigFile{ConfigFileLocation}
|
||||
case err != nil:
|
||||
return nil, err
|
||||
}
|
||||
defer configFile.Close()
|
||||
|
||||
log.Printf("Reading config from %q\n", ConfigFileLocation)
|
||||
config, err := metadata.ReadConfig(configFile)
|
||||
if err != nil {
|
||||
return nil, &ErrBadConfigFile{ConfigFileLocation, err}
|
||||
}
|
||||
|
||||
// Use system defaults if not specified
|
||||
if config.Source == metadata.SourceType_default {
|
||||
config.Source = metadata.DefaultSource
|
||||
log.Printf("Falling back to source of %q", config.Source.String())
|
||||
}
|
||||
if config.Options.Padding == 0 {
|
||||
config.Options.Padding = metadata.DefaultOptions.Padding
|
||||
log.Printf("Falling back to padding of %d", config.Options.Padding)
|
||||
}
|
||||
if config.Options.Contents == metadata.EncryptionOptions_default {
|
||||
config.Options.Contents = metadata.DefaultOptions.Contents
|
||||
log.Printf("Falling back to contents mode of %q", config.Options.Contents)
|
||||
}
|
||||
if config.Options.Filenames == metadata.EncryptionOptions_default {
|
||||
config.Options.Filenames = metadata.DefaultOptions.Filenames
|
||||
log.Printf("Falling back to filenames mode of %q", config.Options.Filenames)
|
||||
}
|
||||
if config.Options.PolicyVersion == 0 {
|
||||
config.Options.PolicyVersion = metadata.DefaultOptions.PolicyVersion
|
||||
log.Printf("Falling back to policy version of %d", config.Options.PolicyVersion)
|
||||
}
|
||||
|
||||
if err := config.CheckValidity(); err != nil {
|
||||
return nil, &ErrBadConfigFile{ConfigFileLocation, err}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// getHashingCosts returns hashing costs so that hashing a password will take
|
||||
// approximately the target time. This is done using the total amount of RAM,
|
||||
// the number of CPUs present, and by running the passphrase hash many times.
|
||||
func getHashingCosts(target time.Duration) (*metadata.HashingCosts, error) {
|
||||
log.Printf("Finding hashing costs that take %v\n", target)
|
||||
|
||||
// Start out with the minimal possible costs that use all the CPUs.
|
||||
nCPUs := int64(runtime.NumCPU())
|
||||
costs := &metadata.HashingCosts{
|
||||
Time: 1,
|
||||
Memory: 8 * nCPUs,
|
||||
Parallelism: nCPUs,
|
||||
}
|
||||
|
||||
// If even the minimal costs are not fast enough, just return the
|
||||
// minimal costs and log a warning.
|
||||
t, err := timeHashingCosts(costs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("Min Costs={%v}\t-> %v\n", costs, t)
|
||||
|
||||
if t > target {
|
||||
log.Printf("time exceeded the target of %v.\n", target)
|
||||
return costs, nil
|
||||
}
|
||||
|
||||
// Now we start doubling the costs until we reach the target.
|
||||
memoryKiBLimit := memoryBytesLimit() / 1024
|
||||
for {
|
||||
// Store a copy of the previous costs
|
||||
costsPrev := *costs
|
||||
tPrev := t
|
||||
|
||||
// Double the memory up to the max, then double the time.
|
||||
if costs.Memory < memoryKiBLimit {
|
||||
costs.Memory = util.MinInt64(2*costs.Memory, memoryKiBLimit)
|
||||
} else {
|
||||
costs.Time *= 2
|
||||
}
|
||||
|
||||
// If our hashing failed, return the last good set of costs.
|
||||
if t, err = timeHashingCosts(costs); err != nil {
|
||||
log.Printf("Hashing with costs={%v} failed: %v\n", costs, err)
|
||||
return &costsPrev, nil
|
||||
}
|
||||
log.Printf("Costs={%v}\t-> %v\n", costs, t)
|
||||
|
||||
// If we have reached the target time, we return a set of costs
|
||||
// based on the linear interpolation between the last two times.
|
||||
if t >= target {
|
||||
f := float64(target-tPrev) / float64(t-tPrev)
|
||||
return &metadata.HashingCosts{
|
||||
Time: betweenCosts(costsPrev.Time, costs.Time, f),
|
||||
Memory: betweenCosts(costsPrev.Memory, costs.Memory, f),
|
||||
Parallelism: costs.Parallelism,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// memoryBytesLimit returns the maximum amount of memory we will use for
|
||||
// passphrase hashing. This will never be more than a reasonable maximum (for
|
||||
// compatibility) or an 8th the available system RAM.
|
||||
func memoryBytesLimit() int64 {
|
||||
// The sysinfo syscall only fails if given a bad address
|
||||
var info unix.Sysinfo_t
|
||||
err := unix.Sysinfo(&info)
|
||||
util.NeverError(err)
|
||||
|
||||
totalRAMBytes := int64(info.Totalram)
|
||||
return util.MinInt64(totalRAMBytes/8, maxMemoryBytes)
|
||||
}
|
||||
|
||||
// betweenCosts returns a cost between a and b. Specifically, it returns the
|
||||
// floor of a + f*(b-a). This way, f=0 returns a and f=1 returns b.
|
||||
func betweenCosts(a, b int64, f float64) int64 {
|
||||
return a + int64(f*float64(b-a))
|
||||
}
|
||||
|
||||
// timeHashingCosts runs the passphrase hash with the specified costs and
|
||||
// returns the time it takes to hash the passphrase.
|
||||
func timeHashingCosts(costs *metadata.HashingCosts) (time.Duration, error) {
|
||||
passphrase, err := crypto.NewKeyFromReader(bytes.NewReader(timingPassphrase))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer passphrase.Wipe()
|
||||
|
||||
// Be sure to measure CPU time, not wall time (time.Now)
|
||||
begin := cpuTimeInNanoseconds()
|
||||
hash, err := crypto.PassphraseHash(passphrase, timingSalt, costs)
|
||||
if err == nil {
|
||||
hash.Wipe()
|
||||
}
|
||||
end := cpuTimeInNanoseconds()
|
||||
|
||||
// This uses a lot of memory, run the garbage collector
|
||||
runtime.GC()
|
||||
|
||||
return time.Duration((end - begin) / costs.Parallelism), nil
|
||||
}
|
||||
|
||||
// cpuTimeInNanoseconds returns the nanosecond count based on the process's CPU usage.
|
||||
// This number has no absolute meaning, only relative meaning to other calls.
|
||||
func cpuTimeInNanoseconds() int64 {
|
||||
var ts unix.Timespec
|
||||
err := unix.ClockGettime(unix.CLOCK_PROCESS_CPUTIME_ID, &ts)
|
||||
// ClockGettime fails if given a bad address or on a VERY old system.
|
||||
util.NeverError(err)
|
||||
return unix.TimespecToNsec(ts)
|
||||
}
|
184
vendor/github.com/google/fscrypt/actions/context.go
generated
vendored
Normal file
184
vendor/github.com/google/fscrypt/actions/context.go
generated
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* context.go - top-level interface to fscrypt packages
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
// Package actions is the high-level interface to the fscrypt packages. The
|
||||
// functions here roughly correspond with commands for the tool in cmd/fscrypt.
|
||||
// All of the actions include a significant amount of logging, so that good
|
||||
// output can be provided for cmd/fscrypt's verbose mode.
|
||||
// The top-level actions currently include:
|
||||
// - Creating a new config file
|
||||
// - Creating a context on which to perform actions
|
||||
// - Creating, unlocking, and modifying Protectors
|
||||
// - Creating, unlocking, and modifying Policies
|
||||
package actions
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os/user"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/google/fscrypt/filesystem"
|
||||
"github.com/google/fscrypt/keyring"
|
||||
"github.com/google/fscrypt/metadata"
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
// ErrLocked indicates that the key hasn't been unwrapped yet.
|
||||
var ErrLocked = errors.New("key needs to be unlocked first")
|
||||
|
||||
// Context contains the necessary global state to perform most of fscrypt's
|
||||
// actions.
|
||||
type Context struct {
|
||||
// Config is the struct loaded from the global config file. It can be
|
||||
// modified after being loaded to customise parameters.
|
||||
Config *metadata.Config
|
||||
// Mount is the filesystem relative to which all Protectors and Policies
|
||||
// are added, edited, removed, and applied, and to which policies using
|
||||
// the filesystem keyring are provisioned.
|
||||
Mount *filesystem.Mount
|
||||
// TargetUser is the user for whom protectors are created, and to whose
|
||||
// keyring policies using the user keyring are provisioned. It's also
|
||||
// the user for whom the keys are claimed in the filesystem keyring when
|
||||
// v2 policies are provisioned.
|
||||
TargetUser *user.User
|
||||
// TrustedUser is the user for whom policies and protectors are allowed
|
||||
// to be read. Specifically, if TrustedUser is set, then only
|
||||
// policies and protectors owned by TrustedUser or by root will be
|
||||
// allowed to be read. If it's nil, then all policies and protectors
|
||||
// the process has filesystem-level read access to will be allowed.
|
||||
TrustedUser *user.User
|
||||
}
|
||||
|
||||
// NewContextFromPath makes a context for the filesystem containing the
|
||||
// specified path and whose Config is loaded from the global config file. On
|
||||
// success, the Context contains a valid Config and Mount. The target user
|
||||
// defaults to the current effective user if none is specified.
|
||||
func NewContextFromPath(path string, targetUser *user.User) (*Context, error) {
|
||||
ctx, err := newContextFromUser(targetUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ctx.Mount, err = filesystem.FindMount(path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("%s is on %s filesystem %q (%s)", path,
|
||||
ctx.Mount.FilesystemType, ctx.Mount.Path, ctx.Mount.Device)
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// NewContextFromMountpoint makes a context for the filesystem at the specified
|
||||
// mountpoint and whose Config is loaded from the global config file. On
|
||||
// success, the Context contains a valid Config and Mount. The target user
|
||||
// defaults to the current effective user if none is specified.
|
||||
func NewContextFromMountpoint(mountpoint string, targetUser *user.User) (*Context, error) {
|
||||
ctx, err := newContextFromUser(targetUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ctx.Mount, err = filesystem.GetMount(mountpoint); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("found %s filesystem %q (%s)", ctx.Mount.FilesystemType,
|
||||
ctx.Mount.Path, ctx.Mount.Device)
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// newContextFromUser makes a context with the corresponding target user, and
|
||||
// whose Config is loaded from the global config file. If the target user is
|
||||
// nil, the effective user is used.
|
||||
func newContextFromUser(targetUser *user.User) (*Context, error) {
|
||||
var err error
|
||||
if targetUser == nil {
|
||||
if targetUser, err = util.EffectiveUser(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
ctx := &Context{TargetUser: targetUser}
|
||||
if ctx.Config, err = getConfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// By default, when running as a non-root user we only read policies and
|
||||
// protectors owned by the user or root. When running as root, we allow
|
||||
// reading all policies and protectors.
|
||||
if !ctx.Config.GetAllowCrossUserMetadata() && !util.IsUserRoot() {
|
||||
ctx.TrustedUser, err = util.EffectiveUser()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("creating context for user %q", targetUser.Username)
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// checkContext verifies that the context contains a valid config and a mount
|
||||
// which is being used with fscrypt.
|
||||
func (ctx *Context) checkContext() error {
|
||||
if err := ctx.Config.CheckValidity(); err != nil {
|
||||
return &ErrBadConfig{ctx.Config, err}
|
||||
}
|
||||
return ctx.Mount.CheckSetup(ctx.TrustedUser)
|
||||
}
|
||||
|
||||
func (ctx *Context) getKeyringOptions() *keyring.Options {
|
||||
return &keyring.Options{
|
||||
Mount: ctx.Mount,
|
||||
User: ctx.TargetUser,
|
||||
UseFsKeyringForV1Policies: ctx.Config.GetUseFsKeyringForV1Policies(),
|
||||
}
|
||||
}
|
||||
|
||||
// getProtectorOption returns the ProtectorOption for the protector on the
|
||||
// context's mountpoint with the specified descriptor.
|
||||
func (ctx *Context) getProtectorOption(protectorDescriptor string) *ProtectorOption {
|
||||
mnt, data, err := ctx.Mount.GetProtector(protectorDescriptor, ctx.TrustedUser)
|
||||
if err != nil {
|
||||
return &ProtectorOption{ProtectorInfo{}, nil, err}
|
||||
}
|
||||
|
||||
info := ProtectorInfo{data}
|
||||
// No linked path if on the same mountpoint
|
||||
if mnt == ctx.Mount {
|
||||
return &ProtectorOption{info, nil, nil}
|
||||
}
|
||||
return &ProtectorOption{info, mnt, nil}
|
||||
}
|
||||
|
||||
// ProtectorOptions creates a slice of all the options for all of the Protectors
|
||||
// on the Context's mountpoint.
|
||||
func (ctx *Context) ProtectorOptions() ([]*ProtectorOption, error) {
|
||||
if err := ctx.checkContext(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
descriptors, err := ctx.Mount.ListProtectors(ctx.TrustedUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
options := make([]*ProtectorOption, len(descriptors))
|
||||
for i, descriptor := range descriptors {
|
||||
options[i] = ctx.getProtectorOption(descriptor)
|
||||
}
|
||||
return options, nil
|
||||
}
|
622
vendor/github.com/google/fscrypt/actions/policy.go
generated
vendored
Normal file
622
vendor/github.com/google/fscrypt/actions/policy.go
generated
vendored
Normal file
@ -0,0 +1,622 @@
|
||||
/*
|
||||
* policy.go - functions for dealing with policies
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/google/fscrypt/crypto"
|
||||
"github.com/google/fscrypt/filesystem"
|
||||
"github.com/google/fscrypt/keyring"
|
||||
"github.com/google/fscrypt/metadata"
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
// ErrAccessDeniedPossiblyV2 indicates that a directory's encryption policy
|
||||
// couldn't be retrieved due to "permission denied", but it looks like it's due
|
||||
// to the directory using a v2 policy but the kernel not supporting it.
|
||||
type ErrAccessDeniedPossiblyV2 struct {
|
||||
DirPath string
|
||||
}
|
||||
|
||||
func (err *ErrAccessDeniedPossiblyV2) Error() string {
|
||||
return fmt.Sprintf(`
|
||||
failed to get encryption policy of %s: permission denied
|
||||
|
||||
This may be caused by the directory using a v2 encryption policy and the
|
||||
current kernel not supporting it. If indeed the case, then this
|
||||
directory can only be used on kernel v5.4 and later. You can create
|
||||
directories accessible on older kernels by changing policy_version to 1
|
||||
in %s.`,
|
||||
err.DirPath, ConfigFileLocation)
|
||||
}
|
||||
|
||||
// ErrAlreadyProtected indicates that a policy is already protected by the given
|
||||
// protector.
|
||||
type ErrAlreadyProtected struct {
|
||||
Policy *Policy
|
||||
Protector *Protector
|
||||
}
|
||||
|
||||
func (err *ErrAlreadyProtected) Error() string {
|
||||
return fmt.Sprintf("policy %s is already protected by protector %s",
|
||||
err.Policy.Descriptor(), err.Protector.Descriptor())
|
||||
}
|
||||
|
||||
// ErrDifferentFilesystem indicates that a policy can't be applied to a
|
||||
// directory on a different filesystem.
|
||||
type ErrDifferentFilesystem struct {
|
||||
PolicyMount *filesystem.Mount
|
||||
PathMount *filesystem.Mount
|
||||
}
|
||||
|
||||
func (err *ErrDifferentFilesystem) Error() string {
|
||||
return fmt.Sprintf(`cannot apply policy from filesystem %q to a
|
||||
directory on filesystem %q. Policies may only protect files on the same
|
||||
filesystem.`, err.PolicyMount.Path, err.PathMount.Path)
|
||||
}
|
||||
|
||||
// ErrMissingPolicyMetadata indicates that a directory is encrypted but its
|
||||
// policy metadata cannot be found.
|
||||
type ErrMissingPolicyMetadata struct {
|
||||
Mount *filesystem.Mount
|
||||
DirPath string
|
||||
Descriptor string
|
||||
}
|
||||
|
||||
func (err *ErrMissingPolicyMetadata) Error() string {
|
||||
return fmt.Sprintf(`filesystem %q does not contain the policy metadata
|
||||
for %q. This directory has either been encrypted with another tool (such
|
||||
as e4crypt), or the file %q has been deleted.`,
|
||||
err.Mount.Path, err.DirPath,
|
||||
err.Mount.PolicyPath(err.Descriptor))
|
||||
}
|
||||
|
||||
// ErrNotProtected indicates that the given policy is not protected by the given
|
||||
// protector.
|
||||
type ErrNotProtected struct {
|
||||
PolicyDescriptor string
|
||||
ProtectorDescriptor string
|
||||
}
|
||||
|
||||
func (err *ErrNotProtected) Error() string {
|
||||
return fmt.Sprintf(`policy %s is not protected by protector %s`,
|
||||
err.PolicyDescriptor, err.ProtectorDescriptor)
|
||||
}
|
||||
|
||||
// ErrOnlyProtector indicates that the last protector can't be removed from a
|
||||
// policy.
|
||||
type ErrOnlyProtector struct {
|
||||
Policy *Policy
|
||||
}
|
||||
|
||||
func (err *ErrOnlyProtector) Error() string {
|
||||
return fmt.Sprintf(`cannot remove the only protector from policy %s. A
|
||||
policy must have at least one protector.`, err.Policy.Descriptor())
|
||||
}
|
||||
|
||||
// ErrPolicyMetadataMismatch indicates that the policy metadata for an encrypted
|
||||
// directory is inconsistent with that directory.
|
||||
type ErrPolicyMetadataMismatch struct {
|
||||
DirPath string
|
||||
Mount *filesystem.Mount
|
||||
PathData *metadata.PolicyData
|
||||
MountData *metadata.PolicyData
|
||||
}
|
||||
|
||||
func (err *ErrPolicyMetadataMismatch) Error() string {
|
||||
return fmt.Sprintf(`inconsistent metadata between encrypted directory %q
|
||||
and its corresponding metadata file %q.
|
||||
|
||||
Directory has descriptor:%s %s
|
||||
|
||||
Metadata file has descriptor:%s %s`,
|
||||
err.DirPath, err.Mount.PolicyPath(err.PathData.KeyDescriptor),
|
||||
err.PathData.KeyDescriptor, err.PathData.Options,
|
||||
err.MountData.KeyDescriptor, err.MountData.Options)
|
||||
}
|
||||
|
||||
// PurgeAllPolicies removes all policy keys on the filesystem from the kernel
|
||||
// keyring. In order for this to fully take effect, the filesystem may also need
|
||||
// to be unmounted or caches dropped.
|
||||
func PurgeAllPolicies(ctx *Context) error {
|
||||
if err := ctx.checkContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
policies, err := ctx.Mount.ListPolicies(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, policyDescriptor := range policies {
|
||||
err = keyring.RemoveEncryptionKey(policyDescriptor, ctx.getKeyringOptions(), false)
|
||||
switch errors.Cause(err) {
|
||||
case nil, keyring.ErrKeyNotPresent:
|
||||
// We don't care if the key has already been removed
|
||||
case keyring.ErrKeyFilesOpen:
|
||||
log.Printf("Key for policy %s couldn't be fully removed because some files are still in-use",
|
||||
policyDescriptor)
|
||||
case keyring.ErrKeyAddedByOtherUsers:
|
||||
log.Printf("Key for policy %s couldn't be fully removed because other user(s) have added it too",
|
||||
policyDescriptor)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Policy represents an unlocked policy, so it contains the PolicyData as well
|
||||
// as the actual protector key. These unlocked Polices can then be applied to a
|
||||
// directory, or have their key material inserted into the keyring (which will
|
||||
// allow encrypted files to be accessed). As with the key struct, a Policy
|
||||
// should be wiped after use.
|
||||
type Policy struct {
|
||||
Context *Context
|
||||
data *metadata.PolicyData
|
||||
key *crypto.Key
|
||||
created bool
|
||||
ownerIfCreating *user.User
|
||||
newLinkedProtectors []string
|
||||
}
|
||||
|
||||
// CreatePolicy creates a Policy protected by given Protector and stores the
|
||||
// appropriate data on the filesystem. On error, no data is changed on the
|
||||
// filesystem.
|
||||
func CreatePolicy(ctx *Context, protector *Protector) (*Policy, error) {
|
||||
if err := ctx.checkContext(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Randomly create the underlying policy key (and wipe if we fail)
|
||||
key, err := crypto.NewRandomKey(metadata.PolicyKeyLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyDescriptor, err := crypto.ComputeKeyDescriptor(key, ctx.Config.Options.PolicyVersion)
|
||||
if err != nil {
|
||||
key.Wipe()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
policy := &Policy{
|
||||
Context: ctx,
|
||||
data: &metadata.PolicyData{
|
||||
Options: ctx.Config.Options,
|
||||
KeyDescriptor: keyDescriptor,
|
||||
},
|
||||
key: key,
|
||||
created: true,
|
||||
}
|
||||
|
||||
policy.ownerIfCreating, err = getOwnerOfMetadataForProtector(protector)
|
||||
if err != nil {
|
||||
policy.Lock()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = policy.AddProtector(protector); err != nil {
|
||||
policy.Lock()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
// GetPolicy retrieves a locked policy with a specific descriptor. The Policy is
|
||||
// still locked in this case, so it must be unlocked before using certain
|
||||
// methods.
|
||||
func GetPolicy(ctx *Context, descriptor string) (*Policy, error) {
|
||||
if err := ctx.checkContext(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ctx.Mount.GetPolicy(descriptor, ctx.TrustedUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("got data for %s from %q", descriptor, ctx.Mount.Path)
|
||||
|
||||
return &Policy{Context: ctx, data: data}, nil
|
||||
}
|
||||
|
||||
// GetPolicyFromPath returns the locked policy descriptor for a file on the
|
||||
// filesystem. The Policy is still locked in this case, so it must be unlocked
|
||||
// before using certain methods. An error is returned if the metadata is
|
||||
// inconsistent or the path is not encrypted.
|
||||
func GetPolicyFromPath(ctx *Context, path string) (*Policy, error) {
|
||||
if err := ctx.checkContext(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We double check that the options agree for both the data we get from
|
||||
// the path, and the data we get from the mountpoint.
|
||||
pathData, err := metadata.GetPolicy(path)
|
||||
err = ctx.Mount.EncryptionSupportError(err)
|
||||
if err != nil {
|
||||
// On kernels that don't support v2 encryption policies, trying
|
||||
// to open a directory with a v2 policy simply gave EACCES. This
|
||||
// is ambiguous with other errors, but try to detect this case
|
||||
// and show a better error message.
|
||||
if os.IsPermission(err) &&
|
||||
filesystem.HaveReadAccessTo(path) &&
|
||||
!keyring.IsFsKeyringSupported(ctx.Mount) {
|
||||
return nil, &ErrAccessDeniedPossiblyV2{path}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
descriptor := pathData.KeyDescriptor
|
||||
log.Printf("found policy %s for %q", descriptor, path)
|
||||
|
||||
mountData, err := ctx.Mount.GetPolicy(descriptor, ctx.TrustedUser)
|
||||
if err != nil {
|
||||
log.Printf("getting policy metadata: %v", err)
|
||||
if _, ok := err.(*filesystem.ErrPolicyNotFound); ok {
|
||||
return nil, &ErrMissingPolicyMetadata{ctx.Mount, path, descriptor}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("found data for policy %s on %q", descriptor, ctx.Mount.Path)
|
||||
|
||||
if !proto.Equal(pathData.Options, mountData.Options) ||
|
||||
pathData.KeyDescriptor != mountData.KeyDescriptor {
|
||||
return nil, &ErrPolicyMetadataMismatch{path, ctx.Mount, pathData, mountData}
|
||||
}
|
||||
log.Print("data from filesystem and path agree")
|
||||
|
||||
return &Policy{Context: ctx, data: mountData}, nil
|
||||
}
|
||||
|
||||
// ProtectorOptions creates a slice of ProtectorOptions for the protectors
|
||||
// protecting this policy.
|
||||
func (policy *Policy) ProtectorOptions() []*ProtectorOption {
|
||||
options := make([]*ProtectorOption, len(policy.data.WrappedPolicyKeys))
|
||||
for i, wrappedPolicyKey := range policy.data.WrappedPolicyKeys {
|
||||
options[i] = policy.Context.getProtectorOption(wrappedPolicyKey.ProtectorDescriptor)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// ProtectorDescriptors creates a slice of the Protector descriptors for the
|
||||
// protectors protecting this policy.
|
||||
func (policy *Policy) ProtectorDescriptors() []string {
|
||||
descriptors := make([]string, len(policy.data.WrappedPolicyKeys))
|
||||
for i, wrappedPolicyKey := range policy.data.WrappedPolicyKeys {
|
||||
descriptors[i] = wrappedPolicyKey.ProtectorDescriptor
|
||||
}
|
||||
return descriptors
|
||||
}
|
||||
|
||||
// Descriptor returns the key descriptor for this policy.
|
||||
func (policy *Policy) Descriptor() string {
|
||||
return policy.data.KeyDescriptor
|
||||
}
|
||||
|
||||
// Options returns the encryption options of this policy.
|
||||
func (policy *Policy) Options() *metadata.EncryptionOptions {
|
||||
return policy.data.Options
|
||||
}
|
||||
|
||||
// Version returns the version of this policy.
|
||||
func (policy *Policy) Version() int64 {
|
||||
return policy.data.Options.PolicyVersion
|
||||
}
|
||||
|
||||
// Destroy removes a policy from the filesystem. It also removes any new
|
||||
// protector links that were created for the policy. This does *not* wipe the
|
||||
// policy's internal key from memory; use Lock() to do that.
|
||||
func (policy *Policy) Destroy() error {
|
||||
for _, protectorDescriptor := range policy.newLinkedProtectors {
|
||||
policy.Context.Mount.RemoveProtector(protectorDescriptor)
|
||||
}
|
||||
return policy.Context.Mount.RemovePolicy(policy.Descriptor())
|
||||
}
|
||||
|
||||
// Revert destroys a policy if it was created, but does nothing if it was just
|
||||
// queried from the filesystem.
|
||||
func (policy *Policy) Revert() error {
|
||||
if !policy.created {
|
||||
return nil
|
||||
}
|
||||
return policy.Destroy()
|
||||
}
|
||||
|
||||
func (policy *Policy) String() string {
|
||||
return fmt.Sprintf("Policy: %s\nMountpoint: %s\nOptions: %v\nProtectors:%+v",
|
||||
policy.Descriptor(), policy.Context.Mount, policy.data.Options,
|
||||
policy.ProtectorDescriptors())
|
||||
}
|
||||
|
||||
// Unlock unwraps the Policy's internal key. As a Protector is needed to unlock
|
||||
// the Policy, callbacks to select the Policy and get the key are needed. This
|
||||
// method will retry the keyFn as necessary to get the correct key for the
|
||||
// selected protector. Does nothing if policy is already unlocked.
|
||||
func (policy *Policy) Unlock(optionFn OptionFunc, keyFn KeyFunc) error {
|
||||
if policy.key != nil {
|
||||
return nil
|
||||
}
|
||||
options := policy.ProtectorOptions()
|
||||
|
||||
// The OptionFunc indicates which option and wrapped key we should use.
|
||||
idx, err := optionFn(policy.Descriptor(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
option := options[idx]
|
||||
if option.LoadError != nil {
|
||||
return option.LoadError
|
||||
}
|
||||
|
||||
log.Printf("protector %s selected in callback", option.Descriptor())
|
||||
protectorKey, err := unwrapProtectorKey(option.ProtectorInfo, keyFn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer protectorKey.Wipe()
|
||||
|
||||
log.Printf("unwrapping policy %s with protector", policy.Descriptor())
|
||||
wrappedPolicyKey := policy.data.WrappedPolicyKeys[idx].WrappedKey
|
||||
policy.key, err = crypto.Unwrap(protectorKey, wrappedPolicyKey)
|
||||
return err
|
||||
}
|
||||
|
||||
// UnlockWithProtector uses an unlocked Protector to unlock a policy. An error
|
||||
// is returned if the Protector is not yet unlocked or does not protect the
|
||||
// policy. Does nothing if policy is already unlocked.
|
||||
func (policy *Policy) UnlockWithProtector(protector *Protector) error {
|
||||
if policy.key != nil {
|
||||
return nil
|
||||
}
|
||||
if protector.key == nil {
|
||||
return ErrLocked
|
||||
}
|
||||
idx, ok := policy.findWrappedKeyIndex(protector.Descriptor())
|
||||
if !ok {
|
||||
return &ErrNotProtected{policy.Descriptor(), protector.Descriptor()}
|
||||
}
|
||||
|
||||
var err error
|
||||
wrappedPolicyKey := policy.data.WrappedPolicyKeys[idx].WrappedKey
|
||||
policy.key, err = crypto.Unwrap(protector.key, wrappedPolicyKey)
|
||||
return err
|
||||
}
|
||||
|
||||
// Lock wipes a Policy's internal Key. It should always be called after using a
|
||||
// Policy. This is often done with a defer statement. There is no effect if
|
||||
// called multiple times.
|
||||
func (policy *Policy) Lock() error {
|
||||
err := policy.key.Wipe()
|
||||
policy.key = nil
|
||||
return err
|
||||
}
|
||||
|
||||
// UsesProtector returns if the policy is protected with the protector
|
||||
func (policy *Policy) UsesProtector(protector *Protector) bool {
|
||||
_, ok := policy.findWrappedKeyIndex(protector.Descriptor())
|
||||
return ok
|
||||
}
|
||||
|
||||
// getOwnerOfMetadataForProtector returns the User to whom the owner of any new
|
||||
// policies or protector links for the given protector should be set.
|
||||
//
|
||||
// This will return a non-nil value only when the protector is a login protector
|
||||
// and the process is running as root. In this scenario, root is setting up
|
||||
// encryption on the user's behalf, so we need to make new policies and
|
||||
// protector links owned by the user (rather than root) to allow them to be read
|
||||
// by the user, just like the login protector itself which is handled elsewhere.
|
||||
func getOwnerOfMetadataForProtector(protector *Protector) (*user.User, error) {
|
||||
if protector.data.Source == metadata.SourceType_pam_passphrase && util.IsUserRoot() {
|
||||
owner, err := util.UserFromUID(protector.data.Uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return owner, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// AddProtector updates the data that is wrapping the Policy Key so that the
|
||||
// provided Protector is now protecting the specified Policy. If an error is
|
||||
// returned, no data has been changed. If the policy and protector are on
|
||||
// different filesystems, a link will be created between them. The policy and
|
||||
// protector must both be unlocked.
|
||||
func (policy *Policy) AddProtector(protector *Protector) error {
|
||||
if policy.UsesProtector(protector) {
|
||||
return &ErrAlreadyProtected{policy, protector}
|
||||
}
|
||||
if policy.key == nil || protector.key == nil {
|
||||
return ErrLocked
|
||||
}
|
||||
|
||||
// If the protector is on a different filesystem, we need to add a link
|
||||
// to it on the policy's filesystem.
|
||||
if policy.Context.Mount != protector.Context.Mount {
|
||||
log.Printf("policy on %s\n protector on %s\n", policy.Context.Mount, protector.Context.Mount)
|
||||
ownerIfCreating, err := getOwnerOfMetadataForProtector(protector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isNewLink, err := policy.Context.Mount.AddLinkedProtector(
|
||||
protector.Descriptor(), protector.Context.Mount,
|
||||
protector.Context.TrustedUser, ownerIfCreating)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isNewLink {
|
||||
policy.newLinkedProtectors = append(policy.newLinkedProtectors,
|
||||
protector.Descriptor())
|
||||
}
|
||||
} else {
|
||||
log.Printf("policy and protector both on %q", policy.Context.Mount)
|
||||
}
|
||||
|
||||
// Create the wrapped policy key
|
||||
wrappedKey, err := crypto.Wrap(protector.key, policy.key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Append the wrapped key to the data
|
||||
policy.addKey(&metadata.WrappedPolicyKey{
|
||||
ProtectorDescriptor: protector.Descriptor(),
|
||||
WrappedKey: wrappedKey,
|
||||
})
|
||||
|
||||
if err := policy.commitData(); err != nil {
|
||||
// revert the addition on failure
|
||||
policy.removeKey(len(policy.data.WrappedPolicyKeys) - 1)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveProtector updates the data that is wrapping the Policy Key so that the
|
||||
// protector with the given descriptor is no longer protecting the specified
|
||||
// Policy. If an error is returned, no data has been changed. Note that the
|
||||
// protector itself won't be removed, nor will a link to the protector be
|
||||
// removed (in the case where the protector and policy are on different
|
||||
// filesystems). The policy can be locked or unlocked.
|
||||
func (policy *Policy) RemoveProtector(protectorDescriptor string) error {
|
||||
idx, ok := policy.findWrappedKeyIndex(protectorDescriptor)
|
||||
if !ok {
|
||||
return &ErrNotProtected{policy.Descriptor(), protectorDescriptor}
|
||||
}
|
||||
|
||||
if len(policy.data.WrappedPolicyKeys) == 1 {
|
||||
return &ErrOnlyProtector{policy}
|
||||
}
|
||||
|
||||
// Remove the wrapped key from the data
|
||||
toRemove := policy.removeKey(idx)
|
||||
|
||||
if err := policy.commitData(); err != nil {
|
||||
// revert the removal on failure (order is irrelevant)
|
||||
policy.addKey(toRemove)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply sets the Policy on a specified directory. Currently we impose the
|
||||
// additional constraint that policies and the directories they are applied to
|
||||
// must reside on the same filesystem.
|
||||
func (policy *Policy) Apply(path string) error {
|
||||
if pathMount, err := filesystem.FindMount(path); err != nil {
|
||||
return err
|
||||
} else if pathMount != policy.Context.Mount {
|
||||
return &ErrDifferentFilesystem{policy.Context.Mount, pathMount}
|
||||
}
|
||||
|
||||
err := metadata.SetPolicy(path, policy.data)
|
||||
return policy.Context.Mount.EncryptionSupportError(err)
|
||||
}
|
||||
|
||||
// GetProvisioningStatus returns the status of this policy's key in the keyring.
|
||||
func (policy *Policy) GetProvisioningStatus() keyring.KeyStatus {
|
||||
status, _ := keyring.GetEncryptionKeyStatus(policy.Descriptor(),
|
||||
policy.Context.getKeyringOptions())
|
||||
return status
|
||||
}
|
||||
|
||||
// IsProvisionedByTargetUser returns true if the policy's key is present in the
|
||||
// target kernel keyring, but not if that keyring is a filesystem keyring and
|
||||
// the key only been added by users other than Context.TargetUser.
|
||||
func (policy *Policy) IsProvisionedByTargetUser() bool {
|
||||
return policy.GetProvisioningStatus() == keyring.KeyPresent
|
||||
}
|
||||
|
||||
// Provision inserts the Policy key into the kernel keyring. This allows reading
|
||||
// and writing of files encrypted with this directory. Requires unlocked Policy.
|
||||
func (policy *Policy) Provision() error {
|
||||
if policy.key == nil {
|
||||
return ErrLocked
|
||||
}
|
||||
return keyring.AddEncryptionKey(policy.key, policy.Descriptor(),
|
||||
policy.Context.getKeyringOptions())
|
||||
}
|
||||
|
||||
// Deprovision removes the Policy key from the kernel keyring. This prevents
|
||||
// reading and writing to the directory --- unless the target keyring is a user
|
||||
// keyring, in which case caches must be dropped too. If the Policy key was
|
||||
// already removed, returns keyring.ErrKeyNotPresent.
|
||||
func (policy *Policy) Deprovision(allUsers bool) error {
|
||||
return keyring.RemoveEncryptionKey(policy.Descriptor(),
|
||||
policy.Context.getKeyringOptions(), allUsers)
|
||||
}
|
||||
|
||||
// NeedsUserKeyring returns true if Provision and Deprovision for this policy
|
||||
// will use a user keyring (deprecated), not a filesystem keyring.
|
||||
func (policy *Policy) NeedsUserKeyring() bool {
|
||||
return policy.Version() == 1 && !policy.Context.Config.GetUseFsKeyringForV1Policies()
|
||||
}
|
||||
|
||||
// NeedsRootToProvision returns true if Provision and Deprovision will require
|
||||
// root for this policy in the current configuration.
|
||||
func (policy *Policy) NeedsRootToProvision() bool {
|
||||
return policy.Version() == 1 && policy.Context.Config.GetUseFsKeyringForV1Policies()
|
||||
}
|
||||
|
||||
// CanBeAppliedWithoutProvisioning returns true if this process can apply this
|
||||
// policy to a directory without first calling Provision.
|
||||
func (policy *Policy) CanBeAppliedWithoutProvisioning() bool {
|
||||
return policy.Version() == 1 || util.IsUserRoot()
|
||||
}
|
||||
|
||||
// commitData writes the Policy's current data to the filesystem.
|
||||
func (policy *Policy) commitData() error {
|
||||
return policy.Context.Mount.AddPolicy(policy.data, policy.ownerIfCreating)
|
||||
}
|
||||
|
||||
// findWrappedPolicyKey returns the index of the wrapped policy key
|
||||
// corresponding to this policy and protector. The returned bool is false if no
|
||||
// wrapped policy key corresponds to the specified protector, true otherwise.
|
||||
func (policy *Policy) findWrappedKeyIndex(protectorDescriptor string) (int, bool) {
|
||||
for idx, wrappedPolicyKey := range policy.data.WrappedPolicyKeys {
|
||||
if wrappedPolicyKey.ProtectorDescriptor == protectorDescriptor {
|
||||
return idx, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// addKey adds the wrapped policy key to end of the wrapped key data.
|
||||
func (policy *Policy) addKey(toAdd *metadata.WrappedPolicyKey) {
|
||||
policy.data.WrappedPolicyKeys = append(policy.data.WrappedPolicyKeys, toAdd)
|
||||
}
|
||||
|
||||
// removeKey removes the wrapped policy key at the specified index. This
|
||||
// does not preserve the order of the wrapped policy key array. If no index is
|
||||
// specified the last key is removed.
|
||||
func (policy *Policy) removeKey(index int) *metadata.WrappedPolicyKey {
|
||||
lastIdx := len(policy.data.WrappedPolicyKeys) - 1
|
||||
toRemove := policy.data.WrappedPolicyKeys[index]
|
||||
|
||||
// See https://github.com/golang/go/wiki/SliceTricks
|
||||
policy.data.WrappedPolicyKeys[index] = policy.data.WrappedPolicyKeys[lastIdx]
|
||||
policy.data.WrappedPolicyKeys[lastIdx] = nil
|
||||
policy.data.WrappedPolicyKeys = policy.data.WrappedPolicyKeys[:lastIdx]
|
||||
|
||||
return toRemove
|
||||
}
|
300
vendor/github.com/google/fscrypt/actions/protector.go
generated
vendored
Normal file
300
vendor/github.com/google/fscrypt/actions/protector.go
generated
vendored
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* protector.go - functions for dealing with protectors
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os/user"
|
||||
|
||||
"github.com/google/fscrypt/crypto"
|
||||
"github.com/google/fscrypt/metadata"
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
// LoginProtectorMountpoint is the mountpoint where login protectors are stored.
|
||||
// This can be overridden by the user of this package.
|
||||
var LoginProtectorMountpoint = "/"
|
||||
|
||||
// ErrLoginProtectorExists indicates that a user already has a login protector.
|
||||
type ErrLoginProtectorExists struct {
|
||||
User *user.User
|
||||
}
|
||||
|
||||
func (err *ErrLoginProtectorExists) Error() string {
|
||||
return fmt.Sprintf("user %q already has a login protector", err.User.Username)
|
||||
}
|
||||
|
||||
// ErrLoginProtectorName indicates that a name was given for a login protector.
|
||||
type ErrLoginProtectorName struct {
|
||||
Name string
|
||||
User *user.User
|
||||
}
|
||||
|
||||
func (err *ErrLoginProtectorName) Error() string {
|
||||
return fmt.Sprintf(`cannot assign name %q to new login protector for
|
||||
user %q because login protectors are identified by user, not by name.`,
|
||||
err.Name, err.User.Username)
|
||||
}
|
||||
|
||||
// ErrMissingProtectorName indicates that a protector name is needed.
|
||||
type ErrMissingProtectorName struct {
|
||||
Source metadata.SourceType
|
||||
}
|
||||
|
||||
func (err *ErrMissingProtectorName) Error() string {
|
||||
return fmt.Sprintf("%s protectors must be named", err.Source)
|
||||
}
|
||||
|
||||
// ErrProtectorNameExists indicates that a protector name already exists.
|
||||
type ErrProtectorNameExists struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (err *ErrProtectorNameExists) Error() string {
|
||||
return fmt.Sprintf("there is already a protector named %q", err.Name)
|
||||
}
|
||||
|
||||
// checkForProtectorWithName returns an error if there is already a protector
|
||||
// on the filesystem with a specific name (or if we cannot read the necessary
|
||||
// data).
|
||||
func checkForProtectorWithName(ctx *Context, name string) error {
|
||||
options, err := ctx.ProtectorOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, option := range options {
|
||||
if option.Name() == name {
|
||||
return &ErrProtectorNameExists{name}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkIfUserHasLoginProtector returns an error if there is already a login
|
||||
// protector on the filesystem for a specific user (or if we cannot read the
|
||||
// necessary data).
|
||||
func checkIfUserHasLoginProtector(ctx *Context, uid int64) error {
|
||||
options, err := ctx.ProtectorOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, option := range options {
|
||||
if option.Source() == metadata.SourceType_pam_passphrase && option.UID() == uid {
|
||||
return &ErrLoginProtectorExists{ctx.TargetUser}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Protector represents an unlocked protector, so it contains the ProtectorData
|
||||
// as well as the actual protector key. These unlocked Protectors are necessary
|
||||
// to unlock policies and create new polices. As with the key struct, a
|
||||
// Protector should be wiped after use.
|
||||
type Protector struct {
|
||||
Context *Context
|
||||
data *metadata.ProtectorData
|
||||
key *crypto.Key
|
||||
created bool
|
||||
ownerIfCreating *user.User
|
||||
}
|
||||
|
||||
// CreateProtector creates an unlocked protector with a given name (name only
|
||||
// needed for custom and raw protector types). The keyFn provided to create the
|
||||
// Protector key will only be called once. If an error is returned, no data has
|
||||
// been changed on the filesystem.
|
||||
func CreateProtector(ctx *Context, name string, keyFn KeyFunc, owner *user.User) (*Protector, error) {
|
||||
if err := ctx.checkContext(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Sanity checks for names
|
||||
if ctx.Config.Source == metadata.SourceType_pam_passphrase {
|
||||
// login protectors don't need a name (we use the username instead)
|
||||
if name != "" {
|
||||
return nil, &ErrLoginProtectorName{name, ctx.TargetUser}
|
||||
}
|
||||
} else {
|
||||
// non-login protectors need a name (so we can distinguish between them)
|
||||
if name == "" {
|
||||
return nil, &ErrMissingProtectorName{ctx.Config.Source}
|
||||
}
|
||||
// we don't want to duplicate naming
|
||||
if err := checkForProtectorWithName(ctx, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
protector := &Protector{
|
||||
Context: ctx,
|
||||
data: &metadata.ProtectorData{
|
||||
Name: name,
|
||||
Source: ctx.Config.Source,
|
||||
},
|
||||
created: true,
|
||||
ownerIfCreating: owner,
|
||||
}
|
||||
|
||||
// Extra data is needed for some SourceTypes
|
||||
switch protector.data.Source {
|
||||
case metadata.SourceType_pam_passphrase:
|
||||
// As the pam passphrases are user specific, we also store the
|
||||
// UID for this kind of source.
|
||||
protector.data.Uid = int64(util.AtoiOrPanic(ctx.TargetUser.Uid))
|
||||
// Make sure we aren't duplicating protectors
|
||||
if err = checkIfUserHasLoginProtector(ctx, protector.data.Uid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fallthrough
|
||||
case metadata.SourceType_custom_passphrase:
|
||||
// Our passphrase sources need costs and a random salt.
|
||||
if protector.data.Salt, err = crypto.NewRandomBuffer(metadata.SaltLen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
protector.data.Costs = ctx.Config.HashCosts
|
||||
}
|
||||
|
||||
// Randomly create the underlying protector key (and wipe if we fail)
|
||||
if protector.key, err = crypto.NewRandomKey(metadata.InternalKeyLen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
protector.data.ProtectorDescriptor, err = crypto.ComputeKeyDescriptor(protector.key, 1)
|
||||
if err != nil {
|
||||
protector.Lock()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = protector.Rewrap(keyFn); err != nil {
|
||||
protector.Lock()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return protector, nil
|
||||
}
|
||||
|
||||
// GetProtector retrieves a Protector with a specific descriptor. The Protector
|
||||
// is still locked in this case, so it must be unlocked before using certain
|
||||
// methods.
|
||||
func GetProtector(ctx *Context, descriptor string) (*Protector, error) {
|
||||
log.Printf("Getting protector %s", descriptor)
|
||||
err := ctx.checkContext()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
protector := &Protector{Context: ctx}
|
||||
protector.data, err = ctx.Mount.GetRegularProtector(descriptor, ctx.TrustedUser)
|
||||
return protector, err
|
||||
}
|
||||
|
||||
// GetProtectorFromOption retrieves a protector based on a protector option.
|
||||
// If the option had a load error, this function returns that error. The
|
||||
// Protector is still locked in this case, so it must be unlocked before using
|
||||
// certain methods.
|
||||
func GetProtectorFromOption(ctx *Context, option *ProtectorOption) (*Protector, error) {
|
||||
log.Printf("Getting protector %s from option", option.Descriptor())
|
||||
if err := ctx.checkContext(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if option.LoadError != nil {
|
||||
return nil, option.LoadError
|
||||
}
|
||||
|
||||
// Replace the context if this is a linked protector
|
||||
if option.LinkedMount != nil {
|
||||
ctx = &Context{ctx.Config, option.LinkedMount, ctx.TargetUser, ctx.TrustedUser}
|
||||
}
|
||||
return &Protector{Context: ctx, data: option.data}, nil
|
||||
}
|
||||
|
||||
// Descriptor returns the protector descriptor.
|
||||
func (protector *Protector) Descriptor() string {
|
||||
return protector.data.ProtectorDescriptor
|
||||
}
|
||||
|
||||
// Destroy removes a protector from the filesystem. The internal key should
|
||||
// still be wiped with Lock().
|
||||
func (protector *Protector) Destroy() error {
|
||||
return protector.Context.Mount.RemoveProtector(protector.Descriptor())
|
||||
}
|
||||
|
||||
// Revert destroys a protector if it was created, but does nothing if it was
|
||||
// just queried from the filesystem.
|
||||
func (protector *Protector) Revert() error {
|
||||
if !protector.created {
|
||||
return nil
|
||||
}
|
||||
return protector.Destroy()
|
||||
}
|
||||
|
||||
func (protector *Protector) String() string {
|
||||
return fmt.Sprintf("Protector: %s\nMountpoint: %s\nSource: %s\nName: %s\nCosts: %v\nUID: %d",
|
||||
protector.Descriptor(), protector.Context.Mount, protector.data.Source,
|
||||
protector.data.Name, protector.data.Costs, protector.data.Uid)
|
||||
}
|
||||
|
||||
// Unlock unwraps the Protector's internal key. The keyFn provided to unwrap the
|
||||
// Protector key will be retried as necessary to get the correct key. Lock()
|
||||
// should be called after use. Does nothing if protector is already unlocked.
|
||||
func (protector *Protector) Unlock(keyFn KeyFunc) (err error) {
|
||||
if protector.key != nil {
|
||||
return
|
||||
}
|
||||
protector.key, err = unwrapProtectorKey(ProtectorInfo{protector.data}, keyFn)
|
||||
return
|
||||
}
|
||||
|
||||
// Lock wipes a Protector's internal Key. It should always be called after using
|
||||
// an unlocked Protector. This is often done with a defer statement. There is
|
||||
// no effect if called multiple times.
|
||||
func (protector *Protector) Lock() error {
|
||||
err := protector.key.Wipe()
|
||||
protector.key = nil
|
||||
return err
|
||||
}
|
||||
|
||||
// Rewrap updates the data that is wrapping the Protector Key. This is useful if
|
||||
// a user's password has changed, for example. The keyFn provided to rewrap
|
||||
// the Protector key will only be called once. Requires unlocked Protector.
|
||||
func (protector *Protector) Rewrap(keyFn KeyFunc) error {
|
||||
if protector.key == nil {
|
||||
return ErrLocked
|
||||
}
|
||||
wrappingKey, err := getWrappingKey(ProtectorInfo{protector.data}, keyFn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Revert change to wrapped key on failure
|
||||
oldWrappedKey := protector.data.WrappedKey
|
||||
defer func() {
|
||||
wrappingKey.Wipe()
|
||||
if err != nil {
|
||||
protector.data.WrappedKey = oldWrappedKey
|
||||
}
|
||||
}()
|
||||
|
||||
if protector.data.WrappedKey, err = crypto.Wrap(wrappingKey, protector.key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return protector.Context.Mount.AddProtector(protector.data, protector.ownerIfCreating)
|
||||
}
|
131
vendor/github.com/google/fscrypt/actions/recovery.go
generated
vendored
Normal file
131
vendor/github.com/google/fscrypt/actions/recovery.go
generated
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* recovery.go - support for generating recovery passphrases
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/fscrypt/crypto"
|
||||
"github.com/google/fscrypt/metadata"
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
// modifiedContextWithSource returns a copy of ctx with the protector source
|
||||
// replaced by source.
|
||||
func modifiedContextWithSource(ctx *Context, source metadata.SourceType) *Context {
|
||||
modifiedConfig := *ctx.Config
|
||||
modifiedConfig.Source = source
|
||||
modifiedCtx := *ctx
|
||||
modifiedCtx.Config = &modifiedConfig
|
||||
return &modifiedCtx
|
||||
}
|
||||
|
||||
// AddRecoveryPassphrase randomly generates a recovery passphrase and adds it as
|
||||
// a custom_passphrase protector for the given Policy.
|
||||
func AddRecoveryPassphrase(policy *Policy, dirname string) (*crypto.Key, *Protector, error) {
|
||||
// 20 random characters in a-z is 94 bits of entropy, which is way more
|
||||
// than enough for a passphrase which still goes through the usual
|
||||
// passphrase hashing which makes it extremely costly to brute force.
|
||||
passphrase, err := crypto.NewRandomPassphrase(20)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
passphrase.Wipe()
|
||||
}
|
||||
}()
|
||||
getPassphraseFn := func(info ProtectorInfo, retry bool) (*crypto.Key, error) {
|
||||
// CreateProtector() wipes the passphrase, but in this case we
|
||||
// still need it for later, so make a copy.
|
||||
return passphrase.Clone()
|
||||
}
|
||||
var recoveryProtector *Protector
|
||||
customCtx := modifiedContextWithSource(policy.Context, metadata.SourceType_custom_passphrase)
|
||||
seq := 1
|
||||
for {
|
||||
// Automatically generate a name for the recovery protector.
|
||||
name := "Recovery passphrase for " + dirname
|
||||
if seq != 1 {
|
||||
name += " (" + strconv.Itoa(seq) + ")"
|
||||
}
|
||||
recoveryProtector, err = CreateProtector(customCtx, name, getPassphraseFn, policy.ownerIfCreating)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if _, ok := err.(*ErrProtectorNameExists); !ok {
|
||||
return nil, nil, err
|
||||
}
|
||||
seq++
|
||||
}
|
||||
if err := policy.AddProtector(recoveryProtector); err != nil {
|
||||
recoveryProtector.Revert()
|
||||
return nil, nil, err
|
||||
}
|
||||
return passphrase, recoveryProtector, nil
|
||||
}
|
||||
|
||||
// WriteRecoveryInstructions writes a recovery passphrase and instructions to a
|
||||
// file. This file should initially be located in the encrypted directory
|
||||
// protected by the passphrase itself. It's up to the user to store the
|
||||
// passphrase in a different location if they actually need it.
|
||||
func WriteRecoveryInstructions(recoveryPassphrase *crypto.Key, recoveryProtector *Protector,
|
||||
policy *Policy, path string) error {
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
str := fmt.Sprintf(
|
||||
`fscrypt automatically generated a recovery passphrase for this directory:
|
||||
|
||||
%s
|
||||
|
||||
It did this because you chose to protect this directory with your login
|
||||
passphrase, but this directory is not on the root filesystem.
|
||||
|
||||
Copy this passphrase to a safe place if you want to still be able to unlock this
|
||||
directory if you re-install the operating system or connect this storage media
|
||||
to a different system (which would result in your login protector being lost).
|
||||
|
||||
To unlock this directory using this recovery passphrase, run 'fscrypt unlock'
|
||||
and select the protector named %q.
|
||||
|
||||
If you want to disable recovery passphrase generation (not recommended),
|
||||
re-create this directory and pass the --no-recovery option to 'fscrypt encrypt'.
|
||||
Alternatively, you can remove this recovery passphrase protector using:
|
||||
|
||||
fscrypt metadata remove-protector-from-policy --force --protector=%s:%s --policy=%s:%s
|
||||
|
||||
It is safe to keep it around though, as the recovery passphrase is high-entropy.
|
||||
`, recoveryPassphrase.Data(), recoveryProtector.data.Name,
|
||||
recoveryProtector.Context.Mount.Path, recoveryProtector.data.ProtectorDescriptor,
|
||||
policy.Context.Mount.Path, policy.data.KeyDescriptor)
|
||||
if _, err = file.WriteString(str); err != nil {
|
||||
return err
|
||||
}
|
||||
if recoveryProtector.ownerIfCreating != nil {
|
||||
if err = util.Chown(file, recoveryProtector.ownerIfCreating); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return file.Sync()
|
||||
}
|
228
vendor/github.com/google/fscrypt/crypto/crypto.go
generated
vendored
Normal file
228
vendor/github.com/google/fscrypt/crypto/crypto.go
generated
vendored
Normal file
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* crypto.go - Cryptographic algorithms used by the rest of fscrypt.
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
// Package crypto manages all the cryptography for fscrypt. This includes:
|
||||
// - Key management (key.go)
|
||||
// - Securely holding keys in memory
|
||||
// - Making recovery keys
|
||||
// - Randomness (rand.go)
|
||||
// - Cryptographic algorithms (crypto.go)
|
||||
// - encryption (AES256-CTR)
|
||||
// - authentication (SHA256-based HMAC)
|
||||
// - key stretching (SHA256-based HKDF)
|
||||
// - key wrapping/unwrapping (Encrypt then MAC)
|
||||
// - passphrase-based key derivation (Argon2id)
|
||||
// - key descriptor computation (double SHA512, or HKDF-SHA512)
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/argon2"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
|
||||
"github.com/google/fscrypt/metadata"
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
// Crypto error values
|
||||
var (
|
||||
ErrBadAuth = errors.New("key authentication check failed")
|
||||
ErrRecoveryCode = errors.New("invalid recovery code")
|
||||
ErrMlockUlimit = errors.New("could not lock key in memory")
|
||||
)
|
||||
|
||||
// panicInputLength panics if "name" has invalid length (expected != actual)
|
||||
func panicInputLength(name string, expected, actual int) {
|
||||
if err := util.CheckValidLength(expected, actual); err != nil {
|
||||
panic(errors.Wrap(err, name))
|
||||
}
|
||||
}
|
||||
|
||||
// checkWrappingKey returns an error if the wrapping key has the wrong length
|
||||
func checkWrappingKey(wrappingKey *Key) error {
|
||||
err := util.CheckValidLength(metadata.InternalKeyLen, wrappingKey.Len())
|
||||
return errors.Wrap(err, "wrapping key")
|
||||
}
|
||||
|
||||
// stretchKey stretches a key of length InternalKeyLen using unsalted HKDF to
|
||||
// make two keys of length InternalKeyLen.
|
||||
func stretchKey(key *Key) (encKey, authKey *Key) {
|
||||
panicInputLength("hkdf key", metadata.InternalKeyLen, key.Len())
|
||||
|
||||
// The new hkdf function uses the hash and key to create a reader that
|
||||
// can be used to securely initialize multiple keys. This means that
|
||||
// reads on the hkdf give independent cryptographic keys. The hkdf will
|
||||
// also always have enough entropy to read two keys.
|
||||
hkdf := hkdf.New(sha256.New, key.data, nil, nil)
|
||||
|
||||
encKey, err := NewFixedLengthKeyFromReader(hkdf, metadata.InternalKeyLen)
|
||||
util.NeverError(err)
|
||||
authKey, err = NewFixedLengthKeyFromReader(hkdf, metadata.InternalKeyLen)
|
||||
util.NeverError(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// aesCTR runs AES256-CTR on the input using the provided key and iv. This
|
||||
// function can be used to either encrypt or decrypt input of any size. Note
|
||||
// that input and output must be the same size.
|
||||
func aesCTR(key *Key, iv, input, output []byte) {
|
||||
panicInputLength("aesCTR key", metadata.InternalKeyLen, key.Len())
|
||||
panicInputLength("aesCTR iv", metadata.IVLen, len(iv))
|
||||
panicInputLength("aesCTR output", len(input), len(output))
|
||||
|
||||
blockCipher, err := aes.NewCipher(key.data)
|
||||
util.NeverError(err) // Key is checked to have correct length
|
||||
|
||||
stream := cipher.NewCTR(blockCipher, iv)
|
||||
stream.XORKeyStream(output, input)
|
||||
}
|
||||
|
||||
// getHMAC returns the SHA256-based HMAC of some data using the provided key.
|
||||
func getHMAC(key *Key, data ...[]byte) []byte {
|
||||
panicInputLength("hmac key", metadata.InternalKeyLen, key.Len())
|
||||
|
||||
mac := hmac.New(sha256.New, key.data)
|
||||
for _, buffer := range data {
|
||||
// SHA256 HMAC should never be unable to write the data
|
||||
_, err := mac.Write(buffer)
|
||||
util.NeverError(err)
|
||||
}
|
||||
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
// Wrap takes a wrapping Key of length InternalKeyLen, and uses it to wrap a
|
||||
// secret Key of any length. This wrapping uses a random IV, the encrypted data,
|
||||
// and an HMAC to verify the wrapping key was correct. All of this is included
|
||||
// in the returned WrappedKeyData structure.
|
||||
func Wrap(wrappingKey, secretKey *Key) (*metadata.WrappedKeyData, error) {
|
||||
if err := checkWrappingKey(wrappingKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := &metadata.WrappedKeyData{EncryptedKey: make([]byte, secretKey.Len())}
|
||||
|
||||
// Get random IV
|
||||
var err error
|
||||
if data.IV, err = NewRandomBuffer(metadata.IVLen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Stretch key for encryption and authentication (unsalted).
|
||||
encKey, authKey := stretchKey(wrappingKey)
|
||||
defer encKey.Wipe()
|
||||
defer authKey.Wipe()
|
||||
|
||||
// Encrypt the secret and include the HMAC of the output ("Encrypt-then-MAC").
|
||||
aesCTR(encKey, data.IV, secretKey.data, data.EncryptedKey)
|
||||
|
||||
data.Hmac = getHMAC(authKey, data.IV, data.EncryptedKey)
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Unwrap takes a wrapping Key of length InternalKeyLen, and uses it to unwrap
|
||||
// the WrappedKeyData to get the unwrapped secret Key. The Wrapped Key data
|
||||
// includes an authentication check, so an error will be returned if that check
|
||||
// fails.
|
||||
func Unwrap(wrappingKey *Key, data *metadata.WrappedKeyData) (*Key, error) {
|
||||
if err := checkWrappingKey(wrappingKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Stretch key for encryption and authentication (unsalted).
|
||||
encKey, authKey := stretchKey(wrappingKey)
|
||||
defer encKey.Wipe()
|
||||
defer authKey.Wipe()
|
||||
|
||||
// Check validity of the HMAC
|
||||
if !hmac.Equal(getHMAC(authKey, data.IV, data.EncryptedKey), data.Hmac) {
|
||||
return nil, ErrBadAuth
|
||||
}
|
||||
|
||||
secretKey, err := NewBlankKey(len(data.EncryptedKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aesCTR(encKey, data.IV, data.EncryptedKey, secretKey.data)
|
||||
|
||||
return secretKey, nil
|
||||
}
|
||||
|
||||
func computeKeyDescriptorV1(key *Key) string {
|
||||
h1 := sha512.Sum512(key.data)
|
||||
h2 := sha512.Sum512(h1[:])
|
||||
length := hex.DecodedLen(metadata.PolicyDescriptorLenV1)
|
||||
return hex.EncodeToString(h2[:length])
|
||||
}
|
||||
|
||||
func computeKeyDescriptorV2(key *Key) (string, error) {
|
||||
// This algorithm is specified by the kernel. It uses unsalted
|
||||
// HKDF-SHA512, where the application-information string is the prefix
|
||||
// "fscrypt\0" followed by the HKDF_CONTEXT_KEY_IDENTIFIER byte.
|
||||
hkdf := hkdf.New(sha512.New, key.data, nil, []byte("fscrypt\x00\x01"))
|
||||
h := make([]byte, hex.DecodedLen(metadata.PolicyDescriptorLenV2))
|
||||
if _, err := io.ReadFull(hkdf, h); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(h), nil
|
||||
}
|
||||
|
||||
// ComputeKeyDescriptor computes the descriptor for a given cryptographic key.
|
||||
// If policyVersion=1, it uses the first 8 bytes of the double application of
|
||||
// SHA512 on the key. Use this for protectors and v1 policy keys.
|
||||
// If policyVersion=2, it uses HKDF-SHA512 to compute a key identifier that's
|
||||
// compatible with the kernel's key identifiers for v2 policy keys.
|
||||
// In both cases, the resulting bytes are formatted as hex.
|
||||
func ComputeKeyDescriptor(key *Key, policyVersion int64) (string, error) {
|
||||
switch policyVersion {
|
||||
case 1:
|
||||
return computeKeyDescriptorV1(key), nil
|
||||
case 2:
|
||||
return computeKeyDescriptorV2(key)
|
||||
default:
|
||||
return "", errors.Errorf("policy version of %d is invalid", policyVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// PassphraseHash uses Argon2id to produce a Key given the passphrase, salt, and
|
||||
// hashing costs. This method is designed to take a long time and consume
|
||||
// considerable memory. For more information, see the documentation at
|
||||
// https://godoc.org/golang.org/x/crypto/argon2.
|
||||
func PassphraseHash(passphrase *Key, salt []byte, costs *metadata.HashingCosts) (*Key, error) {
|
||||
t := uint32(costs.Time)
|
||||
m := uint32(costs.Memory)
|
||||
p := uint8(costs.Parallelism)
|
||||
key := argon2.IDKey(passphrase.data, salt, t, m, p, metadata.InternalKeyLen)
|
||||
|
||||
hash, err := NewBlankKey(metadata.InternalKeyLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(hash.data, key)
|
||||
return hash, nil
|
||||
}
|
354
vendor/github.com/google/fscrypt/crypto/key.go
generated
vendored
Normal file
354
vendor/github.com/google/fscrypt/crypto/key.go
generated
vendored
Normal file
@ -0,0 +1,354 @@
|
||||
/*
|
||||
* key.go - Cryptographic key management for fscrypt. Ensures that sensitive
|
||||
* material is properly handled throughout the program.
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package crypto
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/subtle"
|
||||
"encoding/base32"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/google/fscrypt/metadata"
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// Keys need to readable and writable, but hidden from other processes.
|
||||
keyProtection = unix.PROT_READ | unix.PROT_WRITE
|
||||
keyMmapFlags = unix.MAP_PRIVATE | unix.MAP_ANONYMOUS
|
||||
)
|
||||
|
||||
/*
|
||||
UseMlock determines whether we should use the mlock/munlock syscalls to
|
||||
prevent sensitive data like keys and passphrases from being paged to disk.
|
||||
UseMlock defaults to true, but can be set to false if the application calling
|
||||
into this library has insufficient privileges to lock memory. Code using this
|
||||
package could also bind this setting to a flag by using:
|
||||
|
||||
flag.BoolVar(&crypto.UseMlock, "lock-memory", true, "lock keys in memory")
|
||||
*/
|
||||
var UseMlock = true
|
||||
|
||||
/*
|
||||
Key protects some arbitrary buffer of cryptographic material. Its methods
|
||||
ensure that the Key's data is locked in memory before being used (if
|
||||
UseMlock is set to true), and is wiped and unlocked after use (via the Wipe()
|
||||
method). This data is never accessed outside of the fscrypt/crypto package
|
||||
(except for the UnsafeData method). If a key is successfully created, the
|
||||
Wipe() method should be called after it's use. For example:
|
||||
|
||||
func UseKeyFromStdin() error {
|
||||
key, err := NewKeyFromReader(os.Stdin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer key.Wipe()
|
||||
|
||||
// Do stuff with key
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
The Wipe() method will also be called when a key is garbage collected; however,
|
||||
it is best practice to clear the key as soon as possible, so it spends a minimal
|
||||
amount of time in memory.
|
||||
|
||||
Note that Key is not thread safe, as a key could be wiped while another thread
|
||||
is using it. Also, calling Wipe() from two threads could cause an error as
|
||||
memory could be freed twice.
|
||||
*/
|
||||
type Key struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
// NewBlankKey constructs a blank key of a specified length and returns an error
|
||||
// if we are unable to allocate or lock the necessary memory.
|
||||
func NewBlankKey(length int) (*Key, error) {
|
||||
if length == 0 {
|
||||
return &Key{data: nil}, nil
|
||||
} else if length < 0 {
|
||||
return nil, errors.Errorf("requested key length %d is negative", length)
|
||||
}
|
||||
|
||||
flags := keyMmapFlags
|
||||
if UseMlock {
|
||||
flags |= unix.MAP_LOCKED
|
||||
}
|
||||
|
||||
// See MAP_ANONYMOUS in http://man7.org/linux/man-pages/man2/mmap.2.html
|
||||
data, err := unix.Mmap(-1, 0, length, keyProtection, flags)
|
||||
if err == unix.EAGAIN {
|
||||
return nil, ErrMlockUlimit
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err,
|
||||
"failed to allocate (mmap) key buffer of length %d", length)
|
||||
}
|
||||
|
||||
key := &Key{data: data}
|
||||
|
||||
// Backup finalizer in case user forgets to "defer key.Wipe()"
|
||||
runtime.SetFinalizer(key, (*Key).Wipe)
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// Wipe destroys a Key by zeroing and freeing the memory. The data is zeroed
|
||||
// even if Wipe returns an error, which occurs if we are unable to unlock or
|
||||
// free the key memory. Wipe does nothing if the key is already wiped or is nil.
|
||||
func (key *Key) Wipe() error {
|
||||
// We do nothing if key or key.data is nil so that Wipe() is idempotent
|
||||
// and so Wipe() can be called on keys which have already been cleared.
|
||||
if key != nil && key.data != nil {
|
||||
data := key.data
|
||||
key.data = nil
|
||||
|
||||
for i := range data {
|
||||
data[i] = 0
|
||||
}
|
||||
|
||||
if err := unix.Munmap(data); err != nil {
|
||||
log.Printf("unix.Munmap() failed: %v", err)
|
||||
return errors.Wrapf(err, "failed to free (munmap) key buffer")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Len is the underlying data buffer's length.
|
||||
func (key *Key) Len() int {
|
||||
return len(key.data)
|
||||
}
|
||||
|
||||
// Equals compares the contents of two keys, returning true if they have the same
|
||||
// key data. This function runs in constant time.
|
||||
func (key *Key) Equals(key2 *Key) bool {
|
||||
return subtle.ConstantTimeCompare(key.data, key2.data) == 1
|
||||
}
|
||||
|
||||
// resize returns a new key with size requestedSize and the appropriate data
|
||||
// copied over. The original data is wiped. This method does nothing and returns
|
||||
// itself if the key's length equals requestedSize.
|
||||
func (key *Key) resize(requestedSize int) (*Key, error) {
|
||||
if key.Len() == requestedSize {
|
||||
return key, nil
|
||||
}
|
||||
defer key.Wipe()
|
||||
|
||||
resizedKey, err := NewBlankKey(requestedSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(resizedKey.data, key.data)
|
||||
return resizedKey, nil
|
||||
}
|
||||
|
||||
// Data returns a slice of the key's underlying data. Note that this may become
|
||||
// outdated if the key is resized.
|
||||
func (key *Key) Data() []byte {
|
||||
return key.data
|
||||
}
|
||||
|
||||
// UnsafePtr returns an unsafe pointer to the key's underlying data. Note that
|
||||
// this will only be valid as long as the key is not resized.
|
||||
func (key *Key) UnsafePtr() unsafe.Pointer {
|
||||
return util.Ptr(key.data)
|
||||
}
|
||||
|
||||
// UnsafeToCString makes a copy of the string's data into a null-terminated C
|
||||
// string allocated by C. Note that this method is unsafe as this C copy has no
|
||||
// locking or wiping functionality. The key shouldn't contain any `\0` bytes.
|
||||
func (key *Key) UnsafeToCString() unsafe.Pointer {
|
||||
size := C.size_t(key.Len())
|
||||
data := C.calloc(size+1, 1)
|
||||
C.memcpy(data, util.Ptr(key.data), size)
|
||||
return data
|
||||
}
|
||||
|
||||
// Clone creates a key as a copy of another one.
|
||||
func (key *Key) Clone() (*Key, error) {
|
||||
newKey, err := NewBlankKey(key.Len())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(newKey.data, key.data)
|
||||
return newKey, nil
|
||||
}
|
||||
|
||||
// NewKeyFromCString creates of a copy of some C string's data in a key. Note
|
||||
// that the original C string is not modified at all, so steps must be taken to
|
||||
// ensure that this original copy is secured.
|
||||
func NewKeyFromCString(str unsafe.Pointer) (*Key, error) {
|
||||
size := C.strlen((*C.char)(str))
|
||||
key, err := NewBlankKey(int(size))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
C.memcpy(util.Ptr(key.data), str, size)
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// NewKeyFromReader constructs a key of arbitrary length by reading from reader
|
||||
// until hitting EOF.
|
||||
func NewKeyFromReader(reader io.Reader) (*Key, error) {
|
||||
// Use an initial key size of a page. As Mmap allocates a page anyway,
|
||||
// there isn't much additional overhead from starting with a whole page.
|
||||
key, err := NewBlankKey(os.Getpagesize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalBytesRead := 0
|
||||
for {
|
||||
bytesRead, err := reader.Read(key.data[totalBytesRead:])
|
||||
totalBytesRead += bytesRead
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
// Need to continue reading. Grow key if necessary
|
||||
if key.Len() == totalBytesRead {
|
||||
if key, err = key.resize(2 * key.Len()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case io.EOF:
|
||||
// Getting the EOF error means we are done
|
||||
return key.resize(totalBytesRead)
|
||||
default:
|
||||
// Fail if Read() has a failure
|
||||
key.Wipe()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewFixedLengthKeyFromReader constructs a key with a specified length by
|
||||
// reading exactly length bytes from reader.
|
||||
func NewFixedLengthKeyFromReader(reader io.Reader, length int) (*Key, error) {
|
||||
key, err := NewBlankKey(length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(reader, key.data); err != nil {
|
||||
key.Wipe()
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
var (
|
||||
// The recovery code is base32 with a dash between each block of 8 characters.
|
||||
encoding = base32.StdEncoding
|
||||
blockSize = 8
|
||||
separator = []byte("-")
|
||||
encodedLength = encoding.EncodedLen(metadata.PolicyKeyLen)
|
||||
decodedLength = encoding.DecodedLen(encodedLength)
|
||||
// RecoveryCodeLength is the number of bytes in every recovery code
|
||||
RecoveryCodeLength = (encodedLength/blockSize)*(blockSize+len(separator)) - len(separator)
|
||||
)
|
||||
|
||||
// WriteRecoveryCode outputs key's recovery code to the provided writer.
|
||||
// WARNING: This recovery key is enough to derive the original key, so it must
|
||||
// be given the same level of protection as a raw cryptographic key.
|
||||
func WriteRecoveryCode(key *Key, writer io.Writer) error {
|
||||
if err := util.CheckValidLength(metadata.PolicyKeyLen, key.Len()); err != nil {
|
||||
return errors.Wrap(err, "recovery key")
|
||||
}
|
||||
|
||||
// We store the base32 encoded data (without separators) in a temp key
|
||||
encodedKey, err := NewBlankKey(encodedLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer encodedKey.Wipe()
|
||||
encoding.Encode(encodedKey.data, key.data)
|
||||
|
||||
w := util.NewErrWriter(writer)
|
||||
|
||||
// Write the blocks with separators between them
|
||||
w.Write(encodedKey.data[:blockSize])
|
||||
for blockStart := blockSize; blockStart < encodedLength; blockStart += blockSize {
|
||||
w.Write(separator)
|
||||
|
||||
blockEnd := util.MinInt(blockStart+blockSize, encodedLength)
|
||||
w.Write(encodedKey.data[blockStart:blockEnd])
|
||||
}
|
||||
|
||||
// If any writes have failed, return the error
|
||||
return w.Err()
|
||||
}
|
||||
|
||||
// ReadRecoveryCode gets the recovery code from the provided reader and returns
|
||||
// the corresponding cryptographic key.
|
||||
// WARNING: This recovery key is enough to derive the original key, so it must
|
||||
// be given the same level of protection as a raw cryptographic key.
|
||||
func ReadRecoveryCode(reader io.Reader) (*Key, error) {
|
||||
// We store the base32 encoded data (without separators) in a temp key
|
||||
encodedKey, err := NewBlankKey(encodedLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer encodedKey.Wipe()
|
||||
|
||||
r := util.NewErrReader(reader)
|
||||
|
||||
// Read the other blocks, checking the separators between them
|
||||
r.Read(encodedKey.data[:blockSize])
|
||||
inputSeparator := make([]byte, len(separator))
|
||||
|
||||
for blockStart := blockSize; blockStart < encodedLength; blockStart += blockSize {
|
||||
r.Read(inputSeparator)
|
||||
if r.Err() == nil && !bytes.Equal(separator, inputSeparator) {
|
||||
err = errors.Wrapf(ErrRecoveryCode, "invalid separator %q", inputSeparator)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockEnd := util.MinInt(blockStart+blockSize, encodedLength)
|
||||
r.Read(encodedKey.data[blockStart:blockEnd])
|
||||
}
|
||||
|
||||
// If any reads have failed, return the error
|
||||
if r.Err() != nil {
|
||||
return nil, errors.Wrapf(ErrRecoveryCode, "read error %v", r.Err())
|
||||
}
|
||||
|
||||
// Now we decode the key, resizing if necessary
|
||||
decodedKey, err := NewBlankKey(decodedLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = encoding.Decode(decodedKey.data, encodedKey.data); err != nil {
|
||||
return nil, errors.Wrap(ErrRecoveryCode, err.Error())
|
||||
}
|
||||
return decodedKey.resize(metadata.PolicyKeyLen)
|
||||
}
|
98
vendor/github.com/google/fscrypt/crypto/rand.go
generated
vendored
Normal file
98
vendor/github.com/google/fscrypt/crypto/rand.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* rand.go - Reader used to generate secure random data for fscrypt.
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// NewRandomBuffer uses the Linux Getrandom() syscall to create random bytes. If
|
||||
// the operating system has insufficient randomness, the buffer creation will
|
||||
// fail. This is an improvement over Go's built-in crypto/rand which will still
|
||||
// return bytes if the system has insufficiency entropy.
|
||||
// See: https://github.com/golang/go/issues/19274
|
||||
//
|
||||
// While this syscall was only introduced in Kernel v3.17, it predates the
|
||||
// introduction of filesystem encryption, so it introduces no additional
|
||||
// compatibility issues.
|
||||
func NewRandomBuffer(length int) ([]byte, error) {
|
||||
buffer := make([]byte, length)
|
||||
if _, err := io.ReadFull(randReader{}, buffer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
// NewRandomKey creates a random key of the specified length. This function uses
|
||||
// the same random number generation process as NewRandomBuffer.
|
||||
func NewRandomKey(length int) (*Key, error) {
|
||||
return NewFixedLengthKeyFromReader(randReader{}, length)
|
||||
}
|
||||
|
||||
// NewRandomPassphrase creates a random passphrase of the specified length
|
||||
// containing random alphabetic characters.
|
||||
func NewRandomPassphrase(length int) (*Key, error) {
|
||||
chars := []byte("abcdefghijklmnopqrstuvwxyz")
|
||||
passphrase, err := NewBlankKey(length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < length; {
|
||||
// Get some random bytes.
|
||||
raw, err := NewRandomKey((length - i) * 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Translate the random bytes into random characters.
|
||||
for _, b := range raw.data {
|
||||
if int(b) >= 256-(256%len(chars)) {
|
||||
// Avoid bias towards the first characters in the list.
|
||||
continue
|
||||
}
|
||||
c := chars[int(b)%len(chars)]
|
||||
passphrase.data[i] = c
|
||||
i++
|
||||
if i == length {
|
||||
break
|
||||
}
|
||||
}
|
||||
raw.Wipe()
|
||||
}
|
||||
return passphrase, nil
|
||||
}
|
||||
|
||||
// randReader just calls into Getrandom, so no internal data is needed.
|
||||
type randReader struct{}
|
||||
|
||||
func (r randReader) Read(buffer []byte) (int, error) {
|
||||
n, err := unix.Getrandom(buffer, unix.GRND_NONBLOCK)
|
||||
switch err {
|
||||
case nil:
|
||||
return n, nil
|
||||
case unix.EAGAIN:
|
||||
err = errors.New("insufficient entropy in pool")
|
||||
case unix.ENOSYS:
|
||||
err = errors.New("kernel must be v3.17 or later")
|
||||
}
|
||||
return 0, errors.Wrap(err, "getrandom() failed")
|
||||
}
|
1088
vendor/github.com/google/fscrypt/filesystem/filesystem.go
generated
vendored
Normal file
1088
vendor/github.com/google/fscrypt/filesystem/filesystem.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
578
vendor/github.com/google/fscrypt/filesystem/mountpoint.go
generated
vendored
Normal file
578
vendor/github.com/google/fscrypt/filesystem/mountpoint.go
generated
vendored
Normal file
@ -0,0 +1,578 @@
|
||||
/*
|
||||
* mountpoint.go - Contains all the functionality for finding mountpoints and
|
||||
* using UUIDs to refer to them. Specifically, we can find the mountpoint of a
|
||||
* path, get info about a mountpoint, and find mountpoints with a specific UUID.
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// These maps hold data about the state of the system's filesystems.
|
||||
//
|
||||
// They only contain one Mount per filesystem, even if there are
|
||||
// additional bind mounts, since we want to store fscrypt metadata in
|
||||
// only one place per filesystem. When it is ambiguous which Mount
|
||||
// should be used for a filesystem, mountsByDevice will contain an
|
||||
// explicit nil entry, and mountsByPath won't contain an entry.
|
||||
mountsByDevice map[DeviceNumber]*Mount
|
||||
mountsByPath map[string]*Mount
|
||||
// Used to make the mount functions thread safe
|
||||
mountMutex sync.Mutex
|
||||
// True if the maps have been successfully initialized.
|
||||
mountsInitialized bool
|
||||
// Supported tokens for filesystem links
|
||||
uuidToken = "UUID"
|
||||
pathToken = "PATH"
|
||||
// Location to perform UUID lookup
|
||||
uuidDirectory = "/dev/disk/by-uuid"
|
||||
)
|
||||
|
||||
// Unescape octal-encoded escape sequences in a string from the mountinfo file.
|
||||
// The kernel encodes the ' ', '\t', '\n', and '\\' bytes this way. This
|
||||
// function exactly inverts what the kernel does, including by preserving
|
||||
// invalid UTF-8.
|
||||
func unescapeString(str string) string {
|
||||
var sb strings.Builder
|
||||
for i := 0; i < len(str); i++ {
|
||||
b := str[i]
|
||||
if b == '\\' && i+3 < len(str) {
|
||||
if parsed, err := strconv.ParseInt(str[i+1:i+4], 8, 8); err == nil {
|
||||
b = uint8(parsed)
|
||||
i += 3
|
||||
}
|
||||
}
|
||||
sb.WriteByte(b)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// EscapeString is the reverse of unescapeString. Use this to avoid injecting
|
||||
// spaces or newlines into output that uses these characters as separators.
|
||||
func EscapeString(str string) string {
|
||||
var sb strings.Builder
|
||||
for _, b := range []byte(str) {
|
||||
switch b {
|
||||
case ' ', '\t', '\n', '\\':
|
||||
sb.WriteString(fmt.Sprintf("\\%03o", b))
|
||||
default:
|
||||
sb.WriteByte(b)
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// We get the device name via the device number rather than use the mount source
|
||||
// field directly. This is necessary to handle a rootfs that was mounted via
|
||||
// the kernel command line, since mountinfo always shows /dev/root for that.
|
||||
// This assumes that the device nodes are in the standard location.
|
||||
func getDeviceName(num DeviceNumber) string {
|
||||
linkPath := fmt.Sprintf("/sys/dev/block/%v", num)
|
||||
if target, err := os.Readlink(linkPath); err == nil {
|
||||
return fmt.Sprintf("/dev/%s", filepath.Base(target))
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Parse one line of /proc/self/mountinfo.
|
||||
//
|
||||
// The line contains the following space-separated fields:
|
||||
// [0] mount ID
|
||||
// [1] parent ID
|
||||
// [2] major:minor
|
||||
// [3] root
|
||||
// [4] mount point
|
||||
// [5] mount options
|
||||
// [6...n-1] optional field(s)
|
||||
// [n] separator
|
||||
// [n+1] filesystem type
|
||||
// [n+2] mount source
|
||||
// [n+3] super options
|
||||
//
|
||||
// For more details, see https://www.kernel.org/doc/Documentation/filesystems/proc.txt
|
||||
func parseMountInfoLine(line string) *Mount {
|
||||
fields := strings.Split(line, " ")
|
||||
if len(fields) < 10 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Count the optional fields. In case new fields are appended later,
|
||||
// don't simply assume that n == len(fields) - 4.
|
||||
n := 6
|
||||
for fields[n] != "-" {
|
||||
n++
|
||||
if n >= len(fields) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if n+3 >= len(fields) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var mnt *Mount = &Mount{}
|
||||
var err error
|
||||
mnt.DeviceNumber, err = newDeviceNumberFromString(fields[2])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
mnt.Subtree = unescapeString(fields[3])
|
||||
mnt.Path = unescapeString(fields[4])
|
||||
for _, opt := range strings.Split(fields[5], ",") {
|
||||
if opt == "ro" {
|
||||
mnt.ReadOnly = true
|
||||
}
|
||||
}
|
||||
mnt.FilesystemType = unescapeString(fields[n+1])
|
||||
mnt.Device = getDeviceName(mnt.DeviceNumber)
|
||||
return mnt
|
||||
}
|
||||
|
||||
type mountpointTreeNode struct {
|
||||
mount *Mount
|
||||
parent *mountpointTreeNode
|
||||
children []*mountpointTreeNode
|
||||
}
|
||||
|
||||
func addUncontainedSubtreesRecursive(dst map[string]bool,
|
||||
node *mountpointTreeNode, allUncontainedSubtrees map[string]bool) {
|
||||
if allUncontainedSubtrees[node.mount.Subtree] {
|
||||
dst[node.mount.Subtree] = true
|
||||
}
|
||||
for _, child := range node.children {
|
||||
addUncontainedSubtreesRecursive(dst, child, allUncontainedSubtrees)
|
||||
}
|
||||
}
|
||||
|
||||
// findMainMount finds the "main" Mount of a filesystem. The "main" Mount is
|
||||
// where the filesystem's fscrypt metadata is stored.
|
||||
//
|
||||
// Normally, there is just one Mount and it's of the entire filesystem
|
||||
// (mnt.Subtree == "/"). But in general, the filesystem might be mounted in
|
||||
// multiple places, including "bind mounts" where mnt.Subtree != "/". Also, the
|
||||
// filesystem might have a combination of read-write and read-only mounts.
|
||||
//
|
||||
// To handle most cases, we could just choose a mount with mnt.Subtree == "/",
|
||||
// preferably a read-write mount. However, that doesn't work in containers
|
||||
// where the "/" subtree might not be mounted. Here's a real-world example:
|
||||
//
|
||||
// mnt.Subtree mnt.Path
|
||||
// ----------- --------
|
||||
// /var/lib/lxc/base/rootfs /
|
||||
// /var/cache/pacman/pkg /var/cache/pacman/pkg
|
||||
// /srv/repo/x86_64 /srv/http/x86_64
|
||||
//
|
||||
// In this case, all mnt.Subtree are independent. To handle this case, we must
|
||||
// choose the Mount whose mnt.Path contains the others, i.e. the first one.
|
||||
// Note: the fscrypt metadata won't be usable from outside the container since
|
||||
// it won't be at the real root of the filesystem, but that may be acceptable.
|
||||
//
|
||||
// However, we can't look *only* at mnt.Path, since in some cases mnt.Subtree is
|
||||
// needed to correctly handle bind mounts. For example, in the following case,
|
||||
// the first Mount should be chosen:
|
||||
//
|
||||
// mnt.Subtree mnt.Path
|
||||
// ----------- --------
|
||||
// /foo /foo
|
||||
// /foo/dir /dir
|
||||
//
|
||||
// To solve this, we divide the mounts into non-overlapping trees of mnt.Path.
|
||||
// Then, we choose one of these trees which contains (exactly or via path
|
||||
// prefix) *all* mnt.Subtree. We then return the root of this tree. In both
|
||||
// the above examples, this algorithm returns the first Mount.
|
||||
func findMainMount(filesystemMounts []*Mount) *Mount {
|
||||
// Index this filesystem's mounts by path. Note: paths are unique here,
|
||||
// since non-last mounts were already excluded earlier.
|
||||
//
|
||||
// Also build the set of all mounted subtrees.
|
||||
filesystemMountsByPath := make(map[string]*mountpointTreeNode)
|
||||
allSubtrees := make(map[string]bool)
|
||||
for _, mnt := range filesystemMounts {
|
||||
filesystemMountsByPath[mnt.Path] = &mountpointTreeNode{mount: mnt}
|
||||
allSubtrees[mnt.Subtree] = true
|
||||
}
|
||||
|
||||
// Divide the mounts into non-overlapping trees of mountpoints.
|
||||
for path, mntNode := range filesystemMountsByPath {
|
||||
for path != "/" && mntNode.parent == nil {
|
||||
path = filepath.Dir(path)
|
||||
if parent := filesystemMountsByPath[path]; parent != nil {
|
||||
mntNode.parent = parent
|
||||
parent.children = append(parent.children, mntNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the set of mounted subtrees that aren't contained in any other
|
||||
// mounted subtree.
|
||||
allUncontainedSubtrees := make(map[string]bool)
|
||||
for subtree := range allSubtrees {
|
||||
contained := false
|
||||
for t := subtree; t != "/" && !contained; {
|
||||
t = filepath.Dir(t)
|
||||
contained = allSubtrees[t]
|
||||
}
|
||||
if !contained {
|
||||
allUncontainedSubtrees[subtree] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Select the root of a mountpoint tree whose mounted subtrees contain
|
||||
// *all* mounted subtrees. Equivalently, select a mountpoint tree in
|
||||
// which every uncontained subtree is mounted.
|
||||
var mainMount *Mount
|
||||
for _, mntNode := range filesystemMountsByPath {
|
||||
mnt := mntNode.mount
|
||||
if mntNode.parent != nil {
|
||||
continue
|
||||
}
|
||||
uncontainedSubtrees := make(map[string]bool)
|
||||
addUncontainedSubtreesRecursive(uncontainedSubtrees, mntNode, allUncontainedSubtrees)
|
||||
if len(uncontainedSubtrees) != len(allUncontainedSubtrees) {
|
||||
continue
|
||||
}
|
||||
// If there's more than one eligible mount, they should have the
|
||||
// same Subtree. Otherwise it's ambiguous which one to use.
|
||||
if mainMount != nil && mainMount.Subtree != mnt.Subtree {
|
||||
log.Printf("Unsupported case: %q (%v) has multiple non-overlapping mounts. This filesystem will be ignored!",
|
||||
mnt.Device, mnt.DeviceNumber)
|
||||
return nil
|
||||
}
|
||||
// Prefer a read-write mount to a read-only one.
|
||||
if mainMount == nil || mainMount.ReadOnly {
|
||||
mainMount = mnt
|
||||
}
|
||||
}
|
||||
return mainMount
|
||||
}
|
||||
|
||||
// This is separate from loadMountInfo() only for unit testing.
|
||||
func readMountInfo(r io.Reader) error {
|
||||
mountsByDevice = make(map[DeviceNumber]*Mount)
|
||||
mountsByPath = make(map[string]*Mount)
|
||||
allMountsByDevice := make(map[DeviceNumber][]*Mount)
|
||||
allMountsByPath := make(map[string]*Mount)
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
mnt := parseMountInfoLine(line)
|
||||
if mnt == nil {
|
||||
log.Printf("ignoring invalid mountinfo line %q", line)
|
||||
continue
|
||||
}
|
||||
|
||||
// We can only use mountpoints that are directories for fscrypt.
|
||||
if !isDir(mnt.Path) {
|
||||
log.Printf("ignoring mountpoint %q because it is not a directory", mnt.Path)
|
||||
continue
|
||||
}
|
||||
|
||||
// Note this overrides the info if we have seen the mountpoint
|
||||
// earlier in the file. This is correct behavior because the
|
||||
// mountpoints are listed in mount order.
|
||||
allMountsByPath[mnt.Path] = mnt
|
||||
}
|
||||
// For each filesystem, choose a "main" Mount and discard any additional
|
||||
// bind mounts. fscrypt only cares about the main Mount, since it's
|
||||
// where the fscrypt metadata is stored. Store all the main Mounts in
|
||||
// mountsByDevice and mountsByPath so that they can be found later.
|
||||
for _, mnt := range allMountsByPath {
|
||||
allMountsByDevice[mnt.DeviceNumber] =
|
||||
append(allMountsByDevice[mnt.DeviceNumber], mnt)
|
||||
}
|
||||
for deviceNumber, filesystemMounts := range allMountsByDevice {
|
||||
mnt := findMainMount(filesystemMounts)
|
||||
mountsByDevice[deviceNumber] = mnt // may store an explicit nil entry
|
||||
if mnt != nil {
|
||||
mountsByPath[mnt.Path] = mnt
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadMountInfo populates the Mount mappings by parsing /proc/self/mountinfo.
|
||||
// It returns an error if the Mount mappings cannot be populated.
|
||||
func loadMountInfo() error {
|
||||
if !mountsInitialized {
|
||||
file, err := os.Open("/proc/self/mountinfo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if err := readMountInfo(file); err != nil {
|
||||
return err
|
||||
}
|
||||
mountsInitialized = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func filesystemLacksMainMountError(deviceNumber DeviceNumber) error {
|
||||
return errors.Errorf("Device %q (%v) lacks a \"main\" mountpoint in the current mount namespace, so it's ambiguous where to store the fscrypt metadata.",
|
||||
getDeviceName(deviceNumber), deviceNumber)
|
||||
}
|
||||
|
||||
// AllFilesystems lists all mounted filesystems ordered by path to their "main"
|
||||
// Mount. Use CheckSetup() to see if they are set up for use with fscrypt.
|
||||
func AllFilesystems() ([]*Mount, error) {
|
||||
mountMutex.Lock()
|
||||
defer mountMutex.Unlock()
|
||||
if err := loadMountInfo(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mounts := make([]*Mount, 0, len(mountsByPath))
|
||||
for _, mount := range mountsByPath {
|
||||
mounts = append(mounts, mount)
|
||||
}
|
||||
|
||||
sort.Sort(PathSorter(mounts))
|
||||
return mounts, nil
|
||||
}
|
||||
|
||||
// UpdateMountInfo updates the filesystem mountpoint maps with the current state
|
||||
// of the filesystem mountpoints. Returns error if the initialization fails.
|
||||
func UpdateMountInfo() error {
|
||||
mountMutex.Lock()
|
||||
defer mountMutex.Unlock()
|
||||
mountsInitialized = false
|
||||
return loadMountInfo()
|
||||
}
|
||||
|
||||
// FindMount returns the main Mount object for the filesystem which contains the
|
||||
// file at the specified path. An error is returned if the path is invalid or if
|
||||
// we cannot load the required mount data. If a mount has been updated since the
|
||||
// last call to one of the mount functions, run UpdateMountInfo to see changes.
|
||||
func FindMount(path string) (*Mount, error) {
|
||||
mountMutex.Lock()
|
||||
defer mountMutex.Unlock()
|
||||
if err := loadMountInfo(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// First try to find the mount by the number of the containing device.
|
||||
deviceNumber, err := getNumberOfContainingDevice(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mnt, ok := mountsByDevice[deviceNumber]
|
||||
if ok {
|
||||
if mnt == nil {
|
||||
return nil, filesystemLacksMainMountError(deviceNumber)
|
||||
}
|
||||
return mnt, nil
|
||||
}
|
||||
// The mount couldn't be found by the number of the containing device.
|
||||
// Fall back to walking up the directory hierarchy and checking for a
|
||||
// mount at each directory path. This is necessary for btrfs, where
|
||||
// files report a different st_dev from the /proc/self/mountinfo entry.
|
||||
curPath, err := canonicalizePath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
mnt := mountsByPath[curPath]
|
||||
if mnt != nil {
|
||||
return mnt, nil
|
||||
}
|
||||
// Move to the parent directory unless we have reached the root.
|
||||
parent := filepath.Dir(curPath)
|
||||
if parent == curPath {
|
||||
return nil, errors.Errorf("couldn't find mountpoint containing %q", path)
|
||||
}
|
||||
curPath = parent
|
||||
}
|
||||
}
|
||||
|
||||
// GetMount is like FindMount, except GetMount also returns an error if the path
|
||||
// doesn't name the same file as the filesystem's "main" Mount. For example, if
|
||||
// a filesystem is fully mounted at "/mnt" and if "/mnt/a" exists, then
|
||||
// FindMount("/mnt/a") will succeed whereas GetMount("/mnt/a") will fail. This
|
||||
// is true even if "/mnt/a" is a bind mount of part of the same filesystem.
|
||||
func GetMount(mountpoint string) (*Mount, error) {
|
||||
mnt, err := FindMount(mountpoint)
|
||||
if err != nil {
|
||||
return nil, &ErrNotAMountpoint{mountpoint}
|
||||
}
|
||||
// Check whether 'mountpoint' names the same directory as 'mnt.Path'.
|
||||
// Use os.SameFile() (i.e., compare inode numbers) rather than compare
|
||||
// canonical paths, since filesystems may be mounted in multiple places.
|
||||
fi1, err := os.Stat(mountpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fi2, err := os.Stat(mnt.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !os.SameFile(fi1, fi2) {
|
||||
return nil, &ErrNotAMountpoint{mountpoint}
|
||||
}
|
||||
return mnt, nil
|
||||
}
|
||||
|
||||
func uuidToDeviceNumber(uuid string) (DeviceNumber, error) {
|
||||
uuidSymlinkPath := filepath.Join(uuidDirectory, uuid)
|
||||
return getDeviceNumber(uuidSymlinkPath)
|
||||
}
|
||||
|
||||
func deviceNumberToMount(deviceNumber DeviceNumber) (*Mount, bool) {
|
||||
mountMutex.Lock()
|
||||
defer mountMutex.Unlock()
|
||||
if err := loadMountInfo(); err != nil {
|
||||
log.Print(err)
|
||||
return nil, false
|
||||
}
|
||||
mnt, ok := mountsByDevice[deviceNumber]
|
||||
return mnt, ok
|
||||
}
|
||||
|
||||
// getMountFromLink returns the main Mount, if any, for the filesystem which the
|
||||
// given link points to. The link should contain a series of token-value pairs
|
||||
// (<token>=<value>), one per line. The supported tokens are "UUID" and "PATH".
|
||||
// If the UUID is present and it works, then it is used; otherwise, PATH is used
|
||||
// if it is present. (The fallback from UUID to PATH will keep the link working
|
||||
// if the UUID of the target filesystem changes but its mountpoint doesn't.)
|
||||
//
|
||||
// If a mount has been updated since the last call to one of the mount
|
||||
// functions, make sure to run UpdateMountInfo first.
|
||||
func getMountFromLink(link string) (*Mount, error) {
|
||||
// Parse the link.
|
||||
uuid := ""
|
||||
path := ""
|
||||
lines := strings.Split(link, "\n")
|
||||
for _, line := range lines {
|
||||
line := strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
pair := strings.Split(line, "=")
|
||||
if len(pair) != 2 {
|
||||
log.Printf("ignoring invalid line in filesystem link file: %q", line)
|
||||
continue
|
||||
}
|
||||
token := pair[0]
|
||||
value := pair[1]
|
||||
switch token {
|
||||
case uuidToken:
|
||||
uuid = value
|
||||
case pathToken:
|
||||
path = value
|
||||
default:
|
||||
log.Printf("ignoring unknown link token %q", token)
|
||||
}
|
||||
}
|
||||
// At least one of UUID and PATH must be present.
|
||||
if uuid == "" && path == "" {
|
||||
return nil, &ErrFollowLink{link, errors.Errorf("invalid filesystem link file")}
|
||||
}
|
||||
|
||||
// Try following the UUID.
|
||||
errMsg := ""
|
||||
if uuid != "" {
|
||||
deviceNumber, err := uuidToDeviceNumber(uuid)
|
||||
if err == nil {
|
||||
mnt, ok := deviceNumberToMount(deviceNumber)
|
||||
if mnt != nil {
|
||||
log.Printf("resolved filesystem link using UUID %q", uuid)
|
||||
return mnt, nil
|
||||
}
|
||||
if ok {
|
||||
return nil, &ErrFollowLink{link, filesystemLacksMainMountError(deviceNumber)}
|
||||
}
|
||||
log.Printf("cannot find filesystem with UUID %q", uuid)
|
||||
} else {
|
||||
log.Printf("cannot find filesystem with UUID %q: %v", uuid, err)
|
||||
}
|
||||
errMsg += fmt.Sprintf("cannot find filesystem with UUID %q", uuid)
|
||||
if path != "" {
|
||||
log.Printf("falling back to using mountpoint path instead of UUID")
|
||||
}
|
||||
}
|
||||
// UUID didn't work. As a fallback, try the mountpoint path.
|
||||
if path != "" {
|
||||
mnt, err := GetMount(path)
|
||||
if mnt != nil {
|
||||
log.Printf("resolved filesystem link using mountpoint path %q", path)
|
||||
return mnt, nil
|
||||
}
|
||||
log.Print(err)
|
||||
if errMsg == "" {
|
||||
errMsg = fmt.Sprintf("cannot find filesystem with main mountpoint %q", path)
|
||||
} else {
|
||||
errMsg += fmt.Sprintf(" or main mountpoint %q", path)
|
||||
}
|
||||
}
|
||||
// No method worked; return an error.
|
||||
return nil, &ErrFollowLink{link, errors.New(errMsg)}
|
||||
}
|
||||
|
||||
func (mnt *Mount) getFilesystemUUID() (string, error) {
|
||||
dirContents, err := ioutil.ReadDir(uuidDirectory)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, fileInfo := range dirContents {
|
||||
if fileInfo.Mode()&os.ModeSymlink == 0 {
|
||||
continue // Only interested in UUID symlinks
|
||||
}
|
||||
uuid := fileInfo.Name()
|
||||
deviceNumber, err := uuidToDeviceNumber(uuid)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
continue
|
||||
}
|
||||
if mnt.DeviceNumber == deviceNumber {
|
||||
return uuid, nil
|
||||
}
|
||||
}
|
||||
return "", errors.Errorf("cannot determine UUID of device %q (%v)",
|
||||
mnt.Device, mnt.DeviceNumber)
|
||||
}
|
||||
|
||||
// makeLink creates the contents of a link file which will point to the given
|
||||
// filesystem. This will normally be a string of the form
|
||||
// "UUID=<uuid>\nPATH=<path>\n". If the UUID cannot be determined, the UUID
|
||||
// portion will be omitted.
|
||||
func makeLink(mnt *Mount) (string, error) {
|
||||
uuid, err := mnt.getFilesystemUUID()
|
||||
if err != nil {
|
||||
// The UUID could not be determined. This happens for btrfs
|
||||
// filesystems, as the device number found via
|
||||
// /dev/disk/by-uuid/* for btrfs filesystems differs from the
|
||||
// actual device number of the mounted filesystem. Just rely
|
||||
// entirely on the fallback to mountpoint path.
|
||||
log.Print(err)
|
||||
return fmt.Sprintf("%s=%s\n", pathToken, mnt.Path), nil
|
||||
}
|
||||
return fmt.Sprintf("%s=%s\n%s=%s\n", uuidToken, uuid, pathToken, mnt.Path), nil
|
||||
}
|
128
vendor/github.com/google/fscrypt/filesystem/path.go
generated
vendored
Normal file
128
vendor/github.com/google/fscrypt/filesystem/path.go
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* path.go - Utility functions for dealing with filesystem paths
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// OpenFileOverridingUmask calls os.OpenFile but with the umask overridden so
|
||||
// that no permission bits are masked out if the file is created.
|
||||
func OpenFileOverridingUmask(name string, flag int, perm os.FileMode) (*os.File, error) {
|
||||
oldMask := unix.Umask(0)
|
||||
defer unix.Umask(oldMask)
|
||||
return os.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
// canonicalizePath turns path into an absolute path without symlinks.
|
||||
func canonicalizePath(path string) (string, error) {
|
||||
path, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
path, err = filepath.EvalSymlinks(path)
|
||||
|
||||
// Get a better error if we have an invalid path
|
||||
if pathErr, ok := err.(*os.PathError); ok {
|
||||
err = errors.Wrap(pathErr.Err, pathErr.Path)
|
||||
}
|
||||
|
||||
return path, err
|
||||
}
|
||||
|
||||
// loggedStat runs os.Stat, but it logs the error if stat returns any error
|
||||
// other than nil or IsNotExist.
|
||||
func loggedStat(name string) (os.FileInfo, error) {
|
||||
info, err := os.Stat(name)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
log.Print(err)
|
||||
}
|
||||
return info, err
|
||||
}
|
||||
|
||||
// loggedLstat runs os.Lstat (doesn't dereference trailing symlink), but it logs
|
||||
// the error if lstat returns any error other than nil or IsNotExist.
|
||||
func loggedLstat(name string) (os.FileInfo, error) {
|
||||
info, err := os.Lstat(name)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
log.Print(err)
|
||||
}
|
||||
return info, err
|
||||
}
|
||||
|
||||
// isDir returns true if the path exists and is that of a directory.
|
||||
func isDir(path string) bool {
|
||||
info, err := loggedStat(path)
|
||||
return err == nil && info.IsDir()
|
||||
}
|
||||
|
||||
// isRegularFile returns true if the path exists and is that of a regular file.
|
||||
func isRegularFile(path string) bool {
|
||||
info, err := loggedStat(path)
|
||||
return err == nil && info.Mode().IsRegular()
|
||||
}
|
||||
|
||||
// HaveReadAccessTo returns true if the process has read access to a file or
|
||||
// directory, without actually opening it.
|
||||
func HaveReadAccessTo(path string) bool {
|
||||
return unix.Access(path, unix.R_OK) == nil
|
||||
}
|
||||
|
||||
// DeviceNumber represents a combined major:minor device number.
|
||||
type DeviceNumber uint64
|
||||
|
||||
func (num DeviceNumber) String() string {
|
||||
return fmt.Sprintf("%d:%d", unix.Major(uint64(num)), unix.Minor(uint64(num)))
|
||||
}
|
||||
|
||||
func newDeviceNumberFromString(str string) (DeviceNumber, error) {
|
||||
var major, minor uint32
|
||||
if count, _ := fmt.Sscanf(str, "%d:%d", &major, &minor); count != 2 {
|
||||
return 0, errors.Errorf("invalid device number string %q", str)
|
||||
}
|
||||
return DeviceNumber(unix.Mkdev(major, minor)), nil
|
||||
}
|
||||
|
||||
// getDeviceNumber returns the device number of the device node at the given
|
||||
// path. If there is a symlink at the path, it is dereferenced.
|
||||
func getDeviceNumber(path string) (DeviceNumber, error) {
|
||||
var stat unix.Stat_t
|
||||
if err := unix.Stat(path, &stat); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return DeviceNumber(stat.Rdev), nil
|
||||
}
|
||||
|
||||
// getNumberOfContainingDevice returns the device number of the filesystem which
|
||||
// contains the given file. If the file is a symlink, it is not dereferenced.
|
||||
func getNumberOfContainingDevice(path string) (DeviceNumber, error) {
|
||||
var stat unix.Stat_t
|
||||
if err := unix.Lstat(path, &stat); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return DeviceNumber(stat.Dev), nil
|
||||
}
|
326
vendor/github.com/google/fscrypt/keyring/fs_keyring.go
generated
vendored
Normal file
326
vendor/github.com/google/fscrypt/keyring/fs_keyring.go
generated
vendored
Normal file
@ -0,0 +1,326 @@
|
||||
/*
|
||||
* fs_keyring.go - Add/remove encryption policy keys to/from filesystem
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
* Author: Eric Biggers (ebiggers@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package keyring
|
||||
|
||||
/*
|
||||
#include <string.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/google/fscrypt/crypto"
|
||||
"github.com/google/fscrypt/filesystem"
|
||||
"github.com/google/fscrypt/security"
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
var (
|
||||
fsKeyringSupported bool
|
||||
fsKeyringSupportedKnown bool
|
||||
fsKeyringSupportedLock sync.Mutex
|
||||
)
|
||||
|
||||
func checkForFsKeyringSupport(mount *filesystem.Mount) bool {
|
||||
dir, err := os.Open(mount.Path)
|
||||
if err != nil {
|
||||
log.Printf("Unexpected error opening %q. Assuming filesystem keyring is unsupported.",
|
||||
mount.Path)
|
||||
return false
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
// FS_IOC_ADD_ENCRYPTION_KEY with a NULL argument will fail with ENOTTY
|
||||
// if the ioctl isn't supported. Otherwise it should fail with EFAULT.
|
||||
//
|
||||
// Note that there's no need to check for FS_IOC_REMOVE_ENCRYPTION_KEY
|
||||
// support separately, since it's guaranteed to be available if
|
||||
// FS_IOC_ADD_ENCRYPTION_KEY is. There's also no need to check for
|
||||
// support on every filesystem separately, since either the kernel
|
||||
// supports the ioctls on all fscrypt-capable filesystems or it doesn't.
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, dir.Fd(), unix.FS_IOC_ADD_ENCRYPTION_KEY, 0)
|
||||
if errno == unix.ENOTTY {
|
||||
log.Printf("Kernel doesn't support filesystem keyring. Falling back to user keyring.")
|
||||
return false
|
||||
}
|
||||
if errno == unix.EFAULT {
|
||||
log.Printf("Detected support for filesystem keyring")
|
||||
} else {
|
||||
// EFAULT is expected, but as long as we didn't get ENOTTY the
|
||||
// ioctl should be available.
|
||||
log.Printf("Unexpected error from FS_IOC_ADD_ENCRYPTION_KEY(%q, NULL): %v", mount.Path, errno)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsFsKeyringSupported returns true if the kernel supports the ioctls to
|
||||
// add/remove fscrypt keys directly to/from the filesystem. For support to be
|
||||
// detected, the given Mount must be for a filesystem that supports fscrypt.
|
||||
func IsFsKeyringSupported(mount *filesystem.Mount) bool {
|
||||
fsKeyringSupportedLock.Lock()
|
||||
defer fsKeyringSupportedLock.Unlock()
|
||||
if !fsKeyringSupportedKnown {
|
||||
fsKeyringSupported = checkForFsKeyringSupport(mount)
|
||||
fsKeyringSupportedKnown = true
|
||||
}
|
||||
return fsKeyringSupported
|
||||
}
|
||||
|
||||
// buildKeySpecifier converts the key descriptor string to an FscryptKeySpecifier.
|
||||
func buildKeySpecifier(spec *unix.FscryptKeySpecifier, descriptor string) error {
|
||||
descriptorBytes, err := hex.DecodeString(descriptor)
|
||||
if err != nil {
|
||||
return errors.Errorf("key descriptor %q is invalid", descriptor)
|
||||
}
|
||||
switch len(descriptorBytes) {
|
||||
case unix.FSCRYPT_KEY_DESCRIPTOR_SIZE:
|
||||
spec.Type = unix.FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR
|
||||
case unix.FSCRYPT_KEY_IDENTIFIER_SIZE:
|
||||
spec.Type = unix.FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER
|
||||
default:
|
||||
return errors.Errorf("key descriptor %q has unknown length", descriptor)
|
||||
}
|
||||
copy(spec.U[:], descriptorBytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
type savedPrivs struct {
|
||||
ruid, euid, suid int
|
||||
}
|
||||
|
||||
// dropPrivsIfNeeded drops privileges (UIDs only) to the given user if we're
|
||||
// working with a v2 policy key, and if the user is different from the user the
|
||||
// process is currently running as.
|
||||
//
|
||||
// This is needed to change the effective UID so that FS_IOC_ADD_ENCRYPTION_KEY
|
||||
// and FS_IOC_REMOVE_ENCRYPTION_KEY will add/remove a claim to the key for the
|
||||
// intended user, and so that FS_IOC_GET_ENCRYPTION_KEY_STATUS will return the
|
||||
// correct status flags for the user.
|
||||
func dropPrivsIfNeeded(user *user.User, spec *unix.FscryptKeySpecifier) (*savedPrivs, error) {
|
||||
if spec.Type == unix.FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR {
|
||||
// v1 policy keys don't have any concept of user claims.
|
||||
return nil, nil
|
||||
}
|
||||
targetUID := util.AtoiOrPanic(user.Uid)
|
||||
ruid, euid, suid := security.GetUids()
|
||||
if euid == targetUID {
|
||||
return nil, nil
|
||||
}
|
||||
if err := security.SetUids(targetUID, targetUID, euid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &savedPrivs{ruid, euid, suid}, nil
|
||||
}
|
||||
|
||||
// restorePrivs restores root privileges if needed.
|
||||
func restorePrivs(privs *savedPrivs) error {
|
||||
if privs != nil {
|
||||
return security.SetUids(privs.ruid, privs.euid, privs.suid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateKeyDescriptor validates that the correct key descriptor was provided.
|
||||
// This isn't really necessary; this is just an extra sanity check.
|
||||
func validateKeyDescriptor(spec *unix.FscryptKeySpecifier, descriptor string) (string, error) {
|
||||
if spec.Type != unix.FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER {
|
||||
// v1 policy key: the descriptor is chosen arbitrarily by
|
||||
// userspace, so there's nothing to validate.
|
||||
return descriptor, nil
|
||||
}
|
||||
// v2 policy key. The descriptor ("identifier" in the kernel UAPI) is
|
||||
// calculated as a cryptographic hash of the key itself. The kernel
|
||||
// ignores the provided value, and calculates and returns it itself. So
|
||||
// verify that the returned value is as expected. If it's not, the key
|
||||
// doesn't actually match the encryption policy we thought it was for.
|
||||
actual := hex.EncodeToString(spec.U[:unix.FSCRYPT_KEY_IDENTIFIER_SIZE])
|
||||
if descriptor == actual {
|
||||
return descriptor, nil
|
||||
}
|
||||
return actual,
|
||||
errors.Errorf("provided and actual key descriptors differ (%q != %q)",
|
||||
descriptor, actual)
|
||||
}
|
||||
|
||||
// fsAddEncryptionKey adds the specified encryption key to the specified filesystem.
|
||||
func fsAddEncryptionKey(key *crypto.Key, descriptor string,
|
||||
mount *filesystem.Mount, user *user.User) error {
|
||||
|
||||
dir, err := os.Open(mount.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
argKey, err := crypto.NewBlankKey(int(unsafe.Sizeof(unix.FscryptAddKeyArg{})) + key.Len())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer argKey.Wipe()
|
||||
arg := (*unix.FscryptAddKeyArg)(argKey.UnsafePtr())
|
||||
|
||||
if err = buildKeySpecifier(&arg.Key_spec, descriptor); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
raw := unsafe.Pointer(uintptr(argKey.UnsafePtr()) + unsafe.Sizeof(*arg))
|
||||
arg.Raw_size = uint32(key.Len())
|
||||
C.memcpy(raw, key.UnsafePtr(), C.size_t(key.Len()))
|
||||
|
||||
savedPrivs, err := dropPrivsIfNeeded(user, &arg.Key_spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, dir.Fd(),
|
||||
unix.FS_IOC_ADD_ENCRYPTION_KEY, uintptr(argKey.UnsafePtr()))
|
||||
restorePrivs(savedPrivs)
|
||||
|
||||
log.Printf("FS_IOC_ADD_ENCRYPTION_KEY(%q, %s, <raw>) = %v", mount.Path, descriptor, errno)
|
||||
if errno != 0 {
|
||||
return errors.Wrapf(errno,
|
||||
"error adding key with descriptor %s to filesystem %s",
|
||||
descriptor, mount.Path)
|
||||
}
|
||||
if descriptor, err = validateKeyDescriptor(&arg.Key_spec, descriptor); err != nil {
|
||||
fsRemoveEncryptionKey(descriptor, mount, user)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fsRemoveEncryptionKey removes the specified encryption key from the specified
|
||||
// filesystem.
|
||||
func fsRemoveEncryptionKey(descriptor string, mount *filesystem.Mount,
|
||||
user *user.User) error {
|
||||
|
||||
dir, err := os.Open(mount.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
var arg unix.FscryptRemoveKeyArg
|
||||
if err = buildKeySpecifier(&arg.Key_spec, descriptor); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ioc := uintptr(unix.FS_IOC_REMOVE_ENCRYPTION_KEY)
|
||||
iocName := "FS_IOC_REMOVE_ENCRYPTION_KEY"
|
||||
var savedPrivs *savedPrivs
|
||||
if user == nil {
|
||||
ioc = unix.FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS
|
||||
iocName = "FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS"
|
||||
} else {
|
||||
savedPrivs, err = dropPrivsIfNeeded(user, &arg.Key_spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, dir.Fd(), ioc, uintptr(unsafe.Pointer(&arg)))
|
||||
restorePrivs(savedPrivs)
|
||||
|
||||
log.Printf("%s(%q, %s) = %v, removal_status_flags=0x%x",
|
||||
iocName, mount.Path, descriptor, errno, arg.Removal_status_flags)
|
||||
switch errno {
|
||||
case 0:
|
||||
switch {
|
||||
case arg.Removal_status_flags&unix.FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS != 0:
|
||||
return ErrKeyAddedByOtherUsers
|
||||
case arg.Removal_status_flags&unix.FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY != 0:
|
||||
return ErrKeyFilesOpen
|
||||
}
|
||||
return nil
|
||||
case unix.ENOKEY:
|
||||
// ENOKEY means either the key is completely missing or that the
|
||||
// current user doesn't have a claim to it. Distinguish between
|
||||
// these two cases by getting the key status.
|
||||
if user != nil {
|
||||
status, _ := fsGetEncryptionKeyStatus(descriptor, mount, user)
|
||||
if status == KeyPresentButOnlyOtherUsers {
|
||||
return ErrKeyAddedByOtherUsers
|
||||
}
|
||||
}
|
||||
return ErrKeyNotPresent
|
||||
default:
|
||||
return errors.Wrapf(errno,
|
||||
"error removing key with descriptor %s from filesystem %s",
|
||||
descriptor, mount.Path)
|
||||
}
|
||||
}
|
||||
|
||||
// fsGetEncryptionKeyStatus gets the status of the specified encryption key on
|
||||
// the specified filesystem.
|
||||
func fsGetEncryptionKeyStatus(descriptor string, mount *filesystem.Mount,
|
||||
user *user.User) (KeyStatus, error) {
|
||||
|
||||
dir, err := os.Open(mount.Path)
|
||||
if err != nil {
|
||||
return KeyStatusUnknown, err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
var arg unix.FscryptGetKeyStatusArg
|
||||
err = buildKeySpecifier(&arg.Key_spec, descriptor)
|
||||
if err != nil {
|
||||
return KeyStatusUnknown, err
|
||||
}
|
||||
|
||||
savedPrivs, err := dropPrivsIfNeeded(user, &arg.Key_spec)
|
||||
if err != nil {
|
||||
return KeyStatusUnknown, err
|
||||
}
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, dir.Fd(),
|
||||
unix.FS_IOC_GET_ENCRYPTION_KEY_STATUS, uintptr(unsafe.Pointer(&arg)))
|
||||
restorePrivs(savedPrivs)
|
||||
|
||||
log.Printf("FS_IOC_GET_ENCRYPTION_KEY_STATUS(%q, %s) = %v, status=%d, status_flags=0x%x",
|
||||
mount.Path, descriptor, errno, arg.Status, arg.Status_flags)
|
||||
if errno != 0 {
|
||||
return KeyStatusUnknown,
|
||||
errors.Wrapf(errno,
|
||||
"error getting status of key with descriptor %s on filesystem %s",
|
||||
descriptor, mount.Path)
|
||||
}
|
||||
switch arg.Status {
|
||||
case unix.FSCRYPT_KEY_STATUS_ABSENT:
|
||||
return KeyAbsent, nil
|
||||
case unix.FSCRYPT_KEY_STATUS_PRESENT:
|
||||
if arg.Key_spec.Type != unix.FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR &&
|
||||
(arg.Status_flags&unix.FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF) == 0 {
|
||||
return KeyPresentButOnlyOtherUsers, nil
|
||||
}
|
||||
return KeyPresent, nil
|
||||
case unix.FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED:
|
||||
return KeyAbsentButFilesBusy, nil
|
||||
default:
|
||||
return KeyStatusUnknown,
|
||||
errors.Errorf("unknown key status (%d) for key with descriptor %s on filesystem %s",
|
||||
arg.Status, descriptor, mount.Path)
|
||||
}
|
||||
}
|
175
vendor/github.com/google/fscrypt/keyring/keyring.go
generated
vendored
Normal file
175
vendor/github.com/google/fscrypt/keyring/keyring.go
generated
vendored
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* keyring.go - Add/remove encryption policy keys to/from kernel
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
* Author: Eric Biggers (ebiggers@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
// Package keyring manages adding, removing, and getting the status of
|
||||
// encryption policy keys to/from the kernel. Most public functions are in
|
||||
// keyring.go, and they delegate to either user_keyring.go or fs_keyring.go,
|
||||
// depending on whether a user keyring or a filesystem keyring is being used.
|
||||
//
|
||||
// v2 encryption policies always use the filesystem keyring.
|
||||
// v1 policies use the user keyring by default, but can be configured to use the
|
||||
// filesystem keyring instead (requires root and kernel v5.4+).
|
||||
package keyring
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"os/user"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/google/fscrypt/crypto"
|
||||
"github.com/google/fscrypt/filesystem"
|
||||
"github.com/google/fscrypt/metadata"
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
// Keyring error values
|
||||
var (
|
||||
ErrKeyAddedByOtherUsers = errors.New("other users have added the key too")
|
||||
ErrKeyFilesOpen = errors.New("some files using the key are still open")
|
||||
ErrKeyNotPresent = errors.New("key not present or already removed")
|
||||
ErrV2PoliciesUnsupported = errors.New("kernel is too old to support v2 encryption policies")
|
||||
)
|
||||
|
||||
// Options are the options which specify *which* keyring the key should be
|
||||
// added/removed/gotten to, and how.
|
||||
type Options struct {
|
||||
// Mount is the filesystem to which the key should be
|
||||
// added/removed/gotten.
|
||||
Mount *filesystem.Mount
|
||||
// User is the user for whom the key should be added/removed/gotten.
|
||||
User *user.User
|
||||
// UseFsKeyringForV1Policies is true if keys for v1 encryption policies
|
||||
// should be put in the filesystem's keyring (if supported) rather than
|
||||
// in the user's keyring. Note that this makes AddEncryptionKey and
|
||||
// RemoveEncryptionKey require root privileges.
|
||||
UseFsKeyringForV1Policies bool
|
||||
}
|
||||
|
||||
func shouldUseFsKeyring(descriptor string, options *Options) (bool, error) {
|
||||
// For v1 encryption policy keys, use the filesystem keyring if
|
||||
// use_fs_keyring_for_v1_policies is set in /etc/fscrypt.conf and the
|
||||
// kernel supports it.
|
||||
if len(descriptor) == hex.EncodedLen(unix.FSCRYPT_KEY_DESCRIPTOR_SIZE) {
|
||||
return options.UseFsKeyringForV1Policies && IsFsKeyringSupported(options.Mount), nil
|
||||
}
|
||||
// For v2 encryption policy keys, always use the filesystem keyring; the
|
||||
// kernel doesn't support any other way.
|
||||
if !IsFsKeyringSupported(options.Mount) {
|
||||
return true, ErrV2PoliciesUnsupported
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// buildKeyDescription builds the description for an fscrypt key of type
|
||||
// "logon". For ext4 and f2fs, it uses the legacy filesystem-specific prefixes
|
||||
// for compatibility with kernels before v4.8 and v4.6 respectively. For other
|
||||
// filesystems it uses the generic prefix "fscrypt".
|
||||
func buildKeyDescription(options *Options, descriptor string) string {
|
||||
switch options.Mount.FilesystemType {
|
||||
case "ext4", "f2fs":
|
||||
return options.Mount.FilesystemType + ":" + descriptor
|
||||
default:
|
||||
return unix.FSCRYPT_KEY_DESC_PREFIX + descriptor
|
||||
}
|
||||
}
|
||||
|
||||
// AddEncryptionKey adds an encryption policy key to a kernel keyring. It uses
|
||||
// either the filesystem keyring for the target Mount or the user keyring for
|
||||
// the target User.
|
||||
func AddEncryptionKey(key *crypto.Key, descriptor string, options *Options) error {
|
||||
if err := util.CheckValidLength(metadata.PolicyKeyLen, key.Len()); err != nil {
|
||||
return errors.Wrap(err, "policy key")
|
||||
}
|
||||
useFsKeyring, err := shouldUseFsKeyring(descriptor, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if useFsKeyring {
|
||||
return fsAddEncryptionKey(key, descriptor, options.Mount, options.User)
|
||||
}
|
||||
return userAddKey(key, buildKeyDescription(options, descriptor), options.User)
|
||||
}
|
||||
|
||||
// RemoveEncryptionKey removes an encryption policy key from a kernel keyring.
|
||||
// It uses either the filesystem keyring for the target Mount or the user
|
||||
// keyring for the target User.
|
||||
func RemoveEncryptionKey(descriptor string, options *Options, allUsers bool) error {
|
||||
useFsKeyring, err := shouldUseFsKeyring(descriptor, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if useFsKeyring {
|
||||
user := options.User
|
||||
if allUsers {
|
||||
user = nil
|
||||
}
|
||||
return fsRemoveEncryptionKey(descriptor, options.Mount, user)
|
||||
}
|
||||
return userRemoveKey(buildKeyDescription(options, descriptor), options.User)
|
||||
}
|
||||
|
||||
// KeyStatus is an enum that represents the status of a key in a kernel keyring.
|
||||
type KeyStatus int
|
||||
|
||||
// The possible values of KeyStatus.
|
||||
const (
|
||||
KeyStatusUnknown = 0 + iota
|
||||
KeyAbsent
|
||||
KeyAbsentButFilesBusy
|
||||
KeyPresent
|
||||
KeyPresentButOnlyOtherUsers
|
||||
)
|
||||
|
||||
func (status KeyStatus) String() string {
|
||||
switch status {
|
||||
case KeyStatusUnknown:
|
||||
return "Unknown"
|
||||
case KeyAbsent:
|
||||
return "Absent"
|
||||
case KeyAbsentButFilesBusy:
|
||||
return "AbsentButFilesBusy"
|
||||
case KeyPresent:
|
||||
return "Present"
|
||||
case KeyPresentButOnlyOtherUsers:
|
||||
return "PresentButOnlyOtherUsers"
|
||||
default:
|
||||
return strconv.Itoa(int(status))
|
||||
}
|
||||
}
|
||||
|
||||
// GetEncryptionKeyStatus gets the status of an encryption policy key in a
|
||||
// kernel keyring. It uses either the filesystem keyring for the target Mount
|
||||
// or the user keyring for the target User.
|
||||
func GetEncryptionKeyStatus(descriptor string, options *Options) (KeyStatus, error) {
|
||||
useFsKeyring, err := shouldUseFsKeyring(descriptor, options)
|
||||
if err != nil {
|
||||
return KeyStatusUnknown, err
|
||||
}
|
||||
if useFsKeyring {
|
||||
return fsGetEncryptionKeyStatus(descriptor, options.Mount, options.User)
|
||||
}
|
||||
_, _, err = userFindKey(buildKeyDescription(options, descriptor), options.User)
|
||||
if err != nil {
|
||||
return KeyAbsent, nil
|
||||
}
|
||||
return KeyPresent, nil
|
||||
}
|
251
vendor/github.com/google/fscrypt/keyring/user_keyring.go
generated
vendored
Normal file
251
vendor/github.com/google/fscrypt/keyring/user_keyring.go
generated
vendored
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* user_keyring.go - Add/remove encryption policy keys to/from user keyrings.
|
||||
* This is the deprecated mechanism; see fs_keyring.go for the new mechanism.
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package keyring
|
||||
|
||||
import (
|
||||
"os/user"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/google/fscrypt/crypto"
|
||||
"github.com/google/fscrypt/security"
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
// ErrAccessUserKeyring indicates that a user's keyring cannot be
|
||||
// accessed.
|
||||
type ErrAccessUserKeyring struct {
|
||||
TargetUser *user.User
|
||||
UnderlyingError error
|
||||
}
|
||||
|
||||
func (err *ErrAccessUserKeyring) Error() string {
|
||||
return fmt.Sprintf("could not access user keyring for %q: %s",
|
||||
err.TargetUser.Username, err.UnderlyingError)
|
||||
}
|
||||
|
||||
// ErrSessionUserKeyring indicates that a user's keyring is not linked
|
||||
// into the session keyring.
|
||||
type ErrSessionUserKeyring struct {
|
||||
TargetUser *user.User
|
||||
}
|
||||
|
||||
func (err *ErrSessionUserKeyring) Error() string {
|
||||
return fmt.Sprintf("user keyring for %q is not linked into the session keyring",
|
||||
err.TargetUser.Username)
|
||||
}
|
||||
|
||||
// KeyType is always logon as required by filesystem encryption.
|
||||
const KeyType = "logon"
|
||||
|
||||
// userAddKey puts the provided policy key into the user keyring for the
|
||||
// specified user with the provided description, and type logon.
|
||||
func userAddKey(key *crypto.Key, description string, targetUser *user.User) error {
|
||||
runtime.LockOSThread() // ensure target user keyring remains possessed in thread keyring
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
// Create our payload (containing an FscryptKey)
|
||||
payload, err := crypto.NewBlankKey(int(unsafe.Sizeof(unix.FscryptKey{})))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer payload.Wipe()
|
||||
|
||||
// Cast the payload to an FscryptKey so we can initialize the fields.
|
||||
fscryptKey := (*unix.FscryptKey)(payload.UnsafePtr())
|
||||
// Mode is ignored by the kernel
|
||||
fscryptKey.Mode = 0
|
||||
fscryptKey.Size = uint32(key.Len())
|
||||
copy(fscryptKey.Raw[:], key.Data())
|
||||
|
||||
keyringID, err := UserKeyringID(targetUser, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keyID, err := unix.AddKey(KeyType, description, payload.Data(), keyringID)
|
||||
log.Printf("KeyctlAddKey(%s, %s, <data>, %d) = %d, %v",
|
||||
KeyType, description, keyringID, keyID, err)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err,
|
||||
"error adding key with description %s to user keyring for %q",
|
||||
description, targetUser.Username)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// userRemoveKey tries to remove a policy key from the user keyring with the
|
||||
// provided description. An error is returned if the key does not exist.
|
||||
func userRemoveKey(description string, targetUser *user.User) error {
|
||||
runtime.LockOSThread() // ensure target user keyring remains possessed in thread keyring
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
keyID, keyringID, err := userFindKey(description, targetUser)
|
||||
if err != nil {
|
||||
return ErrKeyNotPresent
|
||||
}
|
||||
|
||||
_, err = unix.KeyctlInt(unix.KEYCTL_UNLINK, keyID, keyringID, 0, 0)
|
||||
log.Printf("KeyctlUnlink(%d, %d) = %v", keyID, keyringID, err)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err,
|
||||
"error removing key with description %s from user keyring for %q",
|
||||
description, targetUser.Username)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// userFindKey tries to locate a key with the provided description in the user
|
||||
// keyring for the target user. The key ID and keyring ID are returned if we can
|
||||
// find the key. An error is returned if the key does not exist.
|
||||
func userFindKey(description string, targetUser *user.User) (int, int, error) {
|
||||
runtime.LockOSThread() // ensure target user keyring remains possessed in thread keyring
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
keyringID, err := UserKeyringID(targetUser, false)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
keyID, err := unix.KeyctlSearch(keyringID, KeyType, description, 0)
|
||||
log.Printf("KeyctlSearch(%d, %s, %s) = %d, %v", keyringID, KeyType, description, keyID, err)
|
||||
if err != nil {
|
||||
return 0, 0, errors.Wrapf(err,
|
||||
"error searching for key %s in user keyring for %q",
|
||||
description, targetUser.Username)
|
||||
}
|
||||
return keyID, keyringID, err
|
||||
}
|
||||
|
||||
// UserKeyringID returns the key id of the target user's user keyring. We also
|
||||
// ensure that the keyring will be accessible by linking it into the thread
|
||||
// keyring and linking it into the root user keyring (permissions allowing). If
|
||||
// checkSession is true, an error is returned if a normal user requests their
|
||||
// user keyring, but it is not in the current session keyring.
|
||||
func UserKeyringID(targetUser *user.User, checkSession bool) (int, error) {
|
||||
runtime.LockOSThread() // ensure target user keyring remains possessed in thread keyring
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
uid := util.AtoiOrPanic(targetUser.Uid)
|
||||
targetKeyring, err := userKeyringIDLookup(uid)
|
||||
if err != nil {
|
||||
return 0, &ErrAccessUserKeyring{targetUser, err}
|
||||
}
|
||||
|
||||
if !util.IsUserRoot() {
|
||||
// Make sure the returned keyring will be accessible by checking
|
||||
// that it is in the session keyring.
|
||||
if checkSession && !isUserKeyringInSession(uid) {
|
||||
return 0, &ErrSessionUserKeyring{targetUser}
|
||||
}
|
||||
return targetKeyring, nil
|
||||
}
|
||||
|
||||
// Make sure the returned keyring will be accessible by linking it into
|
||||
// the root user's user keyring (which will not be garbage collected).
|
||||
rootKeyring, err := userKeyringIDLookup(0)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "error looking up root's user keyring")
|
||||
}
|
||||
|
||||
if rootKeyring != targetKeyring {
|
||||
if err = keyringLink(targetKeyring, rootKeyring); err != nil {
|
||||
return 0, errors.Wrapf(err,
|
||||
"error linking user keyring for %q into root's user keyring",
|
||||
targetUser.Username)
|
||||
}
|
||||
}
|
||||
return targetKeyring, nil
|
||||
}
|
||||
|
||||
func userKeyringIDLookup(uid int) (keyringID int, err error) {
|
||||
|
||||
// Our goals here are to:
|
||||
// - Find the user keyring (for the provided uid)
|
||||
// - Link it into the current thread keyring (so we can use it)
|
||||
// - Make no permanent changes to the process privileges
|
||||
// Complicating this are the facts that:
|
||||
// - The value of KEY_SPEC_USER_KEYRING is determined by the ruid
|
||||
// - Keyring linking permissions use the euid
|
||||
// So we have to change both the ruid and euid to make this work,
|
||||
// setting the suid to 0 so that we can later switch back.
|
||||
ruid, euid, suid := security.GetUids()
|
||||
if ruid != uid || euid != uid {
|
||||
if err = security.SetUids(uid, uid, 0); err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
resetErr := security.SetUids(ruid, euid, suid)
|
||||
if resetErr != nil {
|
||||
err = resetErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// We get the value of KEY_SPEC_USER_KEYRING. Note that this will also
|
||||
// trigger the creation of the uid keyring if it does not yet exist.
|
||||
keyringID, err = unix.KeyctlGetKeyringID(unix.KEY_SPEC_USER_KEYRING, true)
|
||||
log.Printf("keyringID(_uid.%d) = %d, %v", uid, keyringID, err)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// We still want to use this keyring after our privileges are reset. So
|
||||
// we link it into the thread keyring, preventing a loss of access.
|
||||
//
|
||||
// We must be under LockOSThread() for this to work reliably. Note that
|
||||
// we can't just use the process keyring, since it doesn't work reliably
|
||||
// in Go programs, due to the Go runtime creating threads before the
|
||||
// program starts and has a chance to create the process keyring.
|
||||
if err = keyringLink(keyringID, unix.KEY_SPEC_THREAD_KEYRING); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return keyringID, nil
|
||||
}
|
||||
|
||||
// isUserKeyringInSession tells us if the user's uid keyring is in the current
|
||||
// session keyring.
|
||||
func isUserKeyringInSession(uid int) bool {
|
||||
// We cannot use unix.KEY_SPEC_SESSION_KEYRING directly as that might
|
||||
// create a session keyring if one does not exist.
|
||||
sessionKeyring, err := unix.KeyctlGetKeyringID(unix.KEY_SPEC_SESSION_KEYRING, false)
|
||||
log.Printf("keyringID(session) = %d, %v", sessionKeyring, err)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
description := fmt.Sprintf("_uid.%d", uid)
|
||||
id, err := unix.KeyctlSearch(sessionKeyring, "keyring", description, 0)
|
||||
log.Printf("KeyctlSearch(%d, keyring, %s) = %d, %v", sessionKeyring, description, id, err)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func keyringLink(keyID int, keyringID int) error {
|
||||
_, err := unix.KeyctlInt(unix.KEYCTL_LINK, keyID, keyringID, 0, 0)
|
||||
log.Printf("KeyctlLink(%d, %d) = %v", keyID, keyringID, err)
|
||||
return err
|
||||
}
|
221
vendor/github.com/google/fscrypt/metadata/checks.go
generated
vendored
Normal file
221
vendor/github.com/google/fscrypt/metadata/checks.go
generated
vendored
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* checks.go - Some sanity check methods for our metadata structures
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
var errNotInitialized = errors.New("not initialized")
|
||||
|
||||
// Metadata is the interface to all of the protobuf structures that can be
|
||||
// checked for validity.
|
||||
type Metadata interface {
|
||||
CheckValidity() error
|
||||
proto.Message
|
||||
}
|
||||
|
||||
// CheckValidity ensures the mode has a name and isn't empty.
|
||||
func (m EncryptionOptions_Mode) CheckValidity() error {
|
||||
if m == EncryptionOptions_default {
|
||||
return errNotInitialized
|
||||
}
|
||||
if m.String() == "" {
|
||||
return errors.Errorf("unknown %d", m)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckValidity ensures the source has a name and isn't empty.
|
||||
func (s SourceType) CheckValidity() error {
|
||||
if s == SourceType_default {
|
||||
return errNotInitialized
|
||||
}
|
||||
if s.String() == "" {
|
||||
return errors.Errorf("unknown %d", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckValidity ensures the hash costs will be accepted by Argon2.
|
||||
func (h *HashingCosts) CheckValidity() error {
|
||||
if h == nil {
|
||||
return errNotInitialized
|
||||
}
|
||||
if h.Time <= 0 {
|
||||
return errors.Errorf("time=%d is not positive", h.Time)
|
||||
}
|
||||
if h.Parallelism <= 0 {
|
||||
return errors.Errorf("parallelism=%d is not positive", h.Parallelism)
|
||||
}
|
||||
minMemory := 8 * h.Parallelism
|
||||
if h.Memory < minMemory {
|
||||
return errors.Errorf("memory=%d is less than minimum (%d)", h.Memory, minMemory)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckValidity ensures our buffers are the correct length.
|
||||
func (w *WrappedKeyData) CheckValidity() error {
|
||||
if w == nil {
|
||||
return errNotInitialized
|
||||
}
|
||||
if len(w.EncryptedKey) == 0 {
|
||||
return errors.Wrap(errNotInitialized, "encrypted key")
|
||||
}
|
||||
if err := util.CheckValidLength(IVLen, len(w.IV)); err != nil {
|
||||
return errors.Wrap(err, "IV")
|
||||
}
|
||||
return errors.Wrap(util.CheckValidLength(HMACLen, len(w.Hmac)), "HMAC")
|
||||
}
|
||||
|
||||
// CheckValidity ensures our ProtectorData has the correct fields for its source.
|
||||
func (p *ProtectorData) CheckValidity() error {
|
||||
if p == nil {
|
||||
return errNotInitialized
|
||||
}
|
||||
|
||||
if err := p.Source.CheckValidity(); err != nil {
|
||||
return errors.Wrap(err, "protector source")
|
||||
}
|
||||
|
||||
// Source specific checks
|
||||
switch p.Source {
|
||||
case SourceType_pam_passphrase:
|
||||
if p.Uid < 0 {
|
||||
return errors.Errorf("UID=%d is negative", p.Uid)
|
||||
}
|
||||
fallthrough
|
||||
case SourceType_custom_passphrase:
|
||||
if err := p.Costs.CheckValidity(); err != nil {
|
||||
return errors.Wrap(err, "passphrase hashing costs")
|
||||
}
|
||||
if err := util.CheckValidLength(SaltLen, len(p.Salt)); err != nil {
|
||||
return errors.Wrap(err, "passphrase hashing salt")
|
||||
}
|
||||
}
|
||||
|
||||
// Generic checks
|
||||
if err := p.WrappedKey.CheckValidity(); err != nil {
|
||||
return errors.Wrap(err, "wrapped protector key")
|
||||
}
|
||||
if err := util.CheckValidLength(ProtectorDescriptorLen, len(p.ProtectorDescriptor)); err != nil {
|
||||
return errors.Wrap(err, "protector descriptor")
|
||||
|
||||
}
|
||||
err := util.CheckValidLength(InternalKeyLen, len(p.WrappedKey.EncryptedKey))
|
||||
return errors.Wrap(err, "encrypted protector key")
|
||||
}
|
||||
|
||||
// CheckValidity ensures each of the options is valid.
|
||||
func (e *EncryptionOptions) CheckValidity() error {
|
||||
if e == nil {
|
||||
return errNotInitialized
|
||||
}
|
||||
if _, ok := util.Index(e.Padding, paddingArray); !ok {
|
||||
return errors.Errorf("padding of %d is invalid", e.Padding)
|
||||
}
|
||||
if err := e.Contents.CheckValidity(); err != nil {
|
||||
return errors.Wrap(err, "contents encryption mode")
|
||||
}
|
||||
if err := e.Filenames.CheckValidity(); err != nil {
|
||||
return errors.Wrap(err, "filenames encryption mode")
|
||||
}
|
||||
// If PolicyVersion is unset, treat it as 1.
|
||||
if e.PolicyVersion == 0 {
|
||||
e.PolicyVersion = 1
|
||||
}
|
||||
if e.PolicyVersion != 1 && e.PolicyVersion != 2 {
|
||||
return errors.Errorf("policy version of %d is invalid", e.PolicyVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckValidity ensures the fields are valid and have the correct lengths.
|
||||
func (w *WrappedPolicyKey) CheckValidity() error {
|
||||
if w == nil {
|
||||
return errNotInitialized
|
||||
}
|
||||
if err := w.WrappedKey.CheckValidity(); err != nil {
|
||||
return errors.Wrap(err, "wrapped key")
|
||||
}
|
||||
if err := util.CheckValidLength(PolicyKeyLen, len(w.WrappedKey.EncryptedKey)); err != nil {
|
||||
return errors.Wrap(err, "encrypted key")
|
||||
}
|
||||
err := util.CheckValidLength(ProtectorDescriptorLen, len(w.ProtectorDescriptor))
|
||||
return errors.Wrap(err, "wrapping protector descriptor")
|
||||
}
|
||||
|
||||
// CheckValidity ensures the fields and each wrapped key are valid.
|
||||
func (p *PolicyData) CheckValidity() error {
|
||||
if p == nil {
|
||||
return errNotInitialized
|
||||
}
|
||||
// Check each wrapped key
|
||||
for i, w := range p.WrappedPolicyKeys {
|
||||
if err := w.CheckValidity(); err != nil {
|
||||
return errors.Wrapf(err, "policy key slot %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.Options.CheckValidity(); err != nil {
|
||||
return errors.Wrap(err, "policy options")
|
||||
}
|
||||
|
||||
var expectedLen int
|
||||
switch p.Options.PolicyVersion {
|
||||
case 1:
|
||||
expectedLen = PolicyDescriptorLenV1
|
||||
case 2:
|
||||
expectedLen = PolicyDescriptorLenV2
|
||||
default:
|
||||
return errors.Errorf("policy version of %d is invalid", p.Options.PolicyVersion)
|
||||
}
|
||||
|
||||
if err := util.CheckValidLength(expectedLen, len(p.KeyDescriptor)); err != nil {
|
||||
return errors.Wrap(err, "policy key descriptor")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckValidity ensures the Config has all the necessary info for its Source.
|
||||
func (c *Config) CheckValidity() error {
|
||||
// General checks
|
||||
if c == nil {
|
||||
return errNotInitialized
|
||||
}
|
||||
if err := c.Source.CheckValidity(); err != nil {
|
||||
return errors.Wrap(err, "default config source")
|
||||
}
|
||||
|
||||
// Source specific checks
|
||||
switch c.Source {
|
||||
case SourceType_pam_passphrase, SourceType_custom_passphrase:
|
||||
if err := c.HashCosts.CheckValidity(); err != nil {
|
||||
return errors.Wrap(err, "config hashing costs")
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Wrap(c.Options.CheckValidity(), "config options")
|
||||
}
|
59
vendor/github.com/google/fscrypt/metadata/config.go
generated
vendored
Normal file
59
vendor/github.com/google/fscrypt/metadata/config.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* config.go - Parsing for our global config file. The file is simply the JSON
|
||||
* output of the Config protocol buffer.
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
// Package metadata contains all of the on disk structures.
|
||||
// These structures are defined in metadata.proto. The package also
|
||||
// contains functions for manipulating these structures, specifically:
|
||||
// * Reading and Writing the Config file to disk
|
||||
// * Getting and Setting Policies for directories
|
||||
// * Reasonable defaults for a Policy's EncryptionOptions
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
)
|
||||
|
||||
// WriteConfig outputs the Config data as nicely formatted JSON
|
||||
func WriteConfig(config *Config, out io.Writer) error {
|
||||
m := jsonpb.Marshaler{
|
||||
EmitDefaults: true,
|
||||
EnumsAsInts: false,
|
||||
Indent: "\t",
|
||||
OrigName: true,
|
||||
}
|
||||
if err := m.Marshal(out, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := out.Write([]byte{'\n'})
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadConfig writes the JSON data into the config structure
|
||||
func ReadConfig(in io.Reader) (*Config, error) {
|
||||
config := new(Config)
|
||||
// Allow (and ignore) unknown fields for forwards compatibility.
|
||||
u := jsonpb.Unmarshaler{
|
||||
AllowUnknownFields: true,
|
||||
}
|
||||
return config, u.Unmarshal(in, config)
|
||||
}
|
57
vendor/github.com/google/fscrypt/metadata/constants.go
generated
vendored
Normal file
57
vendor/github.com/google/fscrypt/metadata/constants.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* constants.go - Some metadata constants used throughout fscrypt
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Lengths for our keys, buffers, and strings used in fscrypt.
|
||||
const (
|
||||
// Length of policy descriptor (in hex chars) for v1 encryption policies
|
||||
PolicyDescriptorLenV1 = 2 * unix.FSCRYPT_KEY_DESCRIPTOR_SIZE
|
||||
// Length of protector descriptor (in hex chars)
|
||||
ProtectorDescriptorLen = PolicyDescriptorLenV1
|
||||
// Length of policy descriptor (in hex chars) for v2 encryption policies
|
||||
PolicyDescriptorLenV2 = 2 * unix.FSCRYPT_KEY_IDENTIFIER_SIZE
|
||||
// We always use 256-bit keys internally (compared to 512-bit policy keys).
|
||||
InternalKeyLen = 32
|
||||
IVLen = 16
|
||||
SaltLen = 16
|
||||
// We use SHA256 for the HMAC, and len(HMAC) == len(hash size).
|
||||
HMACLen = sha256.Size
|
||||
// PolicyKeyLen is the length of all keys passed directly to the Keyring
|
||||
PolicyKeyLen = unix.FSCRYPT_MAX_KEY_SIZE
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultOptions use the supported encryption modes, max padding, and
|
||||
// policy version 1.
|
||||
DefaultOptions = &EncryptionOptions{
|
||||
Padding: 32,
|
||||
Contents: EncryptionOptions_AES_256_XTS,
|
||||
Filenames: EncryptionOptions_AES_256_CTS,
|
||||
PolicyVersion: 1,
|
||||
}
|
||||
// DefaultSource is the source we use if none is specified.
|
||||
DefaultSource = SourceType_custom_passphrase
|
||||
)
|
589
vendor/github.com/google/fscrypt/metadata/metadata.pb.go
generated
vendored
Normal file
589
vendor/github.com/google/fscrypt/metadata/metadata.pb.go
generated
vendored
Normal file
@ -0,0 +1,589 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: metadata/metadata.proto
|
||||
|
||||
package metadata
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// Specifies the method in which an outside secret is obtained for a Protector
|
||||
type SourceType int32
|
||||
|
||||
const (
|
||||
SourceType_default SourceType = 0
|
||||
SourceType_pam_passphrase SourceType = 1
|
||||
SourceType_custom_passphrase SourceType = 2
|
||||
SourceType_raw_key SourceType = 3
|
||||
)
|
||||
|
||||
var SourceType_name = map[int32]string{
|
||||
0: "default",
|
||||
1: "pam_passphrase",
|
||||
2: "custom_passphrase",
|
||||
3: "raw_key",
|
||||
}
|
||||
var SourceType_value = map[string]int32{
|
||||
"default": 0,
|
||||
"pam_passphrase": 1,
|
||||
"custom_passphrase": 2,
|
||||
"raw_key": 3,
|
||||
}
|
||||
|
||||
func (x SourceType) String() string {
|
||||
return proto.EnumName(SourceType_name, int32(x))
|
||||
}
|
||||
func (SourceType) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_metadata_31965d2849cb292a, []int{0}
|
||||
}
|
||||
|
||||
// Type of encryption; should match declarations of unix.FSCRYPT_MODE
|
||||
type EncryptionOptions_Mode int32
|
||||
|
||||
const (
|
||||
EncryptionOptions_default EncryptionOptions_Mode = 0
|
||||
EncryptionOptions_AES_256_XTS EncryptionOptions_Mode = 1
|
||||
EncryptionOptions_AES_256_GCM EncryptionOptions_Mode = 2
|
||||
EncryptionOptions_AES_256_CBC EncryptionOptions_Mode = 3
|
||||
EncryptionOptions_AES_256_CTS EncryptionOptions_Mode = 4
|
||||
EncryptionOptions_AES_128_CBC EncryptionOptions_Mode = 5
|
||||
EncryptionOptions_AES_128_CTS EncryptionOptions_Mode = 6
|
||||
EncryptionOptions_Adiantum EncryptionOptions_Mode = 9
|
||||
)
|
||||
|
||||
var EncryptionOptions_Mode_name = map[int32]string{
|
||||
0: "default",
|
||||
1: "AES_256_XTS",
|
||||
2: "AES_256_GCM",
|
||||
3: "AES_256_CBC",
|
||||
4: "AES_256_CTS",
|
||||
5: "AES_128_CBC",
|
||||
6: "AES_128_CTS",
|
||||
9: "Adiantum",
|
||||
}
|
||||
var EncryptionOptions_Mode_value = map[string]int32{
|
||||
"default": 0,
|
||||
"AES_256_XTS": 1,
|
||||
"AES_256_GCM": 2,
|
||||
"AES_256_CBC": 3,
|
||||
"AES_256_CTS": 4,
|
||||
"AES_128_CBC": 5,
|
||||
"AES_128_CTS": 6,
|
||||
"Adiantum": 9,
|
||||
}
|
||||
|
||||
func (x EncryptionOptions_Mode) String() string {
|
||||
return proto.EnumName(EncryptionOptions_Mode_name, int32(x))
|
||||
}
|
||||
func (EncryptionOptions_Mode) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_metadata_31965d2849cb292a, []int{3, 0}
|
||||
}
|
||||
|
||||
// Cost parameters to be used in our hashing functions.
|
||||
type HashingCosts struct {
|
||||
Time int64 `protobuf:"varint,2,opt,name=time,proto3" json:"time,omitempty"`
|
||||
Memory int64 `protobuf:"varint,3,opt,name=memory,proto3" json:"memory,omitempty"`
|
||||
Parallelism int64 `protobuf:"varint,4,opt,name=parallelism,proto3" json:"parallelism,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *HashingCosts) Reset() { *m = HashingCosts{} }
|
||||
func (m *HashingCosts) String() string { return proto.CompactTextString(m) }
|
||||
func (*HashingCosts) ProtoMessage() {}
|
||||
func (*HashingCosts) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_metadata_31965d2849cb292a, []int{0}
|
||||
}
|
||||
func (m *HashingCosts) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_HashingCosts.Unmarshal(m, b)
|
||||
}
|
||||
func (m *HashingCosts) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_HashingCosts.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *HashingCosts) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HashingCosts.Merge(dst, src)
|
||||
}
|
||||
func (m *HashingCosts) XXX_Size() int {
|
||||
return xxx_messageInfo_HashingCosts.Size(m)
|
||||
}
|
||||
func (m *HashingCosts) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_HashingCosts.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_HashingCosts proto.InternalMessageInfo
|
||||
|
||||
func (m *HashingCosts) GetTime() int64 {
|
||||
if m != nil {
|
||||
return m.Time
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *HashingCosts) GetMemory() int64 {
|
||||
if m != nil {
|
||||
return m.Memory
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *HashingCosts) GetParallelism() int64 {
|
||||
if m != nil {
|
||||
return m.Parallelism
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// This structure is used for our authenticated wrapping/unwrapping of keys.
|
||||
type WrappedKeyData struct {
|
||||
IV []byte `protobuf:"bytes,1,opt,name=IV,proto3" json:"IV,omitempty"`
|
||||
EncryptedKey []byte `protobuf:"bytes,2,opt,name=encrypted_key,json=encryptedKey,proto3" json:"encrypted_key,omitempty"`
|
||||
Hmac []byte `protobuf:"bytes,3,opt,name=hmac,proto3" json:"hmac,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *WrappedKeyData) Reset() { *m = WrappedKeyData{} }
|
||||
func (m *WrappedKeyData) String() string { return proto.CompactTextString(m) }
|
||||
func (*WrappedKeyData) ProtoMessage() {}
|
||||
func (*WrappedKeyData) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_metadata_31965d2849cb292a, []int{1}
|
||||
}
|
||||
func (m *WrappedKeyData) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_WrappedKeyData.Unmarshal(m, b)
|
||||
}
|
||||
func (m *WrappedKeyData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_WrappedKeyData.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *WrappedKeyData) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_WrappedKeyData.Merge(dst, src)
|
||||
}
|
||||
func (m *WrappedKeyData) XXX_Size() int {
|
||||
return xxx_messageInfo_WrappedKeyData.Size(m)
|
||||
}
|
||||
func (m *WrappedKeyData) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_WrappedKeyData.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_WrappedKeyData proto.InternalMessageInfo
|
||||
|
||||
func (m *WrappedKeyData) GetIV() []byte {
|
||||
if m != nil {
|
||||
return m.IV
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *WrappedKeyData) GetEncryptedKey() []byte {
|
||||
if m != nil {
|
||||
return m.EncryptedKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *WrappedKeyData) GetHmac() []byte {
|
||||
if m != nil {
|
||||
return m.Hmac
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The associated data for each protector
|
||||
type ProtectorData struct {
|
||||
ProtectorDescriptor string `protobuf:"bytes,1,opt,name=protector_descriptor,json=protectorDescriptor,proto3" json:"protector_descriptor,omitempty"`
|
||||
Source SourceType `protobuf:"varint,2,opt,name=source,proto3,enum=metadata.SourceType" json:"source,omitempty"`
|
||||
// These are only used by some of the protector types
|
||||
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Costs *HashingCosts `protobuf:"bytes,4,opt,name=costs,proto3" json:"costs,omitempty"`
|
||||
Salt []byte `protobuf:"bytes,5,opt,name=salt,proto3" json:"salt,omitempty"`
|
||||
Uid int64 `protobuf:"varint,6,opt,name=uid,proto3" json:"uid,omitempty"`
|
||||
WrappedKey *WrappedKeyData `protobuf:"bytes,7,opt,name=wrapped_key,json=wrappedKey,proto3" json:"wrapped_key,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ProtectorData) Reset() { *m = ProtectorData{} }
|
||||
func (m *ProtectorData) String() string { return proto.CompactTextString(m) }
|
||||
func (*ProtectorData) ProtoMessage() {}
|
||||
func (*ProtectorData) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_metadata_31965d2849cb292a, []int{2}
|
||||
}
|
||||
func (m *ProtectorData) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ProtectorData.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ProtectorData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ProtectorData.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *ProtectorData) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ProtectorData.Merge(dst, src)
|
||||
}
|
||||
func (m *ProtectorData) XXX_Size() int {
|
||||
return xxx_messageInfo_ProtectorData.Size(m)
|
||||
}
|
||||
func (m *ProtectorData) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ProtectorData.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ProtectorData proto.InternalMessageInfo
|
||||
|
||||
func (m *ProtectorData) GetProtectorDescriptor() string {
|
||||
if m != nil {
|
||||
return m.ProtectorDescriptor
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ProtectorData) GetSource() SourceType {
|
||||
if m != nil {
|
||||
return m.Source
|
||||
}
|
||||
return SourceType_default
|
||||
}
|
||||
|
||||
func (m *ProtectorData) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ProtectorData) GetCosts() *HashingCosts {
|
||||
if m != nil {
|
||||
return m.Costs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProtectorData) GetSalt() []byte {
|
||||
if m != nil {
|
||||
return m.Salt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProtectorData) GetUid() int64 {
|
||||
if m != nil {
|
||||
return m.Uid
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *ProtectorData) GetWrappedKey() *WrappedKeyData {
|
||||
if m != nil {
|
||||
return m.WrappedKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encryption policy specifics, corresponds to the fscrypt_policy struct
|
||||
type EncryptionOptions struct {
|
||||
Padding int64 `protobuf:"varint,1,opt,name=padding,proto3" json:"padding,omitempty"`
|
||||
Contents EncryptionOptions_Mode `protobuf:"varint,2,opt,name=contents,proto3,enum=metadata.EncryptionOptions_Mode" json:"contents,omitempty"`
|
||||
Filenames EncryptionOptions_Mode `protobuf:"varint,3,opt,name=filenames,proto3,enum=metadata.EncryptionOptions_Mode" json:"filenames,omitempty"`
|
||||
PolicyVersion int64 `protobuf:"varint,4,opt,name=policy_version,json=policyVersion,proto3" json:"policy_version,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *EncryptionOptions) Reset() { *m = EncryptionOptions{} }
|
||||
func (m *EncryptionOptions) String() string { return proto.CompactTextString(m) }
|
||||
func (*EncryptionOptions) ProtoMessage() {}
|
||||
func (*EncryptionOptions) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_metadata_31965d2849cb292a, []int{3}
|
||||
}
|
||||
func (m *EncryptionOptions) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_EncryptionOptions.Unmarshal(m, b)
|
||||
}
|
||||
func (m *EncryptionOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_EncryptionOptions.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *EncryptionOptions) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_EncryptionOptions.Merge(dst, src)
|
||||
}
|
||||
func (m *EncryptionOptions) XXX_Size() int {
|
||||
return xxx_messageInfo_EncryptionOptions.Size(m)
|
||||
}
|
||||
func (m *EncryptionOptions) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_EncryptionOptions.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_EncryptionOptions proto.InternalMessageInfo
|
||||
|
||||
func (m *EncryptionOptions) GetPadding() int64 {
|
||||
if m != nil {
|
||||
return m.Padding
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *EncryptionOptions) GetContents() EncryptionOptions_Mode {
|
||||
if m != nil {
|
||||
return m.Contents
|
||||
}
|
||||
return EncryptionOptions_default
|
||||
}
|
||||
|
||||
func (m *EncryptionOptions) GetFilenames() EncryptionOptions_Mode {
|
||||
if m != nil {
|
||||
return m.Filenames
|
||||
}
|
||||
return EncryptionOptions_default
|
||||
}
|
||||
|
||||
func (m *EncryptionOptions) GetPolicyVersion() int64 {
|
||||
if m != nil {
|
||||
return m.PolicyVersion
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type WrappedPolicyKey struct {
|
||||
ProtectorDescriptor string `protobuf:"bytes,1,opt,name=protector_descriptor,json=protectorDescriptor,proto3" json:"protector_descriptor,omitempty"`
|
||||
WrappedKey *WrappedKeyData `protobuf:"bytes,2,opt,name=wrapped_key,json=wrappedKey,proto3" json:"wrapped_key,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *WrappedPolicyKey) Reset() { *m = WrappedPolicyKey{} }
|
||||
func (m *WrappedPolicyKey) String() string { return proto.CompactTextString(m) }
|
||||
func (*WrappedPolicyKey) ProtoMessage() {}
|
||||
func (*WrappedPolicyKey) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_metadata_31965d2849cb292a, []int{4}
|
||||
}
|
||||
func (m *WrappedPolicyKey) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_WrappedPolicyKey.Unmarshal(m, b)
|
||||
}
|
||||
func (m *WrappedPolicyKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_WrappedPolicyKey.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *WrappedPolicyKey) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_WrappedPolicyKey.Merge(dst, src)
|
||||
}
|
||||
func (m *WrappedPolicyKey) XXX_Size() int {
|
||||
return xxx_messageInfo_WrappedPolicyKey.Size(m)
|
||||
}
|
||||
func (m *WrappedPolicyKey) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_WrappedPolicyKey.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_WrappedPolicyKey proto.InternalMessageInfo
|
||||
|
||||
func (m *WrappedPolicyKey) GetProtectorDescriptor() string {
|
||||
if m != nil {
|
||||
return m.ProtectorDescriptor
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *WrappedPolicyKey) GetWrappedKey() *WrappedKeyData {
|
||||
if m != nil {
|
||||
return m.WrappedKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The associated data for each policy
|
||||
type PolicyData struct {
|
||||
KeyDescriptor string `protobuf:"bytes,1,opt,name=key_descriptor,json=keyDescriptor,proto3" json:"key_descriptor,omitempty"`
|
||||
Options *EncryptionOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
|
||||
WrappedPolicyKeys []*WrappedPolicyKey `protobuf:"bytes,3,rep,name=wrapped_policy_keys,json=wrappedPolicyKeys,proto3" json:"wrapped_policy_keys,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PolicyData) Reset() { *m = PolicyData{} }
|
||||
func (m *PolicyData) String() string { return proto.CompactTextString(m) }
|
||||
func (*PolicyData) ProtoMessage() {}
|
||||
func (*PolicyData) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_metadata_31965d2849cb292a, []int{5}
|
||||
}
|
||||
func (m *PolicyData) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PolicyData.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PolicyData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PolicyData.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *PolicyData) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PolicyData.Merge(dst, src)
|
||||
}
|
||||
func (m *PolicyData) XXX_Size() int {
|
||||
return xxx_messageInfo_PolicyData.Size(m)
|
||||
}
|
||||
func (m *PolicyData) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PolicyData.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PolicyData proto.InternalMessageInfo
|
||||
|
||||
func (m *PolicyData) GetKeyDescriptor() string {
|
||||
if m != nil {
|
||||
return m.KeyDescriptor
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PolicyData) GetOptions() *EncryptionOptions {
|
||||
if m != nil {
|
||||
return m.Options
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PolicyData) GetWrappedPolicyKeys() []*WrappedPolicyKey {
|
||||
if m != nil {
|
||||
return m.WrappedPolicyKeys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Data stored in the config file
|
||||
type Config struct {
|
||||
Source SourceType `protobuf:"varint,1,opt,name=source,proto3,enum=metadata.SourceType" json:"source,omitempty"`
|
||||
HashCosts *HashingCosts `protobuf:"bytes,2,opt,name=hash_costs,json=hashCosts,proto3" json:"hash_costs,omitempty"`
|
||||
Options *EncryptionOptions `protobuf:"bytes,4,opt,name=options,proto3" json:"options,omitempty"`
|
||||
UseFsKeyringForV1Policies bool `protobuf:"varint,5,opt,name=use_fs_keyring_for_v1_policies,json=useFsKeyringForV1Policies,proto3" json:"use_fs_keyring_for_v1_policies,omitempty"`
|
||||
AllowCrossUserMetadata bool `protobuf:"varint,6,opt,name=allow_cross_user_metadata,json=allowCrossUserMetadata,proto3" json:"allow_cross_user_metadata,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Config) Reset() { *m = Config{} }
|
||||
func (m *Config) String() string { return proto.CompactTextString(m) }
|
||||
func (*Config) ProtoMessage() {}
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_metadata_31965d2849cb292a, []int{6}
|
||||
}
|
||||
func (m *Config) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Config.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Config) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Config.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *Config) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Config.Merge(dst, src)
|
||||
}
|
||||
func (m *Config) XXX_Size() int {
|
||||
return xxx_messageInfo_Config.Size(m)
|
||||
}
|
||||
func (m *Config) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Config.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Config proto.InternalMessageInfo
|
||||
|
||||
func (m *Config) GetSource() SourceType {
|
||||
if m != nil {
|
||||
return m.Source
|
||||
}
|
||||
return SourceType_default
|
||||
}
|
||||
|
||||
func (m *Config) GetHashCosts() *HashingCosts {
|
||||
if m != nil {
|
||||
return m.HashCosts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Config) GetOptions() *EncryptionOptions {
|
||||
if m != nil {
|
||||
return m.Options
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Config) GetUseFsKeyringForV1Policies() bool {
|
||||
if m != nil {
|
||||
return m.UseFsKeyringForV1Policies
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Config) GetAllowCrossUserMetadata() bool {
|
||||
if m != nil {
|
||||
return m.AllowCrossUserMetadata
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*HashingCosts)(nil), "metadata.HashingCosts")
|
||||
proto.RegisterType((*WrappedKeyData)(nil), "metadata.WrappedKeyData")
|
||||
proto.RegisterType((*ProtectorData)(nil), "metadata.ProtectorData")
|
||||
proto.RegisterType((*EncryptionOptions)(nil), "metadata.EncryptionOptions")
|
||||
proto.RegisterType((*WrappedPolicyKey)(nil), "metadata.WrappedPolicyKey")
|
||||
proto.RegisterType((*PolicyData)(nil), "metadata.PolicyData")
|
||||
proto.RegisterType((*Config)(nil), "metadata.Config")
|
||||
proto.RegisterEnum("metadata.SourceType", SourceType_name, SourceType_value)
|
||||
proto.RegisterEnum("metadata.EncryptionOptions_Mode", EncryptionOptions_Mode_name, EncryptionOptions_Mode_value)
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("metadata/metadata.proto", fileDescriptor_metadata_31965d2849cb292a) }
|
||||
|
||||
var fileDescriptor_metadata_31965d2849cb292a = []byte{
|
||||
// 748 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xdb, 0x6a, 0xf3, 0x46,
|
||||
0x10, 0xae, 0x24, 0xc7, 0x87, 0xf1, 0xa1, 0xca, 0xfe, 0x69, 0xaa, 0xb4, 0x50, 0x8c, 0x4b, 0x20,
|
||||
0x94, 0x90, 0x62, 0x97, 0x94, 0x06, 0x4a, 0x21, 0x75, 0x92, 0x36, 0x09, 0xa1, 0xe9, 0xda, 0x75,
|
||||
0x5b, 0x28, 0x88, 0x8d, 0xb4, 0xb6, 0x17, 0x4b, 0x5a, 0xb1, 0xbb, 0x8a, 0xd1, 0x5d, 0xef, 0xfa,
|
||||
0x00, 0x7d, 0x97, 0xf6, 0x65, 0xfa, 0x30, 0x45, 0x2b, 0xc9, 0x87, 0x04, 0x42, 0xf2, 0xdf, 0x98,
|
||||
0xd9, 0x6f, 0x67, 0xe6, 0x9b, 0xf9, 0x66, 0xc7, 0x82, 0x8f, 0x43, 0xaa, 0x88, 0x4f, 0x14, 0xf9,
|
||||
0xb2, 0x34, 0x4e, 0x62, 0xc1, 0x15, 0x47, 0xf5, 0xf2, 0xdc, 0xfb, 0x03, 0x5a, 0x3f, 0x12, 0x39,
|
||||
0x67, 0xd1, 0x6c, 0xc8, 0xa5, 0x92, 0x08, 0x41, 0x45, 0xb1, 0x90, 0x3a, 0x66, 0xd7, 0x38, 0xb2,
|
||||
0xb0, 0xb6, 0xd1, 0x3e, 0x54, 0x43, 0x1a, 0x72, 0x91, 0x3a, 0x96, 0x46, 0x8b, 0x13, 0xea, 0x42,
|
||||
0x33, 0x26, 0x82, 0x04, 0x01, 0x0d, 0x98, 0x0c, 0x9d, 0x8a, 0xbe, 0xdc, 0x84, 0x7a, 0xbf, 0x43,
|
||||
0xe7, 0x57, 0x41, 0xe2, 0x98, 0xfa, 0xb7, 0x34, 0xbd, 0x20, 0x8a, 0xa0, 0x0e, 0x98, 0xd7, 0x13,
|
||||
0xc7, 0xe8, 0x1a, 0x47, 0x2d, 0x6c, 0x5e, 0x4f, 0xd0, 0xe7, 0xd0, 0xa6, 0x91, 0x27, 0xd2, 0x58,
|
||||
0x51, 0xdf, 0x5d, 0xd0, 0x54, 0x13, 0xb7, 0x70, 0x6b, 0x05, 0xde, 0xd2, 0x34, 0x2b, 0x6a, 0x1e,
|
||||
0x12, 0x4f, 0xd3, 0xb7, 0xb0, 0xb6, 0x7b, 0x7f, 0x9b, 0xd0, 0xbe, 0x17, 0x5c, 0x51, 0x4f, 0x71,
|
||||
0xa1, 0x53, 0xf7, 0x61, 0x2f, 0x2e, 0x01, 0xd7, 0xa7, 0xd2, 0x13, 0x2c, 0x56, 0x5c, 0x68, 0xb2,
|
||||
0x06, 0x7e, 0xb7, 0xba, 0xbb, 0x58, 0x5d, 0xa1, 0x63, 0xa8, 0x4a, 0x9e, 0x08, 0x2f, 0xef, 0xb7,
|
||||
0x33, 0xd8, 0x3b, 0x59, 0x09, 0x35, 0xd2, 0xf8, 0x38, 0x8d, 0x29, 0x2e, 0x7c, 0xb2, 0x32, 0x22,
|
||||
0x12, 0x52, 0x5d, 0x46, 0x03, 0x6b, 0x1b, 0x1d, 0xc3, 0x8e, 0x97, 0x09, 0xa7, 0xbb, 0x6f, 0x0e,
|
||||
0xf6, 0xd7, 0x09, 0x36, 0x65, 0xc5, 0xb9, 0x53, 0x96, 0x41, 0x92, 0x40, 0x39, 0x3b, 0x79, 0x23,
|
||||
0x99, 0x8d, 0x6c, 0xb0, 0x12, 0xe6, 0x3b, 0x55, 0xad, 0x5e, 0x66, 0xa2, 0x33, 0x68, 0x2e, 0x73,
|
||||
0xd5, 0xb4, 0x22, 0x35, 0x9d, 0xd9, 0x59, 0x67, 0xde, 0x96, 0x14, 0xc3, 0x72, 0x75, 0xee, 0xfd,
|
||||
0x67, 0xc2, 0xee, 0x65, 0x2e, 0x1d, 0xe3, 0xd1, 0x4f, 0xfa, 0x57, 0x22, 0x07, 0x6a, 0x31, 0xf1,
|
||||
0x7d, 0x16, 0xcd, 0xb4, 0x18, 0x16, 0x2e, 0x8f, 0xe8, 0x5b, 0xa8, 0x7b, 0x3c, 0x52, 0x34, 0x52,
|
||||
0xb2, 0x90, 0xa0, 0xbb, 0xe6, 0x79, 0x96, 0xe8, 0xe4, 0x8e, 0xfb, 0x14, 0xaf, 0x22, 0xd0, 0x77,
|
||||
0xd0, 0x98, 0xb2, 0x80, 0x66, 0x42, 0x48, 0xad, 0xca, 0x6b, 0xc2, 0xd7, 0x21, 0xe8, 0x10, 0x3a,
|
||||
0x31, 0x0f, 0x98, 0x97, 0xba, 0x8f, 0x54, 0x48, 0xc6, 0xa3, 0xe2, 0x0d, 0xb5, 0x73, 0x74, 0x92,
|
||||
0x83, 0xbd, 0xbf, 0x0c, 0xa8, 0x64, 0xa1, 0xa8, 0x09, 0x35, 0x9f, 0x4e, 0x49, 0x12, 0x28, 0xfb,
|
||||
0x03, 0xf4, 0x21, 0x34, 0xcf, 0x2f, 0x47, 0xee, 0xe0, 0xf4, 0x6b, 0xf7, 0xb7, 0xf1, 0xc8, 0x36,
|
||||
0x36, 0x81, 0x1f, 0x86, 0x77, 0xb6, 0xb9, 0x09, 0x0c, 0xbf, 0x1f, 0xda, 0xd6, 0x16, 0x30, 0x1e,
|
||||
0xd9, 0x95, 0x12, 0xe8, 0x0f, 0xbe, 0xd1, 0x1e, 0x3b, 0x5b, 0xc0, 0x78, 0x64, 0x57, 0x51, 0x0b,
|
||||
0xea, 0xe7, 0x3e, 0x23, 0x91, 0x4a, 0x42, 0xbb, 0xd1, 0xfb, 0xd3, 0x00, 0xbb, 0x50, 0xff, 0x5e,
|
||||
0x97, 0x98, 0xbd, 0xce, 0xf7, 0x78, 0x77, 0x4f, 0x26, 0x6c, 0xbe, 0x61, 0xc2, 0xff, 0x18, 0x00,
|
||||
0x39, 0xb7, 0x7e, 0xf4, 0x87, 0xd0, 0x59, 0xd0, 0xf4, 0x39, 0x6d, 0x7b, 0x41, 0xd3, 0x0d, 0xc2,
|
||||
0x53, 0xa8, 0xf1, 0x7c, 0x08, 0x05, 0xd9, 0xa7, 0x2f, 0xcc, 0x09, 0x97, 0xbe, 0xe8, 0x06, 0xde,
|
||||
0x95, 0x75, 0x16, 0x83, 0x5a, 0xd0, 0x34, 0x1b, 0xb5, 0x75, 0xd4, 0x1c, 0x7c, 0xf2, 0xac, 0xde,
|
||||
0x95, 0x26, 0x78, 0x77, 0xf9, 0x04, 0x91, 0xbd, 0x7f, 0x4d, 0xa8, 0x0e, 0x79, 0x34, 0x65, 0xb3,
|
||||
0x8d, 0xb5, 0x33, 0x5e, 0xb1, 0x76, 0xa7, 0x00, 0x73, 0x22, 0xe7, 0x6e, 0xbe, 0x67, 0xe6, 0x8b,
|
||||
0x7b, 0xd6, 0xc8, 0x3c, 0xf3, 0x7f, 0xb2, 0x8d, 0x96, 0x2b, 0x6f, 0x68, 0xf9, 0x1c, 0x3e, 0x4b,
|
||||
0x24, 0x75, 0xa7, 0x32, 0x6b, 0x55, 0xb0, 0x68, 0xe6, 0x4e, 0xb9, 0x70, 0x1f, 0xfb, 0xb9, 0x00,
|
||||
0x8c, 0x4a, 0xbd, 0xbc, 0x75, 0x7c, 0x90, 0x48, 0x7a, 0x25, 0x6f, 0x73, 0x9f, 0x2b, 0x2e, 0x26,
|
||||
0xfd, 0xfb, 0xc2, 0x01, 0x9d, 0xc1, 0x01, 0x09, 0x02, 0xbe, 0x74, 0x3d, 0xc1, 0xa5, 0x74, 0x13,
|
||||
0x49, 0x85, 0x5b, 0x52, 0xeb, 0x3d, 0xaf, 0xe3, 0x7d, 0xed, 0x30, 0xcc, 0xee, 0x7f, 0x91, 0x54,
|
||||
0xdc, 0x15, 0xb7, 0x37, 0x95, 0xba, 0x65, 0x57, 0x70, 0xdb, 0xe3, 0x61, 0x4c, 0x14, 0x7b, 0x60,
|
||||
0x01, 0x53, 0xe9, 0x17, 0x3f, 0x03, 0xac, 0x65, 0xd9, 0x5e, 0x02, 0x04, 0x9d, 0x98, 0x84, 0x6e,
|
||||
0x4c, 0xa4, 0x8c, 0xe7, 0x82, 0x48, 0x6a, 0x1b, 0xe8, 0x23, 0xd8, 0xf5, 0x12, 0xa9, 0xf8, 0x16,
|
||||
0x6c, 0x66, 0x71, 0x82, 0x2c, 0xb3, 0xae, 0x6c, 0xeb, 0xa1, 0xaa, 0xbf, 0x03, 0x5f, 0xfd, 0x1f,
|
||||
0x00, 0x00, 0xff, 0xff, 0xe2, 0x78, 0x9e, 0x2e, 0x22, 0x06, 0x00, 0x00,
|
||||
}
|
107
vendor/github.com/google/fscrypt/metadata/metadata.proto
generated
vendored
Normal file
107
vendor/github.com/google/fscrypt/metadata/metadata.proto
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* metadata.proto - File which contains all of the metadata structures which we
|
||||
* write to metadata files. Must be compiled with protoc to use the library.
|
||||
* Compilation can be invoked with go generate.
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
// If you modify this file, be sure to run "go generate" on this package.
|
||||
syntax = "proto3";
|
||||
package metadata;
|
||||
|
||||
// Cost parameters to be used in our hashing functions.
|
||||
message HashingCosts {
|
||||
int64 time = 2;
|
||||
int64 memory = 3;
|
||||
int64 parallelism = 4;
|
||||
}
|
||||
|
||||
// This structure is used for our authenticated wrapping/unwrapping of keys.
|
||||
message WrappedKeyData {
|
||||
bytes IV = 1;
|
||||
bytes encrypted_key = 2;
|
||||
bytes hmac = 3;
|
||||
}
|
||||
|
||||
// Specifies the method in which an outside secret is obtained for a Protector
|
||||
enum SourceType {
|
||||
default = 0;
|
||||
pam_passphrase = 1;
|
||||
custom_passphrase = 2;
|
||||
raw_key = 3;
|
||||
}
|
||||
|
||||
// The associated data for each protector
|
||||
message ProtectorData {
|
||||
string protector_descriptor = 1;
|
||||
SourceType source = 2;
|
||||
|
||||
// These are only used by some of the protector types
|
||||
string name = 3;
|
||||
HashingCosts costs = 4;
|
||||
bytes salt = 5;
|
||||
int64 uid = 6;
|
||||
|
||||
WrappedKeyData wrapped_key = 7;
|
||||
}
|
||||
|
||||
// Encryption policy specifics, corresponds to the fscrypt_policy struct
|
||||
message EncryptionOptions {
|
||||
int64 padding = 1;
|
||||
|
||||
// Type of encryption; should match declarations of unix.FSCRYPT_MODE
|
||||
enum Mode {
|
||||
default = 0;
|
||||
AES_256_XTS = 1;
|
||||
AES_256_GCM = 2;
|
||||
AES_256_CBC = 3;
|
||||
AES_256_CTS = 4;
|
||||
AES_128_CBC = 5;
|
||||
AES_128_CTS = 6;
|
||||
Adiantum = 9;
|
||||
}
|
||||
|
||||
Mode contents = 2;
|
||||
Mode filenames = 3;
|
||||
|
||||
int64 policy_version = 4;
|
||||
}
|
||||
|
||||
message WrappedPolicyKey {
|
||||
string protector_descriptor = 1;
|
||||
WrappedKeyData wrapped_key = 2;
|
||||
}
|
||||
|
||||
// The associated data for each policy
|
||||
message PolicyData {
|
||||
string key_descriptor = 1;
|
||||
EncryptionOptions options = 2;
|
||||
repeated WrappedPolicyKey wrapped_policy_keys = 3;
|
||||
}
|
||||
|
||||
// Data stored in the config file
|
||||
message Config {
|
||||
SourceType source = 1;
|
||||
HashingCosts hash_costs = 2;
|
||||
EncryptionOptions options = 4;
|
||||
bool use_fs_keyring_for_v1_policies = 5;
|
||||
bool allow_cross_user_metadata = 6;
|
||||
|
||||
// reserve the removed field 'string compatibility = 3;'
|
||||
reserved 3;
|
||||
reserved "compatibility";
|
||||
}
|
348
vendor/github.com/google/fscrypt/metadata/policy.go
generated
vendored
Normal file
348
vendor/github.com/google/fscrypt/metadata/policy.go
generated
vendored
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
* policy.go - Functions for getting and setting policies on a specified
|
||||
* directory or file.
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrEncryptionNotSupported indicates that encryption is not supported
|
||||
// on the given filesystem, and there is no way to enable it.
|
||||
ErrEncryptionNotSupported = errors.New("encryption not supported")
|
||||
|
||||
// ErrEncryptionNotEnabled indicates that encryption is not supported on
|
||||
// the given filesystem, but there is a way to enable it.
|
||||
ErrEncryptionNotEnabled = errors.New("encryption not enabled")
|
||||
)
|
||||
|
||||
// ErrAlreadyEncrypted indicates that the path is already encrypted.
|
||||
type ErrAlreadyEncrypted struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func (err *ErrAlreadyEncrypted) Error() string {
|
||||
return fmt.Sprintf("file or directory %q is already encrypted", err.Path)
|
||||
}
|
||||
|
||||
// ErrBadEncryptionOptions indicates that unsupported encryption options were given.
|
||||
type ErrBadEncryptionOptions struct {
|
||||
Path string
|
||||
Options *EncryptionOptions
|
||||
}
|
||||
|
||||
func (err *ErrBadEncryptionOptions) Error() string {
|
||||
return fmt.Sprintf(`cannot encrypt %q because the kernel doesn't support the requested encryption options.
|
||||
|
||||
The options are %s`, err.Path, err.Options)
|
||||
}
|
||||
|
||||
// ErrDirectoryNotOwned indicates a directory can't be encrypted because it's
|
||||
// owned by another user.
|
||||
type ErrDirectoryNotOwned struct {
|
||||
Path string
|
||||
Owner uint32
|
||||
}
|
||||
|
||||
func (err *ErrDirectoryNotOwned) Error() string {
|
||||
owner := strconv.Itoa(int(err.Owner))
|
||||
if u, e := user.LookupId(owner); e == nil && u.Username != "" {
|
||||
owner = u.Username
|
||||
}
|
||||
return fmt.Sprintf(`cannot encrypt %q because it's owned by another user (%s).
|
||||
|
||||
Encryption can only be enabled on a directory you own, even if you have
|
||||
write access to the directory.`, err.Path, owner)
|
||||
}
|
||||
|
||||
// ErrNotEncrypted indicates that the path is not encrypted.
|
||||
type ErrNotEncrypted struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func (err *ErrNotEncrypted) Error() string {
|
||||
return fmt.Sprintf("file or directory %q is not encrypted", err.Path)
|
||||
}
|
||||
|
||||
func policyIoctl(file *os.File, request uintptr, arg unsafe.Pointer) error {
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, file.Fd(), request, uintptr(arg))
|
||||
if errno == 0 {
|
||||
return nil
|
||||
}
|
||||
return errno
|
||||
}
|
||||
|
||||
// Maps EncryptionOptions.Padding <-> FSCRYPT_POLICY_FLAGS
|
||||
var (
|
||||
paddingArray = []int64{4, 8, 16, 32}
|
||||
flagsArray = []int64{unix.FSCRYPT_POLICY_FLAGS_PAD_4, unix.FSCRYPT_POLICY_FLAGS_PAD_8,
|
||||
unix.FSCRYPT_POLICY_FLAGS_PAD_16, unix.FSCRYPT_POLICY_FLAGS_PAD_32}
|
||||
)
|
||||
|
||||
// flagsToPadding returns the amount of padding specified in the policy flags.
|
||||
func flagsToPadding(flags uint8) int64 {
|
||||
paddingFlag := int64(flags & unix.FS_POLICY_FLAGS_PAD_MASK)
|
||||
|
||||
// This lookup should always succeed
|
||||
padding, ok := util.Lookup(paddingFlag, flagsArray, paddingArray)
|
||||
if !ok {
|
||||
log.Panicf("padding flag of %x not found", paddingFlag)
|
||||
}
|
||||
return padding
|
||||
}
|
||||
|
||||
func buildV1PolicyData(policy *unix.FscryptPolicyV1) *PolicyData {
|
||||
return &PolicyData{
|
||||
KeyDescriptor: hex.EncodeToString(policy.Master_key_descriptor[:]),
|
||||
Options: &EncryptionOptions{
|
||||
Padding: flagsToPadding(policy.Flags),
|
||||
Contents: EncryptionOptions_Mode(policy.Contents_encryption_mode),
|
||||
Filenames: EncryptionOptions_Mode(policy.Filenames_encryption_mode),
|
||||
PolicyVersion: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildV2PolicyData(policy *unix.FscryptPolicyV2) *PolicyData {
|
||||
return &PolicyData{
|
||||
KeyDescriptor: hex.EncodeToString(policy.Master_key_identifier[:]),
|
||||
Options: &EncryptionOptions{
|
||||
Padding: flagsToPadding(policy.Flags),
|
||||
Contents: EncryptionOptions_Mode(policy.Contents_encryption_mode),
|
||||
Filenames: EncryptionOptions_Mode(policy.Filenames_encryption_mode),
|
||||
PolicyVersion: 2,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetPolicy returns the Policy data for the given directory or file (includes
|
||||
// the KeyDescriptor and the encryption options). Returns an error if the
|
||||
// path is not encrypted or the policy couldn't be retrieved.
|
||||
func GetPolicy(path string) (*PolicyData, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// First try the new version of the ioctl. This works for both v1 and v2 policies.
|
||||
var arg unix.FscryptGetPolicyExArg
|
||||
arg.Size = uint64(unsafe.Sizeof(arg.Policy))
|
||||
policyPtr := util.Ptr(arg.Policy[:])
|
||||
err = policyIoctl(file, unix.FS_IOC_GET_ENCRYPTION_POLICY_EX, unsafe.Pointer(&arg))
|
||||
if err == unix.ENOTTY {
|
||||
// Fall back to the old version of the ioctl. This works for v1 policies only.
|
||||
err = policyIoctl(file, unix.FS_IOC_GET_ENCRYPTION_POLICY, policyPtr)
|
||||
arg.Size = uint64(unsafe.Sizeof(unix.FscryptPolicyV1{}))
|
||||
}
|
||||
switch err {
|
||||
case nil:
|
||||
break
|
||||
case unix.ENOTTY:
|
||||
return nil, ErrEncryptionNotSupported
|
||||
case unix.EOPNOTSUPP:
|
||||
return nil, ErrEncryptionNotEnabled
|
||||
case unix.ENODATA, unix.ENOENT:
|
||||
// ENOENT was returned instead of ENODATA on some filesystems before v4.11.
|
||||
return nil, &ErrNotEncrypted{path}
|
||||
default:
|
||||
return nil, errors.Wrapf(err, "failed to get encryption policy of %q", path)
|
||||
}
|
||||
switch arg.Policy[0] { // arg.policy.version
|
||||
case unix.FSCRYPT_POLICY_V1:
|
||||
if arg.Size != uint64(unsafe.Sizeof(unix.FscryptPolicyV1{})) {
|
||||
// should never happen
|
||||
return nil, errors.New("unexpected size for v1 policy")
|
||||
}
|
||||
return buildV1PolicyData((*unix.FscryptPolicyV1)(policyPtr)), nil
|
||||
case unix.FSCRYPT_POLICY_V2:
|
||||
if arg.Size != uint64(unsafe.Sizeof(unix.FscryptPolicyV2{})) {
|
||||
// should never happen
|
||||
return nil, errors.New("unexpected size for v2 policy")
|
||||
}
|
||||
return buildV2PolicyData((*unix.FscryptPolicyV2)(policyPtr)), nil
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported encryption policy version [%d]",
|
||||
arg.Policy[0])
|
||||
}
|
||||
}
|
||||
|
||||
// For improved performance, use the DIRECT_KEY flag when using ciphers that
|
||||
// support it, e.g. Adiantum. It is safe because fscrypt won't reuse the key
|
||||
// for any other policy. (Multiple directories with same policy are okay.)
|
||||
func shouldUseDirectKeyFlag(options *EncryptionOptions) bool {
|
||||
// Contents and filenames encryption modes must be the same
|
||||
if options.Contents != options.Filenames {
|
||||
return false
|
||||
}
|
||||
// Currently only Adiantum supports DIRECT_KEY.
|
||||
return options.Contents == EncryptionOptions_Adiantum
|
||||
}
|
||||
|
||||
func buildPolicyFlags(options *EncryptionOptions) uint8 {
|
||||
// This lookup should always succeed (as policy is valid)
|
||||
flags, ok := util.Lookup(options.Padding, paddingArray, flagsArray)
|
||||
if !ok {
|
||||
log.Panicf("padding of %d was not found", options.Padding)
|
||||
}
|
||||
if shouldUseDirectKeyFlag(options) {
|
||||
flags |= unix.FSCRYPT_POLICY_FLAG_DIRECT_KEY
|
||||
}
|
||||
return uint8(flags)
|
||||
}
|
||||
|
||||
func setV1Policy(file *os.File, options *EncryptionOptions, descriptorBytes []byte) error {
|
||||
policy := unix.FscryptPolicyV1{
|
||||
Version: unix.FSCRYPT_POLICY_V1,
|
||||
Contents_encryption_mode: uint8(options.Contents),
|
||||
Filenames_encryption_mode: uint8(options.Filenames),
|
||||
Flags: uint8(buildPolicyFlags(options)),
|
||||
}
|
||||
|
||||
// The descriptor should always be the correct length (as policy is valid)
|
||||
if len(descriptorBytes) != unix.FSCRYPT_KEY_DESCRIPTOR_SIZE {
|
||||
log.Panic("wrong descriptor size for v1 policy")
|
||||
}
|
||||
copy(policy.Master_key_descriptor[:], descriptorBytes)
|
||||
|
||||
return policyIoctl(file, unix.FS_IOC_SET_ENCRYPTION_POLICY, unsafe.Pointer(&policy))
|
||||
}
|
||||
|
||||
func setV2Policy(file *os.File, options *EncryptionOptions, descriptorBytes []byte) error {
|
||||
policy := unix.FscryptPolicyV2{
|
||||
Version: unix.FSCRYPT_POLICY_V2,
|
||||
Contents_encryption_mode: uint8(options.Contents),
|
||||
Filenames_encryption_mode: uint8(options.Filenames),
|
||||
Flags: uint8(buildPolicyFlags(options)),
|
||||
}
|
||||
|
||||
// The descriptor should always be the correct length (as policy is valid)
|
||||
if len(descriptorBytes) != unix.FSCRYPT_KEY_IDENTIFIER_SIZE {
|
||||
log.Panic("wrong descriptor size for v2 policy")
|
||||
}
|
||||
copy(policy.Master_key_identifier[:], descriptorBytes)
|
||||
|
||||
return policyIoctl(file, unix.FS_IOC_SET_ENCRYPTION_POLICY, unsafe.Pointer(&policy))
|
||||
}
|
||||
|
||||
// SetPolicy sets up the specified directory to be encrypted with the specified
|
||||
// policy. Returns an error if we cannot set the policy for any reason (not a
|
||||
// directory, invalid options or KeyDescriptor, etc).
|
||||
func SetPolicy(path string, data *PolicyData) error {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err = data.CheckValidity(); err != nil {
|
||||
return errors.Wrap(err, "invalid policy")
|
||||
}
|
||||
|
||||
descriptorBytes, err := hex.DecodeString(data.KeyDescriptor)
|
||||
if err != nil {
|
||||
return errors.New("invalid key descriptor: " + data.KeyDescriptor)
|
||||
}
|
||||
|
||||
switch data.Options.PolicyVersion {
|
||||
case 1:
|
||||
err = setV1Policy(file, data.Options, descriptorBytes)
|
||||
case 2:
|
||||
err = setV2Policy(file, data.Options, descriptorBytes)
|
||||
default:
|
||||
err = errors.Errorf("policy version of %d is invalid", data.Options.PolicyVersion)
|
||||
}
|
||||
if err == unix.EINVAL {
|
||||
// Before kernel v4.11, many different errors all caused unix.EINVAL to be returned.
|
||||
// We try to disambiguate this error here. This disambiguation will not always give
|
||||
// the correct error due to a potential race condition on path.
|
||||
if info, statErr := os.Stat(path); statErr != nil || !info.IsDir() {
|
||||
// Checking if the path is not a directory
|
||||
err = unix.ENOTDIR
|
||||
} else if _, policyErr := GetPolicy(path); policyErr == nil {
|
||||
// Checking if a policy is already set on this directory
|
||||
err = unix.EEXIST
|
||||
}
|
||||
}
|
||||
switch err {
|
||||
case nil:
|
||||
return nil
|
||||
case unix.EACCES:
|
||||
var stat unix.Stat_t
|
||||
if statErr := unix.Stat(path, &stat); statErr == nil && stat.Uid != uint32(os.Geteuid()) {
|
||||
return &ErrDirectoryNotOwned{path, stat.Uid}
|
||||
}
|
||||
case unix.EEXIST:
|
||||
return &ErrAlreadyEncrypted{path}
|
||||
case unix.EINVAL:
|
||||
return &ErrBadEncryptionOptions{path, data.Options}
|
||||
case unix.ENOTTY:
|
||||
return ErrEncryptionNotSupported
|
||||
case unix.EOPNOTSUPP:
|
||||
return ErrEncryptionNotEnabled
|
||||
}
|
||||
return errors.Wrapf(err, "failed to set encryption policy on %q", path)
|
||||
}
|
||||
|
||||
// CheckSupport returns an error if the filesystem containing path does not
|
||||
// support filesystem encryption. This can be for many reasons including an
|
||||
// incompatible kernel or filesystem or not enabling the right feature flags.
|
||||
func CheckSupport(path string) error {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// On supported directories, giving a bad policy will return EINVAL
|
||||
badPolicy := unix.FscryptPolicyV1{
|
||||
Version: math.MaxUint8,
|
||||
Contents_encryption_mode: math.MaxUint8,
|
||||
Filenames_encryption_mode: math.MaxUint8,
|
||||
Flags: math.MaxUint8,
|
||||
}
|
||||
|
||||
err = policyIoctl(file, unix.FS_IOC_SET_ENCRYPTION_POLICY, unsafe.Pointer(&badPolicy))
|
||||
switch err {
|
||||
case nil:
|
||||
log.Panicf(`FS_IOC_SET_ENCRYPTION_POLICY succeeded when it should have failed.
|
||||
Please open an issue, filesystem %q may be corrupted.`, path)
|
||||
case unix.EINVAL, unix.EACCES:
|
||||
return nil
|
||||
case unix.ENOTTY:
|
||||
return ErrEncryptionNotSupported
|
||||
case unix.EOPNOTSUPP:
|
||||
return ErrEncryptionNotEnabled
|
||||
}
|
||||
return errors.Wrapf(err, "unexpected error checking for encryption support on filesystem %q", path)
|
||||
}
|
49
vendor/github.com/google/fscrypt/security/cache.go
generated
vendored
Normal file
49
vendor/github.com/google/fscrypt/security/cache.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* cache.go - Handles cache clearing and management.
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package security
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// DropFilesystemCache instructs the kernel to free the reclaimable inodes and
|
||||
// dentries. This has the effect of making encrypted directories whose keys are
|
||||
// not present no longer accessible. Requires root privileges.
|
||||
func DropFilesystemCache() error {
|
||||
// Dirty reclaimable inodes must be synced so that they will be freed.
|
||||
log.Print("syncing changes to filesystem")
|
||||
unix.Sync()
|
||||
|
||||
// See: https://www.kernel.org/doc/Documentation/sysctl/vm.txt
|
||||
log.Print("freeing reclaimable inodes and dentries")
|
||||
file, err := os.OpenFile("/proc/sys/vm/drop_caches", os.O_WRONLY|os.O_SYNC, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
// "2" just frees the reclaimable inodes and dentries. The associated
|
||||
// pages to these inodes will be freed. We do not need to free the
|
||||
// entire pagecache (as this will severely impact performance).
|
||||
_, err = file.WriteString("2")
|
||||
return err
|
||||
}
|
156
vendor/github.com/google/fscrypt/security/privileges.go
generated
vendored
Normal file
156
vendor/github.com/google/fscrypt/security/privileges.go
generated
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* privileges.go - Functions for managing users and privileges.
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
// Package security manages:
|
||||
// - Cache clearing (cache.go)
|
||||
// - Privilege manipulation (privileges.go)
|
||||
package security
|
||||
|
||||
// Use the libc versions of setreuid, setregid, and setgroups instead of the
|
||||
// "sys/unix" versions. The "sys/unix" versions use the raw syscalls which
|
||||
// operate on the calling thread only, whereas the libc versions operate on the
|
||||
// whole process. And we need to operate on the whole process, firstly for
|
||||
// pam_fscrypt to prevent the privileges of Go worker threads from diverging
|
||||
// from the PAM stack's "main" thread, violating libc's assumption and causing
|
||||
// an abort() later in the PAM stack; and secondly because Go code may migrate
|
||||
// between OS-level threads while it's running.
|
||||
//
|
||||
// See also: https://github.com/golang/go/issues/1435
|
||||
|
||||
/*
|
||||
#define _GNU_SOURCE // for getresuid and setresuid
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h> // getting and setting uids and gids
|
||||
#include <grp.h> // setgroups
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os/user"
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/google/fscrypt/util"
|
||||
)
|
||||
|
||||
// Privileges encapsulate the effective uid/gid and groups of a process.
|
||||
type Privileges struct {
|
||||
euid C.uid_t
|
||||
egid C.gid_t
|
||||
groups []C.gid_t
|
||||
}
|
||||
|
||||
// ProcessPrivileges returns the process's current effective privileges.
|
||||
func ProcessPrivileges() (*Privileges, error) {
|
||||
ruid := C.getuid()
|
||||
euid := C.geteuid()
|
||||
rgid := C.getgid()
|
||||
egid := C.getegid()
|
||||
|
||||
var groups []C.gid_t
|
||||
n, err := C.getgroups(0, nil)
|
||||
if n < 0 {
|
||||
return nil, err
|
||||
}
|
||||
// If n == 0, the user isn't in any groups, so groups == nil is fine.
|
||||
if n > 0 {
|
||||
groups = make([]C.gid_t, n)
|
||||
n, err = C.getgroups(n, &groups[0])
|
||||
if n < 0 {
|
||||
return nil, err
|
||||
}
|
||||
groups = groups[:n]
|
||||
}
|
||||
log.Printf("Current privs (real, effective): uid=(%d,%d) gid=(%d,%d) groups=%v",
|
||||
ruid, euid, rgid, egid, groups)
|
||||
return &Privileges{euid, egid, groups}, nil
|
||||
}
|
||||
|
||||
// UserPrivileges returns the default privileges for the specified user.
|
||||
func UserPrivileges(user *user.User) (*Privileges, error) {
|
||||
privs := &Privileges{
|
||||
euid: C.uid_t(util.AtoiOrPanic(user.Uid)),
|
||||
egid: C.gid_t(util.AtoiOrPanic(user.Gid)),
|
||||
}
|
||||
userGroups, err := user.GroupIds()
|
||||
if err != nil {
|
||||
return nil, util.SystemError(err.Error())
|
||||
}
|
||||
privs.groups = make([]C.gid_t, len(userGroups))
|
||||
for i, group := range userGroups {
|
||||
privs.groups[i] = C.gid_t(util.AtoiOrPanic(group))
|
||||
}
|
||||
return privs, nil
|
||||
}
|
||||
|
||||
// SetProcessPrivileges sets the privileges of the current process to have those
|
||||
// specified by privs. The original privileges can be obtained by first saving
|
||||
// the output of ProcessPrivileges, calling SetProcessPrivileges with the
|
||||
// desired privs, then calling SetProcessPrivileges with the saved privs.
|
||||
func SetProcessPrivileges(privs *Privileges) error {
|
||||
log.Printf("Setting euid=%d egid=%d groups=%v", privs.euid, privs.egid, privs.groups)
|
||||
|
||||
// If setting privs as root, we need to set the euid to 0 first, so that
|
||||
// we will have the necessary permissions to make the other changes to
|
||||
// the groups/egid/euid, regardless of our original euid.
|
||||
C.seteuid(0)
|
||||
|
||||
// Separately handle the case where the user is in no groups.
|
||||
numGroups := C.size_t(len(privs.groups))
|
||||
groupsPtr := (*C.gid_t)(nil)
|
||||
if numGroups > 0 {
|
||||
groupsPtr = &privs.groups[0]
|
||||
}
|
||||
|
||||
if res, err := C.setgroups(numGroups, groupsPtr); res < 0 {
|
||||
return errors.Wrapf(err.(syscall.Errno), "setting groups")
|
||||
}
|
||||
if res, err := C.setegid(privs.egid); res < 0 {
|
||||
return errors.Wrapf(err.(syscall.Errno), "setting egid")
|
||||
}
|
||||
if res, err := C.seteuid(privs.euid); res < 0 {
|
||||
return errors.Wrapf(err.(syscall.Errno), "setting euid")
|
||||
}
|
||||
ProcessPrivileges()
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUids sets the process's real, effective, and saved UIDs.
|
||||
func SetUids(ruid, euid, suid int) error {
|
||||
log.Printf("Setting ruid=%d euid=%d suid=%d", ruid, euid, suid)
|
||||
// We elevate all the privs before setting them. This prevents issues
|
||||
// with (ruid=1000,euid=1000,suid=0), where just a single call to
|
||||
// setresuid might fail with permission denied.
|
||||
if res, err := C.setresuid(0, 0, 0); res < 0 {
|
||||
return errors.Wrapf(err.(syscall.Errno), "setting uids")
|
||||
}
|
||||
if res, err := C.setresuid(C.uid_t(ruid), C.uid_t(euid), C.uid_t(suid)); res < 0 {
|
||||
return errors.Wrapf(err.(syscall.Errno), "setting uids")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUids gets the process's real, effective, and saved UIDs.
|
||||
func GetUids() (int, int, int) {
|
||||
var ruid, euid, suid C.uid_t
|
||||
C.getresuid(&ruid, &euid, &suid)
|
||||
return int(ruid), int(euid), int(suid)
|
||||
}
|
135
vendor/github.com/google/fscrypt/util/errors.go
generated
vendored
Normal file
135
vendor/github.com/google/fscrypt/util/errors.go
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* errors.go - Custom errors and error functions used by fscrypt
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ErrReader wraps an io.Reader, passing along calls to Read() until a read
|
||||
// fails. Then, the error is stored, and all subsequent calls to Read() do
|
||||
// nothing. This allows you to write code which has many subsequent reads and
|
||||
// do all of the error checking at the end. For example:
|
||||
//
|
||||
// r := NewErrReader(reader)
|
||||
// r.Read(foo)
|
||||
// r.Read(bar)
|
||||
// r.Read(baz)
|
||||
// if r.Err() != nil {
|
||||
// // Handle error
|
||||
// }
|
||||
//
|
||||
// Taken from https://blog.golang.org/errors-are-values by Rob Pike.
|
||||
type ErrReader struct {
|
||||
r io.Reader
|
||||
err error
|
||||
}
|
||||
|
||||
// NewErrReader creates an ErrReader which wraps the provided reader.
|
||||
func NewErrReader(reader io.Reader) *ErrReader {
|
||||
return &ErrReader{r: reader, err: nil}
|
||||
}
|
||||
|
||||
// Read runs ReadFull on the wrapped reader if no errors have occurred.
|
||||
// Otherwise, the previous error is just returned and no reads are attempted.
|
||||
func (e *ErrReader) Read(p []byte) (n int, err error) {
|
||||
if e.err == nil {
|
||||
n, e.err = io.ReadFull(e.r, p)
|
||||
}
|
||||
return n, e.err
|
||||
}
|
||||
|
||||
// Err returns the first encountered err (or nil if no errors occurred).
|
||||
func (e *ErrReader) Err() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// ErrWriter works exactly like ErrReader, except with io.Writer.
|
||||
type ErrWriter struct {
|
||||
w io.Writer
|
||||
err error
|
||||
}
|
||||
|
||||
// NewErrWriter creates an ErrWriter which wraps the provided writer.
|
||||
func NewErrWriter(writer io.Writer) *ErrWriter {
|
||||
return &ErrWriter{w: writer, err: nil}
|
||||
}
|
||||
|
||||
// Write runs the wrapped writer's Write if no errors have occurred. Otherwise,
|
||||
// the previous error is just returned and no writes are attempted.
|
||||
func (e *ErrWriter) Write(p []byte) (n int, err error) {
|
||||
if e.err == nil {
|
||||
n, e.err = e.w.Write(p)
|
||||
}
|
||||
return n, e.err
|
||||
}
|
||||
|
||||
// Err returns the first encountered err (or nil if no errors occurred).
|
||||
func (e *ErrWriter) Err() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// CheckValidLength returns an invalid length error if expected != actual
|
||||
func CheckValidLength(expected, actual int) error {
|
||||
if expected == actual {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("expected length of %d, got %d", expected, actual)
|
||||
}
|
||||
|
||||
// SystemError is an error that should indicate something has gone wrong in the
|
||||
// underlying system (syscall failure, bad ioctl, etc...).
|
||||
type SystemError string
|
||||
|
||||
func (s SystemError) Error() string {
|
||||
return "system error: " + string(s)
|
||||
}
|
||||
|
||||
// NeverError panics if a non-nil error is passed in. It should be used to check
|
||||
// for logic errors, not to handle recoverable errors.
|
||||
func NeverError(err error) {
|
||||
if err != nil {
|
||||
log.Panicf("NeverError() check failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// testEnvVarName is the name of an environment variable that should be
|
||||
// set to an empty mountpoint. This is only used for integration tests.
|
||||
// If not set, integration tests are skipped.
|
||||
testEnvVarName = "TEST_FILESYSTEM_ROOT"
|
||||
// ErrSkipIntegration indicates integration tests shouldn't be run.
|
||||
ErrSkipIntegration = errors.New("skipping integration test")
|
||||
)
|
||||
|
||||
// TestRoot returns a the root of a filesystem specified by testEnvVarName. This
|
||||
// function is only used for integration tests.
|
||||
func TestRoot() (string, error) {
|
||||
path := os.Getenv(testEnvVarName)
|
||||
if path == "" {
|
||||
return "", ErrSkipIntegration
|
||||
}
|
||||
return path, nil
|
||||
}
|
163
vendor/github.com/google/fscrypt/util/util.go
generated
vendored
Normal file
163
vendor/github.com/google/fscrypt/util/util.go
generated
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* util.go - Various helpers used throughout fscrypt
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* Author: Joe Richey (joerichey@google.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
// Package util contains useful components for simplifying Go code.
|
||||
//
|
||||
// The package contains common error types (errors.go) and functions for
|
||||
// converting arrays to pointers.
|
||||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Ptr converts a Go byte array to a pointer to the start of the array.
|
||||
func Ptr(slice []byte) unsafe.Pointer {
|
||||
if len(slice) == 0 {
|
||||
return nil
|
||||
}
|
||||
return unsafe.Pointer(&slice[0])
|
||||
}
|
||||
|
||||
// ByteSlice takes a pointer to some data and views it as a slice of bytes.
|
||||
// Note, indexing into this slice is unsafe.
|
||||
func ByteSlice(ptr unsafe.Pointer) []byte {
|
||||
// Slice must fit in the smallest address space go supports.
|
||||
return (*[1 << 30]byte)(ptr)[:]
|
||||
}
|
||||
|
||||
// PointerSlice takes a pointer to an array of pointers and views it as a slice
|
||||
// of pointers. Note, indexing into this slice is unsafe.
|
||||
func PointerSlice(ptr unsafe.Pointer) []unsafe.Pointer {
|
||||
// Slice must fit in the smallest address space go supports.
|
||||
return (*[1 << 28]unsafe.Pointer)(ptr)[:]
|
||||
}
|
||||
|
||||
// Index returns the first index i such that inVal == inArray[i].
|
||||
// ok is true if we find a match, false otherwise.
|
||||
func Index(inVal int64, inArray []int64) (index int, ok bool) {
|
||||
for index, val := range inArray {
|
||||
if val == inVal {
|
||||
return index, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Lookup finds inVal in inArray and returns the corresponding element in
|
||||
// outArray. Specifically, if inVal == inArray[i], outVal == outArray[i].
|
||||
// ok is true if we find a match, false otherwise.
|
||||
func Lookup(inVal int64, inArray, outArray []int64) (outVal int64, ok bool) {
|
||||
index, ok := Index(inVal, inArray)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
return outArray[index], true
|
||||
}
|
||||
|
||||
// MinInt returns the lesser of a and b.
|
||||
func MinInt(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// MaxInt returns the greater of a and b.
|
||||
func MaxInt(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// MinInt64 returns the lesser of a and b.
|
||||
func MinInt64(a, b int64) int64 {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// ReadLine returns a line of input from standard input. An empty string is
|
||||
// returned if the user didn't insert anything or on error.
|
||||
func ReadLine() (string, error) {
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
scanner.Scan()
|
||||
return scanner.Text(), scanner.Err()
|
||||
}
|
||||
|
||||
// AtoiOrPanic converts a string to an int or it panics. Should only be used in
|
||||
// situations where the input MUST be a decimal number.
|
||||
func AtoiOrPanic(input string) int {
|
||||
i, err := strconv.Atoi(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// UserFromUID returns the User corresponding to the given user id.
|
||||
func UserFromUID(uid int64) (*user.User, error) {
|
||||
return user.LookupId(strconv.FormatInt(uid, 10))
|
||||
}
|
||||
|
||||
// EffectiveUser returns the user entry corresponding to the effective user.
|
||||
func EffectiveUser() (*user.User, error) {
|
||||
return UserFromUID(int64(os.Geteuid()))
|
||||
}
|
||||
|
||||
// IsUserRoot checks if the effective user is root.
|
||||
func IsUserRoot() bool {
|
||||
return os.Geteuid() == 0
|
||||
}
|
||||
|
||||
// Chown changes the owner of a File to a User.
|
||||
func Chown(file *os.File, user *user.User) error {
|
||||
uid := AtoiOrPanic(user.Uid)
|
||||
gid := AtoiOrPanic(user.Gid)
|
||||
return file.Chown(uid, gid)
|
||||
}
|
||||
|
||||
// IsKernelVersionAtLeast returns true if the Linux kernel version is at least
|
||||
// major.minor. If something goes wrong it assumes false.
|
||||
func IsKernelVersionAtLeast(major, minor int) bool {
|
||||
var uname unix.Utsname
|
||||
if err := unix.Uname(&uname); err != nil {
|
||||
log.Printf("Uname failed [%v], assuming old kernel", err)
|
||||
return false
|
||||
}
|
||||
release := string(uname.Release[:])
|
||||
log.Printf("Kernel version is %s", release)
|
||||
var actualMajor, actualMinor int
|
||||
if n, _ := fmt.Sscanf(release, "%d.%d", &actualMajor, &actualMinor); n != 2 {
|
||||
log.Printf("Unrecognized uname format %q, assuming old kernel", release)
|
||||
return false
|
||||
}
|
||||
return actualMajor > major ||
|
||||
(actualMajor == major && actualMinor >= minor)
|
||||
}
|
26
vendor/github.com/pkg/xattr/.gitignore
generated
vendored
Normal file
26
vendor/github.com/pkg/xattr/.gitignore
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
.DS_Store
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
|
||||
*.swp
|
25
vendor/github.com/pkg/xattr/LICENSE
generated
vendored
Normal file
25
vendor/github.com/pkg/xattr/LICENSE
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
Copyright (c) 2012 Dave Cheney. All rights reserved.
|
||||
Copyright (c) 2014 Kuba Podgórski. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
45
vendor/github.com/pkg/xattr/README.md
generated
vendored
Normal file
45
vendor/github.com/pkg/xattr/README.md
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
[![GoDoc](https://godoc.org/github.com/pkg/xattr?status.svg)](http://godoc.org/github.com/pkg/xattr)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/pkg/xattr)](https://goreportcard.com/report/github.com/pkg/xattr)
|
||||
[![Build Status](https://github.com/pkg/xattr/workflows/build/badge.svg)](https://github.com/pkg/xattr/actions?query=workflow%3Abuild)
|
||||
[![Codecov](https://codecov.io/gh/pkg/xattr/branch/master/graph/badge.svg)](https://codecov.io/gh/pkg/xattr)
|
||||
|
||||
xattr
|
||||
=====
|
||||
Extended attribute support for Go (linux + darwin + freebsd + netbsd + solaris).
|
||||
|
||||
"Extended attributes are name:value pairs associated permanently with files and directories, similar to the environment strings associated with a process. An attribute may be defined or undefined. If it is defined, its value may be empty or non-empty." [See more...](https://en.wikipedia.org/wiki/Extended_file_attributes)
|
||||
|
||||
`SetWithFlags` allows to additionally pass system flags to be forwarded to the underlying calls. FreeBSD and NetBSD do not support this and the parameter will be ignored.
|
||||
|
||||
The `L` variants of all functions (`LGet/LSet/...`) are identical to `Get/Set/...` except that they
|
||||
do not reference a symlink that appears at the end of a path. See
|
||||
[GoDoc](http://godoc.org/github.com/pkg/xattr) for details.
|
||||
|
||||
### Example
|
||||
```go
|
||||
const path = "/tmp/myfile"
|
||||
const prefix = "user."
|
||||
|
||||
if err := xattr.Set(path, prefix+"test", []byte("test-attr-value")); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var list []string
|
||||
if list, err = xattr.List(path); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var data []byte
|
||||
if data, err = xattr.Get(path, prefix+"test"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err = xattr.Remove(path, prefix+"test"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// One can also specify the flags parameter to be passed to the OS.
|
||||
if err := xattr.SetWithFlags(path, prefix+"test", []byte("test-attr-value"), xattr.XATTR_CREATE); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
255
vendor/github.com/pkg/xattr/xattr.go
generated
vendored
Normal file
255
vendor/github.com/pkg/xattr/xattr.go
generated
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
Package xattr provides support for extended attributes on linux, darwin and freebsd.
|
||||
Extended attributes are name:value pairs associated permanently with files and directories,
|
||||
similar to the environment strings associated with a process.
|
||||
An attribute may be defined or undefined. If it is defined, its value may be empty or non-empty.
|
||||
More details you can find here: https://en.wikipedia.org/wiki/Extended_file_attributes .
|
||||
|
||||
All functions are provided in triples: Get/LGet/FGet, Set/LSet/FSet etc. The "L"
|
||||
variant will not follow a symlink at the end of the path, and "F" variant accepts
|
||||
a file descriptor instead of a path.
|
||||
|
||||
Example for "L" variant, assuming path is "/symlink1/symlink2", where both components are
|
||||
symlinks:
|
||||
Get will follow "symlink1" and "symlink2" and operate on the target of
|
||||
"symlink2". LGet will follow "symlink1" but operate directly on "symlink2".
|
||||
*/
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Error records an error and the operation, file path and attribute that caused it.
|
||||
type Error struct {
|
||||
Op string
|
||||
Path string
|
||||
Name string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *Error) Error() (errstr string) {
|
||||
if e.Op != "" {
|
||||
errstr += e.Op
|
||||
}
|
||||
if e.Path != "" {
|
||||
if errstr != "" {
|
||||
errstr += " "
|
||||
}
|
||||
errstr += e.Path
|
||||
}
|
||||
if e.Name != "" {
|
||||
if errstr != "" {
|
||||
errstr += " "
|
||||
}
|
||||
errstr += e.Name
|
||||
}
|
||||
if e.Err != nil {
|
||||
if errstr != "" {
|
||||
errstr += ": "
|
||||
}
|
||||
errstr += e.Err.Error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get retrieves extended attribute data associated with path. It will follow
|
||||
// all symlinks along the path.
|
||||
func Get(path, name string) ([]byte, error) {
|
||||
return get(path, name, func(name string, data []byte) (int, error) {
|
||||
return getxattr(path, name, data)
|
||||
})
|
||||
}
|
||||
|
||||
// LGet is like Get but does not follow a symlink at the end of the path.
|
||||
func LGet(path, name string) ([]byte, error) {
|
||||
return get(path, name, func(name string, data []byte) (int, error) {
|
||||
return lgetxattr(path, name, data)
|
||||
})
|
||||
}
|
||||
|
||||
// FGet is like Get but accepts a os.File instead of a file path.
|
||||
func FGet(f *os.File, name string) ([]byte, error) {
|
||||
return get(f.Name(), name, func(name string, data []byte) (int, error) {
|
||||
return fgetxattr(f, name, data)
|
||||
})
|
||||
}
|
||||
|
||||
type getxattrFunc func(name string, data []byte) (int, error)
|
||||
|
||||
// get contains the buffer allocation logic used by both Get and LGet.
|
||||
func get(path string, name string, getxattrFunc getxattrFunc) ([]byte, error) {
|
||||
const (
|
||||
// Start with a 1 KB buffer for the xattr value
|
||||
initialBufSize = 1024
|
||||
|
||||
// The theoretical maximum xattr value size on MacOS is 64 MB. On Linux it's
|
||||
// much smaller at 64 KB. Unless the kernel is evil or buggy, we should never
|
||||
// hit the limit.
|
||||
maxBufSize = 64 * 1024 * 1024
|
||||
|
||||
// Function name as reported in error messages
|
||||
myname = "xattr.get"
|
||||
)
|
||||
|
||||
size := initialBufSize
|
||||
for {
|
||||
data := make([]byte, size)
|
||||
read, err := getxattrFunc(name, data)
|
||||
|
||||
// If the buffer was too small to fit the value, Linux and MacOS react
|
||||
// differently:
|
||||
// Linux: returns an ERANGE error and "-1" bytes.
|
||||
// MacOS: truncates the value and returns "size" bytes. If the value
|
||||
// happens to be exactly as big as the buffer, we cannot know if it was
|
||||
// truncated, and we retry with a bigger buffer. Contrary to documentation,
|
||||
// MacOS never seems to return ERANGE!
|
||||
// To keep the code simple, we always check both conditions, and sometimes
|
||||
// double the buffer size without it being strictly necessary.
|
||||
if err == syscall.ERANGE || read == size {
|
||||
// The buffer was too small. Try again.
|
||||
size <<= 1
|
||||
if size >= maxBufSize {
|
||||
return nil, &Error{myname, path, name, syscall.EOVERFLOW}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, &Error{myname, path, name, err}
|
||||
}
|
||||
return data[:read], nil
|
||||
}
|
||||
}
|
||||
|
||||
// Set associates name and data together as an attribute of path.
|
||||
func Set(path, name string, data []byte) error {
|
||||
if err := setxattr(path, name, data, 0); err != nil {
|
||||
return &Error{"xattr.Set", path, name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LSet is like Set but does not follow a symlink at
|
||||
// the end of the path.
|
||||
func LSet(path, name string, data []byte) error {
|
||||
if err := lsetxattr(path, name, data, 0); err != nil {
|
||||
return &Error{"xattr.LSet", path, name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FSet is like Set but accepts a os.File instead of a file path.
|
||||
func FSet(f *os.File, name string, data []byte) error {
|
||||
if err := fsetxattr(f, name, data, 0); err != nil {
|
||||
return &Error{"xattr.FSet", f.Name(), name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWithFlags associates name and data together as an attribute of path.
|
||||
// Forwards the flags parameter to the syscall layer.
|
||||
func SetWithFlags(path, name string, data []byte, flags int) error {
|
||||
if err := setxattr(path, name, data, flags); err != nil {
|
||||
return &Error{"xattr.SetWithFlags", path, name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LSetWithFlags is like SetWithFlags but does not follow a symlink at
|
||||
// the end of the path.
|
||||
func LSetWithFlags(path, name string, data []byte, flags int) error {
|
||||
if err := lsetxattr(path, name, data, flags); err != nil {
|
||||
return &Error{"xattr.LSetWithFlags", path, name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FSetWithFlags is like SetWithFlags but accepts a os.File instead of a file path.
|
||||
func FSetWithFlags(f *os.File, name string, data []byte, flags int) error {
|
||||
if err := fsetxattr(f, name, data, flags); err != nil {
|
||||
return &Error{"xattr.FSetWithFlags", f.Name(), name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes the attribute associated with the given path.
|
||||
func Remove(path, name string) error {
|
||||
if err := removexattr(path, name); err != nil {
|
||||
return &Error{"xattr.Remove", path, name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LRemove is like Remove but does not follow a symlink at the end of the
|
||||
// path.
|
||||
func LRemove(path, name string) error {
|
||||
if err := lremovexattr(path, name); err != nil {
|
||||
return &Error{"xattr.LRemove", path, name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FRemove is like Remove but accepts a os.File instead of a file path.
|
||||
func FRemove(f *os.File, name string) error {
|
||||
if err := fremovexattr(f, name); err != nil {
|
||||
return &Error{"xattr.FRemove", f.Name(), name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List retrieves a list of names of extended attributes associated
|
||||
// with the given path in the file system.
|
||||
func List(path string) ([]string, error) {
|
||||
return list(path, func(data []byte) (int, error) {
|
||||
return listxattr(path, data)
|
||||
})
|
||||
}
|
||||
|
||||
// LList is like List but does not follow a symlink at the end of the
|
||||
// path.
|
||||
func LList(path string) ([]string, error) {
|
||||
return list(path, func(data []byte) (int, error) {
|
||||
return llistxattr(path, data)
|
||||
})
|
||||
}
|
||||
|
||||
// FList is like List but accepts a os.File instead of a file path.
|
||||
func FList(f *os.File) ([]string, error) {
|
||||
return list(f.Name(), func(data []byte) (int, error) {
|
||||
return flistxattr(f, data)
|
||||
})
|
||||
}
|
||||
|
||||
type listxattrFunc func(data []byte) (int, error)
|
||||
|
||||
// list contains the buffer allocation logic used by both List and LList.
|
||||
func list(path string, listxattrFunc listxattrFunc) ([]string, error) {
|
||||
myname := "xattr.list"
|
||||
// find size.
|
||||
size, err := listxattrFunc(nil)
|
||||
if err != nil {
|
||||
return nil, &Error{myname, path, "", err}
|
||||
}
|
||||
if size > 0 {
|
||||
// `size + 1` because of ERANGE error when reading
|
||||
// from a SMB1 mount point (https://github.com/pkg/xattr/issues/16).
|
||||
buf := make([]byte, size+1)
|
||||
// Read into buffer of that size.
|
||||
read, err := listxattrFunc(buf)
|
||||
if err != nil {
|
||||
return nil, &Error{myname, path, "", err}
|
||||
}
|
||||
return stringsFromByteSlice(buf[:read]), nil
|
||||
}
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
// bytePtrFromSlice returns a pointer to array of bytes and a size.
|
||||
func bytePtrFromSlice(data []byte) (ptr *byte, size int) {
|
||||
size = len(data)
|
||||
if size > 0 {
|
||||
ptr = &data[0]
|
||||
}
|
||||
return
|
||||
}
|
201
vendor/github.com/pkg/xattr/xattr_bsd.go
generated
vendored
Normal file
201
vendor/github.com/pkg/xattr/xattr_bsd.go
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
//go:build freebsd || netbsd
|
||||
// +build freebsd netbsd
|
||||
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// XATTR_SUPPORTED will be true if the current platform is supported
|
||||
XATTR_SUPPORTED = true
|
||||
|
||||
EXTATTR_NAMESPACE_USER = 1
|
||||
|
||||
// ENOATTR is not exported by the syscall package on Linux, because it is
|
||||
// an alias for ENODATA. We export it here so it is available on all
|
||||
// our supported platforms.
|
||||
ENOATTR = syscall.ENOATTR
|
||||
)
|
||||
|
||||
func getxattr(path string, name string, data []byte) (int, error) {
|
||||
return sysGet(syscall.SYS_EXTATTR_GET_FILE, path, name, data)
|
||||
}
|
||||
|
||||
func lgetxattr(path string, name string, data []byte) (int, error) {
|
||||
return sysGet(syscall.SYS_EXTATTR_GET_LINK, path, name, data)
|
||||
}
|
||||
|
||||
func fgetxattr(f *os.File, name string, data []byte) (int, error) {
|
||||
return getxattr(f.Name(), name, data)
|
||||
}
|
||||
|
||||
// sysGet is called by getxattr and lgetxattr with the appropriate syscall
|
||||
// number. This works because syscalls have the same signature and return
|
||||
// values.
|
||||
func sysGet(syscallNum uintptr, path string, name string, data []byte) (int, error) {
|
||||
ptr, nbytes := bytePtrFromSlice(data)
|
||||
/*
|
||||
ssize_t extattr_get_file(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
const char *attrname,
|
||||
void *data,
|
||||
size_t nbytes);
|
||||
|
||||
ssize_t extattr_get_link(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
const char *attrname,
|
||||
void *data,
|
||||
size_t nbytes);
|
||||
*/
|
||||
r0, _, err := syscall.Syscall6(syscallNum, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
|
||||
EXTATTR_NAMESPACE_USER, uintptr(unsafe.Pointer(syscall.StringBytePtr(name))),
|
||||
uintptr(unsafe.Pointer(ptr)), uintptr(nbytes), 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return int(r0), err
|
||||
}
|
||||
return int(r0), nil
|
||||
}
|
||||
|
||||
func setxattr(path string, name string, data []byte, flags int) error {
|
||||
return sysSet(syscall.SYS_EXTATTR_SET_FILE, path, name, data)
|
||||
}
|
||||
|
||||
func lsetxattr(path string, name string, data []byte, flags int) error {
|
||||
return sysSet(syscall.SYS_EXTATTR_SET_LINK, path, name, data)
|
||||
}
|
||||
|
||||
func fsetxattr(f *os.File, name string, data []byte, flags int) error {
|
||||
return setxattr(f.Name(), name, data, flags)
|
||||
}
|
||||
|
||||
// sysSet is called by setxattr and lsetxattr with the appropriate syscall
|
||||
// number. This works because syscalls have the same signature and return
|
||||
// values.
|
||||
func sysSet(syscallNum uintptr, path string, name string, data []byte) error {
|
||||
ptr, nbytes := bytePtrFromSlice(data)
|
||||
/*
|
||||
ssize_t extattr_set_file(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
const char *attrname,
|
||||
const void *data,
|
||||
size_t nbytes
|
||||
);
|
||||
|
||||
ssize_t extattr_set_link(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
const char *attrname,
|
||||
const void *data,
|
||||
size_t nbytes
|
||||
);
|
||||
*/
|
||||
r0, _, err := syscall.Syscall6(syscallNum, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
|
||||
EXTATTR_NAMESPACE_USER, uintptr(unsafe.Pointer(syscall.StringBytePtr(name))),
|
||||
uintptr(unsafe.Pointer(ptr)), uintptr(nbytes), 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
if int(r0) != nbytes {
|
||||
return syscall.E2BIG
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func removexattr(path string, name string) error {
|
||||
return sysRemove(syscall.SYS_EXTATTR_DELETE_FILE, path, name)
|
||||
}
|
||||
|
||||
func lremovexattr(path string, name string) error {
|
||||
return sysRemove(syscall.SYS_EXTATTR_DELETE_LINK, path, name)
|
||||
}
|
||||
|
||||
func fremovexattr(f *os.File, name string) error {
|
||||
return removexattr(f.Name(), name)
|
||||
}
|
||||
|
||||
// sysSet is called by removexattr and lremovexattr with the appropriate syscall
|
||||
// number. This works because syscalls have the same signature and return
|
||||
// values.
|
||||
func sysRemove(syscallNum uintptr, path string, name string) error {
|
||||
/*
|
||||
int extattr_delete_file(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
const char *attrname
|
||||
);
|
||||
|
||||
int extattr_delete_link(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
const char *attrname
|
||||
);
|
||||
*/
|
||||
_, _, err := syscall.Syscall(syscallNum, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
|
||||
EXTATTR_NAMESPACE_USER, uintptr(unsafe.Pointer(syscall.StringBytePtr(name))),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func listxattr(path string, data []byte) (int, error) {
|
||||
return sysList(syscall.SYS_EXTATTR_LIST_FILE, path, data)
|
||||
}
|
||||
|
||||
func llistxattr(path string, data []byte) (int, error) {
|
||||
return sysList(syscall.SYS_EXTATTR_LIST_LINK, path, data)
|
||||
}
|
||||
|
||||
func flistxattr(f *os.File, data []byte) (int, error) {
|
||||
return listxattr(f.Name(), data)
|
||||
}
|
||||
|
||||
// sysSet is called by listxattr and llistxattr with the appropriate syscall
|
||||
// number. This works because syscalls have the same signature and return
|
||||
// values.
|
||||
func sysList(syscallNum uintptr, path string, data []byte) (int, error) {
|
||||
ptr, nbytes := bytePtrFromSlice(data)
|
||||
/*
|
||||
ssize_t extattr_list_file(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
void *data,
|
||||
size_t nbytes
|
||||
);
|
||||
|
||||
ssize_t extattr_list_link(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
void *data,
|
||||
size_t nbytes
|
||||
);
|
||||
*/
|
||||
r0, _, err := syscall.Syscall6(syscallNum, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
|
||||
EXTATTR_NAMESPACE_USER, uintptr(unsafe.Pointer(ptr)), uintptr(nbytes), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return int(r0), err
|
||||
}
|
||||
return int(r0), nil
|
||||
}
|
||||
|
||||
// stringsFromByteSlice converts a sequence of attributes to a []string.
|
||||
// On FreeBSD, each entry consists of a single byte containing the length
|
||||
// of the attribute name, followed by the attribute name.
|
||||
// The name is _not_ terminated by NULL.
|
||||
func stringsFromByteSlice(buf []byte) (result []string) {
|
||||
index := 0
|
||||
for index < len(buf) {
|
||||
next := index + 1 + int(buf[index])
|
||||
result = append(result, string(buf[index+1:next]))
|
||||
index = next
|
||||
}
|
||||
return
|
||||
}
|
90
vendor/github.com/pkg/xattr/xattr_darwin.go
generated
vendored
Normal file
90
vendor/github.com/pkg/xattr/xattr_darwin.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// See https://opensource.apple.com/source/xnu/xnu-1504.15.3/bsd/sys/xattr.h.auto.html
|
||||
const (
|
||||
// XATTR_SUPPORTED will be true if the current platform is supported
|
||||
XATTR_SUPPORTED = true
|
||||
|
||||
XATTR_NOFOLLOW = 0x0001
|
||||
XATTR_CREATE = 0x0002
|
||||
XATTR_REPLACE = 0x0004
|
||||
XATTR_NOSECURITY = 0x0008
|
||||
XATTR_NODEFAULT = 0x0010
|
||||
XATTR_SHOWCOMPRESSION = 0x0020
|
||||
|
||||
// ENOATTR is not exported by the syscall package on Linux, because it is
|
||||
// an alias for ENODATA. We export it here so it is available on all
|
||||
// our supported platforms.
|
||||
ENOATTR = syscall.ENOATTR
|
||||
)
|
||||
|
||||
func getxattr(path string, name string, data []byte) (int, error) {
|
||||
return unix.Getxattr(path, name, data)
|
||||
}
|
||||
|
||||
func lgetxattr(path string, name string, data []byte) (int, error) {
|
||||
return unix.Lgetxattr(path, name, data)
|
||||
}
|
||||
|
||||
func fgetxattr(f *os.File, name string, data []byte) (int, error) {
|
||||
return getxattr(f.Name(), name, data)
|
||||
}
|
||||
|
||||
func setxattr(path string, name string, data []byte, flags int) error {
|
||||
return unix.Setxattr(path, name, data, flags)
|
||||
}
|
||||
|
||||
func lsetxattr(path string, name string, data []byte, flags int) error {
|
||||
return unix.Lsetxattr(path, name, data, flags)
|
||||
}
|
||||
|
||||
func fsetxattr(f *os.File, name string, data []byte, flags int) error {
|
||||
return setxattr(f.Name(), name, data, flags)
|
||||
}
|
||||
|
||||
func removexattr(path string, name string) error {
|
||||
return unix.Removexattr(path, name)
|
||||
}
|
||||
|
||||
func lremovexattr(path string, name string) error {
|
||||
return unix.Lremovexattr(path, name)
|
||||
}
|
||||
|
||||
func fremovexattr(f *os.File, name string) error {
|
||||
return removexattr(f.Name(), name)
|
||||
}
|
||||
|
||||
func listxattr(path string, data []byte) (int, error) {
|
||||
return unix.Listxattr(path, data)
|
||||
}
|
||||
|
||||
func llistxattr(path string, data []byte) (int, error) {
|
||||
return unix.Llistxattr(path, data)
|
||||
}
|
||||
|
||||
func flistxattr(f *os.File, data []byte) (int, error) {
|
||||
return listxattr(f.Name(), data)
|
||||
}
|
||||
|
||||
// stringsFromByteSlice converts a sequence of attributes to a []string.
|
||||
// On Darwin and Linux, each entry is a NULL-terminated string.
|
||||
func stringsFromByteSlice(buf []byte) (result []string) {
|
||||
offset := 0
|
||||
for index, b := range buf {
|
||||
if b == 0 {
|
||||
result = append(result, string(buf[offset:index]))
|
||||
offset = index + 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
142
vendor/github.com/pkg/xattr/xattr_linux.go
generated
vendored
Normal file
142
vendor/github.com/pkg/xattr/xattr_linux.go
generated
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// XATTR_SUPPORTED will be true if the current platform is supported
|
||||
XATTR_SUPPORTED = true
|
||||
|
||||
XATTR_CREATE = unix.XATTR_CREATE
|
||||
XATTR_REPLACE = unix.XATTR_REPLACE
|
||||
|
||||
// ENOATTR is not exported by the syscall package on Linux, because it is
|
||||
// an alias for ENODATA. We export it here so it is available on all
|
||||
// our supported platforms.
|
||||
ENOATTR = syscall.ENODATA
|
||||
)
|
||||
|
||||
// On Linux, FUSE and CIFS filesystems can return EINTR for interrupted system
|
||||
// calls. This function works around this by retrying system calls until they
|
||||
// stop returning EINTR.
|
||||
//
|
||||
// See https://github.com/golang/go/commit/6b420169d798c7ebe733487b56ea5c3fa4aab5ce.
|
||||
func ignoringEINTR(fn func() error) (err error) {
|
||||
for {
|
||||
err = fn()
|
||||
if err != unix.EINTR {
|
||||
break
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func getxattr(path string, name string, data []byte) (int, error) {
|
||||
var r int
|
||||
err := ignoringEINTR(func() (err error) {
|
||||
r, err = unix.Getxattr(path, name, data)
|
||||
return err
|
||||
})
|
||||
return r, err
|
||||
}
|
||||
|
||||
func lgetxattr(path string, name string, data []byte) (int, error) {
|
||||
var r int
|
||||
err := ignoringEINTR(func() (err error) {
|
||||
r, err = unix.Lgetxattr(path, name, data)
|
||||
return err
|
||||
})
|
||||
return r, err
|
||||
}
|
||||
|
||||
func fgetxattr(f *os.File, name string, data []byte) (int, error) {
|
||||
var r int
|
||||
err := ignoringEINTR(func() (err error) {
|
||||
r, err = unix.Fgetxattr(int(f.Fd()), name, data)
|
||||
return err
|
||||
})
|
||||
return r, err
|
||||
}
|
||||
|
||||
func setxattr(path string, name string, data []byte, flags int) error {
|
||||
return ignoringEINTR(func() (err error) {
|
||||
return unix.Setxattr(path, name, data, flags)
|
||||
})
|
||||
}
|
||||
|
||||
func lsetxattr(path string, name string, data []byte, flags int) error {
|
||||
return ignoringEINTR(func() (err error) {
|
||||
return unix.Lsetxattr(path, name, data, flags)
|
||||
})
|
||||
}
|
||||
|
||||
func fsetxattr(f *os.File, name string, data []byte, flags int) error {
|
||||
return ignoringEINTR(func() (err error) {
|
||||
return unix.Fsetxattr(int(f.Fd()), name, data, flags)
|
||||
})
|
||||
}
|
||||
|
||||
func removexattr(path string, name string) error {
|
||||
return ignoringEINTR(func() (err error) {
|
||||
return unix.Removexattr(path, name)
|
||||
})
|
||||
}
|
||||
|
||||
func lremovexattr(path string, name string) error {
|
||||
return ignoringEINTR(func() (err error) {
|
||||
return unix.Lremovexattr(path, name)
|
||||
})
|
||||
}
|
||||
|
||||
func fremovexattr(f *os.File, name string) error {
|
||||
return ignoringEINTR(func() (err error) {
|
||||
return unix.Fremovexattr(int(f.Fd()), name)
|
||||
})
|
||||
}
|
||||
|
||||
func listxattr(path string, data []byte) (int, error) {
|
||||
var r int
|
||||
err := ignoringEINTR(func() (err error) {
|
||||
r, err = unix.Listxattr(path, data)
|
||||
return err
|
||||
})
|
||||
return r, err
|
||||
}
|
||||
|
||||
func llistxattr(path string, data []byte) (int, error) {
|
||||
var r int
|
||||
err := ignoringEINTR(func() (err error) {
|
||||
r, err = unix.Llistxattr(path, data)
|
||||
return err
|
||||
})
|
||||
return r, err
|
||||
}
|
||||
|
||||
func flistxattr(f *os.File, data []byte) (int, error) {
|
||||
var r int
|
||||
err := ignoringEINTR(func() (err error) {
|
||||
r, err = unix.Flistxattr(int(f.Fd()), data)
|
||||
return err
|
||||
})
|
||||
return r, err
|
||||
}
|
||||
|
||||
// stringsFromByteSlice converts a sequence of attributes to a []string.
|
||||
// On Darwin and Linux, each entry is a NULL-terminated string.
|
||||
func stringsFromByteSlice(buf []byte) (result []string) {
|
||||
offset := 0
|
||||
for index, b := range buf {
|
||||
if b == 0 {
|
||||
result = append(result, string(buf[offset:index]))
|
||||
offset = index + 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
165
vendor/github.com/pkg/xattr/xattr_solaris.go
generated
vendored
Normal file
165
vendor/github.com/pkg/xattr/xattr_solaris.go
generated
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
//go:build solaris
|
||||
// +build solaris
|
||||
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// XATTR_SUPPORTED will be true if the current platform is supported
|
||||
XATTR_SUPPORTED = true
|
||||
|
||||
XATTR_CREATE = 0x1
|
||||
XATTR_REPLACE = 0x2
|
||||
|
||||
// ENOATTR is not exported by the syscall package on Linux, because it is
|
||||
// an alias for ENODATA. We export it here so it is available on all
|
||||
// our supported platforms.
|
||||
ENOATTR = syscall.ENODATA
|
||||
)
|
||||
|
||||
func getxattr(path string, name string, data []byte) (int, error) {
|
||||
f, err := os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
return fgetxattr(f, name, data)
|
||||
}
|
||||
|
||||
func lgetxattr(path string, name string, data []byte) (int, error) {
|
||||
return 0, unix.ENOTSUP
|
||||
}
|
||||
|
||||
func fgetxattr(f *os.File, name string, data []byte) (int, error) {
|
||||
fd, err := unix.Openat(int(f.Fd()), name, unix.O_RDONLY|unix.O_XATTR, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() {
|
||||
_ = unix.Close(fd)
|
||||
}()
|
||||
return unix.Read(fd, data)
|
||||
}
|
||||
|
||||
func setxattr(path string, name string, data []byte, flags int) error {
|
||||
f, err := os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = fsetxattr(f, name, data, flags)
|
||||
if err != nil {
|
||||
_ = f.Close()
|
||||
return err
|
||||
}
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
func lsetxattr(path string, name string, data []byte, flags int) error {
|
||||
return unix.ENOTSUP
|
||||
}
|
||||
|
||||
func fsetxattr(f *os.File, name string, data []byte, flags int) error {
|
||||
mode := unix.O_WRONLY | unix.O_XATTR
|
||||
if flags&XATTR_REPLACE != 0 {
|
||||
mode |= unix.O_TRUNC
|
||||
} else if flags&XATTR_CREATE != 0 {
|
||||
mode |= unix.O_CREAT | unix.O_EXCL
|
||||
} else {
|
||||
mode |= unix.O_CREAT | unix.O_TRUNC
|
||||
}
|
||||
fd, err := unix.Openat(int(f.Fd()), name, mode, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = unix.Write(fd, data); err != nil {
|
||||
_ = unix.Close(fd)
|
||||
return err
|
||||
}
|
||||
return unix.Close(fd)
|
||||
}
|
||||
|
||||
func removexattr(path string, name string) error {
|
||||
fd, err := unix.Open(path, unix.O_RDONLY|unix.O_XATTR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f := os.NewFile(uintptr(fd), path)
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
return fremovexattr(f, name)
|
||||
}
|
||||
|
||||
func lremovexattr(path string, name string) error {
|
||||
return unix.ENOTSUP
|
||||
}
|
||||
|
||||
func fremovexattr(f *os.File, name string) error {
|
||||
fd, err := unix.Openat(int(f.Fd()), ".", unix.O_XATTR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = unix.Close(fd)
|
||||
}()
|
||||
return unix.Unlinkat(fd, name, 0)
|
||||
}
|
||||
|
||||
func listxattr(path string, data []byte) (int, error) {
|
||||
f, err := os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
return flistxattr(f, data)
|
||||
}
|
||||
|
||||
func llistxattr(path string, data []byte) (int, error) {
|
||||
return 0, unix.ENOTSUP
|
||||
}
|
||||
|
||||
func flistxattr(f *os.File, data []byte) (int, error) {
|
||||
fd, err := unix.Openat(int(f.Fd()), ".", unix.O_RDONLY|unix.O_XATTR, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
xf := os.NewFile(uintptr(fd), f.Name())
|
||||
defer func() {
|
||||
_ = xf.Close()
|
||||
}()
|
||||
names, err := xf.Readdirnames(-1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var buf []byte
|
||||
for _, name := range names {
|
||||
buf = append(buf, append([]byte(name), '\000')...)
|
||||
}
|
||||
if data == nil {
|
||||
return len(buf), nil
|
||||
}
|
||||
return copy(data, buf), nil
|
||||
}
|
||||
|
||||
// stringsFromByteSlice converts a sequence of attributes to a []string.
|
||||
// On Darwin and Linux, each entry is a NULL-terminated string.
|
||||
func stringsFromByteSlice(buf []byte) (result []string) {
|
||||
offset := 0
|
||||
for index, b := range buf {
|
||||
if b == 0 {
|
||||
result = append(result, string(buf[offset:index]))
|
||||
offset = index + 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
70
vendor/github.com/pkg/xattr/xattr_unsupported.go
generated
vendored
Normal file
70
vendor/github.com/pkg/xattr/xattr_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
//go:build !linux && !freebsd && !netbsd && !darwin && !solaris
|
||||
// +build !linux,!freebsd,!netbsd,!darwin,!solaris
|
||||
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
// We need to use the default for non supported operating systems
|
||||
ENOATTR = syscall.ENODATA
|
||||
)
|
||||
|
||||
// XATTR_SUPPORTED will be true if the current platform is supported
|
||||
const XATTR_SUPPORTED = false
|
||||
|
||||
func getxattr(path string, name string, data []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func lgetxattr(path string, name string, data []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func fgetxattr(f *os.File, name string, data []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func setxattr(path string, name string, data []byte, flags int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func lsetxattr(path string, name string, data []byte, flags int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func fsetxattr(f *os.File, name string, data []byte, flags int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func removexattr(path string, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func lremovexattr(path string, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func fremovexattr(f *os.File, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func listxattr(path string, data []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func llistxattr(path string, data []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func flistxattr(f *os.File, data []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// dummy
|
||||
func stringsFromByteSlice(buf []byte) (result []string) {
|
||||
return []string{}
|
||||
}
|
285
vendor/golang.org/x/crypto/argon2/argon2.go
generated
vendored
Normal file
285
vendor/golang.org/x/crypto/argon2/argon2.go
generated
vendored
Normal file
@ -0,0 +1,285 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package argon2 implements the key derivation function Argon2.
|
||||
// Argon2 was selected as the winner of the Password Hashing Competition and can
|
||||
// be used to derive cryptographic keys from passwords.
|
||||
//
|
||||
// For a detailed specification of Argon2 see [1].
|
||||
//
|
||||
// If you aren't sure which function you need, use Argon2id (IDKey) and
|
||||
// the parameter recommendations for your scenario.
|
||||
//
|
||||
//
|
||||
// Argon2i
|
||||
//
|
||||
// Argon2i (implemented by Key) is the side-channel resistant version of Argon2.
|
||||
// It uses data-independent memory access, which is preferred for password
|
||||
// hashing and password-based key derivation. Argon2i requires more passes over
|
||||
// memory than Argon2id to protect from trade-off attacks. The recommended
|
||||
// parameters (taken from [2]) for non-interactive operations are time=3 and to
|
||||
// use the maximum available memory.
|
||||
//
|
||||
//
|
||||
// Argon2id
|
||||
//
|
||||
// Argon2id (implemented by IDKey) is a hybrid version of Argon2 combining
|
||||
// Argon2i and Argon2d. It uses data-independent memory access for the first
|
||||
// half of the first iteration over the memory and data-dependent memory access
|
||||
// for the rest. Argon2id is side-channel resistant and provides better brute-
|
||||
// force cost savings due to time-memory tradeoffs than Argon2i. The recommended
|
||||
// parameters for non-interactive operations (taken from [2]) are time=1 and to
|
||||
// use the maximum available memory.
|
||||
//
|
||||
// [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
|
||||
// [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3
|
||||
package argon2
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
// The Argon2 version implemented by this package.
|
||||
const Version = 0x13
|
||||
|
||||
const (
|
||||
argon2d = iota
|
||||
argon2i
|
||||
argon2id
|
||||
)
|
||||
|
||||
// Key derives a key from the password, salt, and cost parameters using Argon2i
|
||||
// returning a byte slice of length keyLen that can be used as cryptographic
|
||||
// key. The CPU cost and parallelism degree must be greater than zero.
|
||||
//
|
||||
// For example, you can get a derived key for e.g. AES-256 (which needs a
|
||||
// 32-byte key) by doing:
|
||||
//
|
||||
// key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
|
||||
//
|
||||
// The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number.
|
||||
// If using that amount of memory (32 MB) is not possible in some contexts then
|
||||
// the time parameter can be increased to compensate.
|
||||
//
|
||||
// The time parameter specifies the number of passes over the memory and the
|
||||
// memory parameter specifies the size of the memory in KiB. For example
|
||||
// memory=32*1024 sets the memory cost to ~32 MB. The number of threads can be
|
||||
// adjusted to the number of available CPUs. The cost parameters should be
|
||||
// increased as memory latency and CPU parallelism increases. Remember to get a
|
||||
// good random salt.
|
||||
func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||
return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
|
||||
}
|
||||
|
||||
// IDKey derives a key from the password, salt, and cost parameters using
|
||||
// Argon2id returning a byte slice of length keyLen that can be used as
|
||||
// cryptographic key. The CPU cost and parallelism degree must be greater than
|
||||
// zero.
|
||||
//
|
||||
// For example, you can get a derived key for e.g. AES-256 (which needs a
|
||||
// 32-byte key) by doing:
|
||||
//
|
||||
// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
|
||||
//
|
||||
// The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
|
||||
// If using that amount of memory (64 MB) is not possible in some contexts then
|
||||
// the time parameter can be increased to compensate.
|
||||
//
|
||||
// The time parameter specifies the number of passes over the memory and the
|
||||
// memory parameter specifies the size of the memory in KiB. For example
|
||||
// memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be
|
||||
// adjusted to the numbers of available CPUs. The cost parameters should be
|
||||
// increased as memory latency and CPU parallelism increases. Remember to get a
|
||||
// good random salt.
|
||||
func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||
return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
|
||||
}
|
||||
|
||||
func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||
if time < 1 {
|
||||
panic("argon2: number of rounds too small")
|
||||
}
|
||||
if threads < 1 {
|
||||
panic("argon2: parallelism degree too low")
|
||||
}
|
||||
h0 := initHash(password, salt, secret, data, time, memory, uint32(threads), keyLen, mode)
|
||||
|
||||
memory = memory / (syncPoints * uint32(threads)) * (syncPoints * uint32(threads))
|
||||
if memory < 2*syncPoints*uint32(threads) {
|
||||
memory = 2 * syncPoints * uint32(threads)
|
||||
}
|
||||
B := initBlocks(&h0, memory, uint32(threads))
|
||||
processBlocks(B, time, memory, uint32(threads), mode)
|
||||
return extractKey(B, memory, uint32(threads), keyLen)
|
||||
}
|
||||
|
||||
const (
|
||||
blockLength = 128
|
||||
syncPoints = 4
|
||||
)
|
||||
|
||||
type block [blockLength]uint64
|
||||
|
||||
func initHash(password, salt, key, data []byte, time, memory, threads, keyLen uint32, mode int) [blake2b.Size + 8]byte {
|
||||
var (
|
||||
h0 [blake2b.Size + 8]byte
|
||||
params [24]byte
|
||||
tmp [4]byte
|
||||
)
|
||||
|
||||
b2, _ := blake2b.New512(nil)
|
||||
binary.LittleEndian.PutUint32(params[0:4], threads)
|
||||
binary.LittleEndian.PutUint32(params[4:8], keyLen)
|
||||
binary.LittleEndian.PutUint32(params[8:12], memory)
|
||||
binary.LittleEndian.PutUint32(params[12:16], time)
|
||||
binary.LittleEndian.PutUint32(params[16:20], uint32(Version))
|
||||
binary.LittleEndian.PutUint32(params[20:24], uint32(mode))
|
||||
b2.Write(params[:])
|
||||
binary.LittleEndian.PutUint32(tmp[:], uint32(len(password)))
|
||||
b2.Write(tmp[:])
|
||||
b2.Write(password)
|
||||
binary.LittleEndian.PutUint32(tmp[:], uint32(len(salt)))
|
||||
b2.Write(tmp[:])
|
||||
b2.Write(salt)
|
||||
binary.LittleEndian.PutUint32(tmp[:], uint32(len(key)))
|
||||
b2.Write(tmp[:])
|
||||
b2.Write(key)
|
||||
binary.LittleEndian.PutUint32(tmp[:], uint32(len(data)))
|
||||
b2.Write(tmp[:])
|
||||
b2.Write(data)
|
||||
b2.Sum(h0[:0])
|
||||
return h0
|
||||
}
|
||||
|
||||
func initBlocks(h0 *[blake2b.Size + 8]byte, memory, threads uint32) []block {
|
||||
var block0 [1024]byte
|
||||
B := make([]block, memory)
|
||||
for lane := uint32(0); lane < threads; lane++ {
|
||||
j := lane * (memory / threads)
|
||||
binary.LittleEndian.PutUint32(h0[blake2b.Size+4:], lane)
|
||||
|
||||
binary.LittleEndian.PutUint32(h0[blake2b.Size:], 0)
|
||||
blake2bHash(block0[:], h0[:])
|
||||
for i := range B[j+0] {
|
||||
B[j+0][i] = binary.LittleEndian.Uint64(block0[i*8:])
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(h0[blake2b.Size:], 1)
|
||||
blake2bHash(block0[:], h0[:])
|
||||
for i := range B[j+1] {
|
||||
B[j+1][i] = binary.LittleEndian.Uint64(block0[i*8:])
|
||||
}
|
||||
}
|
||||
return B
|
||||
}
|
||||
|
||||
func processBlocks(B []block, time, memory, threads uint32, mode int) {
|
||||
lanes := memory / threads
|
||||
segments := lanes / syncPoints
|
||||
|
||||
processSegment := func(n, slice, lane uint32, wg *sync.WaitGroup) {
|
||||
var addresses, in, zero block
|
||||
if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
|
||||
in[0] = uint64(n)
|
||||
in[1] = uint64(lane)
|
||||
in[2] = uint64(slice)
|
||||
in[3] = uint64(memory)
|
||||
in[4] = uint64(time)
|
||||
in[5] = uint64(mode)
|
||||
}
|
||||
|
||||
index := uint32(0)
|
||||
if n == 0 && slice == 0 {
|
||||
index = 2 // we have already generated the first two blocks
|
||||
if mode == argon2i || mode == argon2id {
|
||||
in[6]++
|
||||
processBlock(&addresses, &in, &zero)
|
||||
processBlock(&addresses, &addresses, &zero)
|
||||
}
|
||||
}
|
||||
|
||||
offset := lane*lanes + slice*segments + index
|
||||
var random uint64
|
||||
for index < segments {
|
||||
prev := offset - 1
|
||||
if index == 0 && slice == 0 {
|
||||
prev += lanes // last block in lane
|
||||
}
|
||||
if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
|
||||
if index%blockLength == 0 {
|
||||
in[6]++
|
||||
processBlock(&addresses, &in, &zero)
|
||||
processBlock(&addresses, &addresses, &zero)
|
||||
}
|
||||
random = addresses[index%blockLength]
|
||||
} else {
|
||||
random = B[prev][0]
|
||||
}
|
||||
newOffset := indexAlpha(random, lanes, segments, threads, n, slice, lane, index)
|
||||
processBlockXOR(&B[offset], &B[prev], &B[newOffset])
|
||||
index, offset = index+1, offset+1
|
||||
}
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
for n := uint32(0); n < time; n++ {
|
||||
for slice := uint32(0); slice < syncPoints; slice++ {
|
||||
var wg sync.WaitGroup
|
||||
for lane := uint32(0); lane < threads; lane++ {
|
||||
wg.Add(1)
|
||||
go processSegment(n, slice, lane, &wg)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func extractKey(B []block, memory, threads, keyLen uint32) []byte {
|
||||
lanes := memory / threads
|
||||
for lane := uint32(0); lane < threads-1; lane++ {
|
||||
for i, v := range B[(lane*lanes)+lanes-1] {
|
||||
B[memory-1][i] ^= v
|
||||
}
|
||||
}
|
||||
|
||||
var block [1024]byte
|
||||
for i, v := range B[memory-1] {
|
||||
binary.LittleEndian.PutUint64(block[i*8:], v)
|
||||
}
|
||||
key := make([]byte, keyLen)
|
||||
blake2bHash(key, block[:])
|
||||
return key
|
||||
}
|
||||
|
||||
func indexAlpha(rand uint64, lanes, segments, threads, n, slice, lane, index uint32) uint32 {
|
||||
refLane := uint32(rand>>32) % threads
|
||||
if n == 0 && slice == 0 {
|
||||
refLane = lane
|
||||
}
|
||||
m, s := 3*segments, ((slice+1)%syncPoints)*segments
|
||||
if lane == refLane {
|
||||
m += index
|
||||
}
|
||||
if n == 0 {
|
||||
m, s = slice*segments, 0
|
||||
if slice == 0 || lane == refLane {
|
||||
m += index
|
||||
}
|
||||
}
|
||||
if index == 0 || lane == refLane {
|
||||
m--
|
||||
}
|
||||
return phi(rand, uint64(m), uint64(s), refLane, lanes)
|
||||
}
|
||||
|
||||
func phi(rand, m, s uint64, lane, lanes uint32) uint32 {
|
||||
p := rand & 0xFFFFFFFF
|
||||
p = (p * p) >> 32
|
||||
p = (p * m) >> 32
|
||||
return lane*lanes + uint32((s+m-(p+1))%uint64(lanes))
|
||||
}
|
53
vendor/golang.org/x/crypto/argon2/blake2b.go
generated
vendored
Normal file
53
vendor/golang.org/x/crypto/argon2/blake2b.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package argon2
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
// blake2bHash computes an arbitrary long hash value of in
|
||||
// and writes the hash to out.
|
||||
func blake2bHash(out []byte, in []byte) {
|
||||
var b2 hash.Hash
|
||||
if n := len(out); n < blake2b.Size {
|
||||
b2, _ = blake2b.New(n, nil)
|
||||
} else {
|
||||
b2, _ = blake2b.New512(nil)
|
||||
}
|
||||
|
||||
var buffer [blake2b.Size]byte
|
||||
binary.LittleEndian.PutUint32(buffer[:4], uint32(len(out)))
|
||||
b2.Write(buffer[:4])
|
||||
b2.Write(in)
|
||||
|
||||
if len(out) <= blake2b.Size {
|
||||
b2.Sum(out[:0])
|
||||
return
|
||||
}
|
||||
|
||||
outLen := len(out)
|
||||
b2.Sum(buffer[:0])
|
||||
b2.Reset()
|
||||
copy(out, buffer[:32])
|
||||
out = out[32:]
|
||||
for len(out) > blake2b.Size {
|
||||
b2.Write(buffer[:])
|
||||
b2.Sum(buffer[:0])
|
||||
copy(out, buffer[:32])
|
||||
out = out[32:]
|
||||
b2.Reset()
|
||||
}
|
||||
|
||||
if outLen%blake2b.Size > 0 { // outLen > 64
|
||||
r := ((outLen + 31) / 32) - 2 // ⌈τ /32⌉-2
|
||||
b2, _ = blake2b.New(outLen-32*r, nil)
|
||||
}
|
||||
b2.Write(buffer[:])
|
||||
b2.Sum(out[:0])
|
||||
}
|
61
vendor/golang.org/x/crypto/argon2/blamka_amd64.go
generated
vendored
Normal file
61
vendor/golang.org/x/crypto/argon2/blamka_amd64.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
package argon2
|
||||
|
||||
import "golang.org/x/sys/cpu"
|
||||
|
||||
func init() {
|
||||
useSSE4 = cpu.X86.HasSSE41
|
||||
}
|
||||
|
||||
//go:noescape
|
||||
func mixBlocksSSE2(out, a, b, c *block)
|
||||
|
||||
//go:noescape
|
||||
func xorBlocksSSE2(out, a, b, c *block)
|
||||
|
||||
//go:noescape
|
||||
func blamkaSSE4(b *block)
|
||||
|
||||
func processBlockSSE(out, in1, in2 *block, xor bool) {
|
||||
var t block
|
||||
mixBlocksSSE2(&t, in1, in2, &t)
|
||||
if useSSE4 {
|
||||
blamkaSSE4(&t)
|
||||
} else {
|
||||
for i := 0; i < blockLength; i += 16 {
|
||||
blamkaGeneric(
|
||||
&t[i+0], &t[i+1], &t[i+2], &t[i+3],
|
||||
&t[i+4], &t[i+5], &t[i+6], &t[i+7],
|
||||
&t[i+8], &t[i+9], &t[i+10], &t[i+11],
|
||||
&t[i+12], &t[i+13], &t[i+14], &t[i+15],
|
||||
)
|
||||
}
|
||||
for i := 0; i < blockLength/8; i += 2 {
|
||||
blamkaGeneric(
|
||||
&t[i], &t[i+1], &t[16+i], &t[16+i+1],
|
||||
&t[32+i], &t[32+i+1], &t[48+i], &t[48+i+1],
|
||||
&t[64+i], &t[64+i+1], &t[80+i], &t[80+i+1],
|
||||
&t[96+i], &t[96+i+1], &t[112+i], &t[112+i+1],
|
||||
)
|
||||
}
|
||||
}
|
||||
if xor {
|
||||
xorBlocksSSE2(out, in1, in2, &t)
|
||||
} else {
|
||||
mixBlocksSSE2(out, in1, in2, &t)
|
||||
}
|
||||
}
|
||||
|
||||
func processBlock(out, in1, in2 *block) {
|
||||
processBlockSSE(out, in1, in2, false)
|
||||
}
|
||||
|
||||
func processBlockXOR(out, in1, in2 *block) {
|
||||
processBlockSSE(out, in1, in2, true)
|
||||
}
|
244
vendor/golang.org/x/crypto/argon2/blamka_amd64.s
generated
vendored
Normal file
244
vendor/golang.org/x/crypto/argon2/blamka_amd64.s
generated
vendored
Normal file
@ -0,0 +1,244 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
DATA ·c40<>+0x00(SB)/8, $0x0201000706050403
|
||||
DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
|
||||
GLOBL ·c40<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·c48<>+0x00(SB)/8, $0x0100070605040302
|
||||
DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
|
||||
GLOBL ·c48<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \
|
||||
MOVO v4, t1; \
|
||||
MOVO v5, v4; \
|
||||
MOVO t1, v5; \
|
||||
MOVO v6, t1; \
|
||||
PUNPCKLQDQ v6, t2; \
|
||||
PUNPCKHQDQ v7, v6; \
|
||||
PUNPCKHQDQ t2, v6; \
|
||||
PUNPCKLQDQ v7, t2; \
|
||||
MOVO t1, v7; \
|
||||
MOVO v2, t1; \
|
||||
PUNPCKHQDQ t2, v7; \
|
||||
PUNPCKLQDQ v3, t2; \
|
||||
PUNPCKHQDQ t2, v2; \
|
||||
PUNPCKLQDQ t1, t2; \
|
||||
PUNPCKHQDQ t2, v3
|
||||
|
||||
#define SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2) \
|
||||
MOVO v4, t1; \
|
||||
MOVO v5, v4; \
|
||||
MOVO t1, v5; \
|
||||
MOVO v2, t1; \
|
||||
PUNPCKLQDQ v2, t2; \
|
||||
PUNPCKHQDQ v3, v2; \
|
||||
PUNPCKHQDQ t2, v2; \
|
||||
PUNPCKLQDQ v3, t2; \
|
||||
MOVO t1, v3; \
|
||||
MOVO v6, t1; \
|
||||
PUNPCKHQDQ t2, v3; \
|
||||
PUNPCKLQDQ v7, t2; \
|
||||
PUNPCKHQDQ t2, v6; \
|
||||
PUNPCKLQDQ t1, t2; \
|
||||
PUNPCKHQDQ t2, v7
|
||||
|
||||
#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, t0, c40, c48) \
|
||||
MOVO v0, t0; \
|
||||
PMULULQ v2, t0; \
|
||||
PADDQ v2, v0; \
|
||||
PADDQ t0, v0; \
|
||||
PADDQ t0, v0; \
|
||||
PXOR v0, v6; \
|
||||
PSHUFD $0xB1, v6, v6; \
|
||||
MOVO v4, t0; \
|
||||
PMULULQ v6, t0; \
|
||||
PADDQ v6, v4; \
|
||||
PADDQ t0, v4; \
|
||||
PADDQ t0, v4; \
|
||||
PXOR v4, v2; \
|
||||
PSHUFB c40, v2; \
|
||||
MOVO v0, t0; \
|
||||
PMULULQ v2, t0; \
|
||||
PADDQ v2, v0; \
|
||||
PADDQ t0, v0; \
|
||||
PADDQ t0, v0; \
|
||||
PXOR v0, v6; \
|
||||
PSHUFB c48, v6; \
|
||||
MOVO v4, t0; \
|
||||
PMULULQ v6, t0; \
|
||||
PADDQ v6, v4; \
|
||||
PADDQ t0, v4; \
|
||||
PADDQ t0, v4; \
|
||||
PXOR v4, v2; \
|
||||
MOVO v2, t0; \
|
||||
PADDQ v2, t0; \
|
||||
PSRLQ $63, v2; \
|
||||
PXOR t0, v2; \
|
||||
MOVO v1, t0; \
|
||||
PMULULQ v3, t0; \
|
||||
PADDQ v3, v1; \
|
||||
PADDQ t0, v1; \
|
||||
PADDQ t0, v1; \
|
||||
PXOR v1, v7; \
|
||||
PSHUFD $0xB1, v7, v7; \
|
||||
MOVO v5, t0; \
|
||||
PMULULQ v7, t0; \
|
||||
PADDQ v7, v5; \
|
||||
PADDQ t0, v5; \
|
||||
PADDQ t0, v5; \
|
||||
PXOR v5, v3; \
|
||||
PSHUFB c40, v3; \
|
||||
MOVO v1, t0; \
|
||||
PMULULQ v3, t0; \
|
||||
PADDQ v3, v1; \
|
||||
PADDQ t0, v1; \
|
||||
PADDQ t0, v1; \
|
||||
PXOR v1, v7; \
|
||||
PSHUFB c48, v7; \
|
||||
MOVO v5, t0; \
|
||||
PMULULQ v7, t0; \
|
||||
PADDQ v7, v5; \
|
||||
PADDQ t0, v5; \
|
||||
PADDQ t0, v5; \
|
||||
PXOR v5, v3; \
|
||||
MOVO v3, t0; \
|
||||
PADDQ v3, t0; \
|
||||
PSRLQ $63, v3; \
|
||||
PXOR t0, v3
|
||||
|
||||
#define LOAD_MSG_0(block, off) \
|
||||
MOVOU 8*(off+0)(block), X0; \
|
||||
MOVOU 8*(off+2)(block), X1; \
|
||||
MOVOU 8*(off+4)(block), X2; \
|
||||
MOVOU 8*(off+6)(block), X3; \
|
||||
MOVOU 8*(off+8)(block), X4; \
|
||||
MOVOU 8*(off+10)(block), X5; \
|
||||
MOVOU 8*(off+12)(block), X6; \
|
||||
MOVOU 8*(off+14)(block), X7
|
||||
|
||||
#define STORE_MSG_0(block, off) \
|
||||
MOVOU X0, 8*(off+0)(block); \
|
||||
MOVOU X1, 8*(off+2)(block); \
|
||||
MOVOU X2, 8*(off+4)(block); \
|
||||
MOVOU X3, 8*(off+6)(block); \
|
||||
MOVOU X4, 8*(off+8)(block); \
|
||||
MOVOU X5, 8*(off+10)(block); \
|
||||
MOVOU X6, 8*(off+12)(block); \
|
||||
MOVOU X7, 8*(off+14)(block)
|
||||
|
||||
#define LOAD_MSG_1(block, off) \
|
||||
MOVOU 8*off+0*8(block), X0; \
|
||||
MOVOU 8*off+16*8(block), X1; \
|
||||
MOVOU 8*off+32*8(block), X2; \
|
||||
MOVOU 8*off+48*8(block), X3; \
|
||||
MOVOU 8*off+64*8(block), X4; \
|
||||
MOVOU 8*off+80*8(block), X5; \
|
||||
MOVOU 8*off+96*8(block), X6; \
|
||||
MOVOU 8*off+112*8(block), X7
|
||||
|
||||
#define STORE_MSG_1(block, off) \
|
||||
MOVOU X0, 8*off+0*8(block); \
|
||||
MOVOU X1, 8*off+16*8(block); \
|
||||
MOVOU X2, 8*off+32*8(block); \
|
||||
MOVOU X3, 8*off+48*8(block); \
|
||||
MOVOU X4, 8*off+64*8(block); \
|
||||
MOVOU X5, 8*off+80*8(block); \
|
||||
MOVOU X6, 8*off+96*8(block); \
|
||||
MOVOU X7, 8*off+112*8(block)
|
||||
|
||||
#define BLAMKA_ROUND_0(block, off, t0, t1, c40, c48) \
|
||||
LOAD_MSG_0(block, off); \
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1); \
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1); \
|
||||
STORE_MSG_0(block, off)
|
||||
|
||||
#define BLAMKA_ROUND_1(block, off, t0, t1, c40, c48) \
|
||||
LOAD_MSG_1(block, off); \
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1); \
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1); \
|
||||
STORE_MSG_1(block, off)
|
||||
|
||||
// func blamkaSSE4(b *block)
|
||||
TEXT ·blamkaSSE4(SB), 4, $0-8
|
||||
MOVQ b+0(FP), AX
|
||||
|
||||
MOVOU ·c40<>(SB), X10
|
||||
MOVOU ·c48<>(SB), X11
|
||||
|
||||
BLAMKA_ROUND_0(AX, 0, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 16, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 32, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 48, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 64, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 80, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 96, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 112, X8, X9, X10, X11)
|
||||
|
||||
BLAMKA_ROUND_1(AX, 0, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 2, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 4, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 6, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 8, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 10, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 12, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 14, X8, X9, X10, X11)
|
||||
RET
|
||||
|
||||
// func mixBlocksSSE2(out, a, b, c *block)
|
||||
TEXT ·mixBlocksSSE2(SB), 4, $0-32
|
||||
MOVQ out+0(FP), DX
|
||||
MOVQ a+8(FP), AX
|
||||
MOVQ b+16(FP), BX
|
||||
MOVQ a+24(FP), CX
|
||||
MOVQ $128, BP
|
||||
|
||||
loop:
|
||||
MOVOU 0(AX), X0
|
||||
MOVOU 0(BX), X1
|
||||
MOVOU 0(CX), X2
|
||||
PXOR X1, X0
|
||||
PXOR X2, X0
|
||||
MOVOU X0, 0(DX)
|
||||
ADDQ $16, AX
|
||||
ADDQ $16, BX
|
||||
ADDQ $16, CX
|
||||
ADDQ $16, DX
|
||||
SUBQ $2, BP
|
||||
JA loop
|
||||
RET
|
||||
|
||||
// func xorBlocksSSE2(out, a, b, c *block)
|
||||
TEXT ·xorBlocksSSE2(SB), 4, $0-32
|
||||
MOVQ out+0(FP), DX
|
||||
MOVQ a+8(FP), AX
|
||||
MOVQ b+16(FP), BX
|
||||
MOVQ a+24(FP), CX
|
||||
MOVQ $128, BP
|
||||
|
||||
loop:
|
||||
MOVOU 0(AX), X0
|
||||
MOVOU 0(BX), X1
|
||||
MOVOU 0(CX), X2
|
||||
MOVOU 0(DX), X3
|
||||
PXOR X1, X0
|
||||
PXOR X2, X0
|
||||
PXOR X3, X0
|
||||
MOVOU X0, 0(DX)
|
||||
ADDQ $16, AX
|
||||
ADDQ $16, BX
|
||||
ADDQ $16, CX
|
||||
ADDQ $16, DX
|
||||
SUBQ $2, BP
|
||||
JA loop
|
||||
RET
|
163
vendor/golang.org/x/crypto/argon2/blamka_generic.go
generated
vendored
Normal file
163
vendor/golang.org/x/crypto/argon2/blamka_generic.go
generated
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package argon2
|
||||
|
||||
var useSSE4 bool
|
||||
|
||||
func processBlockGeneric(out, in1, in2 *block, xor bool) {
|
||||
var t block
|
||||
for i := range t {
|
||||
t[i] = in1[i] ^ in2[i]
|
||||
}
|
||||
for i := 0; i < blockLength; i += 16 {
|
||||
blamkaGeneric(
|
||||
&t[i+0], &t[i+1], &t[i+2], &t[i+3],
|
||||
&t[i+4], &t[i+5], &t[i+6], &t[i+7],
|
||||
&t[i+8], &t[i+9], &t[i+10], &t[i+11],
|
||||
&t[i+12], &t[i+13], &t[i+14], &t[i+15],
|
||||
)
|
||||
}
|
||||
for i := 0; i < blockLength/8; i += 2 {
|
||||
blamkaGeneric(
|
||||
&t[i], &t[i+1], &t[16+i], &t[16+i+1],
|
||||
&t[32+i], &t[32+i+1], &t[48+i], &t[48+i+1],
|
||||
&t[64+i], &t[64+i+1], &t[80+i], &t[80+i+1],
|
||||
&t[96+i], &t[96+i+1], &t[112+i], &t[112+i+1],
|
||||
)
|
||||
}
|
||||
if xor {
|
||||
for i := range t {
|
||||
out[i] ^= in1[i] ^ in2[i] ^ t[i]
|
||||
}
|
||||
} else {
|
||||
for i := range t {
|
||||
out[i] = in1[i] ^ in2[i] ^ t[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func blamkaGeneric(t00, t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12, t13, t14, t15 *uint64) {
|
||||
v00, v01, v02, v03 := *t00, *t01, *t02, *t03
|
||||
v04, v05, v06, v07 := *t04, *t05, *t06, *t07
|
||||
v08, v09, v10, v11 := *t08, *t09, *t10, *t11
|
||||
v12, v13, v14, v15 := *t12, *t13, *t14, *t15
|
||||
|
||||
v00 += v04 + 2*uint64(uint32(v00))*uint64(uint32(v04))
|
||||
v12 ^= v00
|
||||
v12 = v12>>32 | v12<<32
|
||||
v08 += v12 + 2*uint64(uint32(v08))*uint64(uint32(v12))
|
||||
v04 ^= v08
|
||||
v04 = v04>>24 | v04<<40
|
||||
|
||||
v00 += v04 + 2*uint64(uint32(v00))*uint64(uint32(v04))
|
||||
v12 ^= v00
|
||||
v12 = v12>>16 | v12<<48
|
||||
v08 += v12 + 2*uint64(uint32(v08))*uint64(uint32(v12))
|
||||
v04 ^= v08
|
||||
v04 = v04>>63 | v04<<1
|
||||
|
||||
v01 += v05 + 2*uint64(uint32(v01))*uint64(uint32(v05))
|
||||
v13 ^= v01
|
||||
v13 = v13>>32 | v13<<32
|
||||
v09 += v13 + 2*uint64(uint32(v09))*uint64(uint32(v13))
|
||||
v05 ^= v09
|
||||
v05 = v05>>24 | v05<<40
|
||||
|
||||
v01 += v05 + 2*uint64(uint32(v01))*uint64(uint32(v05))
|
||||
v13 ^= v01
|
||||
v13 = v13>>16 | v13<<48
|
||||
v09 += v13 + 2*uint64(uint32(v09))*uint64(uint32(v13))
|
||||
v05 ^= v09
|
||||
v05 = v05>>63 | v05<<1
|
||||
|
||||
v02 += v06 + 2*uint64(uint32(v02))*uint64(uint32(v06))
|
||||
v14 ^= v02
|
||||
v14 = v14>>32 | v14<<32
|
||||
v10 += v14 + 2*uint64(uint32(v10))*uint64(uint32(v14))
|
||||
v06 ^= v10
|
||||
v06 = v06>>24 | v06<<40
|
||||
|
||||
v02 += v06 + 2*uint64(uint32(v02))*uint64(uint32(v06))
|
||||
v14 ^= v02
|
||||
v14 = v14>>16 | v14<<48
|
||||
v10 += v14 + 2*uint64(uint32(v10))*uint64(uint32(v14))
|
||||
v06 ^= v10
|
||||
v06 = v06>>63 | v06<<1
|
||||
|
||||
v03 += v07 + 2*uint64(uint32(v03))*uint64(uint32(v07))
|
||||
v15 ^= v03
|
||||
v15 = v15>>32 | v15<<32
|
||||
v11 += v15 + 2*uint64(uint32(v11))*uint64(uint32(v15))
|
||||
v07 ^= v11
|
||||
v07 = v07>>24 | v07<<40
|
||||
|
||||
v03 += v07 + 2*uint64(uint32(v03))*uint64(uint32(v07))
|
||||
v15 ^= v03
|
||||
v15 = v15>>16 | v15<<48
|
||||
v11 += v15 + 2*uint64(uint32(v11))*uint64(uint32(v15))
|
||||
v07 ^= v11
|
||||
v07 = v07>>63 | v07<<1
|
||||
|
||||
v00 += v05 + 2*uint64(uint32(v00))*uint64(uint32(v05))
|
||||
v15 ^= v00
|
||||
v15 = v15>>32 | v15<<32
|
||||
v10 += v15 + 2*uint64(uint32(v10))*uint64(uint32(v15))
|
||||
v05 ^= v10
|
||||
v05 = v05>>24 | v05<<40
|
||||
|
||||
v00 += v05 + 2*uint64(uint32(v00))*uint64(uint32(v05))
|
||||
v15 ^= v00
|
||||
v15 = v15>>16 | v15<<48
|
||||
v10 += v15 + 2*uint64(uint32(v10))*uint64(uint32(v15))
|
||||
v05 ^= v10
|
||||
v05 = v05>>63 | v05<<1
|
||||
|
||||
v01 += v06 + 2*uint64(uint32(v01))*uint64(uint32(v06))
|
||||
v12 ^= v01
|
||||
v12 = v12>>32 | v12<<32
|
||||
v11 += v12 + 2*uint64(uint32(v11))*uint64(uint32(v12))
|
||||
v06 ^= v11
|
||||
v06 = v06>>24 | v06<<40
|
||||
|
||||
v01 += v06 + 2*uint64(uint32(v01))*uint64(uint32(v06))
|
||||
v12 ^= v01
|
||||
v12 = v12>>16 | v12<<48
|
||||
v11 += v12 + 2*uint64(uint32(v11))*uint64(uint32(v12))
|
||||
v06 ^= v11
|
||||
v06 = v06>>63 | v06<<1
|
||||
|
||||
v02 += v07 + 2*uint64(uint32(v02))*uint64(uint32(v07))
|
||||
v13 ^= v02
|
||||
v13 = v13>>32 | v13<<32
|
||||
v08 += v13 + 2*uint64(uint32(v08))*uint64(uint32(v13))
|
||||
v07 ^= v08
|
||||
v07 = v07>>24 | v07<<40
|
||||
|
||||
v02 += v07 + 2*uint64(uint32(v02))*uint64(uint32(v07))
|
||||
v13 ^= v02
|
||||
v13 = v13>>16 | v13<<48
|
||||
v08 += v13 + 2*uint64(uint32(v08))*uint64(uint32(v13))
|
||||
v07 ^= v08
|
||||
v07 = v07>>63 | v07<<1
|
||||
|
||||
v03 += v04 + 2*uint64(uint32(v03))*uint64(uint32(v04))
|
||||
v14 ^= v03
|
||||
v14 = v14>>32 | v14<<32
|
||||
v09 += v14 + 2*uint64(uint32(v09))*uint64(uint32(v14))
|
||||
v04 ^= v09
|
||||
v04 = v04>>24 | v04<<40
|
||||
|
||||
v03 += v04 + 2*uint64(uint32(v03))*uint64(uint32(v04))
|
||||
v14 ^= v03
|
||||
v14 = v14>>16 | v14<<48
|
||||
v09 += v14 + 2*uint64(uint32(v09))*uint64(uint32(v14))
|
||||
v04 ^= v09
|
||||
v04 = v04>>63 | v04<<1
|
||||
|
||||
*t00, *t01, *t02, *t03 = v00, v01, v02, v03
|
||||
*t04, *t05, *t06, *t07 = v04, v05, v06, v07
|
||||
*t08, *t09, *t10, *t11 = v08, v09, v10, v11
|
||||
*t12, *t13, *t14, *t15 = v12, v13, v14, v15
|
||||
}
|
16
vendor/golang.org/x/crypto/argon2/blamka_ref.go
generated
vendored
Normal file
16
vendor/golang.org/x/crypto/argon2/blamka_ref.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || purego || !gc
|
||||
// +build !amd64 purego !gc
|
||||
|
||||
package argon2
|
||||
|
||||
func processBlock(out, in1, in2 *block) {
|
||||
processBlockGeneric(out, in1, in2, false)
|
||||
}
|
||||
|
||||
func processBlockXOR(out, in1, in2 *block) {
|
||||
processBlockGeneric(out, in1, in2, true)
|
||||
}
|
93
vendor/golang.org/x/crypto/hkdf/hkdf.go
generated
vendored
Normal file
93
vendor/golang.org/x/crypto/hkdf/hkdf.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package hkdf implements the HMAC-based Extract-and-Expand Key Derivation
|
||||
// Function (HKDF) as defined in RFC 5869.
|
||||
//
|
||||
// HKDF is a cryptographic key derivation function (KDF) with the goal of
|
||||
// expanding limited input keying material into one or more cryptographically
|
||||
// strong secret keys.
|
||||
package hkdf // import "golang.org/x/crypto/hkdf"
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Extract generates a pseudorandom key for use with Expand from an input secret
|
||||
// and an optional independent salt.
|
||||
//
|
||||
// Only use this function if you need to reuse the extracted key with multiple
|
||||
// Expand invocations and different context values. Most common scenarios,
|
||||
// including the generation of multiple keys, should use New instead.
|
||||
func Extract(hash func() hash.Hash, secret, salt []byte) []byte {
|
||||
if salt == nil {
|
||||
salt = make([]byte, hash().Size())
|
||||
}
|
||||
extractor := hmac.New(hash, salt)
|
||||
extractor.Write(secret)
|
||||
return extractor.Sum(nil)
|
||||
}
|
||||
|
||||
type hkdf struct {
|
||||
expander hash.Hash
|
||||
size int
|
||||
|
||||
info []byte
|
||||
counter byte
|
||||
|
||||
prev []byte
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (f *hkdf) Read(p []byte) (int, error) {
|
||||
// Check whether enough data can be generated
|
||||
need := len(p)
|
||||
remains := len(f.buf) + int(255-f.counter+1)*f.size
|
||||
if remains < need {
|
||||
return 0, errors.New("hkdf: entropy limit reached")
|
||||
}
|
||||
// Read any leftover from the buffer
|
||||
n := copy(p, f.buf)
|
||||
p = p[n:]
|
||||
|
||||
// Fill the rest of the buffer
|
||||
for len(p) > 0 {
|
||||
f.expander.Reset()
|
||||
f.expander.Write(f.prev)
|
||||
f.expander.Write(f.info)
|
||||
f.expander.Write([]byte{f.counter})
|
||||
f.prev = f.expander.Sum(f.prev[:0])
|
||||
f.counter++
|
||||
|
||||
// Copy the new batch into p
|
||||
f.buf = f.prev
|
||||
n = copy(p, f.buf)
|
||||
p = p[n:]
|
||||
}
|
||||
// Save leftovers for next run
|
||||
f.buf = f.buf[n:]
|
||||
|
||||
return need, nil
|
||||
}
|
||||
|
||||
// Expand returns a Reader, from which keys can be read, using the given
|
||||
// pseudorandom key and optional context info, skipping the extraction step.
|
||||
//
|
||||
// The pseudorandomKey should have been generated by Extract, or be a uniformly
|
||||
// random or pseudorandom cryptographically strong key. See RFC 5869, Section
|
||||
// 3.3. Most common scenarios will want to use New instead.
|
||||
func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader {
|
||||
expander := hmac.New(hash, pseudorandomKey)
|
||||
return &hkdf{expander, expander.Size(), info, 1, nil, nil}
|
||||
}
|
||||
|
||||
// New returns a Reader, from which keys can be read, using the given hash,
|
||||
// secret, salt and context info. Salt and info can be nil.
|
||||
func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader {
|
||||
prk := Extract(hash, secret, salt)
|
||||
return Expand(hash, prk, info)
|
||||
}
|
Loading…
Reference in New Issue
Block a user