зеркало из https://github.com/mozilla/mig.git
[major] Add support for permissions verification in MIG actions, using PGP module
This commit is contained in:
Родитель
1e550afc5b
Коммит
50eafb97a3
|
@ -0,0 +1,83 @@
|
|||
/* Mozilla InvestiGator
|
||||
|
||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
||||
The contents of this file are subject to the Mozilla Public License Version
|
||||
1.1 (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.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis,
|
||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
for the specific language governing rights and limitations under the
|
||||
License.
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
Mozilla Corporation
|
||||
Portions created by the Initial Developer are Copyright (C) 2014
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s):
|
||||
Julien Vehent jvehent@mozilla.com [:ulfr]
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
use your version of this file under the terms of the MPL, indicate your
|
||||
decision by deleting the provisions above and replace them with the notice
|
||||
and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*/
|
||||
|
||||
package mig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ACL []Permission
|
||||
|
||||
type Permission map[string]struct {
|
||||
RequiredSignatures int
|
||||
RequiredAuthoritativeSigners int
|
||||
AuthoritativeSigners []string
|
||||
NonAuthoritativeSigners []string
|
||||
}
|
||||
|
||||
// verifyPermission controls that the PGP keys, identified by their fingerprints, that
|
||||
// signed an operation are sufficient to allow this operation to run
|
||||
func verifyPermission(operation operation, permName string, perm Permission, fingerprints []string) (err error) {
|
||||
if perm[permName].RequiredSignatures < 1 {
|
||||
return fmt.Errorf("Invalid permission '%s'. Must require at least 1 signature, has %d",
|
||||
permName, perm[permName].RequiredSignatures)
|
||||
}
|
||||
countSignatures := 0
|
||||
countAuthoritativeSignatures := 0
|
||||
for _, fp := range fingerprints {
|
||||
for _, vfp := range perm[permName].AuthoritativeSigners {
|
||||
if strings.ToUpper(fp) == strings.ToUpper(vfp) {
|
||||
countSignatures++
|
||||
countAuthoritativeSignatures++
|
||||
}
|
||||
}
|
||||
for _, vfp := range perm[permName].NonAuthoritativeSigners {
|
||||
if strings.ToUpper(fp) == strings.ToUpper(vfp) {
|
||||
countSignatures++
|
||||
}
|
||||
}
|
||||
}
|
||||
if countAuthoritativeSignatures < perm[permName].RequiredAuthoritativeSigners {
|
||||
return fmt.Errorf("Permission denied for operation '%s'. Insufficient number of Authoritative signatures. Need %d, got %d",
|
||||
operation.Module, perm[permName].RequiredAuthoritativeSigners, countAuthoritativeSignatures)
|
||||
}
|
||||
if countSignatures < perm[permName].RequiredSignatures {
|
||||
return fmt.Errorf("Permission denied for operation '%s'. Insufficient number of signatures. Need %d, got %d",
|
||||
operation.Module, perm[permName].RequiredSignatures, countSignatures)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -43,6 +43,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"mig/pgp"
|
||||
"mig/pgp/verify"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -216,3 +217,51 @@ func (a Action) String() (str string, err error) {
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
// VerifyACL controls that an action has been issued by investigators
|
||||
// that have the right permissions. This function looks at each operation
|
||||
// listed in the action, and find the corresponding permission. If no
|
||||
// permission is found, the default one `default` is used.
|
||||
// The first permission that is found to apply to an operation, but
|
||||
// doesn't allow the operation to run, will fail the verification globally
|
||||
func (a Action) VerifyACL(acl ACL, keyring io.Reader) (err error) {
|
||||
// first, verify all signatures and get a list of PGP
|
||||
// fingerprints of the signers
|
||||
var fingerprints []string
|
||||
astr, err := a.String()
|
||||
if err != nil {
|
||||
return errors.New("Failed to stringify action")
|
||||
}
|
||||
for _, sig := range a.PGPSignatures {
|
||||
fp, err := pgp.GetFingerprintFromSignature(astr, sig, keyring)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to retrieve fingerprint from signatures: %v", err)
|
||||
}
|
||||
fingerprints = append(fingerprints, fp)
|
||||
}
|
||||
|
||||
// Then, for each operation contained in the action, look for
|
||||
// a permission that apply to it, by comparing the operation name
|
||||
// with permission name. If none is found, use the default permission.
|
||||
for _, operation := range a.Operations {
|
||||
for _, permission := range acl {
|
||||
for permName, _ := range permission {
|
||||
if permName == operation.Module {
|
||||
return verifyPermission(operation, permName, permission, fingerprints)
|
||||
}
|
||||
}
|
||||
}
|
||||
// no specific permission found, apply the default permission
|
||||
var defaultPerm Permission
|
||||
for _, permission := range acl {
|
||||
for permName, _ := range permission {
|
||||
if permName == "default" {
|
||||
defaultPerm = permission
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return verifyPermission(operation, "default", defaultPerm, fingerprints)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -38,8 +38,10 @@ package pgp
|
|||
import (
|
||||
"bytes"
|
||||
"code.google.com/p/go.crypto/openpgp"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"mig/pgp/verify"
|
||||
)
|
||||
|
||||
// TransformArmoredPubKeysToKeyring takes a list of public PGP key in armored form and transforms
|
||||
|
@ -72,3 +74,19 @@ func ArmoredPubKeysToKeyring(pubkeys []string) (keyring io.Reader, keycount int,
|
|||
keyring = bytes.NewReader(buf.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
// TransformArmoredPubKeysToKeyring takes a list of public PGP key in armored form and transforms
|
||||
// it into a keyring that can be used in other openpgp's functions
|
||||
func GetFingerprintFromSignature(data string, signature string, keyring io.Reader) (fingerprint string, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("GetFingerprintFromSignature() -> %v", e)
|
||||
}
|
||||
}()
|
||||
_, entity, err := verify.Verify(data, signature, keyring)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fingerprint = hex.EncodeToString(entity.PrimaryKey.Fingerprint[:])
|
||||
return
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ func Verify(data string, signature string, keyring io.Reader) (valid bool, entit
|
|||
valid = false
|
||||
|
||||
// re-armor signature and transform into io.Reader
|
||||
sigReader := strings.NewReader(reArmor(signature))
|
||||
sigReader := strings.NewReader(reArmorSignature(signature))
|
||||
|
||||
// decode armor
|
||||
sigBlock, err := armor.Decode(sigReader)
|
||||
|
@ -82,9 +82,9 @@ func Verify(data string, signature string, keyring io.Reader) (valid bool, entit
|
|||
return
|
||||
}
|
||||
|
||||
// reArmor takes a single line armor and turns it back into an PGP-style
|
||||
// reArmorSignature takes a single line armor and turns it back into an PGP-style
|
||||
// multi-line armored string (thank you, camlistore folks)
|
||||
func reArmor(line string) string {
|
||||
func reArmorSignature(line string) string {
|
||||
lastEq := strings.LastIndex(line, "=")
|
||||
if lastEq == -1 {
|
||||
return ""
|
||||
|
|
Загрузка…
Ссылка в новой задаче