2 Unix SMB/CIFS implementation.
4 Copyright (C) Amitay Isaacs 2011
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "param/param.h"
25 #include "param/pyparam.h"
26 #include "system/dir.h"
27 #include "lib/events/events.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/credentials/pycredentials.h"
30 #include "auth/gensec/gensec.h"
31 #include "libcli/libcli.h"
32 #include "libcli/raw/libcliraw.h"
33 #include "libcli/raw/raw_proto.h"
34 #include "libcli/resolve/resolve.h"
35 #include "libcli/util/pyerrors.h"
36 #include "libcli/smb_composite/smb_composite.h"
37 #include "libcli/security/security_descriptor.h"
38 #include "librpc/rpc/pyrpc_util.h"
40 #ifndef Py_RETURN_NONE
41 #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
44 staticforward PyTypeObject PySMB;
48 struct smb_private_data {
49 struct loadparm_context *lp_ctx;
50 struct cli_credentials *creds;
51 struct tevent_context *ev_ctx;
52 struct smbcli_tree *tree;
56 static void dos_format(char *s)
58 string_replace(s, '/', '\\');
63 * Connect to SMB share using smb_full_connection
65 static NTSTATUS do_smb_connect(TALLOC_CTX *mem_ctx, struct smb_private_data *spdata,
66 const char *hostname, const char *service, struct smbcli_tree **tree)
68 struct smbcli_state *smb_state;
70 struct smbcli_options options;
71 struct smbcli_session_options session_options;
77 smb_state = smbcli_state_init(mem_ctx);
79 lpcfg_smbcli_options(spdata->lp_ctx, &options);
80 lpcfg_smbcli_session_options(spdata->lp_ctx, &session_options);
82 status = smbcli_full_connection(mem_ctx, &smb_state, hostname,
83 lpcfg_smb_ports(spdata->lp_ctx),
86 lpcfg_socket_options(spdata->lp_ctx),
88 lpcfg_resolve_context(spdata->lp_ctx),
92 lpcfg_gensec_settings(mem_ctx, spdata->lp_ctx));
94 if (NT_STATUS_IS_OK(status)) {
95 *tree = smb_state->tree;
103 * Read SMB file and return the contents of the file as python string
105 static PyObject * py_smb_loadfile(py_talloc_Object *self, PyObject *args)
107 struct smb_composite_loadfile io;
108 const char *filename;
110 struct smb_private_data *spdata;
112 if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
118 io.in.fname = filename;
121 status = smb_composite_loadfile(spdata->tree, self->talloc_ctx, &io);
122 PyErr_NTSTATUS_IS_ERR_RAISE(status);
124 return Py_BuildValue("s#", io.out.data, io.out.size);
128 * Create a SMB file with given string as the contents
130 static PyObject * py_smb_savefile(py_talloc_Object *self, PyObject *args)
132 struct smb_composite_savefile io;
133 const char *filename;
136 struct smb_private_data *spdata;
138 if (!PyArg_ParseTuple(args, "ss:savefile", &filename, &data)) {
142 io.in.fname = filename;
143 io.in.data = (unsigned char *)data;
144 io.in.size = strlen(data);
147 status = smb_composite_savefile(spdata->tree, &io);
148 PyErr_NTSTATUS_IS_ERR_RAISE(status);
155 * Callback function to accumulate directory contents in a python list
157 static void py_smb_list_callback(struct clilist_file_info *f, const char *mask, void *state)
159 PyObject *py_dirlist;
162 if(!ISDOT(f->name) && !ISDOTDOT(f->name)) {
163 py_dirlist = (PyObject *)state;
167 PyDict_SetItemString(dict, "name", PyString_FromString(f->name));
169 /* Windows does not always return short_name */
171 PyDict_SetItemString(dict, "short_name", PyString_FromString(f->short_name));
173 PyDict_SetItemString(dict, "short_name", Py_None);
176 PyDict_SetItemString(dict, "size", PyLong_FromUnsignedLongLong(f->size));
177 PyDict_SetItemString(dict, "attrib", PyInt_FromLong(f->attrib));
178 PyDict_SetItemString(dict, "mtime", PyInt_FromLong(f->mtime));
180 PyList_Append(py_dirlist, dict);
187 * List the directory contents for specified directory (Ignore '.' and '..' dirs)
189 static PyObject *py_smb_list(py_talloc_Object *self, PyObject *args, PyObject *kwargs)
191 struct smb_private_data *spdata;
192 PyObject *py_dirlist;
193 const char *kwnames[] = { "directory", "mask", "attribs", NULL };
195 char *user_mask = NULL;
197 uint16_t attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY
198 | FILE_ATTRIBUTE_ARCHIVE;
200 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|sH:list",
201 discard_const_p(char *, kwnames),
202 &base_dir, &user_mask, &attribute)) {
206 if (user_mask == NULL) {
207 mask = talloc_asprintf(self->talloc_ctx, "%s\\*", base_dir);
209 mask = talloc_asprintf(self->talloc_ctx, "%s\\%s", base_dir, user_mask);
215 if((py_dirlist = PyList_New(0)) == NULL) {
220 smbcli_list(spdata->tree, mask, attribute, py_smb_list_callback, (void *)py_dirlist);
231 static PyObject *py_smb_mkdir(py_talloc_Object *self, PyObject *args)
235 struct smb_private_data *spdata;
237 if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
242 status = smbcli_mkdir(spdata->tree, dirname);
243 PyErr_NTSTATUS_IS_ERR_RAISE(status);
252 static PyObject *py_smb_rmdir(py_talloc_Object *self, PyObject *args)
256 struct smb_private_data *spdata;
258 if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
263 status = smbcli_rmdir(spdata->tree, dirname);
264 PyErr_NTSTATUS_IS_ERR_RAISE(status);
271 * Check existence of a path
273 static PyObject *py_smb_chkpath(py_talloc_Object *self, PyObject *args)
277 struct smb_private_data *spdata;
279 if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
284 status = smbcli_chkpath(spdata->tree, path);
286 if (NT_STATUS_IS_OK(status)) {
295 * Read ACL on a given file/directory as a security descriptor object
297 static PyObject *py_smb_getacl(py_talloc_Object *self, PyObject *args, PyObject *kwargs)
300 union smb_fileinfo io;
301 struct smb_private_data *spdata;
302 const char *filename;
304 if (!PyArg_ParseTuple(args, "s:get_acl", &filename)) {
310 io.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
311 io.query_secdesc.in.file.path = filename;
312 io.query_secdesc.in.secinfo_flags = 0;
316 status = smb_raw_query_secdesc(spdata->tree, self->talloc_ctx, &io);
317 PyErr_NTSTATUS_IS_ERR_RAISE(status);
319 return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
320 self->talloc_ctx, io.query_secdesc.out.sd);
325 * Set ACL on file/directory using given security descriptor object
327 static PyObject *py_smb_setacl(py_talloc_Object *self, PyObject *args, PyObject *kwargs)
330 union smb_setfileinfo io;
331 struct smb_private_data *spdata;
332 const char *filename;
334 struct security_descriptor *sd;
336 if (!PyArg_ParseTuple(args, "sO:set_acl", &filename, &py_sd)) {
342 sd = py_talloc_get_type(py_sd, struct security_descriptor);
344 PyErr_Format(PyExc_TypeError,
345 "Expected dcerpc.security.descriptor for security_descriptor argument, got %s", talloc_get_name(py_talloc_get_ptr(py_sd)));
351 io.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
352 io.set_secdesc.in.file.path = filename;
353 io.set_secdesc.in.secinfo_flags = 0;
354 io.set_secdesc.in.sd = sd;
356 status = smb_raw_set_secdesc(spdata->tree, &io);
357 PyErr_NTSTATUS_IS_ERR_RAISE(status);
363 static PyMethodDef py_smb_methods[] = {
364 { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
365 "loadfile(path) -> file contents as a string\n\n \
366 Read contents of a file." },
367 { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
368 "savefile(path, str) -> None\n\n \
369 Write string str to file." },
370 { "list", (PyCFunction)py_smb_list, METH_VARARGS|METH_KEYWORDS,
371 "list(path) -> directory contents as a dictionary\n\n \
372 List contents of a directory. The keys are, \n \
373 \tname: Long name of the directory item\n \
374 \tshort_name: Short name of the directory item\n \
375 \tsize: File size in bytes\n \
376 \tattrib: Attributes\n \
377 \tmtime: Modification time\n" },
378 { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
379 "mkdir(path) -> None\n\n \
380 Create a directory." },
381 { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
382 "rmdir(path) -> None\n\n \
383 Delete a directory." },
384 { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
385 "chkpath(path) -> True or False\n\n \
386 Return true if path exists, false otherwise." },
387 { "get_acl", (PyCFunction)py_smb_getacl, METH_VARARGS,
388 "get_acl(path) -> security_descriptor object\n\n \
389 Get security descriptor for file." },
390 { "set_acl", (PyCFunction)py_smb_setacl, METH_VARARGS,
391 "set_acl(path, security_descriptor) -> None\n\n \
392 Set security descriptor for file." },
397 static PyObject *py_smb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
399 PyObject *py_creds = Py_None;
400 PyObject *py_lp = Py_None;
401 const char *kwnames[] = { "hostname", "service", "creds", "lp", NULL };
402 const char *hostname = NULL;
403 const char *service = NULL;
404 py_talloc_Object *smb;
405 struct smb_private_data *spdata;
408 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zz|OO",
409 discard_const_p(char *, kwnames),
410 &hostname, &service, &py_creds, &py_lp)) {
414 smb = (py_talloc_Object *)type->tp_alloc(type, 0);
419 smb->talloc_ctx = talloc_new(NULL);
420 if (smb->talloc_ctx == NULL) {
425 spdata = talloc_zero(smb->talloc_ctx, struct smb_private_data);
426 if (spdata == NULL) {
432 spdata->lp_ctx = lpcfg_from_py_object(smb->talloc_ctx, py_lp);
433 if (spdata->lp_ctx == NULL) {
437 spdata->creds = PyCredentials_AsCliCredentials(py_creds);
438 spdata->ev_ctx = s4_event_context_init(smb->talloc_ctx);
439 if (spdata->ev_ctx == NULL) {
445 status = do_smb_connect(smb->talloc_ctx, spdata, hostname, service, &spdata->tree);
446 PyErr_NTSTATUS_IS_ERR_RAISE(status);
447 if (spdata->tree == NULL) {
453 return (PyObject *)smb;
457 static PyTypeObject PySMB = {
458 .tp_name = "smb.SMB",
459 .tp_basicsize = sizeof(py_talloc_Object),
460 .tp_new = py_smb_new,
461 .tp_flags = Py_TPFLAGS_DEFAULT,
462 .tp_methods = py_smb_methods,
463 .tp_doc = "SMB(hostname, service[, lp[, creds]]) -> SMB connection object\n",
470 PyTypeObject *talloc_type = PyTalloc_GetObjectType();
471 if (talloc_type == NULL) {
475 PySMB.tp_base = talloc_type;
477 if (PyType_Ready(&PySMB) < 0) {
481 m = Py_InitModule3("smb", NULL, "SMB File I/O support");
487 PyModule_AddObject(m, "SMB", (PyObject *)&PySMB);
489 #define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyInt_FromLong(val))
491 ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
492 ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
493 ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
494 ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
495 ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
496 ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
497 ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
498 ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
499 ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
500 ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
501 ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
502 ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
503 ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
504 ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
505 ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
506 ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);