...
Tawesoft Logo

Source file src/tawesoft.co.uk/go/drop/inheritable.go

Documentation: src/tawesoft.co.uk/go/drop/inheritable.go

     1  package drop
     2  
     3  import (
     4      "errors"
     5      "fmt"
     6      "net"
     7      "os"
     8      "strings"
     9  )
    10  
    11  // Inheritable interface describes a File-based handle that can be passed
    12  // between a parent and child process across a dropping of privileges.
    13  type Inheritable interface {
    14      // String returns some description of the resource.
    15      String() string
    16  
    17      // Open is called by the root process
    18      Open() (*os.File, error)
    19  
    20      // Inherit is called by the child process
    21      Inherit(*os.File) error
    22  
    23      // Close closes any resource generated by a call to Open or Inherit.
    24      Close() error
    25  }
    26  
    27  // InheritableFile is a file handle that survives a dropping of
    28  // privileges.
    29  type InheritableFile struct {
    30      path   string
    31      flags  int         // e.g.  os.O_RDWR|os.O_CREATE
    32      uid    int
    33      gid    int
    34      mode   os.FileMode // e.g. 0600
    35      handle *os.File    // nil until Inherit returns
    36  }
    37  
    38  // NewInheritableFile returns an InhertiableFile that wraps a file handle that
    39  // survives a dropping of privileges.
    40  //
    41  // The parameters uid, gid, and mode, unless equal to -1, -1, or 0, set the
    42  // user, group, and permissions of the file after it has been opened.
    43  //
    44  // WARNING: if these values are supplied from a config file, that config file
    45  // should be writable to root or system accounts only - otherwise, an attacker
    46  // may edit the config file in such a way as to set the permissions of
    47  // arbitrary files.
    48  func NewInheritableFile(path string, flags int, uid int, gid int, mode os.FileMode) *InheritableFile {
    49      return &InheritableFile{
    50          path:   path,
    51          flags:  flags,
    52          uid:    uid,
    53          gid:    gid,
    54          mode:   mode,
    55      }
    56  }
    57  
    58  func (h InheritableFile) String() string {
    59      return fmt.Sprintf("<InheritableFile %q>", h.path)
    60  }
    61  
    62  func (h *InheritableFile) Open() (*os.File, error) {
    63      f, err := os.OpenFile(h.path, h.flags, 0000)
    64      if err != nil { return nil, err }
    65  
    66      err = rootSetMode(h.path, h.uid, h.gid, h.mode)
    67      if err != nil {
    68          f.Close()
    69          return nil, err
    70      }
    71  
    72      h.handle = f
    73  
    74      return f, nil
    75  }
    76  
    77  func (h *InheritableFile) Inherit(f *os.File) error {
    78      f.Seek(0, 0)
    79      h.handle = f
    80      return nil
    81  }
    82  
    83  func (h InheritableFile) Handle() *os.File {
    84      return h.handle
    85  }
    86  
    87  func (h InheritableFile) Close() error {
    88      if h.handle == nil { return nil }
    89      return h.handle.Close()
    90  }
    91  
    92  // InheritableNetListener is a net.Listener that survives a dropping of
    93  // privileges.
    94  //
    95  // Note that On JS and Windows, the File method of most Listeners are not
    96  // implemented, so this will not work.
    97  type InheritableNetListener struct {
    98      network string
    99      address string
   100      uid  int
   101      gid  int
   102      mode os.FileMode
   103      handle net.Listener
   104      fileHandle *os.File
   105  }
   106  
   107  func NewInheritableTCPListener(address string) *InheritableNetListener {
   108      return &InheritableNetListener{
   109          network: "tcp",
   110          address: address,
   111      }
   112  }
   113  
   114  // NewInheritableUnixListener returns an InheritableNetListener for a UNIX socket.
   115  //
   116  // The parameters uid, gid, and mode, unless equal to -1, -1, or 0, set the
   117  // user, group, and permissions of the socket after it has been opened.
   118  //
   119  // WARNING: if these values are supplied from a config file, that config file
   120  // should be writable to root or system accounts only - otherwise, an attacker
   121  // may edit the config file in such a way as to set the permissions of
   122  // arbitrary files.
   123  func NewInheritableUnixListener(address string, uid int, gid int, mode os.FileMode) *InheritableNetListener {
   124      return &InheritableNetListener{
   125          network: "unix",
   126          address: address,
   127          uid:     uid,
   128          gid:     gid,
   129          mode:    mode,
   130      }
   131  }
   132  
   133  func (h InheritableNetListener) String() string {
   134      return fmt.Sprintf("<InheritableNetListener (%s) %q>", h.network, h.address)
   135  }
   136  
   137  func (h *InheritableNetListener) Open() (*os.File, error) {
   138      nl, err := net.Listen(h.network, h.address)
   139      if err != nil { return nil, err }
   140  
   141      h.handle = nl
   142  
   143      if strings.HasPrefix(h.network, "tcp") {
   144          if fl, err := nl.(*net.TCPListener).File(); err != nil {
   145              nl.Close()
   146              return nil, fmt.Errorf("error obtaining TCPListener File handle: %+v", err)
   147          } else {
   148              return fl, nil
   149          }
   150      } else if strings.HasPrefix(h.network, "unix") {
   151          rootSetMode(h.address, h.uid, h.gid, h.mode)
   152  
   153          if fl, err := nl.(*net.UnixListener).File(); err != nil {
   154              nl.Close()
   155              return nil, fmt.Errorf("error obtaining UnixListener File handle: %+v", err)
   156          } else {
   157              return fl, nil
   158          }
   159      }
   160  
   161      nl.Close()
   162      return nil, fmt.Errorf("unsupported network %v", h.network)
   163  }
   164  
   165  func (h *InheritableNetListener) Inherit(f *os.File) error {
   166      fl, err := net.FileListener(f)
   167      if err != nil { return err }
   168      h.handle = fl
   169      return nil
   170  }
   171  
   172  func (h InheritableNetListener) Handle() net.Listener {
   173      return h.handle
   174  }
   175  
   176  func (h *InheritableNetListener) Close() error {
   177      var errs []string
   178      if h.fileHandle != nil {
   179          err := h.fileHandle.Close()
   180          if err != nil {
   181              errs = append(errs, fmt.Sprintf("error closing file handle: %v", err))
   182          }
   183      }
   184      if h.handle != nil {
   185          err := h.handle.Close()
   186          if err != nil {
   187              errs = append(errs, fmt.Sprintf("error closing listener: %v", err))
   188          }
   189      }
   190  
   191      if errs == nil { return nil }
   192      return errors.New(strings.Join(errs, "; "))
   193  }
   194  

View as plain text