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 }