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 }