github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/pkg/acl/acl.go (about)

     1  // Copyright 2016 The rkt Authors
     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  //+build linux
    16  
    17  // Package acl is a wrapper over libacl that dlopens it instead of being linked
    18  // to it. The code is based on go-acl by Joseph Naegele
    19  // (https://github.com/naegelejd/go-acl).
    20  package acl
    21  
    22  // #cgo LDFLAGS: -ldl
    23  // #include <stdlib.h>
    24  // #include <dlfcn.h>
    25  // #include <stdio.h>
    26  // #include <sys/acl.h>
    27  // #include <sys/types.h>
    28  // #include <unistd.h>
    29  //
    30  // acl_t
    31  // my_acl_from_text(void *f, const char *acl)
    32  // {
    33  //   acl_t (*acl_from_text)(const char *);
    34  //
    35  //   acl_from_text = f;
    36  //   return acl_from_text(acl);
    37  // }
    38  //
    39  // int
    40  // my_acl_set_file(void *f, const char *path_p, acl_type_t type, acl_t acl)
    41  // {
    42  //   int (*acl_set_file)(const char *, acl_type_t, acl_t);
    43  //
    44  //   acl_set_file = f;
    45  //   return acl_set_file(path_p, type, acl);
    46  // }
    47  //
    48  // int
    49  // my_acl_free(void *f, acl_t acl)
    50  // {
    51  //   int (*acl_free)(acl_t);
    52  //
    53  //   acl_free = f;
    54  //   return acl_free(acl);
    55  // }
    56  //
    57  // int
    58  // my_acl_valid(void *f, acl_t acl)
    59  // {
    60  //   int (*acl_valid)(acl_t);
    61  //
    62  //   acl_valid = f;
    63  //   return acl_valid(acl);
    64  // }
    65  //
    66  // int
    67  // my_acl_create_entry(void *f, acl_t *acl_p, acl_entry_t *entry_p)
    68  // {
    69  //   int (*acl_create_entry)(acl_t *, acl_entry_t *);
    70  //
    71  //   acl_create_entry = f;
    72  //   return acl_create_entry(acl_p, entry_p);
    73  // }
    74  //
    75  // int
    76  // my_acl_set_tag_type(void *f, acl_entry_t entry_d, acl_tag_t tag_type)
    77  // {
    78  //   int (*acl_set_tag_type)(acl_entry_t, acl_tag_t);
    79  //
    80  //   acl_set_tag_type = f;
    81  //   return acl_set_tag_type(entry_d, tag_type);
    82  // }
    83  //
    84  // int
    85  // my_acl_get_permset(void *f, acl_entry_t entry_d, acl_permset_t *permset_p)
    86  // {
    87  //   int (*acl_get_permset)(acl_entry_t, acl_permset_t *);
    88  //
    89  //   acl_get_permset = f;
    90  //   return acl_get_permset(entry_d, permset_p);
    91  // }
    92  //
    93  // int
    94  // my_acl_add_perm(void *f, acl_permset_t permset_d, acl_perm_t perm)
    95  // {
    96  //   int (*acl_add_perm)(acl_permset_t, acl_perm_t);
    97  //
    98  //   acl_add_perm = f;
    99  //   return acl_add_perm(permset_d, perm);
   100  // }
   101  import "C"
   102  
   103  import (
   104  	"errors"
   105  	"fmt"
   106  	"os"
   107  	"unsafe"
   108  
   109  	"github.com/hashicorp/errwrap"
   110  )
   111  
   112  const (
   113  	otherExec = 1 << iota
   114  	otherWrite
   115  	otherRead
   116  	groupExec
   117  	groupWrite
   118  	groupRead
   119  	userExec
   120  	userWrite
   121  	userRead
   122  )
   123  
   124  const (
   125  	TagUserObj  Tag = C.ACL_USER_OBJ
   126  	TagUser         = C.ACL_USER
   127  	TagGroupObj     = C.ACL_GROUP_OBJ
   128  	TagGroup        = C.ACL_GROUP
   129  	TagMask         = C.ACL_MASK
   130  	TagOther        = C.ACL_OTHER
   131  )
   132  
   133  const (
   134  	PermRead    Perm = C.ACL_READ
   135  	PermWrite        = C.ACL_WRITE
   136  	PermExecute      = C.ACL_EXECUTE
   137  )
   138  
   139  type ACL struct {
   140  	lib *libHandle
   141  	a   C.acl_t
   142  }
   143  
   144  // Entry is an entry in an ACL.
   145  type Entry struct {
   146  	a *ACL
   147  	e C.acl_entry_t
   148  }
   149  
   150  // Permset is a set of permissions.
   151  type Permset struct {
   152  	a *ACL
   153  	p C.acl_permset_t
   154  }
   155  
   156  // Perm represents a permission in the e_perm ACL field
   157  type Perm int
   158  
   159  // Tag represents an ACL e_tag entry
   160  type Tag int
   161  
   162  var ErrSoNotFound = errors.New("unable to open a handle to libacl")
   163  
   164  type libHandle struct {
   165  	handle  unsafe.Pointer
   166  	libname string
   167  }
   168  
   169  func getHandle() (*libHandle, error) {
   170  	for _, name := range []string{
   171  		"libacl.so.1",
   172  		"libacl.so",
   173  	} {
   174  		libname := C.CString(name)
   175  		defer C.free(unsafe.Pointer(libname))
   176  		handle := C.dlopen(libname, C.RTLD_LAZY)
   177  		if handle != nil {
   178  			h := &libHandle{
   179  				handle:  handle,
   180  				libname: name,
   181  			}
   182  			return h, nil
   183  		}
   184  	}
   185  	return nil, ErrSoNotFound
   186  }
   187  
   188  func getSymbolPointer(handle unsafe.Pointer, symbol string) (unsafe.Pointer, error) {
   189  	sym := C.CString(symbol)
   190  	defer C.free(unsafe.Pointer(sym))
   191  
   192  	C.dlerror()
   193  	p := C.dlsym(handle, sym)
   194  	e := C.dlerror()
   195  	if e != nil {
   196  		return nil, errwrap.Wrap(fmt.Errorf("error resolving symbol %q", symbol), errors.New(C.GoString(e)))
   197  	}
   198  
   199  	return p, nil
   200  }
   201  
   202  func (h *libHandle) close() error {
   203  	C.dlerror()
   204  	C.dlclose(h.handle)
   205  	e := C.dlerror()
   206  	if e != nil {
   207  		return errwrap.Wrap(fmt.Errorf("error closing %v", h.libname), errors.New(C.GoString(e)))
   208  	}
   209  	return nil
   210  }
   211  
   212  // InitACL dlopens libacl and returns an ACL object if successful.
   213  func InitACL() (*ACL, error) {
   214  	h, err := getHandle()
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	return &ACL{lib: h}, nil
   220  }
   221  
   222  // ParseACL parses a string representation of an ACL.
   223  func (a *ACL) ParseACL(acl string) error {
   224  	acl_from_text, err := getSymbolPointer(a.lib.handle, "acl_from_text")
   225  	if err != nil {
   226  		return err
   227  	}
   228  	cacl := C.CString(acl)
   229  	defer C.free(unsafe.Pointer(cacl))
   230  
   231  	retACL, err := C.my_acl_from_text(acl_from_text, cacl)
   232  	if retACL == nil {
   233  		return errwrap.Wrap(errors.New("error calling acl_from_text"), err)
   234  	}
   235  
   236  	a.a = retACL
   237  
   238  	return nil
   239  }
   240  
   241  // Free frees libacl's internal structures and closes libacl.
   242  func (a *ACL) Free() error {
   243  	acl_free, err := getSymbolPointer(a.lib.handle, "acl_free")
   244  	if err != nil {
   245  		return err
   246  	}
   247  
   248  	ret, err := C.my_acl_free(acl_free, a.a)
   249  	if ret < 0 {
   250  		return errwrap.Wrap(errors.New("error calling acl_free"), err)
   251  	}
   252  
   253  	return a.lib.close()
   254  }
   255  
   256  // SetFileACLDefault sets the "default" ACL for path.
   257  func (a *ACL) SetFileACLDefault(path string) error {
   258  	acl_set_file, err := getSymbolPointer(a.lib.handle, "acl_set_file")
   259  	if err != nil {
   260  		return err
   261  	}
   262  
   263  	cpath := C.CString(path)
   264  	defer C.free(unsafe.Pointer(cpath))
   265  
   266  	ret, err := C.my_acl_set_file(acl_set_file, cpath, C.ACL_TYPE_DEFAULT, a.a)
   267  	if ret < 0 {
   268  		return errwrap.Wrap(errors.New("error calling acl_set_file"), err)
   269  	}
   270  
   271  	return nil
   272  }
   273  
   274  // Valid checks whether the ACL is valid.
   275  func (a *ACL) Valid() error {
   276  	acl_valid, err := getSymbolPointer(a.lib.handle, "acl_valid")
   277  	if err != nil {
   278  		return err
   279  	}
   280  
   281  	ret, err := C.my_acl_valid(acl_valid, a.a)
   282  	if ret < 0 {
   283  		return errwrap.Wrap(errors.New("invalid acl"), err)
   284  	}
   285  	return nil
   286  }
   287  
   288  // AddBaseEntries adds the base ACL entries from the file permissions.
   289  func (a *ACL) AddBaseEntries(path string) error {
   290  	fi, err := os.Lstat(path)
   291  	if err != nil {
   292  		return err
   293  	}
   294  	mode := fi.Mode().Perm()
   295  	var r, w, x bool
   296  
   297  	// set USER_OBJ entry
   298  	r = mode&userRead == userRead
   299  	w = mode&userWrite == userWrite
   300  	x = mode&userExec == userExec
   301  	if err := a.addBaseEntryFromMode(TagUserObj, r, w, x); err != nil {
   302  		return err
   303  	}
   304  
   305  	// set GROUP_OBJ entry
   306  	r = mode&groupRead == groupRead
   307  	w = mode&groupWrite == groupWrite
   308  	x = mode&groupExec == groupExec
   309  	if err := a.addBaseEntryFromMode(TagGroupObj, r, w, x); err != nil {
   310  		return err
   311  	}
   312  
   313  	// set OTHER entry
   314  	r = mode&otherRead == otherRead
   315  	w = mode&otherWrite == otherWrite
   316  	x = mode&otherExec == otherExec
   317  	if err := a.addBaseEntryFromMode(TagOther, r, w, x); err != nil {
   318  		return err
   319  	}
   320  
   321  	return nil
   322  }
   323  
   324  func (a *ACL) createEntry() (*Entry, error) {
   325  	acl_create_entry, err := getSymbolPointer(a.lib.handle, "acl_create_entry")
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  
   330  	var e C.acl_entry_t
   331  
   332  	rv, err := C.my_acl_create_entry(acl_create_entry, &a.a, &e)
   333  	if rv < 0 {
   334  		return nil, errwrap.Wrap(errors.New("unable to create entry"), err)
   335  	}
   336  	return &Entry{a, e}, nil
   337  }
   338  
   339  func (a *ACL) addBaseEntryFromMode(tag Tag, read, write, execute bool) error {
   340  	e, err := a.createEntry()
   341  	if err != nil {
   342  		return err
   343  	}
   344  	if err = e.setTag(tag); err != nil {
   345  		return err
   346  	}
   347  	p, err := e.getPermset(a)
   348  	if err != nil {
   349  		return err
   350  	}
   351  	if err := p.addPermsFromMode(read, write, execute); err != nil {
   352  		return err
   353  	}
   354  	return nil
   355  }
   356  
   357  func (entry *Entry) getPermset(a *ACL) (*Permset, error) {
   358  	acl_get_permset, err := getSymbolPointer(a.lib.handle, "acl_get_permset")
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  
   363  	var ps C.acl_permset_t
   364  	rv, err := C.my_acl_get_permset(acl_get_permset, entry.e, &ps)
   365  	if rv < 0 {
   366  		return nil, errwrap.Wrap(errors.New("unable to get permset"), err)
   367  	}
   368  	return &Permset{a, ps}, nil
   369  }
   370  
   371  func (entry *Entry) setTag(t Tag) error {
   372  	acl_set_tag_type, err := getSymbolPointer(entry.a.lib.handle, "acl_set_tag_type")
   373  	if err != nil {
   374  		return err
   375  	}
   376  
   377  	rv, err := C.my_acl_set_tag_type(acl_set_tag_type, entry.e, C.acl_tag_t(t))
   378  	if rv < 0 {
   379  		return errwrap.Wrap(errors.New("unable to set tag"), err)
   380  	}
   381  
   382  	return nil
   383  }
   384  
   385  func (pset *Permset) addPerm(perm Perm) error {
   386  	acl_add_perm, err := getSymbolPointer(pset.a.lib.handle, "acl_add_perm")
   387  	if err != nil {
   388  		return err
   389  	}
   390  
   391  	rv, err := C.my_acl_add_perm(acl_add_perm, pset.p, C.acl_perm_t(perm))
   392  	if rv < 0 {
   393  		return errwrap.Wrap(errors.New("unable to add perm to permset"), err)
   394  	}
   395  	return nil
   396  }
   397  
   398  func (p *Permset) addPermsFromMode(read, write, execute bool) error {
   399  	if read {
   400  		if err := p.addPerm(PermRead); err != nil {
   401  			return err
   402  		}
   403  	}
   404  	if write {
   405  		if err := p.addPerm(PermWrite); err != nil {
   406  			return err
   407  		}
   408  	}
   409  	if execute {
   410  		if err := p.addPerm(PermExecute); err != nil {
   411  			return err
   412  		}
   413  	}
   414  	return nil
   415  }