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