s3/libsmb/py: match input argument types with C types
[nivanova/samba-autobuild/.git] / source3 / libsmb / pylibsmb.c
index 21c8b4e388b0b475905f57f1b3e7ff6200474a4e..da2e5e12d0d64ba9acc218e39763c77bfda4bc7e 100644 (file)
@@ -1,6 +1,9 @@
 /*
  * Unix SMB/CIFS implementation.
- * Samba-internal work in progress Python binding for libsmbclient
+ *
+ * SMB client Python bindings used internally by Samba (for things like
+ * samba-tool). These Python bindings may change without warning, and so
+ * should not be used outside of the Samba codebase.
  *
  * Copyright (C) Volker Lendecke 2012
  *
 #include "auth/credentials/pycredentials.h"
 #include "trans2.h"
 #include "libsmb/clirap.h"
+#include "librpc/rpc/pyrpc_util.h"
 
 #define LIST_ATTRIBUTE_MASK \
        (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN)
 
+#define SECINFO_DEFAULT_FLAGS \
+       (SECINFO_OWNER | SECINFO_GROUP | \
+        SECINFO_DACL | SECINFO_PROTECTED_DACL | SECINFO_UNPROTECTED_DACL | \
+        SECINFO_SACL | SECINFO_PROTECTED_SACL | SECINFO_UNPROTECTED_SACL)
+
 static PyTypeObject *get_pytype(const char *module, const char *type)
 {
        PyObject *mod;
@@ -440,7 +449,7 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
        int flags = 0;
 
        static const char *kwlist[] = {
-               "host", "share", "lp", "credentials",
+               "host", "share", "lp", "creds",
                "multi_threaded", "sign", "force_smb1",
                NULL
        };
@@ -847,7 +856,7 @@ static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
                "fnum", "buffer", "offset", "mode", NULL };
 
        if (!ParseTupleAndKeywords(
-                   args, kwds, "I" PYARG_BYTES_LEN "K|I", kwlist,
+                   args, kwds, "i" PYARG_BYTES_LEN "K|I", kwlist,
                    &fnum, &buf, &buflen, &offset, &mode)) {
                return NULL;
        }
@@ -919,7 +928,8 @@ static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args,
 
        /* get a read file handle */
        req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
-                               FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
+                               FILE_READ_DATA | FILE_READ_ATTRIBUTES,
+                               FILE_ATTRIBUTE_NORMAL,
                                FILE_SHARE_READ, FILE_OPEN, 0,
                                SMB2_IMPERSONATION_IMPERSONATION, 0);
        if (!py_tevent_req_wait_exc(self, req)) {
@@ -1002,7 +1012,7 @@ static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
                "fnum", "offset", "size", NULL };
 
        if (!ParseTupleAndKeywords(
-                   args, kwds, "IKI", kwlist, &fnum, &offset,
+                   args, kwds, "iKI", kwlist, &fnum, &offset,
                    &size)) {
                return NULL;
        }
@@ -1114,13 +1124,14 @@ static NTSTATUS list_helper(const char *mntpoint, struct file_info *finfo,
 {
        PyObject *result = (PyObject *)state;
        PyObject *file = NULL;
+       PyObject *size = NULL;
        int ret;
 
        /* suppress '.' and '..' in the results we return */
        if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
                return NT_STATUS_OK;
        }
-
+       size = PyLong_FromUnsignedLongLong(finfo->size);
        /*
         * Build a dictionary representing the file info.
         * Note: Windows does not always return short_name (so it may be None)
@@ -1129,15 +1140,18 @@ static NTSTATUS list_helper(const char *mntpoint, struct file_info *finfo,
                             "name", finfo->name,
                             "attrib", (int)finfo->mode,
                             "short_name", finfo->short_name,
-                            "size", PyLong_FromUnsignedLongLong(finfo->size),
+                            "size", size,
                             "mtime",
                             convert_timespec_to_time_t(finfo->mtime_ts));
 
+       Py_CLEAR(size);
+
        if (file == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
        ret = PyList_Append(result, file);
+       Py_CLEAR(file);
        if (ret == -1) {
                return NT_STATUS_INTERNAL_ERROR;
        }
@@ -1213,10 +1227,10 @@ static PyObject *py_cli_list(struct py_cli_state *self,
        char *user_mask = NULL;
        unsigned int attribute = LIST_ATTRIBUTE_MASK;
        NTSTATUS status;
-       PyObject *result;
+       PyObject *result = NULL;
        const char *kwlist[] = { "directory", "mask", "attribs", NULL };
 
-       if (!ParseTupleAndKeywords(args, kwds, "z|sH:list", kwlist,
+       if (!ParseTupleAndKeywords(args, kwds, "z|sI:list", kwlist,
                                   &base_dir, &user_mask, &attribute)) {
                return NULL;
        }
@@ -1303,7 +1317,7 @@ static NTSTATUS remove_dir(struct py_cli_state *self, const char *dirname)
 static PyObject *py_smb_rmdir(struct py_cli_state *self, PyObject *args)
 {
        NTSTATUS status;
-       const char *dirname;
+       const char *dirname = NULL;
 
        if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
                return NULL;
@@ -1321,7 +1335,7 @@ static PyObject *py_smb_rmdir(struct py_cli_state *self, PyObject *args)
 static PyObject *py_smb_mkdir(struct py_cli_state *self, PyObject *args)
 {
        NTSTATUS status;
-       const char *dirname;
+       const char *dirname = NULL;
 
        if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
                return NULL;
@@ -1369,7 +1383,7 @@ static bool check_dir_path(struct py_cli_state *self, const char *path)
 
 static PyObject *py_smb_chkpath(struct py_cli_state *self, PyObject *args)
 {
-       const char *path;
+       const char *path = NULL;
        bool dir_exists;
 
        if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
@@ -1472,6 +1486,98 @@ static PyObject *py_smb_deltree(struct py_cli_state *self, PyObject *args)
        Py_RETURN_NONE;
 }
 
+/*
+ * Read ACL on a given file/directory as a security descriptor object
+ */
+static PyObject *py_smb_getacl(struct py_cli_state *self, PyObject *args)
+{
+       NTSTATUS status;
+       const char *filename = NULL;
+       unsigned int sinfo = SECINFO_DEFAULT_FLAGS;
+       unsigned int access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+       uint16_t fnum;
+       struct security_descriptor *sd = NULL;
+
+       /* there's no async version of cli_query_security_descriptor() */
+       if (self->thread_state != NULL) {
+               PyErr_SetString(PyExc_RuntimeError,
+                               "get_acl() is not supported on "
+                               "a multi_threaded connection");
+               return NULL;
+       }
+
+       if (!PyArg_ParseTuple(args, "s|II:get_acl", &filename, &sinfo,
+                             &access_mask)) {
+               return NULL;
+       }
+
+       /* get a file handle with the desired access */
+       status = cli_ntcreate(self->cli, filename, 0, access_mask, 0,
+                             FILE_SHARE_READ|FILE_SHARE_WRITE,
+                             FILE_OPEN, 0x0, 0x0, &fnum, NULL);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       /* query the security descriptor for this file */
+       status = cli_query_security_descriptor(self->cli, fnum, sinfo,
+                                              NULL, &sd);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       /* close the file handle and convert the SD to a python struct */
+       status = cli_close(self->cli, fnum);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
+                                   sd, sd);
+}
+
+/*
+ * Set ACL on file/directory using given security descriptor object
+ */
+static PyObject *py_smb_setacl(struct py_cli_state *self, PyObject *args)
+{
+       NTSTATUS status;
+       char *filename = NULL;
+       PyObject *py_sd = NULL;
+       struct security_descriptor *sd = NULL;
+       unsigned int sinfo = SECINFO_DEFAULT_FLAGS;
+       uint16_t fnum;
+
+       /* there's no async version of cli_set_security_descriptor() */
+       if (self->thread_state != NULL) {
+               PyErr_SetString(PyExc_RuntimeError,
+                               "set_acl() is not supported on "
+                               "a multi_threaded connection");
+               return NULL;
+       }
+
+       if (!PyArg_ParseTuple(args, "sO|I:set_acl", &filename, &py_sd,
+                             &sinfo)) {
+               return NULL;
+       }
+
+       sd = pytalloc_get_type(py_sd, struct security_descriptor);
+       if (!sd) {
+               PyErr_Format(PyExc_TypeError,
+                       "Expected dcerpc.security.descriptor as argument, got %s",
+                       talloc_get_name(pytalloc_get_ptr(py_sd)));
+               return NULL;
+       }
+
+       status = cli_ntcreate(self->cli, filename, 0,
+                             SEC_FLAG_MAXIMUM_ALLOWED, 0,
+                             FILE_SHARE_READ|FILE_SHARE_WRITE,
+                             FILE_OPEN, 0x0, 0x0, &fnum, NULL);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       status = cli_set_security_descriptor(self->cli, fnum, sinfo, sd);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       status = cli_close(self->cli, fnum);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       Py_RETURN_NONE;
+}
+
 static PyMethodDef py_cli_state_methods[] = {
        { "settimeout", (PyCFunction)py_cli_settimeout, METH_VARARGS,
          "settimeout(new_timeout_msecs) => return old_timeout_msecs" },
@@ -1521,12 +1627,18 @@ static PyMethodDef py_cli_state_methods[] = {
        { "deltree", (PyCFunction)py_smb_deltree, METH_VARARGS,
          "deltree(path) -> None\n\n"
          "\t\tDelete a directory and all its contents." },
+       { "get_acl", (PyCFunction)py_smb_getacl, METH_VARARGS,
+         "get_acl(path[, security_info=0]) -> security_descriptor object\n\n"
+         "\t\tGet security descriptor for file." },
+       { "set_acl", (PyCFunction)py_smb_setacl, METH_VARARGS,
+         "set_acl(path, security_descriptor[, security_info=0]) -> None\n\n"
+         "\t\tSet security descriptor for file." },
        { NULL, NULL, 0, NULL }
 };
 
 static PyTypeObject py_cli_state_type = {
        PyVarObject_HEAD_INIT(NULL, 0)
-       .tp_name = "libsmb.Conn",
+       .tp_name = "libsmb_samba_internal.Conn",
        .tp_basicsize = sizeof(struct py_cli_state),
        .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
        .tp_doc = "libsmb connection",
@@ -1540,17 +1652,17 @@ static PyMethodDef py_libsmb_methods[] = {
        { NULL },
 };
 
-void initlibsmb(void);
+void initlibsmb_samba_internal(void);
 
 static struct PyModuleDef moduledef = {
     PyModuleDef_HEAD_INIT,
-    .m_name = "libsmb",
+    .m_name = "libsmb_samba_internal",
     .m_doc = "libsmb wrapper",
     .m_size = -1,
     .m_methods = py_libsmb_methods,
 };
 
-MODULE_INIT_FUNC(libsmb)
+MODULE_INIT_FUNC(libsmb_samba_internal)
 {
        PyObject *m = NULL;
 
@@ -1565,5 +1677,25 @@ MODULE_INIT_FUNC(libsmb)
        }
        Py_INCREF(&py_cli_state_type);
        PyModule_AddObject(m, "Conn", (PyObject *)&py_cli_state_type);
+
+#define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyInt_FromLong(val))
+
+       ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
+       ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
+       ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
+       ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
+       ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
+       ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
+       ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
+       ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
+       ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
+       ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
+       ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
+       ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
+       ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
+       ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
+       ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
+       ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
+
        return m;
 }