blob: 2a42d020d94f3ca88e748cff465fb5896d7f8005 [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
Adam Sadovskya4d4a692015-04-20 11:36:49 -07005// Package pathperms provides a library to assist servers implementing
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -07006// GetPermissions/SetPermissions functions and authorizers where there are
Adam Sadovskya4d4a692015-04-20 11:36:49 -07007// path-specific Permissions stored individually in files.
Robert Kroegere95ed6d2015-01-14 17:41:04 -08008// TODO(rjkroege): Add unit tests.
Adam Sadovskya4d4a692015-04-20 11:36:49 -07009package pathperms
Robert Kroegere95ed6d2015-01-14 17:41:04 -080010
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
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -070017 "v.io/v23"
Todd Wang54feabe2015-04-15 23:38:26 -070018 "v.io/v23/context"
Jiri Simsa6ac95222015-02-23 16:11:49 -080019 "v.io/v23/security"
Todd Wang387d8a42015-03-30 17:09:05 -070020 "v.io/v23/security/access"
Jiri Simsa6ac95222015-02-23 16:11:49 -080021 "v.io/v23/verror"
Todd Wangb3511492015-04-07 23:32:34 -070022 "v.io/x/ref/lib/security/serialization"
Robert Kroegere95ed6d2015-01-14 17:41:04 -080023)
24
25const (
Adam Sadovskya4d4a692015-04-20 11:36:49 -070026 pkgPath = "v.io/x/ref/services/internal/pathperms"
27 sigName = "signature"
28 permsName = "data"
Robert Kroegere95ed6d2015-01-14 17:41:04 -080029)
30
31var (
32 ErrOperationFailed = verror.Register(pkgPath+".OperationFailed", verror.NoRetry, "{1:}{2:} operation failed{:_}")
33)
34
Robert Kroegerfbaafa42015-06-10 16:50:22 -070035type pathEntry struct {
36 lk sync.Mutex
37 c int
38}
39
Adam Sadovskya4d4a692015-04-20 11:36:49 -070040// PathStore manages storage of a set of Permissions in the filesystem where each
41// path identifies a specific Permissions in the set. PathStore synchronizes
42// access to its member Permissions.
Robert Kroegeraa23aba2015-02-27 12:55:05 -080043type PathStore struct {
Robert Kroegerfbaafa42015-06-10 16:50:22 -070044 pthlks map[string]*pathEntry
Robert Kroegera5c0ec52015-02-25 16:00:01 -080045 lk sync.Mutex
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -070046 ctx *context.T
Robert Kroegera5c0ec52015-02-25 16:00:01 -080047 principal security.Principal
Robert Kroegere95ed6d2015-01-14 17:41:04 -080048}
49
Robert Kroegeraa23aba2015-02-27 12:55:05 -080050// NewPathStore creates a new instance of the lock map that uses
Adam Sadovskya4d4a692015-04-20 11:36:49 -070051// principal to sign stored Permissions files.
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -070052func NewPathStore(ctx *context.T) *PathStore {
53 return &PathStore{pthlks: make(map[string]*pathEntry), ctx: ctx, principal: v23.GetPrincipal(ctx)}
Robert Kroegere95ed6d2015-01-14 17:41:04 -080054}
55
Benjamin Prosnitzb60efb92015-03-11 17:47:43 -070056// Get returns the Permissions from the data file in dir.
Asim Shankarbf9f0af2015-05-08 13:45:40 -070057func (store *PathStore) Get(dir string) (access.Permissions, string, error) {
Adam Sadovskya4d4a692015-04-20 11:36:49 -070058 permspath := filepath.Join(dir, permsName)
Robert Kroeger8d7a0ef2015-01-14 17:38:40 -080059 sigpath := filepath.Join(dir, sigName)
Robert Kroegeraa23aba2015-02-27 12:55:05 -080060 defer store.lockPath(dir)()
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -070061 return getCore(store.ctx, permspath, sigpath)
Robert Kroegere95ed6d2015-01-14 17:41:04 -080062}
63
64// TODO(rjkroege): Improve lock handling.
Asim Shankarbf9f0af2015-05-08 13:45:40 -070065func (store *PathStore) lockPath(dir string) func() {
Robert Kroegeraa23aba2015-02-27 12:55:05 -080066 store.lk.Lock()
Robert Kroegerfbaafa42015-06-10 16:50:22 -070067 pe, contains := store.pthlks[dir]
Robert Kroegere95ed6d2015-01-14 17:41:04 -080068 if !contains {
Robert Kroegerfbaafa42015-06-10 16:50:22 -070069 pe = &pathEntry{}
70 store.pthlks[dir] = pe
Robert Kroegere95ed6d2015-01-14 17:41:04 -080071 }
Robert Kroegerfbaafa42015-06-10 16:50:22 -070072 pe.c++
Robert Kroegeraa23aba2015-02-27 12:55:05 -080073 store.lk.Unlock()
Robert Kroegerfbaafa42015-06-10 16:50:22 -070074 pe.lk.Lock()
75
76 return func() {
77 pe.lk.Unlock()
78 store.lk.Lock()
79 pe.c--
80 if pe.c == 0 {
81 delete(store.pthlks, dir)
82 }
83 store.lk.Unlock()
84 }
Robert Kroegere95ed6d2015-01-14 17:41:04 -080085}
86
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -070087func getCore(ctx *context.T, permspath, sigpath string) (access.Permissions, string, error) {
88 principal := v23.GetPrincipal(ctx)
Adam Sadovskya4d4a692015-04-20 11:36:49 -070089 f, err := os.Open(permspath)
Robert Kroegere95ed6d2015-01-14 17:41:04 -080090 if err != nil {
91 // This path is rarely a fatal error so log informationally only.
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -070092 ctx.VI(2).Infof("os.Open(%s) failed: %v", permspath, err)
Robert Kroegere95ed6d2015-01-14 17:41:04 -080093 return nil, "", err
94 }
95 defer f.Close()
96
97 s, err := os.Open(sigpath)
98 if err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -070099 ctx.Errorf("Signatures for Permissions are required: %s unavailable: %v", permspath, err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800100 return nil, "", verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800101 }
102 defer s.Close()
103
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700104 // read and verify the signature of the perms file
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800105 vf, err := serialization.NewVerifyingReader(f, s, principal.PublicKey())
106 if err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700107 ctx.Errorf("NewVerifyingReader() failed: %v (perms=%s, sig=%s)", err, permspath, sigpath)
Todd Wangff73e1f2015-02-10 21:45:52 -0800108 return nil, "", verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800109 }
110
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700111 perms, err := access.ReadPermissions(vf)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800112 if err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700113 ctx.Errorf("ReadPermissions(%s) failed: %v", permspath, err)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800114 return nil, "", err
115 }
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700116 version, err := ComputeVersion(perms)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800117 if err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700118 ctx.Errorf("pathperms.ComputeVersion failed: %v", err)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800119 return nil, "", err
120 }
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700121 return perms, version, nil
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800122}
123
Adam Sadovskyb1f9e3c2015-04-08 11:03:49 -0700124// Set writes the specified Permissions to the provided directory with
125// enforcement of version synchronization mechanism and locking.
Asim Shankarbf9f0af2015-05-08 13:45:40 -0700126func (store *PathStore) Set(dir string, perms access.Permissions, version string) error {
Robert Kroeger77834062015-04-21 20:45:44 -0700127 return store.SetShareable(dir, perms, version, false)
128}
129
130// SetShareable writes the specified Permissions to the provided
131// directory with enforcement of version synchronization mechanism and
132// locking with file modes that will give the application read-only
133// access to the permissions file.
Asim Shankarbf9f0af2015-05-08 13:45:40 -0700134func (store *PathStore) SetShareable(dir string, perms access.Permissions, version string, shareable bool) error {
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700135 permspath := filepath.Join(dir, permsName)
Robert Kroeger8d7a0ef2015-01-14 17:38:40 -0800136 sigpath := filepath.Join(dir, sigName)
Robert Kroegeraa23aba2015-02-27 12:55:05 -0800137 defer store.lockPath(dir)()
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700138 _, oversion, err := getCore(store.ctx, permspath, sigpath)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800139 if err != nil && !os.IsNotExist(err) {
Todd Wangff73e1f2015-02-10 21:45:52 -0800140 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800141 }
Adam Sadovskyb1f9e3c2015-04-08 11:03:49 -0700142 if len(version) > 0 && version != oversion {
143 return verror.NewErrBadVersion(nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800144 }
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700145 return write(store.ctx, permspath, sigpath, dir, perms, shareable)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800146}
147
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700148// write writes the specified Permissions to the permsFile with a
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800149// signature in sigFile.
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700150func write(ctx *context.T, permsFile, sigFile, dir string, perms access.Permissions, shareable bool) error {
151 principal := v23.GetPrincipal(ctx)
Robert Kroeger77834062015-04-21 20:45:44 -0700152 filemode := os.FileMode(0600)
153 dirmode := os.FileMode(0700)
154 if shareable {
155 filemode = os.FileMode(0644)
156 dirmode = os.FileMode(0711)
157 }
158
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800159 // Create dir directory if it does not exist
Robert Kroeger77834062015-04-21 20:45:44 -0700160 os.MkdirAll(dir, dirmode)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800161 // Save the object to temporary data and signature files, and then move
162 // those files to the actual data and signature file.
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700163 data, err := ioutil.TempFile(dir, permsName)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800164 if err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700165 ctx.Errorf("Failed to open tmpfile data:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800166 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800167 }
168 defer os.Remove(data.Name())
169 sig, err := ioutil.TempFile(dir, sigName)
170 if err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700171 ctx.Errorf("Failed to open tmpfile sig:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800172 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800173 }
174 defer os.Remove(sig.Name())
175 writer, err := serialization.NewSigningWriteCloser(data, sig, principal, nil)
176 if err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700177 ctx.Errorf("Failed to create NewSigningWriteCloser:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800178 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800179 }
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700180 if err = perms.WriteTo(writer); err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700181 ctx.Errorf("Failed to SavePermissions:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800182 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800183 }
184 if err = writer.Close(); err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700185 ctx.Errorf("Failed to Close() SigningWriteCloser:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800186 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800187 }
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700188 if err := os.Rename(data.Name(), permsFile); err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700189 ctx.Errorf("os.Rename() failed:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800190 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800191 }
Robert Kroeger77834062015-04-21 20:45:44 -0700192 if err := os.Chmod(permsFile, filemode); err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700193 ctx.Errorf("os.Chmod() failed:%v", err)
Robert Kroeger77834062015-04-21 20:45:44 -0700194 return verror.New(ErrOperationFailed, nil)
195 }
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800196 if err := os.Rename(sig.Name(), sigFile); err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700197 ctx.Errorf("os.Rename() failed:%v", err)
Todd Wangff73e1f2015-02-10 21:45:52 -0800198 return verror.New(ErrOperationFailed, nil)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800199 }
Robert Kroeger77834062015-04-21 20:45:44 -0700200 if err := os.Chmod(sigFile, filemode); err != nil {
Cosmos Nicolaou7a4221f2015-06-21 08:02:23 -0700201 ctx.Errorf("os.Chmod() failed:%v", err)
Robert Kroeger77834062015-04-21 20:45:44 -0700202 return verror.New(ErrOperationFailed, nil)
203 }
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800204 return nil
205}
Robert Kroeger7e368972015-02-25 15:49:10 -0800206
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700207func (store *PathStore) PermsForPath(ctx *context.T, path string) (access.Permissions, bool, error) {
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700208 perms, _, err := store.Get(path)
Robert Kroeger7e368972015-02-25 15:49:10 -0800209 if os.IsNotExist(err) {
210 return nil, true, nil
211 } else if err != nil {
212 return nil, false, err
213 }
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700214 return perms, false, nil
Robert Kroeger7e368972015-02-25 15:49:10 -0800215}
Robert Kroeger03f0cc72015-04-02 10:18:01 -0700216
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700217// PrefixPatterns creates a pattern containing all of the prefix patterns of the
218// provided blessings.
Robert Kroeger03f0cc72015-04-02 10:18:01 -0700219func PrefixPatterns(blessings []string) []security.BlessingPattern {
220 var patterns []security.BlessingPattern
221 for _, b := range blessings {
222 patterns = append(patterns, security.BlessingPattern(b).PrefixPatterns()...)
223 }
224 return patterns
225}
226
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700227// PermissionsForBlessings creates the Permissions list that should be used with
228// a newly created object.
Robert Kroeger03f0cc72015-04-02 10:18:01 -0700229func PermissionsForBlessings(blessings []string) access.Permissions {
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700230 perms := make(access.Permissions)
Robert Kroeger03f0cc72015-04-02 10:18:01 -0700231
232 // Add the invoker's blessings and all its prefixes.
233 for _, p := range PrefixPatterns(blessings) {
234 for _, tag := range access.AllTypicalTags() {
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700235 perms.Add(p, string(tag))
Robert Kroeger03f0cc72015-04-02 10:18:01 -0700236 }
237 }
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700238 return perms
Robert Kroeger03f0cc72015-04-02 10:18:01 -0700239}
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700240
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700241// NilAuthPermissions creates Permissions that mimics the default authorization
242// policy (i.e., Permissions is matched by all blessings that are either
243// extensions of one of the local blessings or can be extended to form one of
244// the local blessings.)
Todd Wang4264e4b2015-04-16 22:43:40 -0700245func NilAuthPermissions(ctx *context.T, call security.Call) access.Permissions {
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700246 perms := make(access.Permissions)
Todd Wang4264e4b2015-04-16 22:43:40 -0700247 lb := security.LocalBlessingNames(ctx, call)
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700248 for _, p := range PrefixPatterns(lb) {
249 for _, tag := range access.AllTypicalTags() {
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700250 perms.Add(p, string(tag))
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700251 }
252 }
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700253 return perms
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700254}