Add new public API FPDF_GetDocUserPermissions

FPDF_GetDocUserPermissions return user access
even if the document is opened with the 'owner password'

Useful for retrieving or editing user permissions of a doc,
even though the document was unlocked using the owner password

Bug: pdfium:2040
Change-Id: I76658d72691e8d6593e89e28a99a3fc4ce2c5637
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/108670
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/parser/cpdf_document.cpp b/core/fpdfapi/parser/cpdf_document.cpp
index 8f53f4e..3e7720c 100644
--- a/core/fpdfapi/parser/cpdf_document.cpp
+++ b/core/fpdfapi/parser/cpdf_document.cpp
@@ -399,8 +399,8 @@
   return CountPages(std::move(pPages), &visited_pages).value_or(0);
 }
 
-uint32_t CPDF_Document::GetUserPermissions() const {
-  return m_pParser ? m_pParser->GetPermissions() : 0;
+uint32_t CPDF_Document::GetUserPermissions(bool get_owner_perms) const {
+  return m_pParser ? m_pParser->GetPermissions(get_owner_perms) : 0;
 }
 
 RetainPtr<CPDF_StreamAcc> CPDF_Document::GetFontFileStreamAcc(
diff --git a/core/fpdfapi/parser/cpdf_document.h b/core/fpdfapi/parser/cpdf_document.h
index a261bbc..554831a 100644
--- a/core/fpdfapi/parser/cpdf_document.h
+++ b/core/fpdfapi/parser/cpdf_document.h
@@ -107,7 +107,9 @@
   RetainPtr<const CPDF_Dictionary> GetPageDictionary(int iPage);
   RetainPtr<CPDF_Dictionary> GetMutablePageDictionary(int iPage);
   int GetPageIndex(uint32_t objnum);
-  uint32_t GetUserPermissions() const;
+  // When `get_owner_perms` is true, returns full permissions if unlocked by
+  // owner.
+  uint32_t GetUserPermissions(bool get_owner_perms) const;
 
   // PageDataIface wrappers, try to avoid explicit getter calls.
   RetainPtr<CPDF_StreamAcc> GetFontFileStreamAcc(
diff --git a/core/fpdfapi/parser/cpdf_parser.cpp b/core/fpdfapi/parser/cpdf_parser.cpp
index 098e67e..dd4af5a 100644
--- a/core/fpdfapi/parser/cpdf_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_parser.cpp
@@ -1069,8 +1069,10 @@
   return ToDictionary(m_pSyntax->GetObjectBody(m_pObjectsHolder));
 }
 
-uint32_t CPDF_Parser::GetPermissions() const {
-  return m_pSecurityHandler ? m_pSecurityHandler->GetPermissions() : 0xFFFFFFFF;
+uint32_t CPDF_Parser::GetPermissions(bool get_owner_perms) const {
+  return m_pSecurityHandler
+             ? m_pSecurityHandler->GetPermissions(get_owner_perms)
+             : 0xFFFFFFFF;
 }
 
 std::unique_ptr<CPDF_LinearizedHeader> CPDF_Parser::ParseLinearizedHeader() {
diff --git a/core/fpdfapi/parser/cpdf_parser.h b/core/fpdfapi/parser/cpdf_parser.h
index 09dc724..ea8409a 100644
--- a/core/fpdfapi/parser/cpdf_parser.h
+++ b/core/fpdfapi/parser/cpdf_parser.h
@@ -87,7 +87,7 @@
 
   FX_FILESIZE GetLastXRefOffset() const { return m_LastXRefOffset; }
 
-  uint32_t GetPermissions() const;
+  uint32_t GetPermissions(bool get_owner_perms) const;
   uint32_t GetRootObjNum() const;
   uint32_t GetInfoObjNum() const;
   RetainPtr<const CPDF_Array> GetIDArray() const;
diff --git a/core/fpdfapi/parser/cpdf_security_handler.cpp b/core/fpdfapi/parser/cpdf_security_handler.cpp
index badb1b5..598e0dc 100644
--- a/core/fpdfapi/parser/cpdf_security_handler.cpp
+++ b/core/fpdfapi/parser/cpdf_security_handler.cpp
@@ -216,8 +216,9 @@
   return CheckPassword(password, false);
 }
 
-uint32_t CPDF_SecurityHandler::GetPermissions() const {
-  uint32_t dwPermission = m_bOwnerUnlocked ? 0xFFFFFFFF : m_Permissions;
+uint32_t CPDF_SecurityHandler::GetPermissions(bool get_owner_perms) const {
+  uint32_t dwPermission =
+      m_bOwnerUnlocked && get_owner_perms ? 0xFFFFFFFF : m_Permissions;
   if (m_pEncryptDict &&
       m_pEncryptDict->GetByteStringFor("Filter") == "Standard") {
     // See PDF Reference 1.7, page 123, table 3.20.
diff --git a/core/fpdfapi/parser/cpdf_security_handler.h b/core/fpdfapi/parser/cpdf_security_handler.h
index 300dd9b..a53436f 100644
--- a/core/fpdfapi/parser/cpdf_security_handler.h
+++ b/core/fpdfapi/parser/cpdf_security_handler.h
@@ -34,7 +34,9 @@
                 const CPDF_Array* pIdArray,
                 const ByteString& user_password);
 
-  uint32_t GetPermissions() const;
+  // When `get_owner_perms` is true, returns full permissions if unlocked by
+  // owner.
+  uint32_t GetPermissions(bool get_owner_perms) const;
   bool IsMetadataEncrypted() const;
 
   CPDF_CryptoHandler* GetCryptoHandler() const {
diff --git a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
index cf1a2b5..1475cfd 100644
--- a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
+++ b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
@@ -108,12 +108,16 @@
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, Unencrypted) {
   ASSERT_TRUE(OpenDocument("about_blank.pdf"));
+  // parser is missing a security handler, so always results in 0xFFFFFFFF
   EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocPermissions(document()));
+  EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocUserPermissions(document()));
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UnencryptedWithPassword) {
   ASSERT_TRUE(OpenDocumentWithPassword("about_blank.pdf", "foobar"));
+  // parser is missing a security handler, so always results in 0xFFFFFFFF
   EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocPermissions(document()));
+  EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocUserPermissions(document()));
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, NoPassword) {
@@ -127,11 +131,13 @@
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPassword) {
   ASSERT_TRUE(OpenDocumentWithPassword("encrypted.pdf", "1234"));
   EXPECT_EQ(0xFFFFF2C0, FPDF_GetDocPermissions(document()));
+  EXPECT_EQ(0xFFFFF2C0, FPDF_GetDocUserPermissions(document()));
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPassword) {
   ASSERT_TRUE(OpenDocumentWithPassword("encrypted.pdf", "5678"));
   EXPECT_EQ(0xFFFFFFFC, FPDF_GetDocPermissions(document()));
+  EXPECT_EQ(0xFFFFF2C0, FPDF_GetDocUserPermissions(document()));
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, PasswordAfterGenerateSave) {
diff --git a/fpdfsdk/cpdfsdk_formfillenvironment.cpp b/fpdfsdk/cpdfsdk_formfillenvironment.cpp
index 9aade3a..075e43e 100644
--- a/fpdfsdk/cpdfsdk_formfillenvironment.cpp
+++ b/fpdfsdk/cpdfsdk_formfillenvironment.cpp
@@ -802,7 +802,7 @@
 }
 
 bool CPDFSDK_FormFillEnvironment::HasPermissions(uint32_t flags) const {
-  return !!(m_pCPDFDoc->GetUserPermissions() & flags);
+  return !!(m_pCPDFDoc->GetUserPermissions(/*get_owner_perms=*/true) & flags);
 }
 
 void CPDFSDK_FormFillEnvironment::SendOnFocusChange(
diff --git a/fpdfsdk/cpdfsdk_widget.cpp b/fpdfsdk/cpdfsdk_widget.cpp
index 0b8083e..162502c 100644
--- a/fpdfsdk/cpdfsdk_widget.cpp
+++ b/fpdfsdk/cpdfsdk_widget.cpp
@@ -713,7 +713,8 @@
 
   bool do_hit_test = GetFieldType() == FormFieldType::kPushButton;
   if (!do_hit_test) {
-    uint32_t perms = GetPDFPage()->GetDocument()->GetUserPermissions();
+    uint32_t perms = GetPDFPage()->GetDocument()->GetUserPermissions(
+        /*get_owner_perms=*/true);
     do_hit_test = (perms & pdfium::access_permissions::kFillForm) ||
                   (perms & pdfium::access_permissions::kModifyAnnotation);
   }
diff --git a/fpdfsdk/fpdf_view.cpp b/fpdfsdk/fpdf_view.cpp
index f93561c..9e7228f 100644
--- a/fpdfsdk/fpdf_view.cpp
+++ b/fpdfsdk/fpdf_view.cpp
@@ -368,7 +368,13 @@
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDF_GetDocPermissions(FPDF_DOCUMENT document) {
   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  return pDoc ? pDoc->GetUserPermissions() : 0;
+  return pDoc ? pDoc->GetUserPermissions(/*get_owner_perms=*/true) : 0;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_GetDocUserPermissions(FPDF_DOCUMENT document) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  return pDoc ? pDoc->GetUserPermissions(/*get_owner_perms=*/false) : 0;
 }
 
 FPDF_EXPORT int FPDF_CALLCONV
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index bb25225..90f6052 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -486,6 +486,7 @@
     CHK(FPDF_GetArrayBufferAllocatorSharedInstance);
 #endif
     CHK(FPDF_GetDocPermissions);
+    CHK(FPDF_GetDocUserPermissions);
     CHK(FPDF_GetFileVersion);
     CHK(FPDF_GetLastError);
     CHK(FPDF_GetNamedDest);
diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp
index d3fb581..be7e15d 100644
--- a/fpdfsdk/fpdf_view_embeddertest.cpp
+++ b/fpdfsdk/fpdf_view_embeddertest.cpp
@@ -484,7 +484,9 @@
   EXPECT_TRUE(FPDF_GetFileVersion(document(), &version));
   EXPECT_EQ(14, version);
 
+  // 0xFFFFFFFF because no security handler present
   EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocPermissions(document()));
+  EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocUserPermissions(document()));
   EXPECT_EQ(-1, FPDF_GetSecurityHandlerRevision(document()));
   CloseDocument();
 
@@ -497,7 +499,9 @@
   EXPECT_TRUE(FPDF_GetFileVersion(document(), &version));
   EXPECT_EQ(14, version);
 
+  // 0xFFFFFFFF because no security handler present
   EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocPermissions(document()));
+  EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocUserPermissions(document()));
   EXPECT_EQ(-1, FPDF_GetSecurityHandlerRevision(document()));
   // CloseDocument() called by TearDown().
 }
diff --git a/public/fpdfview.h b/public/fpdfview.h
index 0c901a7..c066e6c 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -652,17 +652,29 @@
                     unsigned int* buffer,
                     unsigned long length);
 
-// Function: FPDF_GetDocPermission
+// Function: FPDF_GetDocPermissions
 //          Get file permission flags of the document.
 // Parameters:
 //          document    -   Handle to a document. Returned by FPDF_LoadDocument.
 // Return value:
 //          A 32-bit integer indicating permission flags. Please refer to the
 //          PDF Reference for detailed descriptions. If the document is not
-//          protected, 0xffffffff will be returned.
+//          protected or was unlocked by the owner, 0xffffffff will be returned.
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDF_GetDocPermissions(FPDF_DOCUMENT document);
 
+// Function: FPDF_GetDocUserPermissions
+//          Get user file permission flags of the document.
+// Parameters:
+//          document    -   Handle to a document. Returned by FPDF_LoadDocument.
+// Return value:
+//          A 32-bit integer indicating permission flags. Please refer to the
+//          PDF Reference for detailed descriptions. If the document is not
+//          protected, 0xffffffff will be returned. Always returns user
+//          permissions, even if the document was unlocked by the owner.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_GetDocUserPermissions(FPDF_DOCUMENT document);
+
 // Function: FPDF_GetSecurityHandlerRevision
 //          Get the revision for the security handler.
 // Parameters: