mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-12-18 11:00:25 +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/csi-addons/spec v0.1.2-0.20220906123848-52ce69f90900
|
||||||
github.com/gemalto/kmip-go v0.0.8-0.20220721195433-3fe83e2d3f26
|
github.com/gemalto/kmip-go v0.0.8-0.20220721195433-3fe83e2d3f26
|
||||||
github.com/golang/protobuf v1.5.2
|
github.com/golang/protobuf v1.5.2
|
||||||
github.com/google/uuid v1.3.0
|
|
||||||
github.com/google/fscrypt v0.3.3
|
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-middleware v1.3.0
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
||||||
github.com/hashicorp/vault/api v1.7.2
|
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-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-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-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-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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
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