summaryrefslogtreecommitdiffstats
path: root/extensions/permissions/PermissionDelegateHandler.h
blob: f5b7e096c739b454fa4dc5e4a757332f8c335ca1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * Permission delegate handler provides a policy of how top-level can
 * delegate permission to embedded iframes.
 *
 * This class includes a mechanism to delegate permission using feature
 * policy. Feature policy will assure that only cross-origin iframes which
 * have been explicitly granted access will have the opportunity to request
 * permission.
 *
 * For example if an iframe has not been granted access to geolocation by
 * Feature Policy, geolocation request from the iframe will be automatically
 * denied. if the top-level origin already has access to geolocation and the
 * iframe has been granted access to geolocation by Feature Policy, the iframe
 * will also have access to geolocation. If the top-level frame did not have
 * access to geolocation, and the iframe has been granted access to geolocation
 * by Feature Policy, a request from the cross-origin iframe would trigger a
 * prompt using of the top-level origin.
 */

#ifndef mozilla_PermissionDelegateHandler_h
#define mozilla_PermissionDelegateHandler_h

#include "mozilla/Array.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupports.h"
#include "nsIPermissionDelegateHandler.h"
#include "nsIPermissionManager.h"
#include "nsCOMPtr.h"

class nsIPrincipal;
class nsIContentPermissionRequest;

namespace mozilla {
namespace dom {
class Document;
class WindowContext;
}  // namespace dom

class PermissionDelegateHandler final : public nsIPermissionDelegateHandler {
 public:
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  NS_DECL_CYCLE_COLLECTION_CLASS(PermissionDelegateHandler)

  NS_DECL_NSIPERMISSIONDELEGATEHANDLER

  explicit PermissionDelegateHandler() = default;
  explicit PermissionDelegateHandler(mozilla::dom::Document* aDocument);

  static constexpr size_t DELEGATED_PERMISSION_COUNT = 11;

  typedef struct DelegatedPermissionList {
    Array<uint32_t, DELEGATED_PERMISSION_COUNT> mPermissions;

    bool operator==(const DelegatedPermissionList& aOther) const {
      return mPermissions == aOther.mPermissions;
    }
  } DelegatedPermissionList;

  bool Initialize();

  /*
   * Indicates if we has the right to make permission request with aType
   */
  bool HasPermissionDelegated(const nsACString& aType);

  /*
   * Get permission state, which applied permission delegate policy.
   *
   * @param aType the permission type to get
   * @param aPermission out argument which will be a permission type that we
   *                    will return from this function.
   * @param aExactHostMatch whether to look for the exact host name or also for
   *                        subdomains that can have the same permission.
   */
  nsresult GetPermission(const nsACString& aType, uint32_t* aPermission,
                         bool aExactHostMatch);

  /*
   * Get permission state for permission api, which applied
   * permission delegate policy.
   *
   * @param aType the permission type to get
   * @param aExactHostMatch whether to look for the exact host name or also for
   *                        subdomains that can have the same permission.
   * @param aPermission out argument which will be a permission type that we
   *                    will return from this function.
   */
  nsresult GetPermissionForPermissionsAPI(const nsACString& aType,
                                          uint32_t* aPermission);

  enum PermissionDelegatePolicy {
    /* Always delegate permission from top level to iframe and the iframe
     * should use top level origin to get/set permission.*/
    eDelegateUseTopOrigin,

    /* Permission is delegated using Feature Policy. Permission is denied by
     * default in cross origin iframe and the iframe only could get/set
     * permission if there's allow attribute set in iframe. e.g allow =
     * "geolocation" */
    eDelegateUseFeaturePolicy,

    /* Persistent denied permissions in cross origin iframe */
    ePersistDeniedCrossOrigin,

    /* This is the old behavior of cross origin iframe permission. The
     * permission delegation should not have an effect on iframe. The cross
     * origin iframe get/set permissions by its origin */
    eDelegateUseIframeOrigin,
  };

  /*
   * Indicates matching between Feature Policy and Permissions name defined in
   * Permissions Manager, not DOM Permissions API. Permissions API exposed in
   * DOM only supports "geo" at the moment but Permissions Manager also supports
   * "camera", "microphone".
   */
  typedef struct {
    const char* mPermissionName;
    const char16_t* mFeatureName;
    PermissionDelegatePolicy mPolicy;
  } PermissionDelegateInfo;

  /**
   * The loader maintains a weak reference to the document with
   * which it is initialized. This call forces the reference to
   * be dropped.
   */
  void DropDocumentReference() { mDocument = nullptr; }

  /*
   * Helper function to return the delegate info value for aPermissionName.
   * @param aPermissionName the permission name to get
   */
  static const PermissionDelegateInfo* GetPermissionDelegateInfo(
      const nsAString& aPermissionName);

  /*
   * Helper function to return the delegate principal. This will return nullptr,
   * or request's principal or top level principal based on the delegate policy
   * will be applied for a given type.
   * We use this function when prompting, no need to perform permission check
   * (deny/allow).
   *
   * @param aType the permission type to get
   * @param aRequest  The request which the principal is get from.
   * @param aResult out argument which will be a principal that we
   *                will return from this function.
   */
  static nsresult GetDelegatePrincipal(const nsACString& aType,
                                       nsIContentPermissionRequest* aRequest,
                                       nsIPrincipal** aResult);

  /**
   * Populate all delegated permissions to the WindowContext of the associated
   * document. We only populate the permissions for the top-level content.
   */
  void PopulateAllDelegatedPermissions();

  /**
   * Update the given delegated permission to the WindowContext. We only
   * update it for the top-level content.
   */
  void UpdateDelegatedPermission(const nsACString& aType);

 private:
  ~PermissionDelegateHandler() = default;

  /*
   * Check whether the permission is blocked by FeaturePolicy directive.
   * Default allowlist for a featureName of permission used in permissions
   * delegate should be set to eSelf, to ensure that permission is denied by
   * default and only have the opportunity to request permission with allow
   * attribute.
   */
  bool HasFeaturePolicyAllowed(const PermissionDelegateInfo* info) const;

  /**
   * A helper function to test the permission and set the result to the given
   * list. It will return true if the permission is changed, otherwise false.
   */
  bool UpdateDelegatePermissionInternal(
      PermissionDelegateHandler::DelegatedPermissionList& aList,
      const nsACString& aType, size_t aIdx,
      nsresult (NS_STDCALL nsIPermissionManager::*aTestFunc)(nsIPrincipal*,
                                                             const nsACString&,
                                                             uint32_t*));

  // A weak pointer to our document. Nulled out by DropDocumentReference.
  mozilla::dom::Document* mDocument;

  nsCOMPtr<nsIPrincipal> mPrincipal;
  RefPtr<nsIPermissionManager> mPermissionManager;
};

}  // namespace mozilla

#endif  // mozilla_PermissionDelegateHandler_h