diff --git a/britney.py b/britney.py
index ace53f5..aa376c4 100755
--- a/britney.py
+++ b/britney.py
@@ -62,6 +62,9 @@ Other than source and binary packages, Britney loads the following data:
   * Hints, which contains lists of commands which modify the standard behaviour
     of Britney (see Britney.read_hints).
 
+  * Blocks, which contains user-supplied blocks read from Launchpad bugs
+    (see LPBlockBugPolicy).
+
 For a more detailed explanation about the format of these files, please read
 the documentation of the related methods. The exact meaning of them will be
 instead explained in the chapter "Excuses Generation".
@@ -199,6 +202,7 @@ from britney2.hints import HintParser
 from britney2.installability.builder import build_installability_tester, ma_parse_depends
 from britney2.migrationitem import MigrationItem
 from britney2.policies.policy import AgePolicy, RCBugPolicy, PiupartsPolicy, PolicyVerdict
+from britney2.policies.policy import LPBlockBugPolicy
 from britney2.utils import (old_libraries_format, undo_changes,
                             compute_reverse_tree, possibly_compressed,
                             read_nuninst, write_nuninst, write_heidi,
@@ -526,6 +530,7 @@ class Britney(object):
         self.policies.append(AgePolicy(self.options, self.suite_info, MINDAYS))
         self.policies.append(RCBugPolicy(self.options, self.suite_info))
         self.policies.append(PiupartsPolicy(self.options, self.suite_info))
+        self.policies.append(LPBlockBugPolicy(self.options, self.suite_info))
 
         for policy in self.policies:
             policy.register_hints(self._hint_parser)
diff --git a/britney2/policies/policy.py b/britney2/policies/policy.py
index ab1bee2..013d18a 100644
--- a/britney2/policies/policy.py
+++ b/britney2/policies/policy.py
@@ -654,3 +654,50 @@ class PiupartsPolicy(BasePolicy):
             summary[source] = (state, url)
 
         return summary
+
+
+class LPBlockBugPolicy(BasePolicy):
+    """block-proposed Launchpad bug policy for source migrations
+
+    This policy will read an user-supplied "Blocks" file from the unstable
+    directory (provided by an external script) with rows of the following
+    format:
+
+        <source-name> <bug> <date>
+
+    The dates are expressed as the number of seconds from the Unix epoch
+    (1970-01-01 00:00:00 UTC).
+    """
+    def __init__(self, options, suite_info):
+        super().__init__('block-bugs', options, suite_info, {'unstable'})
+
+    def initialise(self, britney):
+        super().initialise(britney)
+        self.blocks = {}  # srcpkg -> [(bug, date), ...]
+
+        filename = os.path.join(self.options.unstable, "Blocks")
+        self.log("Loading user-supplied block data from %s" % filename)
+        for line in open(filename):
+            l = line.split()
+            if len(l) != 3:
+                self.log("Blocks, ignoring malformed line %s" % line, type='W')
+                continue
+            try:
+                self.blocks.setdefault(l[0], [])
+                self.blocks[l[0]].append((l[1], int(l[2])))
+            except ValueError:
+                self.log("Blocks, unable to parse \"%s\"" % line, type='E')
+
+    def apply_policy_impl(self, block_bugs_info, suite, source_name, source_data_tdist, source_data_srcdist, excuse):
+        try:
+            blocks = self.blocks[source_name]
+        except KeyError:
+            return PolicyVerdict.PASS
+
+        for bug, date in blocks:
+            block_bugs_info[bug] = date
+            excuse.addhtml("Not touching package as requested in <a href=\"https://launchpad.net/bugs/%s\">bug %s</a> on %s" %
+                           (bug, bug, time.asctime(time.gmtime(date))))
+        excuse.addreason('block')
+
+        return PolicyVerdict.REJECTED_PERMANENTLY