diff --git a/doc/sql/query.ebnf b/doc/sql/query.ebnf
index 388dfdd1d0552c58a17ab4a25b0c68628583ebcf..6632d84c378d1b95b3f330e60e242fb8d3201091 100644
--- a/doc/sql/query.ebnf
+++ b/doc/sql/query.ebnf
@@ -13,6 +13,8 @@ privilege ::= (('CREATE' | 'ALTER' | 'DROP') 'USER')
               | ('DROP' 'ON' 'ROLE' (role | user))
               | (('READ' | 'WRITE' | 'CREATE' | 'ALTER' | 'DROP') 'TABLE')
               | (('ALTER' | 'DROP' | 'READ' | 'WRITE') 'ON' 'TABLE'? table)
+              | (('CREATE' | 'EXECUTE') 'PROCEDURE')
+              | ('EXECUTE' 'PROCEDURE' procedure ('(' type (',' type)* ')')?)
               | role
 create_procedure ::= 'CREATE PROCEDURE' procedure '(' type (',' type)* ')'
                      ('language' 'SQL')? (('as' '$$' body '$$') | ('begin' 'atomic' body 'end'))
diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs
index bb5cd6f798ac549cd1b2c2536520711c50cc7fab..453e945ec52b04e8fdb3740c808ab3839b68dc1f 100644
--- a/sbroad-core/src/frontend/sql.rs
+++ b/sbroad-core/src/frontend/sql.rs
@@ -655,6 +655,15 @@ fn parse_grant_revoke(
                     let table_name = parse_identifier(ast, *table_node_id)?;
                     GrantRevokeType::specific_table(privilege, table_name)?
                 }
+                Rule::PrivBlockProcedure => GrantRevokeType::procedure(privilege)?,
+                Rule::PrivBlockSpecificProcedure => {
+                    let proc_node_id = inner_privilege_block_node.children.first().expect(
+                        "Expected to see Name as a first child of PrivBlockSpecificProcedure",
+                    );
+                    let proc_node = ast.nodes.get_node(*proc_node_id)?;
+                    let (proc_name, proc_params) = parse_proc_with_optional_params(ast, proc_node)?;
+                    GrantRevokeType::specific_procedure(privilege, proc_name, proc_params)?
+                }
                 _ => {
                     return Err(SbroadError::Invalid(
                         Entity::ParseNode,
diff --git a/sbroad-core/src/frontend/sql/query.pest b/sbroad-core/src/frontend/sql/query.pest
index ba399ce5e3d05e8d7cbe5094ff57d979d5a9e0ee..ae0fd58c0e8e885e3352101379ef30b574e44968 100644
--- a/sbroad-core/src/frontend/sql/query.pest
+++ b/sbroad-core/src/frontend/sql/query.pest
@@ -30,7 +30,8 @@ ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser | GrantPrivil
     RevokePrivilege = { ^"revoke" ~ PrivBlock ~ ^"from" ~ Identifier ~ TimeoutOption? }
         PrivBlock = _{ PrivBlockPrivilege | PrivBlockRolePass }
             PrivBlockPrivilege = {Privilege ~ (PrivBlockUser | PrivBlockSpecificUser | PrivBlockRole
-                                                | PrivBlockSpecificRole | PrivBlockTable | PrivBlockSpecificTable)}
+                                                | PrivBlockSpecificRole | PrivBlockTable | PrivBlockSpecificTable
+                                                | PrivBlockProcedure | PrivBlockSpecificProcedure)}
             PrivBlockUser = { ^"user" }
             PrivBlockSpecificUser = { ^"on" ~ ^"user" ~ Identifier }
             PrivBlockRole = { ^"role" }
@@ -38,6 +39,8 @@ ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser | GrantPrivil
             PrivBlockTable = { ^"table" }
             PrivBlockSpecificTable = { ^"on" ~ ^"table" ~ Table }
             PrivBlockRolePass = { Identifier }
+            PrivBlockProcedure = { ^"procedure" }
+            PrivBlockSpecificProcedure = { ^"on" ~ ^"procedure" ~ ProcWithOptionalParams }
         Privilege = _{ PrivilegeRead | PrivilegeWrite | PrivilegeExecute |
                       PrivilegeCreate | PrivilegeAlter | PrivilegeDrop |
                       PrivilegeSession | PrivilegeUsage }
diff --git a/sbroad-core/src/ir/acl.rs b/sbroad-core/src/ir/acl.rs
index 339c9e1d46d3134baf5c043ebef143da804f95e1..1bfb77377ec332664af239e3388b5dac04a56a14 100644
--- a/sbroad-core/src/ir/acl.rs
+++ b/sbroad-core/src/ir/acl.rs
@@ -2,6 +2,8 @@ use crate::ir::{Entity, Node, Plan, SbroadError};
 use serde::{Deserialize, Serialize};
 use tarantool::decimal::Decimal;
 
+use super::ddl::ParamDef;
+
 ::tarantool::define_str_enum! {
     /// Revoked or granted privilege.
     pub enum Privilege {
@@ -43,6 +45,14 @@ pub enum GrantRevokeType {
         privilege: Privilege,
         table_name: String,
     },
+    Procedure {
+        privilege: Privilege,
+    },
+    SpecificProcedure {
+        privilege: Privilege,
+        proc_name: String,
+        proc_params: Option<Vec<ParamDef>>,
+    },
     RolePass {
         role_name: String,
     },
@@ -131,6 +141,31 @@ impl GrantRevokeType {
         })
     }
 
+    /// # Errors
+    /// - Unacceptable privilege for procedure was passed.
+    pub fn procedure(privilege: Privilege) -> Result<Self, SbroadError> {
+        check_privilege(
+            privilege,
+            &[Privilege::Create, Privilege::Drop, Privilege::Execute],
+        )?;
+        Ok(Self::Procedure { privilege })
+    }
+
+    /// # Errors
+    /// - Unacceptable privilege for specific procedure was passed.
+    pub fn specific_procedure(
+        privilege: Privilege,
+        proc_name: String,
+        proc_params: Option<Vec<ParamDef>>,
+    ) -> Result<Self, SbroadError> {
+        check_privilege(privilege, &[Privilege::Drop, Privilege::Execute])?;
+        Ok(Self::SpecificProcedure {
+            privilege,
+            proc_name,
+            proc_params,
+        })
+    }
+
     #[must_use]
     pub fn role_pass(role_name: String) -> Self {
         Self::RolePass { role_name }