github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/go/tao/datalog_guard_test.go (about)

     1  // Copyright (c) 2014, Kevin Walsh.  All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package tao
    16  
    17  import (
    18  	"fmt"
    19  	"io/ioutil"
    20  	"os"
    21  	"testing"
    22  
    23  	"github.com/golang/protobuf/proto"
    24  	"github.com/jlmucb/cloudproxy/go/tao/auth"
    25  )
    26  
    27  func makeDatalogGuard() (*DatalogGuard, *Keys, string, error) {
    28  	tmpdir, err := ioutil.TempDir("", "test_datalog_guard")
    29  	if err != nil {
    30  		return nil, nil, "",
    31  			fmt.Errorf("Couldn't get a temp directory for the datalog guard test")
    32  	}
    33  	keys, err := NewTemporaryKeys(Signing)
    34  	if err != nil {
    35  		return nil, nil, "", err
    36  	}
    37  	g, err := NewDatalogGuardFromConfig(keys.VerifyingKey, DatalogGuardDetails{
    38  		SignedRulesPath: proto.String(tmpdir + "/rules"),
    39  	})
    40  	if err != nil {
    41  		return nil, nil, "", err
    42  	}
    43  
    44  	// Add a bogus rule.
    45  	bogusOSString := `ext.PCRs("17, 18", "000, 000")`
    46  	var prin auth.PrinTail
    47  	fmt.Sscanf(bogusOSString, "%s", &prin)
    48  	pred := auth.MakePredicate("BogusTPM", prin)
    49  	if err = g.AddRule(fmt.Sprint(pred)); err != nil {
    50  		return nil, nil, "", err
    51  	}
    52  	return g, keys, tmpdir, nil
    53  }
    54  
    55  var subj = auth.NewKeyPrin([]byte("test1"))  // key([1b4f0e9851971998e732078544c96b36c3d01cedf7caa332359d6f1d83567014])
    56  var subj2 = auth.NewKeyPrin([]byte("test2")) // key([60303ae22b998861bce3b28f33eec1be758a213c86c93c076dbe9f558c11c752])
    57  
    58  
    59  func TestDatalogSaveReload(t *testing.T) {
    60  	g, keys, tmpdir, err := makeDatalogGuard()
    61  	if err != nil {
    62  		t.Fatal(err)
    63  	}
    64  	defer os.RemoveAll(tmpdir)
    65  	err = g.Save(keys.SigningKey)
    66  	if err != nil {
    67  		t.Fatal(err)
    68  	}
    69  	err = g.ReloadIfModified()
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  	err = g.Authorize(subj, "read", []string{"somefile"})
    74  	if err != nil {
    75  		t.Fatal(err)
    76  	}
    77  	err = g.Save(keys.SigningKey)
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  	err = g.ReloadIfModified()
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  	if g.RuleCount() != 2 {
    86  		t.Fatal("wrong number of rules")
    87  	}
    88  	if g.GetRule(1) != `Authorized(key([1b4f0e9851971998e732078544c96b36c3d01cedf7caa332359d6f1d83567014]), "read", "somefile")` {
    89  		t.Fatalf("wrong rule: %s", g.GetRule(1))
    90  	}
    91  }
    92  
    93  func TestDatalogAuthorizeRetract(t *testing.T) {
    94  	g, _, tmpdir, err := makeDatalogGuard()
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  	defer os.RemoveAll(tmpdir)
    99  
   100  	err = g.Authorize(subj, "read", []string{"somefile"})
   101  	if err != nil {
   102  		t.Fatal(err)
   103  	}
   104  
   105  	ok := g.IsAuthorized(subj, "read", []string{"somefile"})
   106  	if !ok {
   107  		t.Fatal("denied, should have been authorized")
   108  	}
   109  
   110  	ok = g.IsAuthorized(subj, "read", []string{"otherfile"})
   111  	if ok {
   112  		t.Fatal("authorized, should have been denied")
   113  	}
   114  
   115  	err = g.Retract(subj, "read", []string{"somefile"})
   116  	if err != nil {
   117  		t.Fatal(err)
   118  	}
   119  
   120  	ok = g.IsAuthorized(subj, "read", []string{"somefile"})
   121  	if ok {
   122  		t.Fatal("authorized, should have been denied")
   123  	}
   124  }
   125  
   126  func TestDatalogRules(t *testing.T) {
   127  	g, _, tmpdir, err := makeDatalogGuard()
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  	defer os.RemoveAll(tmpdir)
   132  
   133  	err = g.AddRule(fmt.Sprintf(`(forall F: IsFile(F) implies Authorized(%v, "read", F))`, subj))
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	err = g.AddRule(fmt.Sprintf(`IsFile("somefile")`))
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  
   143  	err = g.AddRule(fmt.Sprintf(`IsFile("otherfile")`))
   144  	if err != nil {
   145  		t.Fatal(err)
   146  	}
   147  
   148  	ok := g.IsAuthorized(subj, "read", []string{"somefile"})
   149  	if !ok {
   150  		t.Fatal("denied, should have been authorized")
   151  	}
   152  
   153  	ok = g.IsAuthorized(subj, "read", []string{"otherfile"})
   154  	if !ok {
   155  		t.Fatal("denied, should have been authorized")
   156  	}
   157  
   158  	ok = g.IsAuthorized(subj, "write", []string{"somefile"})
   159  	if ok {
   160  		t.Fatal("authorized, should have been denied")
   161  	}
   162  
   163  	ok = g.IsAuthorized(subj2, "read", []string{"somefile"})
   164  	if ok {
   165  		t.Fatal("authorized, should have been denied")
   166  	}
   167  }
   168  
   169  // datalogProg contains simple test rules for authorization.
   170  var datalogProg = []string{
   171  	"(forall P: MemberProgram(P) implies Authorized(P, \"Execute\"))",
   172  	"(MemberProgram(key([70])))",
   173  }
   174  
   175  func TestDatalogSimpleTranslation(t *testing.T) {
   176  	g, keys, tmpdir, err := makeDatalogGuard()
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  	defer os.RemoveAll(tmpdir)
   181  
   182  	for _, s := range datalogProg {
   183  		if err := g.AddRule(s); err != nil {
   184  			t.Fatal("Couldn't add rule '", s, "':", err)
   185  		}
   186  	}
   187  
   188  	kprin := auth.Prin{
   189  		Type:    "key",
   190  		KeyHash: auth.Bytes([]byte{0x70}),
   191  	}
   192  	if !g.IsAuthorized(kprin, "Execute", nil) {
   193  		t.Fatal("Simple authorization check failed")
   194  	}
   195  
   196  	if err := g.Save(keys.SigningKey); err != nil {
   197  		t.Fatal("Couldn't save the guard:", err)
   198  	}
   199  
   200  	ok, err := g.Query("MemberProgram(key([70]))")
   201  	if err != nil {
   202  		t.Fatal("Couldn't query the guard:", err)
   203  	}
   204  	if !ok {
   205  		t.Fatal("A simple sanity-check query failed")
   206  	}
   207  
   208  	ok, err = g.Query("Authorized(key([70]), \"Execute\")")
   209  	if err != nil {
   210  		t.Fatal("Couldn't query the guard:", err)
   211  	}
   212  	if !ok {
   213  		t.Fatal("A simple authorized query didn't succeed")
   214  	}
   215  }
   216  
   217  // datalogSubprinProg contains rules that use the custom primitive subprin.
   218  var datalogSubprinProg = []string{
   219  	"(forall Y: forall P: forall Q: TrustedOS(P) and TrustedProgramHash(Q) and Subprin(Y, P, Q) implies Authorized(Y, \"Execute\"))",
   220  	"(TrustedOS(key([70])))",
   221  	"(TrustedProgramHash(ext.Hash([71])))",
   222  }
   223  
   224  func TestDatalogSubprin(t *testing.T) {
   225  	g, _, tmpdir, err := makeDatalogGuard()
   226  	if err != nil {
   227  		t.Fatal(err)
   228  	}
   229  	defer os.RemoveAll(tmpdir)
   230  
   231  	for _, s := range datalogSubprinProg {
   232  		if err := g.AddRule(s); err != nil {
   233  			t.Fatal("Couldn't add rule '", s, "':", err)
   234  		}
   235  	}
   236  
   237  	pprin := auth.Prin{
   238  		Type:    "key",
   239  		KeyHash: auth.Bytes([]byte{0x70}),
   240  		Ext: []auth.PrinExt{
   241  			auth.PrinExt{
   242  				Name: "Hash",
   243  				Arg:  []auth.Term{auth.Bytes([]byte{0x71})},
   244  			},
   245  		},
   246  	}
   247  	if !g.IsAuthorized(pprin, "Execute", nil) {
   248  		t.Fatal("Subprin authorization check failed")
   249  	}
   250  }
   251  
   252  var datalogFormLengthChecks = []struct {
   253  	query  string
   254  	length int
   255  }{
   256  	{"P(key([70]).Program([71]))", 2},
   257  	{"not P(key([70]).Program([71]))", 2},
   258  	{"P()", 0},
   259  	{"P() and Q(key([70]).Program([71]))", 2},
   260  	{"P() or Q(key([70]).Program([71]))", 2},
   261  	{"P(key([70]).Program([71])) and Q(key([70]).Program([71]))", 2},
   262  	{"P(key([70]).Program([71]).N([72])) and Q(key([70]).Program([71]))", 3},
   263  	{"P() implies Q(key([70]).Program([71]))", 2},
   264  	{"tpm([70]) speaksfor key([70]).Program([71])", 2},
   265  	{"tpm([70]).PCRs(\"17,18\", \"a4c7,b876\") says key([72]) speaksfor key([73]).A(\"B\").C()", 3},
   266  	{"forall X: forall Y: TPM(X) and TrustedHost(Y) implies M(key([70]))", 1},
   267  	{"exists X: P(X)", 0},
   268  	{"exists X: P(X, key([71]).P())", 2},
   269  }
   270  
   271  func TestDatalogMaxFormLength(t *testing.T) {
   272  	for _, v := range datalogFormLengthChecks {
   273  		var form auth.AnyForm
   274  		if fmt.Sscanf("("+v.query+")", "%v", &form); form.Form == nil {
   275  			t.Errorf("fmt.Sscanf(%q) failed", v.query)
   276  		}
   277  		l := getMaxFormLength(form.Form)
   278  		if l != v.length {
   279  			t.Errorf("%q had length %d, want %d", v.query, l, v.length)
   280  		}
   281  	}
   282  }
   283  
   284  var datalogTermLengthChecks = []struct {
   285  	query  string
   286  	length int
   287  }{
   288  	{"5", 0},
   289  	{"\"a string\"", 0},
   290  	{"[716475a8e3]", 0},
   291  	{"ext.Program([7154])", 1},
   292  	{"ext.P().Q().R().S().T()", 5},
   293  	{"key([70]).Program([71])", 2},
   294  }
   295  
   296  func TestDatalogMaxTermLength(t *testing.T) {
   297  	for _, v := range datalogTermLengthChecks {
   298  		var term auth.AnyTerm
   299  		if fmt.Sscanf(v.query, "%v", &term); term.Term == nil {
   300  			t.Errorf("fmt.Sscanf(%q) failed", v.query)
   301  		}
   302  		l := getMaxTermLength(term.Term)
   303  		if l != v.length {
   304  			t.Errorf("%q had length %d, want %d", v.query, l, v.length)
   305  		}
   306  	}
   307  }
   308  
   309  var datalogLoops = []string{
   310  	"(forall X: forall Y: forall P: A(X) and B(Y) and Subprin(P, X, Y) implies A(P))",
   311  	"(A(key([70])))",
   312  	"(B(ext.Hash([71])))",
   313  }
   314  
   315  var datalogLoopQueries = []struct {
   316  	query    string
   317  	expected bool
   318  }{
   319  	{"A(key([70]).Hash([71]))", true},
   320  	{"A(key([70]))", true},
   321  	{"A(key([70]).Hash([72]))", false},
   322  }
   323  
   324  func TestDatalogLoop(t *testing.T) {
   325  	g, key, tmpdir, err := makeDatalogGuard()
   326  	if err != nil {
   327  		t.Fatalf("makeDatalogGuard failed: %v", err)
   328  	}
   329  	defer os.RemoveAll(tmpdir)
   330  	if err = g.Save(key.SigningKey); err != nil {
   331  		t.Fatalf("Failed to save DatalogGuard: %v", err)
   332  	}
   333  
   334  	for _, s := range datalogLoops {
   335  		if err := g.AddRule(s); err != nil {
   336  			t.Fatalf("Couldn't add rule '%s': %s", s, err)
   337  		}
   338  	}
   339  
   340  	for _, q := range datalogLoopQueries {
   341  		ok, err := g.Query(q.query)
   342  		if err != nil {
   343  			t.Errorf("Query(%q) failed: %v", q.query, err)
   344  		}
   345  		if ok != q.expected {
   346  			t.Errorf("Query(%q) = %t; want %t", q.query, ok, q.expected)
   347  		}
   348  	}
   349  }
   350  
   351  func TestDatalogSignedSubprincipal(t *testing.T) {
   352  	g, key, tmpdir, err := makeDatalogGuard()
   353  	if err != nil {
   354  		t.Fatalf("makeDatalogGuard failed: %v", err)
   355  	}
   356  	defer os.RemoveAll(tmpdir)
   357  	name := g.Subprincipal().String()
   358  	k := key.SigningKey.ToPrincipal().String()
   359  	if name != ".DatalogGuard("+k+")" {
   360  		t.Fatalf("Datalog guard has wrong name: %v", name)
   361  	}
   362  }
   363  
   364  func TestDatalogUnsignedSubprincipal(t *testing.T) {
   365  	g := NewTemporaryDatalogGuard()
   366  	err := g.Authorize(subj, "read", []string{"somefile"})
   367  	if err != nil {
   368  		t.Fatal(err)
   369  	}
   370  	name := g.Subprincipal().String()
   371  	if name != ".DatalogGuard([289de2090f7354a8effd50428d9fa56b869cb9f67f002de5e5ebe7d3caa0e931])" {
   372  		t.Fatalf("Datalog guard has wrong name: %v", name)
   373  	}
   374  }