github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/go/tao/datalog_guard.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 // This interface was derived from the code in src/tao/tao_guard.h. 16 17 package tao 18 19 import ( 20 "crypto/sha256" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "strings" 25 "time" 26 27 "github.com/golang/glog" 28 "github.com/golang/protobuf/proto" 29 "github.com/jlmucb/cloudproxy/go/tao/auth" 30 "github.com/jlmucb/cloudproxy/go/util" 31 "github.com/kevinawalsh/datalog" 32 "github.com/kevinawalsh/datalog/dlengine" 33 ) 34 35 // Signing context for signatures on a set of Tao datalog rules. 36 const ( 37 DatalogRulesSigningContext = "Datalog Rules Signing Context V1" 38 ) 39 40 // DatalogGuard implements a datalog-based policy engine. Rules in this engine 41 // have the form: 42 // (forall X, Y, Z... : F implies G) 43 // where 44 // F is a predicate or a conjunction of predicates 45 // G is a predicate 46 // All predicate arguments must be either concrete terms (Int, Str, Prin, etc.) 47 // or term-valued variables (TermVar) bound by the quantification. Any variable 48 // appearing in G must also appear in F. If there are no variables, the 49 // quantification can be omitted. The implication and its antecedent F can be 50 // omitted (in which case there can be no variables so the quantification must 51 // be omitted as well). 52 // 53 // TODO(kwalsh) We could easily support a slightly broader class of formulas, 54 // e.g. by allowing G to be a conjunct of predicates, or by allowing a 55 // disjunction of conjunctions for F. Anything beyond that seems complicated. 56 // 57 // Datalog translation 58 // 59 // We assume K speaksfor the guard, where K is the key used to sign the policy 60 // file. If there is no signing key, a temporary principal (with a bogus key) is 61 // used for K instead. All deduction takes place within the worldview of Guard. 62 // Other than this relationship between K and the guard, we don't model the says 63 // and speaksfor logic within datalog. 64 // 65 // Term objects are usually translated to datalog by just printing them. In this 66 // case, a Prin object must not contain any TermVar objects. TermVar objects 67 // must be uppercase. 68 // 69 // "Term says Pred(...)" is translated to "says(Term, \"Pred\", ...)". 70 // 71 // "Pred(...)" alone is translated to "says(K, \"Pred\", ...)". 72 // 73 // "forall ... F1 and F2 and ... imp G" is translated to "G :- F1, F2, ...". 74 // Not safe for concurrent use by goroutines. 75 type DatalogGuard struct { 76 Config DatalogGuardDetails 77 Key *Verifier 78 // TODO(kwalsh) maybe use a version number or timestamp inside the file? 79 modTime time.Time // Modification time of signed rules file at time of reading. 80 db DatalogRules 81 dl *dlengine.Engine 82 sp *subprinPrim 83 } 84 85 // subprinPrim is a custom datalog primitive that implements subprincipal 86 // detection. The predicate Subprin(S, P, E) in auth is special-cased in 87 // DatalogGuard to write in datalog to subprin/3 with arguments S, P, E. 88 type subprinPrim struct { 89 datalog.DistinctPred 90 max int 91 } 92 93 // String returns a string representation of the subprin custom datalog 94 // predicate. 95 func (sp *subprinPrim) String() string { 96 return "subprin" 97 } 98 99 func (sp *subprinPrim) Assert(c *datalog.Clause) error { 100 return newError("datalog: can't assert for custom predicates") 101 } 102 103 func (sp *subprinPrim) Retract(c *datalog.Clause) error { 104 return newError("datalog: can't retract for custom predicates") 105 } 106 107 // parseRootExtPrins parses a pair of terms as a key/tpm principal and an 108 // extension principal tail. Both Terms must implement fmt.Stringer. 109 func parseRootExtPrins(o datalog.Term, e datalog.Term) (oprin auth.Prin, eprin auth.PrinTail, err error) { 110 // Report subprin(O.E, O, E) as discovered. 111 ostringer, ok1 := o.(fmt.Stringer) 112 estringer, ok2 := e.(fmt.Stringer) 113 if !ok1 || !ok2 { 114 err = fmt.Errorf("arguments 2 and 3 must implement fmt.Stringer in subprin/3") 115 return 116 } 117 118 // The first must be a regular rooted principal, and the second must be 119 // an ext principal tail. 120 var ostr string 121 if _, err = fmt.Sscanf(ostringer.String(), "%q", &ostr); err != nil { 122 return 123 } 124 125 var estr string 126 if _, err = fmt.Sscanf(estringer.String(), "%q", &estr); err != nil { 127 return 128 } 129 130 if _, err = fmt.Sscanf(ostr, "%v", &oprin); err != nil { 131 return 132 } 133 if _, err = fmt.Sscanf(estr, "%v", &eprin); err != nil { 134 return 135 } 136 return 137 } 138 139 // parseCompositePrin parses a Term (which must implement fmt.Stringer) as a 140 // principal with at least one extension. 141 func parseCompositePrin(p datalog.Term) (prin auth.Prin, err error) { 142 // Parse p as Parent.Ext and report subprin(Parent.Ext, Parent, Ext). 143 pstringer, ok := p.(fmt.Stringer) 144 if !ok { 145 err = fmt.Errorf("A composite principal must be a Stringer") 146 return 147 } 148 149 // Due to the way the translation works between DatalogGuard and the Datalog 150 // engine, this is a quoted string. So, trim the quotes at the beginning and 151 // the end of the string before parsing it. 152 var pstr string 153 if _, err = fmt.Sscanf(pstringer.String(), "%q", &pstr); err != nil { 154 return 155 } 156 if _, err = fmt.Sscanf(pstr, "%v", &prin); err != nil { 157 return 158 } 159 if len(prin.Ext) < 1 { 160 err = fmt.Errorf("A composite principal must have extensions") 161 return 162 } 163 164 return 165 } 166 167 // Search implements the subprinPrim custom datalog primitive by parsing 168 // constant arguments of subprin/3 as principals and reporting any clauses it 169 // discovers. 170 func (sp *subprinPrim) Search(target *datalog.Literal, discovered func(c *datalog.Clause)) { 171 p := target.Arg[0] 172 o := target.Arg[1] 173 e := target.Arg[2] 174 if p.Constant() && o.Variable() && e.Variable() { 175 prin, err := parseCompositePrin(p) 176 if err != nil { 177 return 178 } 179 extIndex := len(prin.Ext) - 1 180 trimmedPrin := auth.Prin{ 181 Type: prin.Type, 182 KeyHash: prin.KeyHash, 183 Ext: prin.Ext[:extIndex], 184 } 185 extPrin := auth.PrinTail{ 186 Ext: []auth.PrinExt{prin.Ext[extIndex]}, 187 } 188 189 parentIdent := dlengine.NewIdent(fmt.Sprintf("%q", trimmedPrin.String())) 190 extIdent := dlengine.NewIdent(fmt.Sprintf("%q", extPrin.String())) 191 discovered(datalog.NewClause(datalog.NewLiteral(sp, p, parentIdent, extIdent))) 192 } else if p.Variable() && o.Constant() && e.Constant() { 193 oprin, eprin, err := parseRootExtPrins(o, e) 194 if err != nil { 195 return 196 } 197 oprin.Ext = append(oprin.Ext, eprin.Ext...) 198 oeIdent := dlengine.NewIdent(fmt.Sprintf("%q", oprin.String())) 199 if len(oprin.Ext)+1 <= sp.max { 200 discovered(datalog.NewClause(datalog.NewLiteral(sp, oeIdent, o, e))) 201 } 202 } else if p.Constant() && o.Constant() && e.Constant() { 203 // Check that the constraint holds and report it as discovered. 204 prin, err := parseCompositePrin(p) 205 if err != nil { 206 return 207 } 208 oprin, eprin, err := parseRootExtPrins(o, e) 209 if err != nil { 210 return 211 } 212 213 // Extend the root principal with the extension from the ext principal 214 // and check identity. Make sure the constructed principal does 215 // not exceed the given maximum principal length. 216 oprin.Ext = append(oprin.Ext, eprin.Ext...) 217 if prin.Identical(oprin) { 218 discovered(datalog.NewClause(datalog.NewLiteral(sp, p, o, e))) 219 } 220 } 221 } 222 223 // NewTemporaryDatalogGuard returns a new datalog guard with a fresh, unsigned, 224 // non-persistent rule set. It adds a custom predicate subprin(P, O, E) to check 225 // if a principal P is a subprincipal O.E. 226 func NewTemporaryDatalogGuard() Guard { 227 sp := &subprinPrim{max: 1} 228 sp.SetArity(3) 229 eng := dlengine.NewEngine() 230 eng.AddPred(sp) 231 return &DatalogGuard{dl: eng, sp: sp} 232 } 233 234 // NewDatalogGuardFromConfig returns a new datalog guard that uses a signed, 235 // persistent rule set. ReloadIfModified() should be called to load the rule 236 // set. 237 func NewDatalogGuardFromConfig(verifier *Verifier, config DatalogGuardDetails) (*DatalogGuard, error) { 238 if verifier == nil || config.GetSignedRulesPath() == "" { 239 return nil, newError("datalog guard missing key or path") 240 } 241 dg := NewDatalogGuard(verifier) 242 dg.Config = config 243 return dg, nil 244 } 245 246 // NewDatalogGuard returns a new datalog guard without configuring a rules 247 // file. 248 func NewDatalogGuard(verifier *Verifier) *DatalogGuard { 249 sp := &subprinPrim{max: 1} 250 sp.SetArity(3) 251 eng := dlengine.NewEngine() 252 eng.AddPred(sp) 253 dg := &DatalogGuard{Key: verifier, dl: eng, sp: sp} 254 dg.Config.SignedRulesPath = nil 255 return dg 256 } 257 258 // Subprincipal returns subprincipal DatalogGuard, for temporary guards, or 259 // DatalogGuard(<key>) for persistent guards. 260 func (g *DatalogGuard) Subprincipal() auth.SubPrin { 261 if g.Key == nil { 262 rules, err := proto.Marshal(&g.db) 263 if err != nil { 264 return nil 265 } 266 hash := sha256.Sum256(rules) 267 e := auth.PrinExt{Name: "DatalogGuard", Arg: []auth.Term{auth.Bytes(hash[:])}} 268 return auth.SubPrin{e} 269 } 270 e := auth.PrinExt{Name: "DatalogGuard", Arg: []auth.Term{g.Key.ToPrincipal()}} 271 return auth.SubPrin{e} 272 } 273 274 // ReloadIfModified reads all persistent policy data from disk if the file 275 // timestamp is more recent than the last time it was read. 276 func (g *DatalogGuard) ReloadIfModified() error { 277 if g.Key == nil { 278 return nil 279 } 280 file, err := os.Open(g.Config.GetSignedRulesPath()) 281 if err != nil { 282 return err 283 } 284 defer file.Close() 285 286 // before parsing, check the timestamp 287 info, err := file.Stat() 288 if err != nil { 289 return err 290 } 291 if !info.ModTime().After(g.modTime) { 292 return nil 293 } 294 295 serialized, err := ioutil.ReadAll(file) 296 if err != nil { 297 return err 298 } 299 var sdb SignedDatalogRules 300 if err := proto.Unmarshal(serialized, &sdb); err != nil { 301 return err 302 } 303 if ok, err := g.Key.Verify(sdb.SerializedRules, DatalogRulesSigningContext, sdb.Signature); !ok { 304 if err != nil { 305 return err 306 } 307 return newError("datalog rule signature did not verify") 308 } 309 var db DatalogRules 310 if err := proto.Unmarshal(sdb.SerializedRules, &db); err != nil { 311 return err 312 } 313 // Only clear the rules set, since g.assert already skips datalog rules that 314 // are already present in the engine. 315 g.db.Rules = nil 316 g.modTime = info.ModTime() 317 for _, rule := range db.Rules { 318 r, err := auth.UnmarshalForm(rule) 319 if err != nil { 320 return err 321 } 322 err = g.assert(r) 323 if err != nil { 324 return err 325 } 326 } 327 return nil 328 } 329 330 // GetSignedDatalogRules serializes and signs the datalog rules and returns 331 // a SignedDatalogRules pointer. 332 func (g *DatalogGuard) GetSignedDatalogRules(signer *Signer) (*SignedDatalogRules, error) { 333 if signer == nil { 334 return nil, newError("datalog temporary ruleset can't be saved") 335 } 336 rules, err := proto.Marshal(&g.db) 337 if err != nil { 338 return nil, err 339 } 340 sig, err := signer.Sign(rules, DatalogRulesSigningContext) 341 if err != nil { 342 return nil, err 343 } 344 sdb := &SignedDatalogRules{ 345 SerializedRules: rules, 346 Signature: sig, 347 } 348 return sdb, nil 349 } 350 351 // Save writes all persistent policy data to disk, signed by key. 352 func (g *DatalogGuard) Save(signer *Signer) error { 353 sdb, err := g.GetSignedDatalogRules(signer) 354 if err != nil { 355 return err 356 } 357 serialized, err := proto.Marshal(sdb) 358 if err != nil { 359 return err 360 } 361 if err := util.WritePath(g.Config.GetSignedRulesPath(), serialized, 0777, 0666); err != nil { 362 return err 363 } 364 return nil 365 } 366 367 func setContains(vars []string, v string) bool { 368 for _, s := range vars { 369 if s == v { 370 return true 371 } 372 } 373 return false 374 } 375 376 func setRemove(vars *[]string, v string) { 377 if vars == nil { 378 return 379 } 380 for i := 0; i < len(*vars); i++ { 381 if (*vars)[i] == v { 382 (*vars)[i] = (*vars)[len(*vars)-1] 383 *vars = (*vars)[:len(*vars)-1] 384 i-- 385 } 386 } 387 } 388 389 func stripQuantifiers(q auth.Form) (f auth.Form, vars []string) { 390 for { 391 var v string 392 switch f := q.(type) { 393 case auth.Forall: 394 v = f.Var 395 q = f.Body 396 case *auth.Forall: 397 v = f.Var 398 q = f.Body 399 default: 400 return q, vars 401 } 402 if !setContains(vars, v) { 403 vars = append(vars, v) 404 } 405 } 406 } 407 408 func flattenConjuncts(f ...auth.Form) (conjuncts []auth.Form) { 409 for _, f := range f { 410 switch f := f.(type) { 411 case auth.And: 412 conjuncts = append(conjuncts, flattenConjuncts(f.Conjunct...)...) 413 case *auth.And: 414 conjuncts = append(conjuncts, flattenConjuncts(f.Conjunct...)...) 415 default: 416 conjuncts = append(conjuncts, f) 417 } 418 } 419 return 420 } 421 422 func stripConditions(f auth.Form) (conds []auth.Form, consequent auth.Form) { 423 switch f := f.(type) { 424 case auth.Implies: 425 conds = flattenConjuncts(f.Antecedent) 426 consequent = f.Consequent 427 case *auth.Implies: 428 conds = flattenConjuncts(f.Antecedent) 429 consequent = f.Consequent 430 default: 431 consequent = f 432 } 433 return 434 } 435 436 func checkTermVarUsage(vars []string, unusedVars *[]string, e ...auth.Term) error { 437 for _, e := range e { 438 switch e := e.(type) { 439 case auth.TermVar, *auth.TermVar: 440 if !setContains(vars, e.String()) { 441 return fmt.Errorf("illegal quantification variable: %v\n", e) 442 } 443 setRemove(unusedVars, e.String()) 444 case auth.Prin: 445 err := checkTermVarUsage(vars, unusedVars, e.KeyHash) 446 if err != nil { 447 return err 448 } 449 for _, ext := range e.Ext { 450 err := checkTermVarUsage(vars, unusedVars, ext.Arg...) 451 if err != nil { 452 return err 453 } 454 } 455 case *auth.Prin: 456 err := checkTermVarUsage(vars, unusedVars, e.KeyHash) 457 if err != nil { 458 return err 459 } 460 for _, ext := range e.Ext { 461 err := checkTermVarUsage(vars, unusedVars, ext.Arg...) 462 if err != nil { 463 return err 464 } 465 } 466 } 467 } 468 return nil 469 } 470 471 func checkFormVarUsage(vars []string, unusedVars *[]string, e ...auth.Form) error { 472 for _, e := range e { 473 switch e := e.(type) { 474 case auth.Pred: 475 err := checkTermVarUsage(vars, unusedVars, e.Arg...) 476 if err != nil { 477 return err 478 } 479 case *auth.Pred: 480 err := checkTermVarUsage(vars, unusedVars, e.Arg...) 481 if err != nil { 482 return err 483 } 484 } 485 } 486 return nil 487 } 488 489 func (g *DatalogGuard) stmtToDatalog(f auth.Form, vars []string, unusedVars *[]string) (string, error) { 490 speaker := "guard" 491 if g.Key != nil { 492 speaker = g.Key.ToPrincipal().String() 493 } 494 stmt, ok := f.(*auth.Says) 495 if !ok { 496 var val auth.Says 497 val, ok = (f).(auth.Says) 498 if ok { 499 stmt = &val 500 } 501 } 502 if ok { 503 err := checkTermVarUsage(vars, unusedVars, stmt.Speaker) 504 if err != nil { 505 return "", err 506 } 507 } 508 err := checkFormVarUsage(vars, unusedVars, f) 509 if err != nil { 510 return "", err 511 } 512 pred, ok := f.(*auth.Pred) 513 if !ok { 514 var val auth.Pred 515 val, ok = f.(auth.Pred) 516 if ok { 517 pred = &val 518 } 519 } 520 if !ok { 521 return "", fmt.Errorf("unsupported datalog statement: %v", f) 522 } 523 // Special-case: the principal named "Subprin" maps directly to subprinPrim. 524 var args []string 525 if pred.Name != "Subprin" { 526 args = []string{fmt.Sprintf("%q", speaker), fmt.Sprintf("%q", pred.Name)} 527 } 528 529 for _, arg := range pred.Arg { 530 if _, ok := arg.(auth.TermVar); ok { 531 // Don't quote variables, since otherwise they won't work as 532 // variables in the datalog representation. 533 args = append(args, fmt.Sprintf("%s", arg.String())) 534 } else { 535 args = append(args, fmt.Sprintf("%q", arg.String())) 536 } 537 } 538 if pred.Name == "Subprin" { 539 s := "subprin(" + strings.Join(args, ", ") + ")" 540 return s, nil 541 542 } 543 return "says(" + strings.Join(args, ", ") + ")", nil 544 } 545 546 // formToDatalogRule converts (a subset of) auth.Form to datalog syntax. 547 func (g *DatalogGuard) formToDatalogRule(f auth.Form) (string, error) { 548 f, vars := stripQuantifiers(f) 549 conditions, consequent := stripConditions(f) 550 // vars must be upper-case 551 for _, v := range vars { 552 if len(v) == 0 || v[0] < 'A' || v[0] > 'Z' { 553 return "", fmt.Errorf("illegal quantification variable") 554 } 555 } 556 // convert the conditions 557 dcond := make([]string, len(conditions)) 558 unusedVars := append([]string{}, vars...) 559 for i, cond := range conditions { 560 var err error 561 dcond[i], err = g.stmtToDatalog(cond, vars, &unusedVars) 562 if err != nil { 563 return "", err 564 } 565 } 566 // check for safety 567 if len(unusedVars) > 0 { 568 return "", fmt.Errorf("unsafe datalog variable usage: % s", unusedVars) 569 } 570 goal, err := g.stmtToDatalog(consequent, vars, nil) 571 if err != nil { 572 return "", err 573 } 574 if len(dcond) > 0 { 575 return goal + " :- " + strings.Join(dcond, ", "), nil 576 } 577 return goal, nil 578 } 579 580 func (g *DatalogGuard) findRule(f auth.Form) (string, int, error) { 581 rule, err := g.formToDatalogRule(f) 582 if err != nil { 583 return "", -1, err 584 } 585 for i, ser := range g.db.Rules { 586 f2, err := auth.UnmarshalForm(ser) 587 if err != nil { 588 continue 589 } 590 rule2, err := g.formToDatalogRule(f2) 591 if err != nil { 592 continue 593 } 594 if rule == rule2 { 595 return rule, i, nil 596 } 597 } 598 return rule, -1, nil 599 } 600 601 func (g *DatalogGuard) assert(f auth.Form) error { 602 rule, idx, err := g.findRule(f) 603 if err != nil { 604 return err 605 } 606 if idx >= 0 { 607 return nil 608 } 609 err = g.dl.Assert(rule) 610 if err != nil { 611 return err 612 } 613 g.db.Rules = append(g.db.Rules, auth.Marshal(f)) 614 return nil 615 } 616 617 func (g *DatalogGuard) retract(f auth.Form) error { 618 rule, idx, err := g.findRule(f) 619 if err != nil { 620 return err 621 } 622 if idx < 0 { 623 return fmt.Errorf("no such rule") 624 } 625 err = g.dl.Retract(rule) 626 if err != nil { 627 return err 628 } 629 g.db.Rules = append(g.db.Rules[:idx], g.db.Rules[idx+1:]...) 630 return nil 631 } 632 633 func max(x, y int) int { 634 if x > y { 635 return x 636 } 637 return y 638 } 639 640 // Figure out the max length of a principal in a term, where the length of a 641 // principal is one more than the length of the extensions of the principal. 642 func getMaxTermLength(t auth.Term) int { 643 if t == nil { 644 return 0 645 } 646 647 switch t.(type) { 648 case auth.Prin: 649 // TODO(tmroeder): this and the next case are not fully 650 // general, since there might be an Arg that has a longer 651 // principal. 652 return 1 + len(t.(auth.Prin).Ext) 653 case *auth.Prin: 654 return 1 + len(t.(*auth.Prin).Ext) 655 case auth.PrinTail: 656 return len(t.(auth.PrinTail).Ext) 657 case *auth.PrinTail: 658 return len(t.(*auth.PrinTail).Ext) 659 case auth.Str, auth.Bytes, auth.Int, auth.TermVar: 660 return 0 661 default: 662 return 0 663 } 664 } 665 666 // Figure out the max length of a principal in a form, where the length of a 667 // principal is one more than the length of the extensions of the principal. 668 func getMaxFormLength(f auth.Form) int { 669 if f == nil { 670 return 0 671 } 672 673 m := 0 674 switch f.(type) { 675 case auth.Pred: 676 for _, t := range f.(auth.Pred).Arg { 677 m = max(m, getMaxTermLength(t)) 678 } 679 case *auth.Pred: 680 for _, t := range f.(*auth.Pred).Arg { 681 m = max(m, getMaxTermLength(t)) 682 } 683 case auth.Const, *auth.Const: 684 return 0 685 case auth.Not: 686 return getMaxFormLength(f.(auth.Not).Negand) 687 case *auth.Not: 688 return getMaxFormLength(f.(*auth.Not).Negand) 689 case auth.And: 690 for _, c := range f.(auth.And).Conjunct { 691 m = max(m, getMaxFormLength(c)) 692 } 693 case *auth.And: 694 for _, c := range f.(*auth.And).Conjunct { 695 m = max(m, getMaxFormLength(c)) 696 } 697 case auth.Or: 698 for _, d := range f.(auth.Or).Disjunct { 699 m = max(m, getMaxFormLength(d)) 700 } 701 case *auth.Or: 702 for _, d := range f.(*auth.Or).Disjunct { 703 m = max(m, getMaxFormLength(d)) 704 } 705 case auth.Implies: 706 first := getMaxFormLength(f.(auth.Implies).Antecedent) 707 second := getMaxFormLength(f.(auth.Implies).Consequent) 708 return max(first, second) 709 case *auth.Implies: 710 first := getMaxFormLength(f.(*auth.Implies).Antecedent) 711 second := getMaxFormLength(f.(*auth.Implies).Consequent) 712 return max(first, second) 713 case auth.Speaksfor: 714 first := getMaxTermLength(f.(auth.Speaksfor).Delegate) 715 second := getMaxTermLength(f.(auth.Speaksfor).Delegator) 716 return max(first, second) 717 case *auth.Speaksfor: 718 first := getMaxTermLength(f.(*auth.Speaksfor).Delegate) 719 second := getMaxTermLength(f.(*auth.Speaksfor).Delegator) 720 return max(first, second) 721 case auth.Says: 722 sl := getMaxTermLength(f.(auth.Says).Speaker) 723 ml := getMaxFormLength(f.(auth.Says).Message) 724 return max(sl, ml) 725 case *auth.Says: 726 sl := getMaxTermLength(f.(*auth.Says).Speaker) 727 ml := getMaxFormLength(f.(*auth.Says).Message) 728 return max(sl, ml) 729 case auth.Forall: 730 return getMaxFormLength(f.(auth.Forall).Body) 731 case *auth.Forall: 732 return getMaxFormLength(f.(*auth.Forall).Body) 733 case auth.Exists: 734 return getMaxFormLength(f.(auth.Exists).Body) 735 case *auth.Exists: 736 return getMaxFormLength(f.(*auth.Exists).Body) 737 default: 738 return 0 739 } 740 741 return m 742 } 743 744 func (g *DatalogGuard) query(f auth.Form) (bool, error) { 745 g.sp.max = getMaxFormLength(f) 746 747 q, err := g.stmtToDatalog(f, nil, nil) 748 if err != nil { 749 return false, err 750 } 751 ans, err := g.dl.Query(q) 752 if err != nil { 753 return false, err 754 } 755 return len(ans) > 0, nil 756 } 757 758 func makeDatalogPredicate(p auth.Prin, op string, args []string) auth.Pred { 759 a := []interface{}{p, op} 760 for _, s := range args { 761 a = append(a, s) 762 } 763 return auth.MakePredicate("Authorized", a...) 764 } 765 766 // Authorize adds an authorization for p to perform op(args). 767 func (g *DatalogGuard) Authorize(p auth.Prin, op string, args []string) error { 768 return g.assert(makeDatalogPredicate(p, op, args)) 769 } 770 771 // Retract removes an authorization for p to perform op(args). 772 func (g *DatalogGuard) Retract(p auth.Prin, op string, args []string) error { 773 return g.retract(makeDatalogPredicate(p, op, args)) 774 } 775 776 // IsAuthorized checks whether p is authorized to perform op(args). 777 func (g *DatalogGuard) IsAuthorized(p auth.Prin, op string, args []string) bool { 778 ok, _ := g.query(makeDatalogPredicate(p, op, args)) 779 return ok 780 } 781 782 // AddRule adds a policy rule. 783 func (g *DatalogGuard) AddRule(rule string) error { 784 glog.Infof("Adding rule '%s'", rule) 785 var r auth.AnyForm 786 _, err := fmt.Sscanf("("+rule+")", "%v", &r) 787 if err != nil { 788 return err 789 } 790 return g.assert(r.Form) 791 } 792 793 // RetractRule removes a rule previously added via AddRule() or the 794 // equivalent Authorize() call. 795 func (g *DatalogGuard) RetractRule(rule string) error { 796 err := g.ReloadIfModified() 797 if err != nil { 798 return err 799 } 800 var r auth.AnyForm 801 _, err = fmt.Sscanf("("+rule+")", "%v", &r) 802 if err != nil { 803 return err 804 } 805 return g.retract(r.Form) 806 } 807 808 // Clear removes all rules. 809 func (g *DatalogGuard) Clear() error { 810 g.db.Rules = nil 811 g.dl = dlengine.NewEngine() 812 return nil 813 } 814 815 // Query the policy. Implementations of this interface should support 816 // at least queries of the form: Authorized(P, op, args...). 817 func (g *DatalogGuard) Query(query string) (bool, error) { 818 err := g.ReloadIfModified() 819 if err != nil { 820 return false, err 821 } 822 var r auth.AnyForm 823 _, err = fmt.Sscanf("("+query+")", "%v", &r) 824 if err != nil { 825 return false, err 826 } 827 828 return g.query(r.Form) 829 } 830 831 // RuleCount returns a count of the total number of rules. 832 func (g *DatalogGuard) RuleCount() int { 833 return len(g.db.Rules) 834 } 835 836 // GetRule returns the ith policy rule, if it exists. 837 func (g *DatalogGuard) GetRule(i int) string { 838 if i < 0 || i >= len(g.db.Rules) { 839 return "" 840 } 841 rule := g.db.Rules[i] 842 r, err := auth.UnmarshalForm(rule) 843 if err != nil { 844 return "" 845 } 846 return r.String() 847 } 848 849 // RuleDebugString returns a debug string for the ith policy rule, if it exists. 850 func (g *DatalogGuard) RuleDebugString(i int) string { 851 if i < 0 || i >= len(g.db.Rules) { 852 return "" 853 } 854 rule := g.db.Rules[i] 855 r, err := auth.UnmarshalForm(rule) 856 if err != nil { 857 return "" 858 } 859 return r.ShortString() 860 } 861 862 // String returns a string suitable for showing users authorization info. 863 func (g *DatalogGuard) String() string { 864 rules := make([]string, len(g.db.Rules)) 865 for i := range g.db.Rules { 866 rules[i] = g.GetRule(i) 867 } 868 return "DatalogGuard{\n" + strings.Join(rules, "\n") + "}\n" 869 }