github.com/apptainer/singularity@v3.1.1+incompatible/internal/app/singularity/capability_manage_linux.go (about)

     1  // Copyright (c) 2018-2019, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  package singularity
     7  
     8  import (
     9  	"fmt"
    10  	"os"
    11  	"syscall"
    12  
    13  	"github.com/sylabs/singularity/internal/pkg/sylog"
    14  	"github.com/sylabs/singularity/internal/pkg/util/user"
    15  	"github.com/sylabs/singularity/pkg/util/capabilities"
    16  )
    17  
    18  // CapManageConfig specifies what capability set to edit in the capability file
    19  type CapManageConfig struct {
    20  	Caps  string
    21  	User  string
    22  	Group string
    23  }
    24  
    25  type manageType struct {
    26  	UserFn  func(*capabilities.Config, string, []string) error
    27  	GroupFn func(*capabilities.Config, string, []string) error
    28  }
    29  
    30  // CapabilityAdd adds the specified capability set to the capability file
    31  func CapabilityAdd(capFile string, c CapManageConfig) error {
    32  	addType := manageType{
    33  		UserFn: func(c *capabilities.Config, a string, b []string) error {
    34  			return c.AddUserCaps(a, b)
    35  		},
    36  		GroupFn: func(c *capabilities.Config, a string, b []string) error {
    37  			return c.AddGroupCaps(a, b)
    38  		},
    39  	}
    40  
    41  	return manageCaps(capFile, c, addType)
    42  }
    43  
    44  // CapabilityDrop drops the specified capability set from the capability file
    45  func CapabilityDrop(capFile string, c CapManageConfig) error {
    46  	dropType := manageType{
    47  		UserFn: func(c *capabilities.Config, a string, b []string) error {
    48  			return c.DropUserCaps(a, b)
    49  		},
    50  		GroupFn: func(c *capabilities.Config, a string, b []string) error {
    51  			return c.DropGroupCaps(a, b)
    52  		},
    53  	}
    54  
    55  	return manageCaps(capFile, c, dropType)
    56  }
    57  
    58  func manageCaps(capFile string, c CapManageConfig, t manageType) error {
    59  	if os.Getuid() != 0 {
    60  		return fmt.Errorf("while managing capability file: only root user can manage capabilities")
    61  	}
    62  
    63  	oldmask := syscall.Umask(0)
    64  	defer syscall.Umask(oldmask)
    65  
    66  	file, err := os.OpenFile(capFile, os.O_RDWR|os.O_CREATE, 0644)
    67  	if err != nil {
    68  		return fmt.Errorf("while opening capability config file: %s", err)
    69  	}
    70  	defer file.Close()
    71  
    72  	capConfig, err := capabilities.ReadFrom(file)
    73  	if err != nil {
    74  		return fmt.Errorf("while parsing capability config data: %s", err)
    75  	}
    76  
    77  	caps, ign := capabilities.Split(c.Caps)
    78  	if len(ign) > 0 {
    79  		sylog.Warningf("Ignoring unknown capabilities: %s", ign)
    80  	}
    81  
    82  	if c.User == "" && c.Group == "" {
    83  		return fmt.Errorf("no user or group specified")
    84  	}
    85  
    86  	if c.User != "" {
    87  		if !userExists(c.User) {
    88  			return fmt.Errorf("while setting capabilities for user %s: user does not exist", c.User)
    89  		}
    90  
    91  		if err := t.UserFn(capConfig, c.User, caps); err != nil {
    92  			return fmt.Errorf("while setting capabilities for user %s: %s", c.User, err)
    93  		}
    94  	}
    95  
    96  	if c.Group != "" {
    97  		if !groupExists(c.Group) {
    98  			return fmt.Errorf("while setting capabilities for group %s: group does not exist", c.Group)
    99  		}
   100  
   101  		if err := t.GroupFn(capConfig, c.Group, caps); err != nil {
   102  			return fmt.Errorf("while setting capabilities for group %s: %s", c.Group, err)
   103  		}
   104  	}
   105  
   106  	if err := file.Truncate(0); err != nil {
   107  		return fmt.Errorf("while truncating capability config file: %s", err)
   108  	}
   109  
   110  	if n, err := file.Seek(0, os.SEEK_SET); err != nil || n != 0 {
   111  		return fmt.Errorf("failed to reset %s cursor: %s", file.Name(), err)
   112  	}
   113  
   114  	if _, err := capConfig.WriteTo(file); err != nil {
   115  		return fmt.Errorf("while writing capability data to file: %s", err)
   116  	}
   117  
   118  	if err := file.Sync(); err != nil {
   119  		return fmt.Errorf("failed to flush capability config file %s: %s", file.Name(), err)
   120  	}
   121  
   122  	return nil
   123  }
   124  
   125  func userExists(usr string) bool {
   126  	if _, err := user.GetPwNam(usr); err != nil {
   127  		return false
   128  	}
   129  	return true
   130  }
   131  
   132  func groupExists(group string) bool {
   133  	if _, err := user.GetGrNam(group); err != nil {
   134  		return false
   135  	}
   136  	return true
   137  }