1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.adservices.service.common; 18 19 import static android.adservices.common.AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED; 20 import static android.adservices.common.AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_MATCH_NOT_FOUND; 21 import static android.adservices.common.AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED_MANIFEST_ADSERVICES_CONFIG_NO_PERMISSION; 22 import static android.adservices.common.AdServicesStatusUtils.STATUS_PERMISSION_NOT_REQUESTED; 23 import static android.adservices.common.AdServicesStatusUtils.STATUS_UNAUTHORIZED; 24 25 import static com.android.adservices.service.common.AppManifestConfigCall.API_AD_SELECTION; 26 import static com.android.adservices.service.common.AppManifestConfigCall.API_CUSTOM_AUDIENCES; 27 import static com.android.adservices.service.common.AppManifestConfigCall.API_PROTECTED_SIGNALS; 28 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_AD_TECH_NOT_AUTHORIZED_BY_APP; 29 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_ANY_PERMISSION_FAILURE; 30 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_ENROLLMENT_DATA_MATCH_NOT_FOUND; 31 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_ENROLLMENT_NOT_FOUND; 32 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_INVALID_API_TYPE; 33 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED; 34 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_NOT_ALLOWED_ENROLLMENT_FROM_URI_BLOCKED; 35 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_NO_MATCH_PACKAGE_NAME; 36 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_PERMISSION_FAILURE; 37 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED; 38 import static com.android.adservices.service.stats.AdsRelevanceStatusUtils.checkAndLogCelByApiNameLoggingId; 39 40 import android.adservices.common.AdServicesStatusUtils; 41 import android.adservices.common.AdTechIdentifier; 42 import android.annotation.NonNull; 43 import android.content.Context; 44 import android.content.pm.PackageManager; 45 import android.net.Uri; 46 import android.os.Build; 47 import android.util.Pair; 48 49 import androidx.annotation.RequiresApi; 50 51 import com.android.adservices.LoggerFactory; 52 import com.android.adservices.data.enrollment.EnrollmentDao; 53 import com.android.adservices.errorlogging.ErrorLogUtil; 54 import com.android.adservices.service.FlagsFactory; 55 import com.android.adservices.service.enrollment.EnrollmentData; 56 import com.android.adservices.service.enrollment.EnrollmentStatus; 57 import com.android.adservices.service.enrollment.EnrollmentUtil; 58 import com.android.adservices.service.stats.AdServicesLogger; 59 import com.android.adservices.service.stats.AdsRelevanceStatusUtils; 60 import com.android.internal.annotations.VisibleForTesting; 61 62 import java.util.Collection; 63 import java.util.Locale; 64 import java.util.Objects; 65 66 /** Verify caller of FLEDGE API has the permission of performing certain behaviour. */ 67 @RequiresApi(Build.VERSION_CODES.S) 68 public class FledgeAuthorizationFilter { 69 70 @VisibleForTesting public static final String INVALID_API_TYPE = "Invalid apiType: %d"; 71 private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger(); 72 @NonNull private final PackageManager mPackageManager; 73 @NonNull private final EnrollmentDao mEnrollmentDao; 74 @NonNull private final AdServicesLogger mAdServicesLogger; 75 private EnrollmentUtil mEnrollmentUtil; 76 77 @VisibleForTesting FledgeAuthorizationFilter( @onNull PackageManager packageManager, @NonNull EnrollmentDao enrollmentDao, @NonNull AdServicesLogger adServicesLogger)78 public FledgeAuthorizationFilter( 79 @NonNull PackageManager packageManager, 80 @NonNull EnrollmentDao enrollmentDao, 81 @NonNull AdServicesLogger adServicesLogger) { 82 this(packageManager, enrollmentDao, adServicesLogger, null); 83 } 84 85 @VisibleForTesting FledgeAuthorizationFilter( @onNull PackageManager packageManager, @NonNull EnrollmentDao enrollmentDao, @NonNull AdServicesLogger adServicesLogger, EnrollmentUtil enrollmentUtil)86 public FledgeAuthorizationFilter( 87 @NonNull PackageManager packageManager, 88 @NonNull EnrollmentDao enrollmentDao, 89 @NonNull AdServicesLogger adServicesLogger, 90 EnrollmentUtil enrollmentUtil) { 91 Objects.requireNonNull(packageManager); 92 Objects.requireNonNull(enrollmentDao); 93 Objects.requireNonNull(adServicesLogger); 94 95 mPackageManager = packageManager; 96 mEnrollmentDao = enrollmentDao; 97 mAdServicesLogger = adServicesLogger; 98 mEnrollmentUtil = enrollmentUtil; 99 } 100 101 /** Creates an instance of {@link FledgeAuthorizationFilter}. */ 102 // TODO(b/311183933): Remove passed in Context from static method. 103 @SuppressWarnings("AvoidStaticContext") create( @onNull Context context, @NonNull AdServicesLogger adServicesLogger)104 public static FledgeAuthorizationFilter create( 105 @NonNull Context context, @NonNull AdServicesLogger adServicesLogger) { 106 Objects.requireNonNull(context); 107 108 return new FledgeAuthorizationFilter( 109 context.getPackageManager(), 110 EnrollmentDao.getInstance(), 111 adServicesLogger, 112 EnrollmentUtil.getInstance()); 113 } 114 115 /** 116 * Check if the package name provided by the caller is one of the package of the calling uid. 117 * 118 * @param callingPackageName the caller-supplied package name 119 * @param callingUid the uid get from the Binder 120 * @param apiNameLoggingId the id of the api being called 121 * @throws CallerMismatchException if the package name provided does not associate with the uid. 122 */ assertCallingPackageName( @onNull String callingPackageName, int callingUid, int apiNameLoggingId)123 public void assertCallingPackageName( 124 @NonNull String callingPackageName, int callingUid, int apiNameLoggingId) 125 throws CallerMismatchException { 126 Objects.requireNonNull(callingPackageName); 127 128 sLogger.v( 129 "Asserting package name \"%s\" is valid for uid %d", 130 callingPackageName, callingUid); 131 132 String[] packageNamesForUid = mPackageManager.getPackagesForUid(callingUid); 133 for (String packageNameForUid : packageNamesForUid) { 134 sLogger.v("Candidate package name \"%s\"", packageNameForUid); 135 if (callingPackageName.equals(packageNameForUid)) return; 136 } 137 138 sLogger.v("No match found, failing calling package name match in API %d", apiNameLoggingId); 139 mAdServicesLogger.logFledgeApiCallStats( 140 apiNameLoggingId, callingPackageName, STATUS_UNAUTHORIZED, /*latencyMs=*/ 0); 141 int celApiNameId = AdsRelevanceStatusUtils.getCelPpApiNameId(apiNameLoggingId); 142 if (celApiNameId != AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED) { 143 ErrorLogUtil.e( 144 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_NO_MATCH_PACKAGE_NAME, 145 celApiNameId); 146 } 147 throw new CallerMismatchException(); 148 } 149 150 /** 151 * Check if the app had declared a permission, and throw an error if it has not 152 * 153 * @param context api service context 154 * @param apiNameLoggingId the id of the api being called 155 * @throws SecurityException if the package did not declare custom audience permission 156 */ assertAppDeclaredPermission( @onNull Context context, @NonNull String appPackageName, int apiNameLoggingId, @NonNull String permission)157 public void assertAppDeclaredPermission( 158 @NonNull Context context, 159 @NonNull String appPackageName, 160 int apiNameLoggingId, 161 @NonNull String permission) 162 throws SecurityException { 163 Objects.requireNonNull(context); 164 Objects.requireNonNull(appPackageName); 165 Objects.requireNonNull(permission); 166 167 if (!PermissionHelper.hasPermission(context, appPackageName, permission)) { 168 logAndThrowPermissionFailure(appPackageName, apiNameLoggingId, permission); 169 } 170 } 171 172 /** 173 * Check if the app had declared any of the listed permissions, and throw an error if it has not 174 * 175 * @param context api service context 176 * @param appPackageName the package name of the calling app 177 * @param apiNameLoggingId the id of the api being called 178 * @param permissions lists of permissions that allow calling the API 179 * @throws SecurityException if the package did not declare custom audience permission 180 */ assertAppDeclaredAnyPermission( @onNull Context context, @NonNull String appPackageName, int apiNameLoggingId, @NonNull Collection<String> permissions)181 public void assertAppDeclaredAnyPermission( 182 @NonNull Context context, 183 @NonNull String appPackageName, 184 int apiNameLoggingId, 185 @NonNull Collection<String> permissions) 186 throws SecurityException { 187 Objects.requireNonNull(context); 188 Objects.requireNonNull(appPackageName); 189 Objects.requireNonNull(permissions); 190 191 for (String permission : permissions) { 192 if (PermissionHelper.hasPermission(context, appPackageName, permission)) { 193 return; // Found a valid permission 194 } 195 } 196 logAndThrowMultiplePermissionFailure(appPackageName, apiNameLoggingId, permissions); 197 } 198 logAndThrowPermissionFailure( String callerAppPackageName, int apiNameLoggingId, String permission)199 private void logAndThrowPermissionFailure( 200 String callerAppPackageName, int apiNameLoggingId, String permission) { 201 sLogger.v("Permission %s not declared by caller in API %d", permission, apiNameLoggingId); 202 mAdServicesLogger.logFledgeApiCallStats( 203 apiNameLoggingId, 204 callerAppPackageName, 205 STATUS_PERMISSION_NOT_REQUESTED, 206 /*latencyMs=*/ 0); 207 int celApiNameId = AdsRelevanceStatusUtils.getCelPpApiNameId(apiNameLoggingId); 208 if (celApiNameId != AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED) { 209 // TODO(b/376542959): replace this temporary solution for CEL inside Binder thread. 210 AdsRelevanceStatusUtils.logCelInsideBinderThread( 211 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_PERMISSION_FAILURE, 212 celApiNameId); 213 } 214 throw new SecurityException( 215 AdServicesStatusUtils.SECURITY_EXCEPTION_PERMISSION_NOT_REQUESTED_ERROR_MESSAGE); 216 } 217 logAndThrowMultiplePermissionFailure( String callerAppPackageName, int apiNameLoggingId, Collection<String> permissions)218 private void logAndThrowMultiplePermissionFailure( 219 String callerAppPackageName, int apiNameLoggingId, Collection<String> permissions) { 220 sLogger.v("Permissions %s not declared by caller in API %d", permissions, apiNameLoggingId); 221 mAdServicesLogger.logFledgeApiCallStats( 222 apiNameLoggingId, 223 callerAppPackageName, 224 STATUS_PERMISSION_NOT_REQUESTED, 225 /*latencyMs=*/ 0); 226 int celApiNameId = AdsRelevanceStatusUtils.getCelPpApiNameId(apiNameLoggingId); 227 if (celApiNameId != AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED) { 228 // TODO(b/376542959): replace this temporary solution for CEL inside Binder thread. 229 AdsRelevanceStatusUtils.logCelInsideBinderThread( 230 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_ANY_PERMISSION_FAILURE, 231 celApiNameId); 232 } 233 throw new SecurityException( 234 AdServicesStatusUtils.SECURITY_EXCEPTION_PERMISSION_NOT_REQUESTED_ERROR_MESSAGE); 235 } 236 237 /** 238 * Check if a certain ad tech is enrolled and authorized to perform the operation for the 239 * package. 240 * 241 * @param context api service context 242 * @param appPackageName the package name to check against 243 * @param adTechIdentifier the ad tech to check against 244 * @param apiNameLoggingId the id of the api being called 245 * @throws AdTechNotAllowedException if the ad tech is not authorized to perform the operation 246 */ assertAdTechAllowed( @onNull Context context, @NonNull String appPackageName, @NonNull AdTechIdentifier adTechIdentifier, int apiNameLoggingId, @AppManifestConfigCall.ApiType int apiType)247 public void assertAdTechAllowed( 248 @NonNull Context context, 249 @NonNull String appPackageName, 250 @NonNull AdTechIdentifier adTechIdentifier, 251 int apiNameLoggingId, 252 @AppManifestConfigCall.ApiType int apiType) 253 throws AdTechNotAllowedException { 254 Objects.requireNonNull(context); 255 Objects.requireNonNull(appPackageName); 256 Objects.requireNonNull(adTechIdentifier); 257 258 int buildId = -1; 259 int dataFileGroupStatus = 0; 260 if (mEnrollmentUtil != null) { 261 buildId = mEnrollmentUtil.getBuildId(); 262 dataFileGroupStatus = mEnrollmentUtil.getFileGroupStatus(); 263 } 264 int enrollmentRecordsCount = mEnrollmentDao.getEnrollmentRecordCountForLogging(); 265 EnrollmentData enrollmentData = getEnrollmentData(adTechIdentifier, apiType); 266 int celApiNameId = AdsRelevanceStatusUtils.getCelPpApiNameId(apiNameLoggingId); 267 268 if (enrollmentData == null) { 269 sLogger.v( 270 "Enrollment data match not found for ad tech \"%s\" while calling API %d", 271 adTechIdentifier.toString(), apiNameLoggingId); 272 mAdServicesLogger.logFledgeApiCallStats( 273 apiNameLoggingId, 274 appPackageName, 275 STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_MATCH_NOT_FOUND, 276 /*latencyMs=*/ 0); 277 if (mEnrollmentUtil != null) { 278 mEnrollmentUtil.logEnrollmentFailedStats( 279 mAdServicesLogger, 280 buildId, 281 dataFileGroupStatus, 282 enrollmentRecordsCount, 283 adTechIdentifier.toString(), 284 EnrollmentStatus.ErrorCause.ENROLLMENT_NOT_FOUND_ERROR_CAUSE.getValue()); 285 } 286 if (celApiNameId != AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED) { 287 ErrorLogUtil.e( 288 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_ENROLLMENT_DATA_MATCH_NOT_FOUND, 289 celApiNameId); 290 } 291 throw new AdTechNotAllowedException(); 292 } 293 294 if (!isAllowedAccess(appPackageName, apiType, enrollmentData)) { 295 sLogger.v( 296 "App package name \"%s\" with ad tech identifier \"%s\" not authorized to call" 297 + " API %d", 298 appPackageName, adTechIdentifier.toString(), apiNameLoggingId); 299 mAdServicesLogger.logFledgeApiCallStats( 300 apiNameLoggingId, 301 appPackageName, 302 STATUS_CALLER_NOT_ALLOWED_MANIFEST_ADSERVICES_CONFIG_NO_PERMISSION, 303 /*latencyMs=*/ 0); 304 mEnrollmentUtil.logEnrollmentFailedStats( 305 mAdServicesLogger, 306 buildId, 307 dataFileGroupStatus, 308 enrollmentRecordsCount, 309 adTechIdentifier.toString(), 310 EnrollmentStatus.ErrorCause.UNKNOWN_ERROR_CAUSE.getValue()); 311 if (celApiNameId != AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED) { 312 ErrorLogUtil.e( 313 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_AD_TECH_NOT_AUTHORIZED_BY_APP, 314 celApiNameId); 315 } 316 throw new AdTechNotAllowedException(); 317 } 318 319 // Check if enrollment is in blocklist. 320 if (FlagsFactory.getFlags().isEnrollmentBlocklisted(enrollmentData.getEnrollmentId())) { 321 sLogger.v( 322 "App package name \"%s\" with ad tech identifier \"%s\" not authorized to call" 323 + " API %d", 324 appPackageName, adTechIdentifier.toString(), apiNameLoggingId); 325 mAdServicesLogger.logFledgeApiCallStats( 326 apiNameLoggingId, 327 appPackageName, 328 STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED, 329 /*latencyMs=*/ 0); 330 mEnrollmentUtil.logEnrollmentFailedStats( 331 mAdServicesLogger, 332 buildId, 333 dataFileGroupStatus, 334 enrollmentRecordsCount, 335 adTechIdentifier.toString(), 336 EnrollmentStatus.ErrorCause.ENROLLMENT_BLOCKLISTED_ERROR_CAUSE.getValue()); 337 if (celApiNameId != AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED) { 338 ErrorLogUtil.e( 339 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED, 340 celApiNameId); 341 } 342 throw new AdTechNotAllowedException(); 343 } 344 } 345 getEnrollmentData(AdTechIdentifier adTechIdentifier, int apiType)346 private EnrollmentData getEnrollmentData(AdTechIdentifier adTechIdentifier, int apiType) { 347 if (apiType == API_CUSTOM_AUDIENCES) { 348 return mEnrollmentDao.getEnrollmentDataForFledgeByAdTechIdentifier(adTechIdentifier); 349 } else if (apiType == API_PROTECTED_SIGNALS) { 350 return mEnrollmentDao.getEnrollmentDataForPASByAdTechIdentifier(adTechIdentifier); 351 } else if (apiType == API_AD_SELECTION) { 352 EnrollmentData caEnrollment = 353 mEnrollmentDao.getEnrollmentDataForFledgeByAdTechIdentifier(adTechIdentifier); 354 if (caEnrollment == null) { 355 return mEnrollmentDao.getEnrollmentDataForPASByAdTechIdentifier(adTechIdentifier); 356 } 357 return caEnrollment; 358 } else { 359 throw new IllegalStateException(String.format(INVALID_API_TYPE, apiType)); 360 } 361 } 362 getAdTechIdentifierEnrollmentDataPair( Uri uriForAdTech, int apiType, int apiNameLoggingId)363 private Pair<AdTechIdentifier, EnrollmentData> getAdTechIdentifierEnrollmentDataPair( 364 Uri uriForAdTech, int apiType, int apiNameLoggingId) { 365 if (apiType == API_CUSTOM_AUDIENCES) { 366 return mEnrollmentDao.getEnrollmentDataForFledgeByMatchingAdTechIdentifier( 367 uriForAdTech); 368 } else if (apiType == API_PROTECTED_SIGNALS) { 369 return mEnrollmentDao.getEnrollmentDataForPASByMatchingAdTechIdentifier(uriForAdTech); 370 } else if (apiType == API_AD_SELECTION) { 371 Pair<AdTechIdentifier, EnrollmentData> caEnrollment = 372 mEnrollmentDao.getEnrollmentDataForFledgeByMatchingAdTechIdentifier( 373 uriForAdTech); 374 if (caEnrollment == null) { 375 return mEnrollmentDao.getEnrollmentDataForPASByMatchingAdTechIdentifier( 376 uriForAdTech); 377 } 378 return caEnrollment; 379 } else { 380 IllegalStateException exception = 381 new IllegalStateException( 382 String.format(Locale.ENGLISH, INVALID_API_TYPE, apiType)); 383 checkAndLogCelByApiNameLoggingId( 384 exception, 385 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_INVALID_API_TYPE, 386 apiNameLoggingId); 387 throw exception; 388 } 389 } 390 isAllowedAccess( String appPackageName, @AppManifestConfigCall.ApiType int apiType, EnrollmentData enrollmentData)391 private boolean isAllowedAccess( 392 String appPackageName, 393 @AppManifestConfigCall.ApiType int apiType, 394 EnrollmentData enrollmentData) { 395 if (apiType == API_CUSTOM_AUDIENCES) { 396 return AppManifestConfigHelper.isAllowedCustomAudiencesAccess( 397 appPackageName, enrollmentData.getEnrollmentId()); 398 } else if (apiType == API_PROTECTED_SIGNALS) { 399 return AppManifestConfigHelper.isAllowedProtectedSignalsAccess( 400 appPackageName, enrollmentData.getEnrollmentId()); 401 } else if (apiType == API_AD_SELECTION) { 402 return AppManifestConfigHelper.isAllowedAdSelectionAccess( 403 appPackageName, enrollmentData.getEnrollmentId()); 404 } else { 405 throw new IllegalStateException( 406 String.format(Locale.ENGLISH, INVALID_API_TYPE, apiType)); 407 } 408 } 409 410 /** 411 * Extract and return an {@link AdTechIdentifier} from the given {@link Uri} after checking if 412 * the ad tech is enrolled and authorized to perform the operation for the package. 413 * 414 * @param context API service context 415 * @param appPackageName the package name to check against 416 * @param uriForAdTech a {@link Uri} matching the ad tech to check against 417 * @param apiNameLoggingId the logging ID of the API being called 418 * @return an {@link AdTechIdentifier} which is allowed to perform the operation 419 * @throws AdTechNotAllowedException if the ad tech is not authorized to perform the operation 420 */ 421 @NonNull getAndAssertAdTechFromUriAllowed( @onNull Context context, @NonNull String appPackageName, @NonNull Uri uriForAdTech, int apiNameLoggingId, @AppManifestConfigCall.ApiType int apiType)422 public AdTechIdentifier getAndAssertAdTechFromUriAllowed( 423 @NonNull Context context, 424 @NonNull String appPackageName, 425 @NonNull Uri uriForAdTech, 426 int apiNameLoggingId, 427 @AppManifestConfigCall.ApiType int apiType) 428 throws AdTechNotAllowedException { 429 Objects.requireNonNull(context); 430 Objects.requireNonNull(appPackageName); 431 Objects.requireNonNull(uriForAdTech); 432 433 int buildId = -1; 434 int dataFileGroupStatus = 0; 435 if (mEnrollmentUtil != null) { 436 buildId = mEnrollmentUtil.getBuildId(); 437 dataFileGroupStatus = mEnrollmentUtil.getFileGroupStatus(); 438 } 439 int enrollmentRecordsCount = mEnrollmentDao.getEnrollmentRecordCountForLogging(); 440 Pair<AdTechIdentifier, EnrollmentData> enrollmentResult = 441 getAdTechIdentifierEnrollmentDataPair(uriForAdTech, apiType, apiNameLoggingId); 442 443 if (enrollmentResult == null) { 444 sLogger.v( 445 "Enrollment data match not found for URI \"%s\" while calling API %d", 446 uriForAdTech, apiNameLoggingId); 447 mAdServicesLogger.logFledgeApiCallStats( 448 apiNameLoggingId, 449 appPackageName, 450 STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_MATCH_NOT_FOUND, 451 /*latencyMs=*/ 0); 452 mEnrollmentUtil.logEnrollmentFailedStats( 453 mAdServicesLogger, 454 buildId, 455 dataFileGroupStatus, 456 enrollmentRecordsCount, 457 uriForAdTech.toString(), 458 EnrollmentStatus.ErrorCause.ENROLLMENT_NOT_FOUND_ERROR_CAUSE.getValue()); 459 throw new AdTechNotAllowedException(); 460 } 461 462 AdTechIdentifier adTechIdentifier = enrollmentResult.first; 463 EnrollmentData enrollmentData = enrollmentResult.second; 464 465 boolean isAllowedAccess = isAllowedAccess(appPackageName, apiType, enrollmentData); 466 467 boolean isEnrollmentBlocklisted = 468 FlagsFactory.getFlags().isEnrollmentBlocklisted(enrollmentData.getEnrollmentId()); 469 int errorCause = EnrollmentStatus.ErrorCause.UNKNOWN_ERROR_CAUSE.getValue(); 470 if (!isAllowedAccess || isEnrollmentBlocklisted) { 471 sLogger.v( 472 "App package name \"%s\" with ad tech identifier \"%s\" from URI \"%s\" not" 473 + " authorized to call API %d", 474 appPackageName, adTechIdentifier.toString(), uriForAdTech, apiNameLoggingId); 475 476 int resultCode; 477 if (!isAllowedAccess) { 478 resultCode = STATUS_CALLER_NOT_ALLOWED_MANIFEST_ADSERVICES_CONFIG_NO_PERMISSION; 479 } else { 480 resultCode = STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED; 481 errorCause = 482 EnrollmentStatus.ErrorCause.ENROLLMENT_BLOCKLISTED_ERROR_CAUSE.getValue(); 483 } 484 mAdServicesLogger.logFledgeApiCallStats( 485 apiNameLoggingId, appPackageName, resultCode, /*latencyMs=*/ 0); 486 mEnrollmentUtil.logEnrollmentFailedStats( 487 mAdServicesLogger, 488 buildId, 489 dataFileGroupStatus, 490 enrollmentRecordsCount, 491 uriForAdTech.toString(), 492 errorCause); 493 throw new AdTechNotAllowedException(); 494 } 495 496 return adTechIdentifier; 497 } 498 499 /** 500 * Asserts that an {@link AdTechIdentifier} extracted from the given {@link Uri} is enrolled. 501 * 502 * <p>This method does not check for any app-specific authorization. 503 * 504 * <p>This method does not log general API called stats, since URL checks are generally done 505 * after initial API enrollment validation. 506 * 507 * @param uriForAdTech a {@link Uri} matching the ad tech to check against 508 * @param apiNameLoggingId the logging ID of the API being called 509 * @throws AdTechNotAllowedException if the ad tech is not authorized to perform the operation 510 */ 511 @NonNull assertAdTechFromUriEnrolled( @onNull Uri uriForAdTech, int apiNameLoggingId, @AppManifestConfigCall.ApiType int apiType)512 public void assertAdTechFromUriEnrolled( 513 @NonNull Uri uriForAdTech, 514 int apiNameLoggingId, 515 @AppManifestConfigCall.ApiType int apiType) 516 throws AdTechNotAllowedException { 517 Objects.requireNonNull(uriForAdTech); 518 519 int buildId = -1; 520 int dataFileGroupStatus = 0; 521 if (mEnrollmentUtil != null) { 522 buildId = mEnrollmentUtil.getBuildId(); 523 dataFileGroupStatus = mEnrollmentUtil.getFileGroupStatus(); 524 } 525 int enrollmentRecordsCount = mEnrollmentDao.getEnrollmentRecordCountForLogging(); 526 527 Pair<AdTechIdentifier, EnrollmentData> enrollmentResult = 528 getAdTechIdentifierEnrollmentDataPair(uriForAdTech, apiType, apiNameLoggingId); 529 530 if (enrollmentResult == null) { 531 sLogger.v( 532 "Enrollment data match not found for URI \"%s\" while calling API %d", 533 uriForAdTech, apiNameLoggingId); 534 mEnrollmentUtil.logEnrollmentFailedStats( 535 mAdServicesLogger, 536 buildId, 537 dataFileGroupStatus, 538 enrollmentRecordsCount, 539 uriForAdTech.toString(), 540 EnrollmentStatus.ErrorCause.ENROLLMENT_NOT_FOUND_ERROR_CAUSE.getValue()); 541 AdTechNotAllowedException exception = new AdTechNotAllowedException(); 542 checkAndLogCelByApiNameLoggingId( 543 exception, 544 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_ENROLLMENT_NOT_FOUND, 545 apiNameLoggingId); 546 throw exception; 547 } 548 549 AdTechIdentifier adTechIdentifier = enrollmentResult.first; 550 EnrollmentData enrollmentData = enrollmentResult.second; 551 552 if (FlagsFactory.getFlags().isEnrollmentBlocklisted(enrollmentData.getEnrollmentId())) { 553 sLogger.v( 554 "Enrollment for ad tech identifier \"%s\" from URI \"%s\" blocklisted while" 555 + " calling API %d", 556 adTechIdentifier, uriForAdTech, apiNameLoggingId); 557 558 mEnrollmentUtil.logEnrollmentFailedStats( 559 mAdServicesLogger, 560 buildId, 561 dataFileGroupStatus, 562 enrollmentRecordsCount, 563 uriForAdTech.toString(), 564 EnrollmentStatus.ErrorCause.ENROLLMENT_BLOCKLISTED_ERROR_CAUSE.getValue()); 565 AdTechNotAllowedException exception = new AdTechNotAllowedException(); 566 checkAndLogCelByApiNameLoggingId( 567 exception, 568 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_NOT_ALLOWED_ENROLLMENT_FROM_URI_BLOCKED, 569 apiNameLoggingId); 570 throw exception; 571 } 572 } 573 574 /** 575 * Check if a certain ad tech is enrolled for FLEDGE. 576 * 577 * @param adTechIdentifier the ad tech to check against 578 * @param apiNameLoggingId the id of the api being called 579 * @throws AdTechNotAllowedException if the ad tech is not enrolled in FLEDGE 580 */ assertAdTechEnrolled( @onNull AdTechIdentifier adTechIdentifier, int apiNameLoggingId)581 public void assertAdTechEnrolled( 582 @NonNull AdTechIdentifier adTechIdentifier, int apiNameLoggingId) 583 throws AdTechNotAllowedException { 584 Objects.requireNonNull(adTechIdentifier); 585 586 int buildId = -1; 587 int dataFileGroupStatus = 0; 588 if (mEnrollmentUtil != null) { 589 buildId = mEnrollmentUtil.getBuildId(); 590 dataFileGroupStatus = mEnrollmentUtil.getFileGroupStatus(); 591 } 592 int enrollmentRecordsCount = mEnrollmentDao.getEnrollmentRecordCountForLogging(); 593 594 EnrollmentData enrollmentData = 595 mEnrollmentDao.getEnrollmentDataForFledgeByAdTechIdentifier(adTechIdentifier); 596 597 if (enrollmentData == null) { 598 sLogger.v( 599 "Enrollment data match not found for ad tech \"%s\" while calling API %d", 600 adTechIdentifier, apiNameLoggingId); 601 mAdServicesLogger.logFledgeApiCallStats( 602 apiNameLoggingId, 603 STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_MATCH_NOT_FOUND, 604 /*latencyMs=*/ 0); 605 606 mEnrollmentUtil.logEnrollmentFailedStats( 607 mAdServicesLogger, 608 buildId, 609 dataFileGroupStatus, 610 enrollmentRecordsCount, 611 adTechIdentifier.toString(), 612 EnrollmentStatus.ErrorCause.ENROLLMENT_NOT_FOUND_ERROR_CAUSE.getValue()); 613 throw new AdTechNotAllowedException(); 614 } 615 616 if (FlagsFactory.getFlags().isEnrollmentBlocklisted(enrollmentData.getEnrollmentId())) { 617 sLogger.v( 618 "Enrollment for ad tech identifier \"%s\" blocklisted while calling API %d", 619 adTechIdentifier, apiNameLoggingId); 620 mAdServicesLogger.logFledgeApiCallStats( 621 apiNameLoggingId, 622 STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED, 623 /* latencyMs= */ 0); 624 625 mEnrollmentUtil.logEnrollmentFailedStats( 626 mAdServicesLogger, 627 buildId, 628 dataFileGroupStatus, 629 enrollmentRecordsCount, 630 adTechIdentifier.toString(), 631 EnrollmentStatus.ErrorCause.ENROLLMENT_BLOCKLISTED_ERROR_CAUSE.getValue()); 632 throw new AdTechNotAllowedException(); 633 } 634 } 635 636 /** 637 * Internal exception for easy assertion catches specific to checking that a caller matches the 638 * given package name. 639 * 640 * <p>This exception is not meant to be exposed externally and should not be passed outside of 641 * the service. 642 */ 643 public static class CallerMismatchException extends SecurityException { 644 /** 645 * Creates a {@link CallerMismatchException}, used in cases where a caller should match the 646 * package name provided to the API. 647 */ CallerMismatchException()648 public CallerMismatchException() { 649 super( 650 AdServicesStatusUtils 651 .SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ON_BEHALF_ERROR_MESSAGE); 652 } 653 } 654 655 /** 656 * Internal exception for easy assertion catches specific to checking that an ad tech is allowed 657 * to use the PP APIs. 658 * 659 * <p>This exception is not meant to be exposed externally and should not be passed outside of 660 * the service. 661 */ 662 public static class AdTechNotAllowedException extends SecurityException { 663 /** 664 * Creates a {@link AdTechNotAllowedException}, used in cases where an ad tech should have 665 * been allowed to use the PP APIs. 666 */ AdTechNotAllowedException()667 public AdTechNotAllowedException() { 668 super(AdServicesStatusUtils.SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE); 669 } 670 } 671 } 672