diff --git a/core/modules/media/media.install b/core/modules/media/media.install
index 86a95188d1..092bdf3054 100644
--- a/core/modules/media/media.install
+++ b/core/modules/media/media.install
@@ -6,6 +6,8 @@
  */
 
 use Drupal\user\RoleInterface;
+use Drupal\user\Entity\Role;
+use Drupal\media\Entity\MediaType;
 
 /**
  * Implements hook_install().
@@ -77,3 +79,59 @@ function media_requirements($phase) {
 
   return $requirements;
 }
+
+/**
+ * Introduce per-bundle permissions.
+ */
+function media_update_8500() {
+
+  /** @var \Drupal\user\RoleInterface $role */
+  foreach (Role::loadMultiple() as $role) {
+
+    $media_types = MediaType::loadMultiple();
+    if ($role->hasPermission('update media')) {
+      $role->grantPermission('edit own media');
+
+      /** @var \Drupal\media\MediaTypeInterface $media_type */
+      foreach ($media_types as $media_type) {
+        $role->grantPermission("edit own {$media_type->id()} media");
+      }
+    }
+
+    if ($role->hasPermission('update any media')) {
+      $role->grantPermission('edit any media');
+
+      /** @var \Drupal\media\MediaTypeInterface $media_type */
+      foreach ($media_types as $media_type) {
+        $role->grantPermission("edit any {$media_type->id()} media");
+      }
+    }
+
+    if ($role->hasPermission('delete media')) {
+      $role->grantPermission('delete own media');
+
+      /** @var \Drupal\media\MediaTypeInterface $media_type */
+      foreach ($media_types as $media_type) {
+        $role->grantPermission("delete own {$media_type->id()} media");
+      }
+    }
+
+    if ($role->hasPermission('delete any media')) {
+
+      /** @var \Drupal\media\MediaTypeInterface $media_type */
+      foreach ($media_types as $media_type) {
+        $role->grantPermission("delete any {$media_type->id()} media");
+      }
+    }
+
+    if ($role->hasPermission('create media')) {
+
+      /** @var \Drupal\media\MediaTypeInterface $media_type */
+      foreach ($media_types as $media_type) {
+        $role->grantPermission("create {$media_type->id()} media");
+      }
+    }
+
+    $role->save();
+  }
+}
diff --git a/core/modules/media/media.permissions.yml b/core/modules/media/media.permissions.yml
index 79eadb5058..69f4b314c1 100644
--- a/core/modules/media/media.permissions.yml
+++ b/core/modules/media/media.permissions.yml
@@ -1,29 +1,23 @@
 administer media:
   title: 'Administer media'
   restrict access: TRUE
-
 administer media types:
   title: 'Administer media types'
   restrict access: TRUE
-
 view media:
   title: 'View media'
-
-update media:
-  title: 'Update own media'
-
-update any media:
-  title: 'Update any media'
-
-delete media:
+edit own media:
+  title: 'Edit own media'
+edit any media:
+  title: 'Edit any media'
+delete own media:
   title:  'Delete own media'
-
 delete any media:
   title:  'Delete any media'
-
 create media:
   title: 'Create media'
-
 view all media revisions:
   title: 'View all media revisions'
   description: 'To view a revision, you also need permission to view the media item.'
+permission_callbacks:
+  - \Drupal\media\MediaPermissions::mediaTypePermissions
diff --git a/core/modules/media/src/MediaAccessControlHandler.php b/core/modules/media/src/MediaAccessControlHandler.php
index 434abfe719..d82efaeedd 100644
--- a/core/modules/media/src/MediaAccessControlHandler.php
+++ b/core/modules/media/src/MediaAccessControlHandler.php
@@ -20,6 +20,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
       return AccessResult::allowed()->cachePerPermissions();
     }
 
+    $type = $entity->bundle();
     $is_owner = ($account->id() && $account->id() === $entity->getOwnerId());
     switch ($operation) {
       case 'view':
@@ -32,10 +33,16 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
         return $access_result;
 
       case 'update':
-        if ($account->hasPermission('update any media')) {
+        if ($account->hasPermission('edit any media')) {
           return AccessResult::allowed()->cachePerPermissions();
         }
-        return AccessResult::allowedIf($account->hasPermission('update media') && $is_owner)
+        if ($account->hasPermission('edit any ' . $type . ' media')) {
+          return AccessResult::allowed()->cachePerPermissions();
+        }
+        if ($account->hasPermission('edit own ' . $type . ' media') && $is_owner) {
+          return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity);
+        }
+        return AccessResult::allowedIf($account->hasPermission('edit own media') && $is_owner)
           ->cachePerPermissions()
           ->cachePerUser()
           ->addCacheableDependency($entity);
@@ -44,7 +51,13 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
         if ($account->hasPermission('delete any media')) {
           return AccessResult::allowed()->cachePerPermissions();
         }
-        return AccessResult::allowedIf($account->hasPermission('delete media') && $is_owner)
+        if ($account->hasPermission('delete any ' . $type . ' media')) {
+          return AccessResult::allowed()->cachePerPermissions();
+        }
+        if ($account->hasPermission('delete own ' . $type . ' media') && $is_owner) {
+          return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity);
+        }
+        return AccessResult::allowedIf($account->hasPermission('delete own media') && $is_owner)
           ->cachePerPermissions()
           ->cachePerUser()
           ->addCacheableDependency($entity);
@@ -58,7 +71,12 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
    * {@inheritdoc}
    */
   protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
-    return AccessResult::allowedIfHasPermissions($account, ['administer media', 'create media'], 'OR');
+    $permissions = [
+      'administer media',
+      'create media',
+      'create ' . $entity_bundle . ' media',
+    ];
+    return AccessResult::allowedIfHasPermissions($account, $permissions, 'OR');
   }
 
 }
diff --git a/core/modules/media/src/MediaPermissions.php b/core/modules/media/src/MediaPermissions.php
new file mode 100644
index 0000000000..1c08cc21d3
--- /dev/null
+++ b/core/modules/media/src/MediaPermissions.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Drupal\media;
+
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides dynamic permissions for each media type.
+ */
+class MediaPermissions implements ContainerInjectionInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * MediaPermissions constructor.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
+   *   The entity type manager service.
+   */
+  public function __construct(EntityTypeManagerInterface $entityTypeManager) {
+    $this->entityTypeManager = $entityTypeManager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('entity_type.manager'));
+  }
+
+  /**
+   * Returns an array of media type permissions.
+   *
+   * @return array
+   *   The media type permissions.
+   *
+   * @see \Drupal\user\PermissionHandlerInterface::getPermissions()
+   */
+  public function mediaTypePermissions() {
+    $perms = [];
+    // Generate media permissions for all media types.
+    $media_types = $this->entityTypeManager
+      ->getStorage('media_type')->loadMultiple();
+    foreach ($media_types as $type) {
+      $perms += $this->buildPermissions($type);
+    }
+    return $perms;
+  }
+
+  /**
+   * Returns a list of media permissions for a given media type.
+   *
+   * @param \Drupal\media\MediaTypeInterface $type
+   *   The media type.
+   *
+   * @return array
+   *   An associative array of permission names and descriptions.
+   */
+  protected function buildPermissions(MediaTypeInterface $type) {
+    $type_id = $type->id();
+    $type_params = ['%type_name' => $type->label()];
+
+    return [
+      "create $type_id media" => [
+        'title' => $this->t('%type_name: Create new media', $type_params),
+      ],
+      "edit own $type_id media" => [
+        'title' => $this->t('%type_name: Edit own media', $type_params),
+      ],
+      "edit any $type_id media" => [
+        'title' => $this->t('%type_name: Edit any media', $type_params),
+      ],
+      "delete own $type_id media" => [
+        'title' => $this->t('%type_name: Delete own media', $type_params),
+      ],
+      "delete any $type_id media" => [
+        'title' => $this->t('%type_name: Delete any media', $type_params),
+      ],
+    ];
+  }
+
+}
diff --git a/core/modules/media/tests/fixtures/update/drupal-8.media-enabled.php b/core/modules/media/tests/fixtures/update/drupal-8.media-enabled.php
new file mode 100644
index 0000000000..e497807428
Binary files /dev/null and b/core/modules/media/tests/fixtures/update/drupal-8.media-enabled.php differ
diff --git a/core/modules/media/tests/src/Functional/MediaAccessTest.php b/core/modules/media/tests/src/Functional/MediaAccessTest.php
index ba02f083bf..e12a3d59f9 100644
--- a/core/modules/media/tests/src/Functional/MediaAccessTest.php
+++ b/core/modules/media/tests/src/Functional/MediaAccessTest.php
@@ -67,46 +67,106 @@ public function testMediaAccess() {
     $this->assertCacheContext('user.permissions');
     $assert_session->statusCodeEquals(200);
 
+    // Test 'create BUNDLE media' permission.
+    $this->drupalGet('media/add/' . $media_type->id());
+    $this->assertCacheContext('user.permissions');
+    $assert_session->statusCodeEquals(403);
+    $permissions = ['create ' . $media_type->id() . ' media'];
+    $this->grantPermissions($role, $permissions);
+    $this->drupalGet('media/add/' . $media_type->id());
+    $this->assertCacheContext('user.permissions');
+    $assert_session->statusCodeEquals(200);
+    user_role_revoke_permissions($role->id(), $permissions);
+    $role = Role::load(RoleInterface::AUTHENTICATED_ID);
+
     // Test 'create media' permission.
     $this->drupalGet('media/add/' . $media_type->id());
     $this->assertCacheContext('user.permissions');
     $assert_session->statusCodeEquals(403);
-    $this->grantPermissions($role, ['create media']);
+    $permissions = ['create media'];
+    $this->grantPermissions($role, $permissions);
     $this->drupalGet('media/add/' . $media_type->id());
     $this->assertCacheContext('user.permissions');
     $assert_session->statusCodeEquals(200);
+    user_role_revoke_permissions($role->id(), $permissions);
+    $role = Role::load(RoleInterface::AUTHENTICATED_ID);
+
+    // Test 'edit own BUNDLE media' and 'delete own BUNDLE media' permissions.
+    $this->drupalGet('media/' . $user_media->id() . '/edit');
+    $this->assertCacheContext('user');
+    $assert_session->statusCodeEquals(403);
+    $this->drupalGet('media/' . $user_media->id() . '/delete');
+    $this->assertCacheContext('user');
+    $assert_session->statusCodeEquals(403);
+    $permissions = [
+      'edit own ' . $user_media->bundle() . ' media',
+      'delete own ' . $user_media->bundle() . ' media',
+    ];
+    $this->grantPermissions($role, $permissions);
+    $this->drupalGet('media/' . $user_media->id() . '/edit');
+    $this->assertCacheContext('user');
+    $assert_session->statusCodeEquals(200);
+    $this->drupalGet('media/' . $user_media->id() . '/delete');
+    $this->assertCacheContext('user');
+    $assert_session->statusCodeEquals(200);
+    user_role_revoke_permissions($role->id(), $permissions);
+    $role = Role::load(RoleInterface::AUTHENTICATED_ID);
+
+    // Test 'edit any BUNDLE media' and 'delete any BUNDLE media' permissions.
+    $this->drupalGet('media/' . $media->id() . '/edit');
+    $this->assertCacheContext('user');
+    $assert_session->statusCodeEquals(403);
+    $this->drupalGet('media/' . $media->id() . '/delete');
+    $this->assertCacheContext('user');
+    $assert_session->statusCodeEquals(403);
+    $permissions = [
+      'edit any ' . $media->bundle() . ' media',
+      'delete any ' . $media->bundle() . ' media',
+    ];
+    $this->grantPermissions($role, $permissions);
+    $this->drupalGet('media/' . $media->id() . '/edit');
+    $this->assertCacheContext('user.permissions');
+    $assert_session->statusCodeEquals(200);
+    $this->drupalGet('media/' . $media->id() . '/delete');
+    $this->assertCacheContext('user.permissions');
+    $assert_session->statusCodeEquals(200);
+    user_role_revoke_permissions($role->id(), $permissions);
+    $role = Role::load(RoleInterface::AUTHENTICATED_ID);
 
-    // Test 'update media' and 'delete media' permissions.
+    // Test 'edit own media' and 'delete own media' permissions.
     $this->drupalGet('media/' . $user_media->id() . '/edit');
     $this->assertCacheContext('user');
     $assert_session->statusCodeEquals(403);
     $this->drupalGet('media/' . $user_media->id() . '/delete');
     $this->assertCacheContext('user');
     $assert_session->statusCodeEquals(403);
-    $this->grantPermissions($role, ['update media']);
-    $this->grantPermissions($role, ['delete media']);
+    $permissions = ['edit own media', 'delete own media'];
+    $this->grantPermissions($role, $permissions);
     $this->drupalGet('media/' . $user_media->id() . '/edit');
     $this->assertCacheContext('user');
     $assert_session->statusCodeEquals(200);
     $this->drupalGet('media/' . $user_media->id() . '/delete');
     $this->assertCacheContext('user');
     $assert_session->statusCodeEquals(200);
+    user_role_revoke_permissions($role->id(), $permissions);
+    $role = Role::load(RoleInterface::AUTHENTICATED_ID);
 
-    // Test 'update any media' and 'delete any media' permissions.
+    // Test 'edit any media' and 'delete any media' permissions.
     $this->drupalGet('media/' . $media->id() . '/edit');
     $this->assertCacheContext('user');
     $assert_session->statusCodeEquals(403);
     $this->drupalGet('media/' . $media->id() . '/delete');
     $this->assertCacheContext('user');
     $assert_session->statusCodeEquals(403);
-    $this->grantPermissions($role, ['update any media']);
-    $this->grantPermissions($role, ['delete any media']);
+    $permissions = ['edit any media', 'delete any media'];
+    $this->grantPermissions($role, $permissions);
     $this->drupalGet('media/' . $media->id() . '/edit');
     $this->assertCacheContext('user.permissions');
     $assert_session->statusCodeEquals(200);
     $this->drupalGet('media/' . $media->id() . '/delete');
     $this->assertCacheContext('user.permissions');
     $assert_session->statusCodeEquals(200);
+    user_role_revoke_permissions($role->id(), $permissions);
   }
 
 }
diff --git a/core/modules/media/tests/src/Functional/MediaFunctionalTestTrait.php b/core/modules/media/tests/src/Functional/MediaFunctionalTestTrait.php
index 75e0d06c16..93a907670e 100644
--- a/core/modules/media/tests/src/Functional/MediaFunctionalTestTrait.php
+++ b/core/modules/media/tests/src/Functional/MediaFunctionalTestTrait.php
@@ -21,9 +21,9 @@
     'administer media types',
     'view media',
     'create media',
-    'update media',
-    'update any media',
-    'delete media',
+    'edit own media',
+    'edit any media',
+    'delete own media',
     'delete any media',
     // Other permissions.
     'administer views',
diff --git a/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
new file mode 100644
index 0000000000..dd998ca051
--- /dev/null
+++ b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Drupal\Tests\media\Functional\Update;
+
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+use Drupal\user\Entity\Role;
+
+/**
+ * Tests that media settings are properly updated during database updates.
+ *
+ * @group media
+ */
+class MediaUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
+      __DIR__ . '/../../../fixtures/update/drupal-8.media-enabled.php',
+    ];
+  }
+
+  /**
+   * Tests that media permissions are correctly migrated.
+   *
+   * @see media_update_8500()
+   */
+  public function testBundlePermission() {
+
+    $role = Role::load(Role::AUTHENTICATED_ID);
+    $this->grantPermissions($role, [
+      'update media',
+      'update any media',
+      'delete media',
+      'delete any media',
+      'create media',
+    ]);
+
+    // Run updates.
+    $this->runUpdates();
+
+    $role = Role::load(Role::AUTHENTICATED_ID);
+
+    $this->assertTrue($role->hasPermission('edit own media'));
+    $this->assertTrue($role->hasPermission('edit any media'));
+    $this->assertTrue($role->hasPermission('delete own media'));
+    $this->assertTrue($role->hasPermission('create own media'));
+  }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Media/MediaResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Media/MediaResourceTestBase.php
index f5bfa14291..2da095df51 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Media/MediaResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Media/MediaResourceTestBase.php
@@ -49,7 +49,7 @@ protected function setUpAuthorization($method) {
         break;
 
       case 'PATCH':
-        $this->grantPermissionsToTestedRole(['update any media']);
+        $this->grantPermissionsToTestedRole(['edit any media']);
         break;
 
       case 'DELETE':
