github.com/stulluk/snapd@v0.0.0-20210611110309-f6d5d5bd24b0/daemon/access_test.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_test 21 22 import ( 23 "net/http" 24 "net/http/httptest" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/client" 29 "github.com/snapcore/snapd/daemon" 30 "github.com/snapcore/snapd/dirs" 31 "github.com/snapcore/snapd/logger" 32 "github.com/snapcore/snapd/overlord/auth" 33 "github.com/snapcore/snapd/polkit" 34 "github.com/snapcore/snapd/testutil" 35 ) 36 37 type accessSuite struct{} 38 39 var _ = Suite(&accessSuite{}) 40 41 func (s *accessSuite) TestOpenAccess(c *C) { 42 var ac daemon.AccessChecker = daemon.OpenAccess{} 43 44 // openAccess denies access from snapd-snap.socket 45 ucred := &daemon.Ucrednet{Uid: 42, Pid: 100, Socket: dirs.SnapSocket} 46 c.Check(ac.CheckAccess(nil, ucred, nil), Equals, daemon.AccessForbidden) 47 48 // Access allowed from snapd.socket 49 ucred.Socket = dirs.SnapdSocket 50 c.Check(ac.CheckAccess(nil, ucred, nil), Equals, daemon.AccessOK) 51 52 // Access forbidden without peer credentials. This will need 53 // to be revisited if the API is ever exposed over TCP. 54 c.Check(ac.CheckAccess(nil, nil, nil), Equals, daemon.AccessForbidden) 55 } 56 57 func (s *accessSuite) TestAuthenticatedAccess(c *C) { 58 restore := daemon.MockCheckPolkitAction(func(r *http.Request, ucred *daemon.Ucrednet, action string) daemon.AccessResult { 59 // Polkit is not consulted if no action is specified 60 c.Fail() 61 return daemon.AccessForbidden 62 }) 63 defer restore() 64 65 var ac daemon.AccessChecker = daemon.AuthenticatedAccess{} 66 67 req := httptest.NewRequest("GET", "/", nil) 68 user := &auth.UserState{} 69 70 // authenticatedAccess denies access from snapd-snap.socket 71 ucred := &daemon.Ucrednet{Uid: 0, Pid: 100, Socket: dirs.SnapSocket} 72 c.Check(ac.CheckAccess(req, ucred, nil), Equals, daemon.AccessForbidden) 73 c.Check(ac.CheckAccess(req, ucred, user), Equals, daemon.AccessForbidden) 74 75 // the same for unknown sockets 76 ucred = &daemon.Ucrednet{Uid: 0, Pid: 100, Socket: "unexpected.socket"} 77 c.Check(ac.CheckAccess(req, ucred, nil), Equals, daemon.AccessForbidden) 78 79 // With macaroon auth, a normal user is granted access 80 ucred = &daemon.Ucrednet{Uid: 42, Pid: 100, Socket: dirs.SnapdSocket} 81 c.Check(ac.CheckAccess(req, ucred, user), Equals, daemon.AccessOK) 82 83 // Macaroon access requires peer credentials 84 c.Check(ac.CheckAccess(req, nil, user), Equals, daemon.AccessForbidden) 85 86 // Without macaroon auth, normal users are unauthorized 87 c.Check(ac.CheckAccess(req, ucred, nil), Equals, daemon.AccessUnauthorized) 88 89 // The root user is granted access without a macaroon 90 ucred = &daemon.Ucrednet{Uid: 0, Pid: 100, Socket: dirs.SnapdSocket} 91 c.Check(ac.CheckAccess(req, ucred, nil), Equals, daemon.AccessOK) 92 } 93 94 func (s *accessSuite) TestAuthenticatedAccessPolkit(c *C) { 95 var ac daemon.AccessChecker = daemon.AuthenticatedAccess{Polkit: "action-id"} 96 97 req := httptest.NewRequest("GET", "/", nil) 98 user := &auth.UserState{} 99 ucred := &daemon.Ucrednet{Uid: 0, Pid: 100, Socket: dirs.SnapdSocket} 100 101 // polkit is not checked if any of: 102 // * ucred is missing 103 // * macaroon auth is provided 104 // * user is root 105 restore := daemon.MockCheckPolkitAction(func(r *http.Request, ucred *daemon.Ucrednet, action string) daemon.AccessResult { 106 c.Fail() 107 return daemon.AccessForbidden 108 }) 109 defer restore() 110 c.Check(ac.CheckAccess(req, nil, nil), Equals, daemon.AccessForbidden) 111 c.Check(ac.CheckAccess(req, nil, user), Equals, daemon.AccessForbidden) 112 c.Check(ac.CheckAccess(req, ucred, nil), Equals, daemon.AccessOK) 113 114 // polkit is checked for regular users without macaroon auth 115 restore = daemon.MockCheckPolkitAction(func(r *http.Request, u *daemon.Ucrednet, action string) daemon.AccessResult { 116 c.Check(r, Equals, req) 117 c.Check(u, Equals, ucred) 118 c.Check(action, Equals, "action-id") 119 return daemon.AccessOK 120 }) 121 defer restore() 122 ucred = &daemon.Ucrednet{Uid: 42, Pid: 100, Socket: dirs.SnapdSocket} 123 c.Check(ac.CheckAccess(req, ucred, nil), Equals, daemon.AccessOK) 124 } 125 126 func (s *accessSuite) TestCheckPolkitActionImpl(c *C) { 127 logbuf, restore := logger.MockLogger() 128 defer restore() 129 130 req := httptest.NewRequest("GET", "/", nil) 131 ucred := &daemon.Ucrednet{Uid: 42, Pid: 1000, Socket: dirs.SnapdSocket} 132 133 // Access granted if polkit authorizes the request 134 restore = daemon.MockPolkitCheckAuthorization(func(pid int32, uid uint32, actionId string, details map[string]string, flags polkit.CheckFlags) (bool, error) { 135 c.Check(pid, Equals, int32(1000)) 136 c.Check(uid, Equals, uint32(42)) 137 c.Check(actionId, Equals, "action-id") 138 c.Check(details, IsNil) 139 c.Check(flags, Equals, polkit.CheckFlags(0)) 140 return true, nil 141 }) 142 defer restore() 143 c.Check(daemon.CheckPolkitActionImpl(req, ucred, "action-id"), Equals, daemon.AccessOK) 144 c.Check(logbuf.String(), Equals, "") 145 146 // Unauthorized if polkit denies the request 147 restore = daemon.MockPolkitCheckAuthorization(func(pid int32, uid uint32, actionId string, details map[string]string, flags polkit.CheckFlags) (bool, error) { 148 return false, nil 149 }) 150 defer restore() 151 c.Check(daemon.CheckPolkitActionImpl(req, ucred, "action-id"), Equals, daemon.AccessUnauthorized) 152 c.Check(logbuf.String(), Equals, "") 153 154 // Cancelled if the user dismisses the auth check 155 restore = daemon.MockPolkitCheckAuthorization(func(pid int32, uid uint32, actionId string, details map[string]string, flags polkit.CheckFlags) (bool, error) { 156 return false, polkit.ErrDismissed 157 }) 158 defer restore() 159 c.Check(daemon.CheckPolkitActionImpl(req, ucred, "action-id"), Equals, daemon.AccessCancelled) 160 c.Check(logbuf.String(), Equals, "") 161 162 // The X-Allow-Interaction header can be set to tell polkitd 163 // that interaction with the user is allowed. 164 req.Header.Set(client.AllowInteractionHeader, "true") 165 restore = daemon.MockPolkitCheckAuthorization(func(pid int32, uid uint32, actionId string, details map[string]string, flags polkit.CheckFlags) (bool, error) { 166 c.Check(flags, Equals, polkit.CheckFlags(polkit.CheckAllowInteraction)) 167 return true, nil 168 }) 169 defer restore() 170 c.Check(daemon.CheckPolkitActionImpl(req, ucred, "action-id"), Equals, daemon.AccessOK) 171 c.Check(logbuf.String(), Equals, "") 172 173 // Bad values in the request header are logged 174 req.Header.Set(client.AllowInteractionHeader, "garbage") 175 restore = daemon.MockPolkitCheckAuthorization(func(pid int32, uid uint32, actionId string, details map[string]string, flags polkit.CheckFlags) (bool, error) { 176 c.Check(flags, Equals, polkit.CheckFlags(0)) 177 return true, nil 178 }) 179 defer restore() 180 c.Check(daemon.CheckPolkitActionImpl(req, ucred, "action-id"), Equals, daemon.AccessOK) 181 c.Check(logbuf.String(), testutil.Contains, "error parsing X-Allow-Interaction header:") 182 } 183 184 func (s *accessSuite) TestRootAccess(c *C) { 185 var ac daemon.AccessChecker = daemon.RootAccess{} 186 187 user := &auth.UserState{} 188 189 // rootAccess denies access without ucred 190 c.Check(ac.CheckAccess(nil, nil, nil), Equals, daemon.AccessForbidden) 191 c.Check(ac.CheckAccess(nil, nil, user), Equals, daemon.AccessForbidden) 192 193 // rootAccess denies access from snapd-snap.socket 194 ucred := &daemon.Ucrednet{Uid: 0, Pid: 100, Socket: dirs.SnapSocket} 195 c.Check(ac.CheckAccess(nil, ucred, nil), Equals, daemon.AccessForbidden) 196 c.Check(ac.CheckAccess(nil, ucred, user), Equals, daemon.AccessForbidden) 197 198 // Non-root users are forbidden, even with macaroon auth 199 ucred = &daemon.Ucrednet{Uid: 42, Pid: 100, Socket: dirs.SnapdSocket} 200 c.Check(ac.CheckAccess(nil, ucred, nil), Equals, daemon.AccessForbidden) 201 c.Check(ac.CheckAccess(nil, ucred, user), Equals, daemon.AccessForbidden) 202 203 // Root is granted access 204 ucred = &daemon.Ucrednet{Uid: 0, Pid: 100, Socket: dirs.SnapdSocket} 205 c.Check(ac.CheckAccess(nil, ucred, nil), Equals, daemon.AccessOK) 206 } 207 208 func (s *accessSuite) TestSnapAccess(c *C) { 209 var ac daemon.AccessChecker = daemon.SnapAccess{} 210 211 // snapAccess allows access from snapd-snap.socket 212 ucred := &daemon.Ucrednet{Uid: 42, Pid: 100, Socket: dirs.SnapSocket} 213 c.Check(ac.CheckAccess(nil, ucred, nil), Equals, daemon.AccessOK) 214 215 // access is forbidden on the main socket or without peer creds 216 ucred.Socket = dirs.SnapdSocket 217 c.Check(ac.CheckAccess(nil, ucred, nil), Equals, daemon.AccessForbidden) 218 c.Check(ac.CheckAccess(nil, nil, nil), Equals, daemon.AccessForbidden) 219 }