github.com/stulluk/snapd@v0.0.0-20210611110309-f6d5d5bd24b0/daemon/access.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 daemon
    21  
    22  import (
    23  	"net/http"
    24  	"strconv"
    25  
    26  	"github.com/snapcore/snapd/client"
    27  	"github.com/snapcore/snapd/dirs"
    28  	"github.com/snapcore/snapd/logger"
    29  	"github.com/snapcore/snapd/overlord/auth"
    30  	"github.com/snapcore/snapd/polkit"
    31  )
    32  
    33  type accessResult int
    34  
    35  const (
    36  	accessOK accessResult = iota
    37  	accessUnauthorized
    38  	accessForbidden
    39  	accessCancelled
    40  )
    41  
    42  var polkitCheckAuthorization = polkit.CheckAuthorization
    43  
    44  var checkPolkitAction = checkPolkitActionImpl
    45  
    46  func checkPolkitActionImpl(r *http.Request, ucred *ucrednet, action string) accessResult {
    47  	var flags polkit.CheckFlags
    48  	allowHeader := r.Header.Get(client.AllowInteractionHeader)
    49  	if allowHeader != "" {
    50  		if allow, err := strconv.ParseBool(allowHeader); err != nil {
    51  			logger.Noticef("error parsing %s header: %s", client.AllowInteractionHeader, err)
    52  		} else if allow {
    53  			flags |= polkit.CheckAllowInteraction
    54  		}
    55  	}
    56  	// Pass both pid and uid from the peer ucred to avoid pid race
    57  	switch authorized, err := polkitCheckAuthorization(ucred.Pid, ucred.Uid, action, nil, flags); err {
    58  	case nil:
    59  		if authorized {
    60  			// polkit says user is authorised
    61  			return accessOK
    62  		}
    63  	case polkit.ErrDismissed:
    64  		return accessCancelled
    65  	default:
    66  		logger.Noticef("polkit error: %s", err)
    67  	}
    68  	return accessUnauthorized
    69  }
    70  
    71  // accessChecker checks whether a particular request is allowed.
    72  //
    73  // An access checker will either allow a request, deny it, or return
    74  // accessUnknown, which indicates the decision should be delegated to
    75  // the next access checker.
    76  type accessChecker interface {
    77  	CheckAccess(r *http.Request, ucred *ucrednet, user *auth.UserState) accessResult
    78  }
    79  
    80  // requireSnapdSocket ensures the request was received via snapd.socket.
    81  func requireSnapdSocket(ucred *ucrednet) accessResult {
    82  	if ucred == nil {
    83  		return accessForbidden
    84  	}
    85  
    86  	if ucred.Socket != dirs.SnapdSocket {
    87  		return accessForbidden
    88  	}
    89  
    90  	return accessOK
    91  }
    92  
    93  // openAccess allows requests without authentication, provided they
    94  // have peer credentials and were not received on snapd-snap.socket
    95  type openAccess struct{}
    96  
    97  func (ac openAccess) CheckAccess(r *http.Request, ucred *ucrednet, user *auth.UserState) accessResult {
    98  	return requireSnapdSocket(ucred)
    99  }
   100  
   101  // authenticatedAccess allows requests from authenticated users,
   102  // provided they were not received on snapd-snap.socket
   103  //
   104  // A user is considered authenticated if they provide a macaroon, are
   105  // the root user according to peer credentials, or granted access by
   106  // Polkit.
   107  type authenticatedAccess struct {
   108  	Polkit string
   109  }
   110  
   111  func (ac authenticatedAccess) CheckAccess(r *http.Request, ucred *ucrednet, user *auth.UserState) accessResult {
   112  	if result := requireSnapdSocket(ucred); result != accessOK {
   113  		return result
   114  	}
   115  
   116  	if user != nil {
   117  		return accessOK
   118  	}
   119  
   120  	if ucred.Uid == 0 {
   121  		return accessOK
   122  	}
   123  
   124  	// We check polkit last because it may result in the user
   125  	// being prompted for authorisation. This should be avoided if
   126  	// access is otherwise granted.
   127  	if ac.Polkit != "" {
   128  		return checkPolkitAction(r, ucred, ac.Polkit)
   129  	}
   130  
   131  	return accessUnauthorized
   132  }
   133  
   134  // rootAccess allows requests from the root uid, provided they
   135  // were not received on snapd-snap.socket
   136  type rootAccess struct{}
   137  
   138  func (ac rootAccess) CheckAccess(r *http.Request, ucred *ucrednet, user *auth.UserState) accessResult {
   139  	if result := requireSnapdSocket(ucred); result != accessOK {
   140  		return result
   141  	}
   142  
   143  	if ucred.Uid == 0 {
   144  		return accessOK
   145  	}
   146  	return accessForbidden
   147  }
   148  
   149  // snapAccess allows requests from the snapd-snap.socket
   150  type snapAccess struct{}
   151  
   152  func (ac snapAccess) CheckAccess(r *http.Request, ucred *ucrednet, user *auth.UserState) accessResult {
   153  	if ucred == nil {
   154  		return accessForbidden
   155  	}
   156  
   157  	if ucred.Socket == dirs.SnapSocket {
   158  		return accessOK
   159  	}
   160  	// FIXME: should snapctl access be allowed on the main socket?
   161  	return accessForbidden
   162  }