diff --git a/cbits/hs_zk.c b/cbits/hs_zk.c
--- a/cbits/hs_zk.c
+++ b/cbits/hs_zk.c
@@ -207,6 +207,41 @@
   hs_thread_done();
 }
 
+/**
+ * \brief signature of a completion function that returns an ACL.
+ *
+ * This method will be invoked at the end of a asynchronous call and also as
+ * a result of connection loss or timeout.
+ * \param rc the error code of the call. Connection loss/timeout triggers
+ * the completion with one of the following error codes:
+ * ZCONNECTIONLOSS -- lost connection to the server
+ * ZOPERATIONTIMEOUT -- connection timed out
+ * Data related events trigger the completion with error codes listed the
+ * Exceptions section of the documentation of the function that initiated the
+ * call. (Zero indicates call was successful.)
+ * \param acl a pointer to the structure containng the ACL of a node. If a non
+ *   zero error code is returned, the content of strings is undefined. The
+ *   programmer is NOT responsible for freeing acl.
+ * \param stat a pointer to the stat information for the node involved in
+ *   this function. If a non zero error code is returned, the content of
+ *   stat is undefined. The programmer is NOT responsible for freeing stat.
+ * \param data the pointer that was passed by the caller when the function
+ *   that this completion corresponds to was invoked. The programmer
+ *   is responsible for any memory freeing associated with the data
+ *   pointer.
+ */
+void hs_acl_completion_fn(int rc, struct ACL_vector* acl, struct Stat* stat,
+                          const void* data) {
+  hs_acl_completion_t* acl_completion = (hs_acl_completion_t*)data;
+  acl_completion->rc = rc;
+  if (!rc) {
+    acl_completion->acl = dup_acl_vector(acl);
+    acl_completion->stat = dup_stat(stat);
+  }
+  hs_try_putmvar(acl_completion->cap, acl_completion->mvar);
+  hs_thread_done();
+}
+
 // ----------------------------------------------------------------------------
 
 zhandle_t* hs_zookeeper_init(HsStablePtr mvar, HsInt cap,
@@ -220,6 +255,8 @@
   return zh;
 }
 
+// ----------------------------------------------------------------------------
+
 int hs_zoo_acreate(zhandle_t* zh, const char* path, const char* value,
                    HsInt offset, HsInt valuelen, const struct ACL_vector* acl,
                    int mode, HsStablePtr mvar, HsInt cap,
@@ -326,6 +363,13 @@
                              hs_strings_stat_completion_fn, strings_stat);
 }
 
+int hs_zoo_aget_acl(zhandle_t* zh, const char* path, HsStablePtr mvar,
+                    HsInt cap, hs_acl_completion_t* completion) {
+  completion->mvar = mvar;
+  completion->cap = cap;
+  return zoo_aget_acl(zh, path, hs_acl_completion_fn, completion);
+}
+
 int hs_zoo_amulti(zhandle_t* zh, int count, const zoo_op_t* ops,
                   zoo_op_result_t* results, HsStablePtr mvar, HsInt cap,
                   hs_void_completion_t* void_completion) {
@@ -348,5 +392,3 @@
                         stat_t* stat) {
   zoo_set_op_init(op, path, value + valoffset, valuelen, version, stat);
 }
-
-// ----------------------------------------------------------------------------
diff --git a/include/hs_zk.h b/include/hs_zk.h
--- a/include/hs_zk.h
+++ b/include/hs_zk.h
@@ -9,6 +9,8 @@
 
 typedef struct Stat stat_t;
 typedef struct String_vector string_vector_t;
+typedef struct ACL acl_t;
+typedef struct ACL_vector acl_vector_t;
 
 const stat_t* dup_stat(const stat_t* old_stat) {
   stat_t* new_stat = (stat_t*)malloc(sizeof(stat_t));
@@ -17,9 +19,13 @@
 }
 
 const string_vector_t* dup_string_vector(const string_vector_t* old_strings) {
+  int count = old_strings->count;
+  if (count < 0) {
+    fprintf(stderr, "dup_string_vector error: count %d\n", count);
+    return NULL;
+  }
   string_vector_t* new_strings =
       (string_vector_t*)malloc(sizeof(string_vector_t));
-  int count = old_strings->count;
   char** vals = malloc(count * sizeof(char*));
   for (int i = 0; i < count; ++i) {
     vals[i] = strdup(old_strings->data[i]);
@@ -29,6 +35,26 @@
   return new_strings;
 }
 
+const acl_vector_t* dup_acl_vector(const acl_vector_t* old_acls) {
+  int count = old_acls->count;
+  if (count < 0) {
+    fprintf(stderr, "dup_acl_vector error: count %d\n", count);
+    return NULL;
+  }
+  acl_t* data = (acl_t*)malloc(count * sizeof(acl_t));
+  acl_t* old_data = old_acls->data;
+  for (int i = 0; i < count; ++i) {
+    data[i].perms = old_data[i].perms;
+    data[i].id.scheme = strdup(old_data[i].id.scheme);
+    data[i].id.id = strdup(old_data[i].id.id);
+  }
+
+  acl_vector_t* new_acls = (acl_vector_t*)malloc(sizeof(acl_vector_t));
+  new_acls->count = count;
+  new_acls->data = data;
+  return new_acls;
+}
+
 typedef struct hs_watcher_ctx_t {
   HsStablePtr mvar;
   HsInt cap;
@@ -82,12 +108,23 @@
   const stat_t* stat;
 } hs_strings_stat_completion_t;
 
+typedef struct hs_acl_completion_t {
+  HsStablePtr mvar;
+  HsInt cap;
+  int rc;
+  const struct ACL_vector* acl;
+  const struct Stat* stat;
+} hs_acl_completion_t;
+
 // ----------------------------------------------------------------------------
 
 zhandle_t* hs_zookeeper_init(HsStablePtr mvar, HsInt cap,
                              hs_watcher_ctx_t* watcher_ctx, const char* host,
                              int recv_timeout, const clientid_t* clientid,
                              int flags);
+
+int hs_zoo_aget_acl(zhandle_t* zh, const char* path, HsStablePtr mvar,
+                    HsInt cap, hs_acl_completion_t* completion);
 
 int hs_zoo_acreate(zhandle_t* zh, const char* path, const char* value,
                    HsInt offset, HsInt valuelen, const struct ACL_vector* acl,
diff --git a/src/ZooKeeper.hs b/src/ZooKeeper.hs
--- a/src/ZooKeeper.hs
+++ b/src/ZooKeeper.hs
@@ -6,12 +6,6 @@
   , Res.withResource
   , Res.Resource
 
-  , zooGetClientID
-  , zooState
-  , zooRecvTimeout
-
-  , isUnrecoverable
-
   , zooCreate
   , zooSet
   , zooGet
@@ -23,6 +17,7 @@
   , zooDelete
   , zooExists
   , zooWatchExists
+  , zooGetAcl
 
   , zooMulti
   , zooCreateOpInit
@@ -30,6 +25,11 @@
   , zooSetOpInit
   , zooCheckOpInit
 
+  , zooClientID
+  , zooState
+  , zooRecvTimeout
+  , isUnrecoverable
+
   , zookeeperInit
   , zookeeperClose
   ) where
@@ -82,16 +82,6 @@
 zookeeperResInit host timeout mclientid flags =
   Res.initResource (zookeeperInit host timeout mclientid flags) zookeeperClose
 
--- | Checks if the current zookeeper connection state can't be recovered.
---
--- If True, the application must close the zhandle and then try to reconnect.
-isUnrecoverable
-  :: T.ZHandle
-  -- ^ The zookeeper handle obtained by a call to 'zookeeperResInit'
-  -> IO Bool
-  -- ^ Return True if connection is unrecoverable
-isUnrecoverable zh = (< 0) <$> I.c_is_unrecoverable zh
-
 -- | Create a node.
 --
 -- This method will create a node in ZooKeeper. A node can only be created if
@@ -472,22 +462,32 @@
           csize I.peekRet I.peekData stringsfn'
           (I.c_hs_zoo_awget_children2 zh path')
 
--- | Return the client session id, only valid if the connections
--- is currently connected (ie. last watcher state is 'T.ZooConnectedState')
-zooGetClientID :: T.ZHandle -> IO T.ClientID
-zooGetClientID = I.c_zoo_client_id
-
--- | Get the state of the zookeeper connection
+-- | Gets the acl associated with a node.
 --
--- The return valud will be one of the State Consts
-zooState :: T.ZHandle  -> IO T.ZooState
-zooState = I.c_zoo_state
+-- Throw one of the following exceptions on failure:
+--
+-- ZBADARGUMENTS - invalid input parameters
+-- ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+-- ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+zooGetAcl
+  :: HasCallStack
+  => T.ZHandle
+  -- ^ The zookeeper handle obtained by a call to 'zookeeperResInit'
+  -> CBytes
+  -- ^ The name of the node. Expressed as a file name with slashes
+  -- separating ancestors of the node.
+  -> IO T.AclCompletion
+  -- ^ The result when the request completes.
+  --
+  -- Throw one of the following exceptions if the request completes failed:
+  --
+  -- * ZNONODE the node does not exist.
+  -- * ZNOAUTH the client does not have permission.
+zooGetAcl zh path = CBytes.withCBytesUnsafe path $ \path' -> do
+  let csize = I.csize @T.AclCompletion
+      cfunc = I.c_hs_zoo_aget_acl zh path'
+   in E.throwZooErrorIfLeft =<< I.withZKAsync csize I.peekRet I.peekData cfunc
 
--- | Return the timeout for this session, only valid if the connections
--- is currently connected (ie. last watcher state is ZOO_CONNECTED_STATE). This
--- value may change after a server re-connect.
-zooRecvTimeout :: T.ZHandle  -> IO CInt
-zooRecvTimeout = I.c_zoo_recv_timeout
 -------------------------------------------------------------------------------
 
 -- | Atomically commits multiple zookeeper operations.
@@ -676,3 +676,30 @@
 {-# INLINABLE zookeeperClose #-}
 zookeeperClose :: T.ZHandle -> IO ()
 zookeeperClose = void . E.throwZooErrorIfNotOK <=< I.c_zookeeper_close_safe
+
+-- | Return the client session id, only valid if the connections
+-- is currently connected (ie. last watcher state is 'T.ZooConnectedState')
+zooClientID :: T.ZHandle -> IO T.ClientID
+zooClientID = I.c_zoo_client_id
+
+-- | Checks if the current zookeeper connection state can't be recovered.
+--
+-- If True, the application must close the zhandle and then try to reconnect.
+isUnrecoverable
+  :: T.ZHandle
+  -- ^ The zookeeper handle obtained by a call to 'zookeeperResInit'
+  -> IO Bool
+  -- ^ Return True if connection is unrecoverable
+isUnrecoverable zh = (< 0) <$> I.c_is_unrecoverable zh
+
+-- | Get the state of the zookeeper connection
+--
+-- The return valud will be one of the State Consts
+zooState :: T.ZHandle -> IO T.ZooState
+zooState = I.c_zoo_state
+
+-- | Return the timeout for this session, only valid if the connections
+-- is currently connected (ie. last watcher state is ZOO_CONNECTED_STATE). This
+-- value may change after a server re-connect.
+zooRecvTimeout :: T.ZHandle -> IO CInt
+zooRecvTimeout = I.c_zoo_recv_timeout
diff --git a/src/ZooKeeper/Internal/FFI.hsc b/src/ZooKeeper/Internal/FFI.hsc
--- a/src/ZooKeeper/Internal/FFI.hsc
+++ b/src/ZooKeeper/Internal/FFI.hsc
@@ -65,6 +65,12 @@
 foreign import ccall unsafe "hs_zk.h zoo_recv_timeout"
   c_zoo_recv_timeout :: ZHandle -> IO CInt
 
+foreign import ccall unsafe "hs_zk.h hs_zoo_aget_acl"
+  c_hs_zoo_aget_acl
+    :: ZHandle -> BA## Word8
+    -> StablePtr PrimMVar -> Int -> Ptr AclCompletion
+    -> IO CInt
+
 foreign import ccall unsafe "hs_zk.h hs_zoo_acreate"
   c_hs_zoo_acreate
     :: ZHandle
diff --git a/src/ZooKeeper/Internal/Types.hsc b/src/ZooKeeper/Internal/Types.hsc
--- a/src/ZooKeeper/Internal/Types.hsc
+++ b/src/ZooKeeper/Internal/Types.hsc
@@ -44,8 +44,8 @@
 -------------------------------------------------------------------------------
 
 -- | ACL permissions.
-newtype ZooPerm = ZooPerm { unAcl :: CInt }
-  deriving Eq
+newtype ZooPerm = ZooPerm { unZooPerm :: CInt }
+  deriving (Eq, Bits)
 
 instance Show ZooPerm where
   show ZooPermRead   = "ZooPermRead"
@@ -56,25 +56,60 @@
   show ZooPermAll    = "ZooPermAll"
   show (ZooPerm x)   = "ZooPerm: 0x" ++ showHex x ""
 
-pattern ZooPermRead :: ZooPerm
-pattern ZooPermRead = ZooPerm (#const ZOO_PERM_READ)
-
-pattern ZooPermWrite :: ZooPerm
-pattern ZooPermWrite = ZooPerm (#const ZOO_PERM_WRITE)
-
-pattern ZooPermCreate :: ZooPerm
+pattern ZooPermRead, ZooPermWrite, ZooPermCreate, ZooPermDelete, ZooPermAdmin :: ZooPerm
+pattern ZooPermRead   = ZooPerm (#const ZOO_PERM_READ)
+pattern ZooPermWrite  = ZooPerm (#const ZOO_PERM_WRITE)
 pattern ZooPermCreate = ZooPerm (#const ZOO_PERM_CREATE)
-
-pattern ZooPermDelete :: ZooPerm
 pattern ZooPermDelete = ZooPerm (#const ZOO_PERM_DELETE)
-
-pattern ZooPermAdmin :: ZooPerm
-pattern ZooPermAdmin = ZooPerm (#const ZOO_PERM_ADMIN)
+pattern ZooPermAdmin  = ZooPerm (#const ZOO_PERM_ADMIN)
 
 pattern ZooPermAll :: ZooPerm
 pattern ZooPermAll = ZooPerm (#const ZOO_PERM_ALL)
 
-newtype AclVector = AclVector { unAclVector :: Ptr () }
+{-# INLINE toZooPerms #-}
+toZooPerms :: CInt -> [ZooPerm]
+toZooPerms n = go allPerms
+  where
+    go [] = []
+    go (x:xs)
+      | ZooPerm n .&. x == x = x : (go $! xs)
+      | otherwise            = go $! xs
+    allPerms = [ZooPermRead, ZooPermWrite, ZooPermCreate, ZooPermDelete, ZooPermAdmin]
+
+{-# INLINE fromZooPerms #-}
+fromZooPerms :: [ZooPerm] -> CInt
+fromZooPerms = foldr (.|.) 0 . map unZooPerm
+
+{-# INLINE compactZooPerms #-}
+compactZooPerms :: [ZooPerm] -> ZooPerm
+compactZooPerms = ZooPerm . fromZooPerms
+
+data ZooAcl = ZooAcl
+  { aclPerms    :: [ZooPerm]
+  , aclIdScheme :: CBytes
+  , aclId       :: CBytes
+  } deriving Show
+
+{-# INLINE sizeOfZooAcl #-}
+sizeOfZooAcl :: Int
+sizeOfZooAcl = (#size acl_t)
+
+peekZooAcl :: Ptr ZooAcl -> IO ZooAcl
+peekZooAcl ptr = do
+  perms <- toZooPerms <$> (#peek acl_t, perms) ptr
+  scheme_ptr <- (#peek acl_t, id.scheme) ptr
+  id_ptr <- (#peek acl_t, id.id) ptr
+  scheme <- CBytes.fromCString scheme_ptr <* free scheme_ptr
+  acl_id <- CBytes.fromCString id_ptr <* free id_ptr
+  return $ ZooAcl perms scheme acl_id
+
+-- TODO
+unsafeAllocaZooAcl :: ZooAcl -> IO Z.ByteArray
+unsafeAllocaZooAcl = undefined
+
+-- FIXME: consider this
+-- data AclVector = AclVector (Ptr ()) | AclList [ZooAcl]
+newtype AclVector = AclVector (Ptr ())
   deriving (Show, Eq)
 
 -- | This is a completely open ACL
@@ -89,6 +124,18 @@
 foreign import ccall unsafe "hs_zk.h &ZOO_CREATOR_ALL_ACL"
   zooCreatorAllAcl :: AclVector
 
+toAclList :: AclVector -> IO [ZooAcl]
+toAclList (AclVector ptr) = do
+  count <- fromIntegral @Int32 <$> (#peek acl_vector_t, count) ptr
+  data_ptr <- (#peek acl_vector_t, data) ptr
+  forM [0..count-1] $ \idx -> do
+    let data_ptr' = data_ptr `plusPtr` (idx * sizeOfZooAcl)
+    peekZooAcl data_ptr'
+
+-- TODO
+fromAclList :: [ZooAcl] -> IO AclVector
+fromAclList = undefined
+
 -------------------------------------------------------------------------------
 
 -- | Interest Consts
@@ -240,17 +287,17 @@
 --pattern ZooPersistentSequentialWithTTL = CreateMode (#const ZOO_PERSISTENT_SEQUENTIAL_WITH_TTL)
 
 data Stat = Stat
-  { statCzxid          :: Int64
-  , statMzxid          :: Int64
-  , statCtime          :: Int64
-  , statMtime          :: Int64
-  , statVersion        :: Int32
-  , statCversion       :: Int32
-  , statAversion       :: Int32
-  , statEphemeralOwner :: Int64
-  , statDataLength     :: Int32
-  , statNumChildren    :: Int32
-  , statPzxid          :: Int64
+  { statCzxid          :: {-# UNPACK #-} !Int64
+  , statMzxid          :: {-# UNPACK #-} !Int64
+  , statCtime          :: {-# UNPACK #-} !Int64
+  , statMtime          :: {-# UNPACK #-} !Int64
+  , statVersion        :: {-# UNPACK #-} !Int32
+  , statCversion       :: {-# UNPACK #-} !Int32
+  , statAversion       :: {-# UNPACK #-} !Int32
+  , statEphemeralOwner :: {-# UNPACK #-} !Int64
+  , statDataLength     :: {-# UNPACK #-} !Int32
+  , statNumChildren    :: {-# UNPACK #-} !Int32
+  , statPzxid          :: {-# UNPACK #-} !Int64
   } deriving (Show, Eq)
 
 statSize :: Int
@@ -391,6 +438,20 @@
     stat_ptr <- (#peek hs_strings_stat_completion_t, stat) ptr
     stat <- peekStat stat_ptr
     return $ StringsStatCompletion vals stat
+
+data AclCompletion = AclCompletion
+  { aclCompletionAcls :: [ZooAcl]
+  , aclCompletionStat :: Stat
+  } deriving Show
+
+instance Completion AclCompletion where
+  csize = (#size hs_acl_completion_t)
+  peekRet ptr = (#peek hs_acl_completion_t, rc) ptr
+  peekData ptr = do
+    acls <- toAclList . AclVector =<< (#peek hs_acl_completion_t, acl) ptr
+    stat_ptr <- (#peek hs_acl_completion_t, stat) ptr
+    stat <- peekStat stat_ptr
+    return $ AclCompletion acls stat
 
 -------------------------------------------------------------------------------
 
diff --git a/src/ZooKeeper/Types.hs b/src/ZooKeeper/Types.hs
--- a/src/ZooKeeper/Types.hs
+++ b/src/ZooKeeper/Types.hs
@@ -9,6 +9,7 @@
   , I.zooOpenAclUnsafe
   , I.zooReadAclUnsafe
   , I.zooCreatorAllAcl
+  , I.ZooAcl (..)
 
   , I.HsWatcherCtx (..)
 
@@ -18,6 +19,7 @@
   , I.StringCompletion (..)
   , I.StringsCompletion (..)
   , I.StringsStatCompletion (..)
+  , I.AclCompletion (..)
 
   , I.Stat (..)
 
@@ -46,6 +48,15 @@
   , pattern I.ZooLogWarn
   , pattern I.ZooLogInfo
   , pattern I.ZooLogDebug
+
+  , I.ZooPerm
+  , pattern I.ZooPermRead
+  , pattern I.ZooPermWrite
+  , pattern I.ZooPermCreate
+  , pattern I.ZooPermDelete
+  , pattern I.ZooPermAdmin
+  , pattern I.ZooPermAll
+  , I.compactZooPerms
 
   , I.StringVector (..)
   ) where
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -5,21 +5,26 @@
 import           Control.Concurrent
 import           Control.Monad       (void)
 import           Data.Version        (makeVersion)
+import           Foreign.C
 import           Test.Hspec
 import           ZooKeeper
 import           ZooKeeper.Exception
 import           ZooKeeper.Types
 
+recvTimeout :: CInt
+recvTimeout = 5000
+
 client :: Resource ZHandle
-client = zookeeperResInit "127.0.0.1:2182" 5000 Nothing 0
+client = zookeeperResInit "127.0.0.1:2182" recvTimeout Nothing 0
 
 main :: IO ()
 main = withResource client $ \zh -> do
-  hspec $ smoke zh
-  hspec $ multiSpec zh
+  hspec $ opSpec zh
+  hspec $ multiOpSpec zh
+  hspec $ propSpec zh
 
-smoke :: ZHandle -> Spec
-smoke zh = do
+opSpec :: ZHandle -> Spec
+opSpec zh = do
   describe "ZooKeeper.zooVersion" $ do
     it "version should be 3.4.* - 3.6.*" $ do
       zooVersion `shouldSatisfy` (>= makeVersion [3, 4, 0])
@@ -70,16 +75,29 @@
       unStrVec . strsCompletionValues <$> zooGetChildren zh "/y" `shouldReturn` []
       zooDelete zh "/y" Nothing `shouldReturn` ()
 
+  describe "ZooKeeper.zooGetAcl" $ do
+    it "get acl of READ permission" $ do
+      void $ zooCreate zh "/acl1" Nothing zooReadAclUnsafe ZooEphemeral
+      compactZooPerms . aclPerms . head . aclCompletionAcls <$> zooGetAcl zh "/acl1"
+        `shouldReturn` ZooPermRead
+
+    it "get acl of ALL permission" $ do
+      void $ zooCreate zh "/acl2" Nothing zooOpenAclUnsafe ZooEphemeral
+      compactZooPerms . aclPerms . head . aclCompletionAcls <$> zooGetAcl zh "/acl2"
+        `shouldReturn` ZooPermAll
+
+propSpec :: ZHandle -> Spec
+propSpec zh = do
   describe "ZooKeeper.zooState" $ do
     it "test get state" $ do
       zooState zh `shouldReturn` ZooConnectedState
 
   describe "ZooKeeper.zooRecvTimeout" $ do
     it "test receive timeout" $ do
-      zooRecvTimeout zh `shouldReturn` 5000
+      zooRecvTimeout zh `shouldReturn` recvTimeout
 
-multiSpec :: ZHandle -> Spec
-multiSpec zh = describe "ZooKeeper.zooMulti" $ do
+multiOpSpec :: ZHandle -> Spec
+multiOpSpec zh = describe "ZooKeeper.zooMulti" $ do
   it "Test basic multi-op functionality" $ do
     let op0 = zooCreateOpInit "/multi" (Just "") 64 zooOpenAclUnsafe ZooPersistent
     let op1 = zooCreateOpInit "/multi/a" (Just "") 64 zooOpenAclUnsafe ZooPersistent
diff --git a/zoovisitor.cabal b/zoovisitor.cabal
--- a/zoovisitor.cabal
+++ b/zoovisitor.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.2
 name:               zoovisitor
-version:            0.1.1.0
+version:            0.1.1.1
 synopsis:
   A haskell binding to Apache Zookeeper C library(mt) using Haskell Z project.
 
@@ -41,7 +41,7 @@
   build-depends:
     , base    >=4.12  && <5
     , Z-Data  >=0.7.2 && <1.0
-    , Z-IO    ^>=0.7
+    , Z-IO    >=0.7   && <1.0
 
   includes:           hs_zk.h
   c-sources:          cbits/hs_zk.c
