gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/builtin/polkit.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2021 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package builtin
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"path/filepath"
    27  	"strings"
    28  
    29  	"gitee.com/mysnapcore/mysnapd/interfaces"
    30  	"gitee.com/mysnapcore/mysnapd/interfaces/polkit"
    31  	"gitee.com/mysnapcore/mysnapd/osutil"
    32  	"gitee.com/mysnapcore/mysnapd/polkit/validate"
    33  	"gitee.com/mysnapcore/mysnapd/snap"
    34  )
    35  
    36  const polkitSummary = `allows access to polkitd to check authorisation`
    37  
    38  const polkitBaseDeclarationPlugs = `
    39    polkit:
    40      allow-installation: false
    41      deny-auto-connection: true
    42  `
    43  
    44  const polkitBaseDeclarationSlots = `
    45    polkit:
    46      allow-installation:
    47        slot-snap-type:
    48          - core
    49      deny-auto-connection: true
    50  `
    51  
    52  const polkitConnectedPlugAppArmor = `
    53  # Description: Can talk to polkitd's CheckAuthorization API
    54  
    55  #include <abstractions/dbus-strict>
    56  
    57  dbus (send)
    58      bus=system
    59      path="/org/freedesktop/PolicyKit1/Authority"
    60      interface="org.freedesktop.PolicyKit1.Authority"
    61      member="{,Cancel}CheckAuthorization"
    62      peer=(name="org.freedesktop.PolicyKit1", label=unconfined),
    63  dbus (send)
    64      bus=system
    65      path="/org/freedesktop/PolicyKit1/Authority"
    66      interface="org.freedesktop.DBus.Properties"
    67      peer=(name="org.freedesktop.PolicyKit1", label=unconfined),
    68  dbus (send)
    69      bus=system
    70      path="/org/freedesktop/PolicyKit1/Authority"
    71      interface="org.freedesktop.DBus.Introspectable"
    72      member="Introspect"
    73      peer=(name="org.freedesktop.PolicyKit1", label=unconfined),
    74  `
    75  
    76  type polkitInterface struct {
    77  	commonInterface
    78  }
    79  
    80  func (iface *polkitInterface) getActionPrefix(attribs interfaces.Attrer) (string, error) {
    81  	var prefix string
    82  	if err := attribs.Attr("action-prefix", &prefix); err != nil {
    83  		return "", err
    84  	}
    85  	if err := interfaces.ValidateDBusBusName(prefix); err != nil {
    86  		return "", fmt.Errorf("plug has invalid action-prefix: %q", prefix)
    87  	}
    88  	return prefix, nil
    89  }
    90  
    91  func loadPolkitPolicy(filename, actionPrefix string) (polkit.Policy, error) {
    92  	content, err := ioutil.ReadFile(filename)
    93  	if err != nil {
    94  		return nil, fmt.Errorf(`cannot read file %q: %v`, filename, err)
    95  	}
    96  
    97  	// Check that the file content is a valid polkit policy file
    98  	actionIDs, err := validate.ValidatePolicy(bytes.NewReader(content))
    99  	if err != nil {
   100  		return nil, fmt.Errorf(`cannot validate policy file %q: %v`, filename, err)
   101  	}
   102  
   103  	// Check that the action IDs in the policy file match the action prefix
   104  	for _, id := range actionIDs {
   105  		if id != actionPrefix && !strings.HasPrefix(id, actionPrefix+".") {
   106  			return nil, fmt.Errorf(`policy file %q contains unexpected action ID %q`, filename, id)
   107  		}
   108  	}
   109  
   110  	return polkit.Policy(content), nil
   111  }
   112  
   113  func (iface *polkitInterface) PolkitConnectedPlug(spec *polkit.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
   114  	actionPrefix, err := iface.getActionPrefix(plug)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	mountDir := plug.Snap().MountDir()
   120  	policyFiles, err := filepath.Glob(filepath.Join(mountDir, "meta", "polkit", plug.Name()+".*.policy"))
   121  	if err != nil {
   122  		return err
   123  	}
   124  	if len(policyFiles) == 0 {
   125  		return fmt.Errorf("cannot find any policy files for plug %q", plug.Name())
   126  	}
   127  	for _, filename := range policyFiles {
   128  		suffix := strings.TrimSuffix(filepath.Base(filename), ".policy")
   129  		policy, err := loadPolkitPolicy(filename, actionPrefix)
   130  		if err != nil {
   131  			return err
   132  		}
   133  		if err := spec.AddPolicy(suffix, policy); err != nil {
   134  			return err
   135  		}
   136  	}
   137  	return nil
   138  }
   139  
   140  func (iface *polkitInterface) BeforePreparePlug(plug *snap.PlugInfo) error {
   141  	_, err := iface.getActionPrefix(plug)
   142  	return err
   143  }
   144  
   145  func init() {
   146  	registerIface(&polkitInterface{
   147  		commonInterface{
   148  			name:                  "polkit",
   149  			summary:               polkitSummary,
   150  			implicitOnCore:        osutil.IsExecutable("/usr/libexec/polkitd"),
   151  			implicitOnClassic:     true,
   152  			baseDeclarationPlugs:  polkitBaseDeclarationPlugs,
   153  			baseDeclarationSlots:  polkitBaseDeclarationSlots,
   154  			connectedPlugAppArmor: polkitConnectedPlugAppArmor,
   155  		},
   156  	})
   157  }