blob: e94e59c5007f60cab9e2fbaa866bccbdbb4681bb [file] [log] [blame]
Robert Kroegere95ed6d2015-01-14 17:41:04 -08001// Package acls provides a library to assist servers implementing
Robert Kroegera9f1e6b2015-02-23 18:01:12 -08002// GetACL/SetACL functions and authorizers where there are
3// path-specific ACLs stored individually in files.
Robert Kroegere95ed6d2015-01-14 17:41:04 -08004// TODO(rjkroege): Add unit tests.
5package acls
6
7import (
8 "io/ioutil"
9 "os"
Robert Kroeger8d7a0ef2015-01-14 17:38:40 -080010 "path/filepath"
Robert Kroegere95ed6d2015-01-14 17:41:04 -080011 "sync"
12
Jiri Simsa6ac95222015-02-23 16:11:49 -080013 "v.io/v23/security"
14 "v.io/v23/services/security/access"
15 "v.io/v23/verror"
Jiri Simsa337af232015-02-27 14:36:46 -080016 "v.io/x/lib/vlog"
Robert Kroegere95ed6d2015-01-14 17:41:04 -080017
Jiri Simsaffceefa2015-02-28 11:03:34 -080018 "v.io/x/ref/security/serialization"
Robert Kroegere95ed6d2015-01-14 17:41:04 -080019)
20
21const (
Jiri Simsaffceefa2015-02-28 11:03:34 -080022 pkgPath = "v.io/x/ref/services/mgmt/lib/acls"
Robert Kroegere95ed6d2015-01-14 17:41:04 -080023 sigName = "signature"
24 aclName = "data"
25)
26
27var (
28 ErrOperationFailed = verror.Register(pkgPath+".OperationFailed", verror.NoRetry, "{1:}{2:} operation failed{:_}")
29)
30
Robert Kroegeraa23aba2015-02-27 12:55:05 -080031// PathStore manages storage of a set of ACLs in the filesystem where each
32// path identifies a specific ACL in the set. PathStore synchronizes
33// access to its member ACLs.
34type PathStore struct {
Robert Kroegera5c0ec52015-02-25 16:00:01 -080035 // TODO(rjkroege): Garbage collect the locks map.
36 pthlks map[string]*sync.Mutex
37 lk sync.Mutex
38 principal security.Principal
Robert Kroegere95ed6d2015-01-14 17:41:04 -080039}
40
Robert Kroegeraa23aba2015-02-27 12:55:05 -080041// NewPathStore creates a new instance of the lock map that uses
Robert Kroegera5c0ec52015-02-25 16:00:01 -080042// principal to sign stored ACL files.
Robert Kroegeraa23aba2015-02-27 12:55:05 -080043func NewPathStore(principal security.Principal) *PathStore {
44 return &PathStore{pthlks: make(map[string]*sync.Mutex), principal: principal}
Robert Kroegere95ed6d2015-01-14 17:41:04 -080045}
46
Robert Kroegeraa23aba2015-02-27 12:55:05 -080047// Get returns the TaggedACLMap from the data file in dir.
48func (store PathStore) Get(dir string) (access.TaggedACLMap, string, error) {
Robert Kroeger8d7a0ef2015-01-14 17:38:40 -080049 aclpath := filepath.Join(dir, aclName)
50 sigpath := filepath.Join(dir, sigName)
Robert Kroegeraa23aba2015-02-27 12:55:05 -080051 defer store.lockPath(dir)()
52 return getCore(store.principal, aclpath, sigpath)
Robert Kroegere95ed6d2015-01-14 17:41:04 -080053}
54
55// TODO(rjkroege): Improve lock handling.
Robert Kroegeraa23aba2015-02-27 12:55:05 -080056func (store PathStore) lockPath(dir string) func() {
57 store.lk.Lock()
58 lck, contains := store.pthlks[dir]
Robert Kroegere95ed6d2015-01-14 17:41:04 -080059 if !contains {
60 lck = new(sync.Mutex)
Robert Kroegeraa23aba2015-02-27 12:55:05 -080061 store.pthlks[dir] = lck
Robert Kroegere95ed6d2015-01-14 17:41:04 -080062 }
Robert Kroegeraa23aba2015-02-27 12:55:05 -080063 store.lk.Unlock()
Robert Kroegere95ed6d2015-01-14 17:41:04 -080064 lck.Lock()
65 return lck.Unlock
66}
67
68func getCore(principal security.Principal, aclpath, sigpath string) (access.TaggedACLMap, string, error) {
69 f, err := os.Open(aclpath)
70 if err != nil {
71 // This path is rarely a fatal error so log informationally only.
72 vlog.VI(2).Infof("os.Open(%s) failed: %v", aclpath, err)
73 return nil, "", err
74 }
75 defer f.Close()
76
77 s, err := os.Open(sigpath)
78 if err != nil {
79 vlog.Errorf("Signatures for ACLs are required: %s unavailable: %v", aclpath, err)
Todd Wangff73e1f2015-02-10 21:45:52 -080080 return nil, "", verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -080081 }
82 defer s.Close()
83
84 // read and verify the signature of the acl file
85 vf, err := serialization.NewVerifyingReader(f, s, principal.PublicKey())
86 if err != nil {
87 vlog.Errorf("NewVerifyingReader() failed: %v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -080088 return nil, "", verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -080089 }
90
91 acl, err := access.ReadTaggedACLMap(vf)
92 if err != nil {
93 vlog.Errorf("ReadTaggedACLMap(%s) failed: %v", aclpath, err)
94 return nil, "", err
95 }
96 etag, err := ComputeEtag(acl)
97 if err != nil {
98 vlog.Errorf("acls.ComputeEtag failed: %v", err)
99 return nil, "", err
100 }
101 return acl, etag, nil
102}
103
Robert Kroegeraa23aba2015-02-27 12:55:05 -0800104// Set writes the specified TaggedACLMap to the provided
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800105// directory with enforcement of etag synchronization mechanism and
106// locking.
Robert Kroegeraa23aba2015-02-27 12:55:05 -0800107func (store PathStore) Set(dir string, acl access.TaggedACLMap, etag string) error {
Robert Kroeger8d7a0ef2015-01-14 17:38:40 -0800108 aclpath := filepath.Join(dir, aclName)
109 sigpath := filepath.Join(dir, sigName)
Robert Kroegeraa23aba2015-02-27 12:55:05 -0800110 defer store.lockPath(dir)()
111 _, oetag, err := getCore(store.principal, aclpath, sigpath)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800112 if err != nil && !os.IsNotExist(err) {
Todd Wangff73e1f2015-02-10 21:45:52 -0800113 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800114 }
115 if len(etag) > 0 && etag != oetag {
Adam Sadovsky44f2b472015-02-18 14:52:28 -0800116 return verror.NewErrBadEtag(nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800117 }
Robert Kroegeraa23aba2015-02-27 12:55:05 -0800118 return write(store.principal, aclpath, sigpath, dir, acl)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800119}
120
121// write writes the specified TaggedACLMap to the aclFile with a
122// signature in sigFile.
123func write(principal security.Principal, aclFile, sigFile, dir string, acl access.TaggedACLMap) error {
124 // Create dir directory if it does not exist
125 os.MkdirAll(dir, os.FileMode(0700))
126 // Save the object to temporary data and signature files, and then move
127 // those files to the actual data and signature file.
128 data, err := ioutil.TempFile(dir, aclName)
129 if err != nil {
130 vlog.Errorf("Failed to open tmpfile data:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800131 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800132 }
133 defer os.Remove(data.Name())
134 sig, err := ioutil.TempFile(dir, sigName)
135 if err != nil {
136 vlog.Errorf("Failed to open tmpfile sig:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800137 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800138 }
139 defer os.Remove(sig.Name())
140 writer, err := serialization.NewSigningWriteCloser(data, sig, principal, nil)
141 if err != nil {
142 vlog.Errorf("Failed to create NewSigningWriteCloser:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800143 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800144 }
145 if err = acl.WriteTo(writer); err != nil {
146 vlog.Errorf("Failed to SaveACL:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800147 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800148 }
149 if err = writer.Close(); err != nil {
150 vlog.Errorf("Failed to Close() SigningWriteCloser:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800151 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800152 }
153 if err := os.Rename(data.Name(), aclFile); err != nil {
154 vlog.Errorf("os.Rename() failed:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800155 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800156 }
157 if err := os.Rename(sig.Name(), sigFile); err != nil {
158 vlog.Errorf("os.Rename() failed:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800159 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800160 }
161 return nil
162}
Robert Kroeger7e368972015-02-25 15:49:10 -0800163
Robert Kroegeraa23aba2015-02-27 12:55:05 -0800164func (store PathStore) TAMForPath(path string) (access.TaggedACLMap, bool, error) {
165 tam, _, err := store.Get(path)
Robert Kroeger7e368972015-02-25 15:49:10 -0800166 if os.IsNotExist(err) {
167 return nil, true, nil
168 } else if err != nil {
169 return nil, false, err
170 }
171 return tam, false, nil
172}