blob: 510233a6c02d1a44220a67b09601c0042e2b032b [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Robert Kroegere95ed6d2015-01-14 17:41:04 -08005// Package acls provides a library to assist servers implementing
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -07006// GetPermissions/SetPermissions functions and authorizers where there are
7// path-specific AccessLists stored individually in files.
Robert Kroegere95ed6d2015-01-14 17:41:04 -08008// TODO(rjkroege): Add unit tests.
9package acls
10
11import (
12 "io/ioutil"
13 "os"
Robert Kroeger8d7a0ef2015-01-14 17:38:40 -080014 "path/filepath"
Robert Kroegere95ed6d2015-01-14 17:41:04 -080015 "sync"
16
Jiri Simsa6ac95222015-02-23 16:11:49 -080017 "v.io/v23/security"
18 "v.io/v23/services/security/access"
19 "v.io/v23/verror"
Jiri Simsa337af232015-02-27 14:36:46 -080020 "v.io/x/lib/vlog"
Robert Kroegere95ed6d2015-01-14 17:41:04 -080021
Jiri Simsaffceefa2015-02-28 11:03:34 -080022 "v.io/x/ref/security/serialization"
Robert Kroegere95ed6d2015-01-14 17:41:04 -080023)
24
25const (
Jiri Simsaffceefa2015-02-28 11:03:34 -080026 pkgPath = "v.io/x/ref/services/mgmt/lib/acls"
Robert Kroegere95ed6d2015-01-14 17:41:04 -080027 sigName = "signature"
28 aclName = "data"
29)
30
31var (
32 ErrOperationFailed = verror.Register(pkgPath+".OperationFailed", verror.NoRetry, "{1:}{2:} operation failed{:_}")
33)
34
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -070035// PathStore manages storage of a set of AccessLists in the filesystem where each
36// path identifies a specific AccessList in the set. PathStore synchronizes
37// access to its member AccessLists.
Robert Kroegeraa23aba2015-02-27 12:55:05 -080038type PathStore struct {
Robert Kroegera5c0ec52015-02-25 16:00:01 -080039 // TODO(rjkroege): Garbage collect the locks map.
40 pthlks map[string]*sync.Mutex
41 lk sync.Mutex
42 principal security.Principal
Robert Kroegere95ed6d2015-01-14 17:41:04 -080043}
44
Robert Kroegeraa23aba2015-02-27 12:55:05 -080045// NewPathStore creates a new instance of the lock map that uses
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -070046// principal to sign stored AccessList files.
Robert Kroegeraa23aba2015-02-27 12:55:05 -080047func NewPathStore(principal security.Principal) *PathStore {
48 return &PathStore{pthlks: make(map[string]*sync.Mutex), principal: principal}
Robert Kroegere95ed6d2015-01-14 17:41:04 -080049}
50
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -070051// Get returns the Permissions from the data file in dir.
52func (store PathStore) Get(dir string) (access.Permissions, string, error) {
Robert Kroeger8d7a0ef2015-01-14 17:38:40 -080053 aclpath := filepath.Join(dir, aclName)
54 sigpath := filepath.Join(dir, sigName)
Robert Kroegeraa23aba2015-02-27 12:55:05 -080055 defer store.lockPath(dir)()
56 return getCore(store.principal, aclpath, sigpath)
Robert Kroegere95ed6d2015-01-14 17:41:04 -080057}
58
59// TODO(rjkroege): Improve lock handling.
Robert Kroegeraa23aba2015-02-27 12:55:05 -080060func (store PathStore) lockPath(dir string) func() {
61 store.lk.Lock()
62 lck, contains := store.pthlks[dir]
Robert Kroegere95ed6d2015-01-14 17:41:04 -080063 if !contains {
64 lck = new(sync.Mutex)
Robert Kroegeraa23aba2015-02-27 12:55:05 -080065 store.pthlks[dir] = lck
Robert Kroegere95ed6d2015-01-14 17:41:04 -080066 }
Robert Kroegeraa23aba2015-02-27 12:55:05 -080067 store.lk.Unlock()
Robert Kroegere95ed6d2015-01-14 17:41:04 -080068 lck.Lock()
69 return lck.Unlock
70}
71
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -070072func getCore(principal security.Principal, aclpath, sigpath string) (access.Permissions, string, error) {
Robert Kroegere95ed6d2015-01-14 17:41:04 -080073 f, err := os.Open(aclpath)
74 if err != nil {
75 // This path is rarely a fatal error so log informationally only.
76 vlog.VI(2).Infof("os.Open(%s) failed: %v", aclpath, err)
77 return nil, "", err
78 }
79 defer f.Close()
80
81 s, err := os.Open(sigpath)
82 if err != nil {
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -070083 vlog.Errorf("Signatures for AccessLists are required: %s unavailable: %v", aclpath, err)
Todd Wangff73e1f2015-02-10 21:45:52 -080084 return nil, "", verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -080085 }
86 defer s.Close()
87
88 // read and verify the signature of the acl file
89 vf, err := serialization.NewVerifyingReader(f, s, principal.PublicKey())
90 if err != nil {
Arup Mukherjeec49b36e2015-03-06 10:36:36 -080091 vlog.Errorf("NewVerifyingReader() failed: %v (acl=%s, sig=%s)", err, aclpath, sigpath)
Todd Wangff73e1f2015-02-10 21:45:52 -080092 return nil, "", verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -080093 }
94
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -070095 acl, err := access.ReadPermissions(vf)
Robert Kroegere95ed6d2015-01-14 17:41:04 -080096 if err != nil {
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -070097 vlog.Errorf("ReadPermissions(%s) failed: %v", aclpath, err)
Robert Kroegere95ed6d2015-01-14 17:41:04 -080098 return nil, "", err
99 }
100 etag, err := ComputeEtag(acl)
101 if err != nil {
102 vlog.Errorf("acls.ComputeEtag failed: %v", err)
103 return nil, "", err
104 }
105 return acl, etag, nil
106}
107
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -0700108// Set writes the specified Permissions to the provided
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800109// directory with enforcement of etag synchronization mechanism and
110// locking.
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -0700111func (store PathStore) Set(dir string, acl access.Permissions, etag string) error {
Robert Kroeger8d7a0ef2015-01-14 17:38:40 -0800112 aclpath := filepath.Join(dir, aclName)
113 sigpath := filepath.Join(dir, sigName)
Robert Kroegeraa23aba2015-02-27 12:55:05 -0800114 defer store.lockPath(dir)()
115 _, oetag, err := getCore(store.principal, aclpath, sigpath)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800116 if err != nil && !os.IsNotExist(err) {
Todd Wangff73e1f2015-02-10 21:45:52 -0800117 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800118 }
119 if len(etag) > 0 && etag != oetag {
Adam Sadovsky44f2b472015-02-18 14:52:28 -0800120 return verror.NewErrBadEtag(nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800121 }
Robert Kroegeraa23aba2015-02-27 12:55:05 -0800122 return write(store.principal, aclpath, sigpath, dir, acl)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800123}
124
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -0700125// write writes the specified Permissions to the aclFile with a
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800126// signature in sigFile.
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -0700127func write(principal security.Principal, aclFile, sigFile, dir string, acl access.Permissions) error {
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800128 // Create dir directory if it does not exist
129 os.MkdirAll(dir, os.FileMode(0700))
130 // Save the object to temporary data and signature files, and then move
131 // those files to the actual data and signature file.
132 data, err := ioutil.TempFile(dir, aclName)
133 if err != nil {
134 vlog.Errorf("Failed to open tmpfile data:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800135 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800136 }
137 defer os.Remove(data.Name())
138 sig, err := ioutil.TempFile(dir, sigName)
139 if err != nil {
140 vlog.Errorf("Failed to open tmpfile sig:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800141 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800142 }
143 defer os.Remove(sig.Name())
144 writer, err := serialization.NewSigningWriteCloser(data, sig, principal, nil)
145 if err != nil {
146 vlog.Errorf("Failed to create NewSigningWriteCloser:%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 = acl.WriteTo(writer); err != nil {
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -0700150 vlog.Errorf("Failed to SaveAccessList:%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 = writer.Close(); err != nil {
154 vlog.Errorf("Failed to Close() SigningWriteCloser:%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(data.Name(), aclFile); 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 if err := os.Rename(sig.Name(), sigFile); err != nil {
162 vlog.Errorf("os.Rename() failed:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800163 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800164 }
165 return nil
166}
Robert Kroeger7e368972015-02-25 15:49:10 -0800167
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -0700168func (store PathStore) TAMForPath(path string) (access.Permissions, bool, error) {
Robert Kroegeraa23aba2015-02-27 12:55:05 -0800169 tam, _, err := store.Get(path)
Robert Kroeger7e368972015-02-25 15:49:10 -0800170 if os.IsNotExist(err) {
171 return nil, true, nil
172 } else if err != nil {
173 return nil, false, err
174 }
175 return tam, false, nil
176}