smbd: Fix bug 10284
[Samba.git] / source4 / libcli / pysmb.c
blobfb981c7f18994544cb12785704fa23b12c2ca3f0
1 /*
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/>.
20 #include <Python.h>
21 #include <tevent.h>
22 #include <pytalloc.h>
23 #include "includes.h"
24 #include "param/param.h"
25 #include "param/pyparam.h"
26 #include "system/dir.h"
27 #include "system/filesys.h"
28 #include "lib/events/events.h"
29 #include "auth/credentials/credentials.h"
30 #include "auth/credentials/pycredentials.h"
31 #include "auth/gensec/gensec.h"
32 #include "libcli/libcli.h"
33 #include "libcli/raw/libcliraw.h"
34 #include "libcli/raw/raw_proto.h"
35 #include "libcli/resolve/resolve.h"
36 #include "libcli/util/pyerrors.h"
37 #include "libcli/smb_composite/smb_composite.h"
38 #include "libcli/security/security_descriptor.h"
39 #include "librpc/rpc/pyrpc_util.h"
41 #ifndef Py_RETURN_NONE
42 #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
43 #endif
45 staticforward PyTypeObject PySMB;
47 void initsmb(void);
49 struct smb_private_data {
50 struct loadparm_context *lp_ctx;
51 struct cli_credentials *creds;
52 struct tevent_context *ev_ctx;
53 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;
69 NTSTATUS status;
70 struct smbcli_options options;
71 struct smbcli_session_options session_options;
73 *tree = NULL;
75 gensec_init();
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),
84 service,
85 NULL,
86 lpcfg_socket_options(spdata->lp_ctx),
87 spdata->creds,
88 lpcfg_resolve_context(spdata->lp_ctx),
89 spdata->ev_ctx,
90 &options,
91 &session_options,
92 lpcfg_gensec_settings(mem_ctx, spdata->lp_ctx));
94 if (NT_STATUS_IS_OK(status)) {
95 *tree = smb_state->tree;
98 return status;
103 * Read SMB file and return the contents of the file as python string
105 static PyObject * py_smb_loadfile(pytalloc_Object *self, PyObject *args)
107 struct smb_composite_loadfile io;
108 const char *filename;
109 NTSTATUS status;
110 struct smb_private_data *spdata;
112 if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
113 return NULL;
116 ZERO_STRUCT(io);
118 io.in.fname = filename;
120 spdata = self->ptr;
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(pytalloc_Object *self, PyObject *args)
132 struct smb_composite_savefile io;
133 const char *filename;
134 char *data;
135 NTSTATUS status;
136 struct smb_private_data *spdata;
138 if (!PyArg_ParseTuple(args, "ss:savefile", &filename, &data)) {
139 return NULL;
142 io.in.fname = filename;
143 io.in.data = (unsigned char *)data;
144 io.in.size = strlen(data);
146 spdata = self->ptr;
147 status = smb_composite_savefile(spdata->tree, &io);
148 PyErr_NTSTATUS_IS_ERR_RAISE(status);
150 Py_RETURN_NONE;
154 * Callback function to accumulate directory contents in a python list
156 static void py_smb_list_callback(struct clilist_file_info *f, const char *mask, void *state)
158 PyObject *py_dirlist;
159 PyObject *dict;
161 if(!ISDOT(f->name) && !ISDOTDOT(f->name)) {
162 py_dirlist = (PyObject *)state;
164 dict = PyDict_New();
165 if(dict) {
166 PyDict_SetItemString(dict, "name", PyString_FromString(f->name));
168 /* Windows does not always return short_name */
169 if (f->short_name) {
170 PyDict_SetItemString(dict, "short_name", PyString_FromString(f->short_name));
171 } else {
172 PyDict_SetItemString(dict, "short_name", Py_None);
175 PyDict_SetItemString(dict, "size", PyLong_FromUnsignedLongLong(f->size));
176 PyDict_SetItemString(dict, "attrib", PyInt_FromLong(f->attrib));
177 PyDict_SetItemString(dict, "mtime", PyInt_FromLong(f->mtime));
179 PyList_Append(py_dirlist, dict);
185 * List the directory contents for specified directory (Ignore '.' and '..' dirs)
187 static PyObject *py_smb_list(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
189 struct smb_private_data *spdata;
190 PyObject *py_dirlist;
191 const char *kwnames[] = { "directory", "mask", "attribs", NULL };
192 char *base_dir;
193 char *user_mask = NULL;
194 char *mask;
195 uint16_t attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY
196 | FILE_ATTRIBUTE_ARCHIVE;
198 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|sH:list",
199 discard_const_p(char *, kwnames),
200 &base_dir, &user_mask, &attribute)) {
201 return NULL;
204 if (user_mask == NULL) {
205 mask = talloc_asprintf(self->talloc_ctx, "%s\\*", base_dir);
206 } else {
207 mask = talloc_asprintf(self->talloc_ctx, "%s\\%s", base_dir, user_mask);
209 dos_format(mask);
211 spdata = self->ptr;
213 if((py_dirlist = PyList_New(0)) == NULL) {
214 PyErr_NoMemory();
215 return NULL;
218 smbcli_list(spdata->tree, mask, attribute, py_smb_list_callback, (void *)py_dirlist);
220 talloc_free(mask);
222 return py_dirlist;
226 * Create a directory
228 static PyObject *py_smb_mkdir(pytalloc_Object *self, PyObject *args)
230 NTSTATUS status;
231 const char *dirname;
232 struct smb_private_data *spdata;
234 if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
235 return NULL;
238 spdata = self->ptr;
239 status = smbcli_mkdir(spdata->tree, dirname);
240 PyErr_NTSTATUS_IS_ERR_RAISE(status);
242 Py_RETURN_NONE;
246 * Remove a directory
248 static PyObject *py_smb_rmdir(pytalloc_Object *self, PyObject *args)
250 NTSTATUS status;
251 const char *dirname;
252 struct smb_private_data *spdata;
254 if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
255 return NULL;
258 spdata = self->ptr;
259 status = smbcli_rmdir(spdata->tree, dirname);
260 PyErr_NTSTATUS_IS_ERR_RAISE(status);
262 Py_RETURN_NONE;
266 * Remove a directory and all its contents
268 static PyObject *py_smb_deltree(pytalloc_Object *self, PyObject *args)
270 int status;
271 const char *dirname;
272 struct smb_private_data *spdata;
274 if (!PyArg_ParseTuple(args, "s:deltree", &dirname)) {
275 return NULL;
278 spdata = self->ptr;
279 status = smbcli_deltree(spdata->tree, dirname);
280 if (status <= 0) {
281 return NULL;
284 Py_RETURN_NONE;
288 * Check existence of a path
290 static PyObject *py_smb_chkpath(pytalloc_Object *self, PyObject *args)
292 NTSTATUS status;
293 const char *path;
294 struct smb_private_data *spdata;
296 if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
297 return NULL;
300 spdata = self->ptr;
301 status = smbcli_chkpath(spdata->tree, path);
303 if (NT_STATUS_IS_OK(status)) {
304 Py_RETURN_TRUE;
307 Py_RETURN_FALSE;
311 * Read ACL on a given file/directory as a security descriptor object
313 static PyObject *py_smb_getacl(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
315 NTSTATUS status;
316 union smb_open io;
317 union smb_fileinfo fio;
318 struct smb_private_data *spdata;
319 const char *filename;
320 uint32_t sinfo = 0;
321 int access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
322 int fnum;
324 if (!PyArg_ParseTuple(args, "s|Ii:get_acl", &filename, &sinfo, &access_mask)) {
325 return NULL;
328 ZERO_STRUCT(io);
330 spdata = self->ptr;
332 io.generic.level = RAW_OPEN_NTCREATEX;
333 io.ntcreatex.in.root_fid.fnum = 0;
334 io.ntcreatex.in.flags = 0;
335 io.ntcreatex.in.access_mask = access_mask;
336 io.ntcreatex.in.create_options = 0;
337 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
338 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
339 NTCREATEX_SHARE_ACCESS_WRITE;
340 io.ntcreatex.in.alloc_size = 0;
341 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
342 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
343 io.ntcreatex.in.security_flags = 0;
344 io.ntcreatex.in.fname = filename;
346 status = smb_raw_open(spdata->tree, self->talloc_ctx, &io);
347 PyErr_NTSTATUS_IS_ERR_RAISE(status);
349 fnum = io.ntcreatex.out.file.fnum;
351 ZERO_STRUCT(fio);
353 fio.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
354 fio.query_secdesc.in.file.fnum = fnum;
355 if (sinfo)
356 fio.query_secdesc.in.secinfo_flags = sinfo;
357 else
358 fio.query_secdesc.in.secinfo_flags = SECINFO_OWNER |
359 SECINFO_GROUP |
360 SECINFO_DACL |
361 SECINFO_PROTECTED_DACL |
362 SECINFO_UNPROTECTED_DACL |
363 SECINFO_SACL |
364 SECINFO_PROTECTED_SACL |
365 SECINFO_UNPROTECTED_SACL;
367 status = smb_raw_query_secdesc(spdata->tree, self->talloc_ctx, &fio);
368 smbcli_close(spdata->tree, fnum);
370 PyErr_NTSTATUS_IS_ERR_RAISE(status);
372 return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
373 self->talloc_ctx, fio.query_secdesc.out.sd);
377 * Set ACL on file/directory using given security descriptor object
379 static PyObject *py_smb_setacl(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
381 NTSTATUS status;
382 union smb_open io;
383 union smb_setfileinfo fio;
384 struct smb_private_data *spdata;
385 const char *filename;
386 PyObject *py_sd;
387 struct security_descriptor *sd;
388 uint32_t sinfo = 0;
389 int fnum;
391 if (!PyArg_ParseTuple(args, "sO|I:get_acl", &filename, &py_sd, &sinfo)) {
392 return NULL;
395 spdata = self->ptr;
397 sd = pytalloc_get_type(py_sd, struct security_descriptor);
398 if (!sd) {
399 PyErr_Format(PyExc_TypeError,
400 "Expected dcerpc.security.descriptor as argument, got %s",
401 talloc_get_name(pytalloc_get_ptr(py_sd)));
402 return NULL;
405 ZERO_STRUCT(io);
407 spdata = self->ptr;
409 io.generic.level = RAW_OPEN_NTCREATEX;
410 io.ntcreatex.in.root_fid.fnum = 0;
411 io.ntcreatex.in.flags = 0;
412 io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
413 io.ntcreatex.in.create_options = 0;
414 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
415 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
416 NTCREATEX_SHARE_ACCESS_WRITE;
417 io.ntcreatex.in.alloc_size = 0;
418 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
419 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
420 io.ntcreatex.in.security_flags = 0;
421 io.ntcreatex.in.fname = filename;
423 status = smb_raw_open(spdata->tree, self->talloc_ctx, &io);
424 PyErr_NTSTATUS_IS_ERR_RAISE(status);
426 fnum = io.ntcreatex.out.file.fnum;
428 ZERO_STRUCT(fio);
430 fio.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
431 fio.set_secdesc.in.file.fnum = fnum;
432 if (sinfo)
433 fio.set_secdesc.in.secinfo_flags = sinfo;
434 else
435 fio.set_secdesc.in.secinfo_flags = SECINFO_OWNER |
436 SECINFO_GROUP |
437 SECINFO_DACL |
438 SECINFO_PROTECTED_DACL |
439 SECINFO_UNPROTECTED_DACL |
440 SECINFO_SACL |
441 SECINFO_PROTECTED_SACL |
442 SECINFO_UNPROTECTED_SACL;
444 fio.set_secdesc.in.sd = sd;
446 status = smb_raw_set_secdesc(spdata->tree, &fio);
447 smbcli_close(spdata->tree, fnum);
449 PyErr_NTSTATUS_IS_ERR_RAISE(status);
451 Py_RETURN_NONE;
455 * Open the file with the parameters passed in and return an object if OK
457 static PyObject *py_open_file(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
459 NTSTATUS status;
460 union smb_open io;
461 struct smb_private_data *spdata;
462 const char *filename;
463 uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
464 uint32_t share_access = NTCREATEX_SHARE_ACCESS_READ |
465 NTCREATEX_SHARE_ACCESS_WRITE;
466 uint32_t open_disposition = NTCREATEX_DISP_OPEN;
467 uint32_t create_options = 0;
468 TALLOC_CTX *mem_ctx;
469 int fnum;
471 if (!PyArg_ParseTuple(args, "s|iiii:open_file",
472 &filename,
473 &access_mask,
474 &share_access,
475 &open_disposition,
476 &create_options)) {
477 return NULL;
480 ZERO_STRUCT(io);
482 spdata = self->ptr;
484 mem_ctx = talloc_new(NULL);
486 io.generic.level = RAW_OPEN_NTCREATEX;
487 io.ntcreatex.in.root_fid.fnum = 0;
488 io.ntcreatex.in.flags = 0;
489 io.ntcreatex.in.access_mask = access_mask;
490 io.ntcreatex.in.create_options = create_options;
491 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
492 io.ntcreatex.in.share_access = share_access;
493 io.ntcreatex.in.alloc_size = 0;
494 io.ntcreatex.in.open_disposition = open_disposition;
495 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
496 io.ntcreatex.in.security_flags = 0;
497 io.ntcreatex.in.fname = filename;
499 status = smb_raw_open(spdata->tree, mem_ctx, &io);
500 talloc_free(mem_ctx);
502 PyErr_NTSTATUS_IS_ERR_RAISE(status);
504 fnum = io.ntcreatex.out.file.fnum;
506 return Py_BuildValue("i", fnum);
510 * Close the file based on the fnum passed in
512 static PyObject *py_close_file(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
514 struct smb_private_data *spdata;
515 int fnum;
517 if (!PyArg_ParseTuple(args, "i:close_file", &fnum)) {
518 return NULL;
521 spdata = self->ptr;
524 * Should check the status ...
526 smbcli_close(spdata->tree, fnum);
528 Py_RETURN_NONE;
531 static PyMethodDef py_smb_methods[] = {
532 { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
533 "loadfile(path) -> file contents as a string\n\n \
534 Read contents of a file." },
535 { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
536 "savefile(path, str) -> None\n\n \
537 Write string str to file." },
538 { "list", (PyCFunction)py_smb_list, METH_VARARGS|METH_KEYWORDS,
539 "list(path) -> directory contents as a dictionary\n\n \
540 List contents of a directory. The keys are, \n \
541 \tname: Long name of the directory item\n \
542 \tshort_name: Short name of the directory item\n \
543 \tsize: File size in bytes\n \
544 \tattrib: Attributes\n \
545 \tmtime: Modification time\n" },
546 { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
547 "mkdir(path) -> None\n\n \
548 Create a directory." },
549 { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
550 "rmdir(path) -> None\n\n \
551 Delete a directory." },
552 { "deltree", (PyCFunction)py_smb_deltree, METH_VARARGS,
553 "deltree(path) -> None\n\n \
554 Delete a directory and all its contents." },
555 { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
556 "chkpath(path) -> True or False\n\n \
557 Return true if path exists, false otherwise." },
558 { "get_acl", (PyCFunction)py_smb_getacl, METH_VARARGS,
559 "get_acl(path[, security_info=0]) -> security_descriptor object\n\n \
560 Get security descriptor for file." },
561 { "set_acl", (PyCFunction)py_smb_setacl, METH_VARARGS,
562 "set_acl(path, security_descriptor[, security_info=0]) -> None\n\n \
563 Set security descriptor for file." },
564 { "open_file", (PyCFunction)py_open_file, METH_VARARGS,
565 "open_file(path, access_mask[, share_access[, open_disposition[, create_options]]] -> fnum\n\n \
566 Open a file. Throws NTSTATUS exceptions on errors." },
567 { "close_file", (PyCFunction)py_close_file, METH_VARARGS,
568 "close_file(fnum) -> None\n\n \
569 Close the file based on fnum."},
570 { NULL },
573 static PyObject *py_smb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
575 PyObject *py_creds = Py_None;
576 PyObject *py_lp = Py_None;
577 const char *kwnames[] = { "hostname", "service", "creds", "lp", NULL };
578 const char *hostname = NULL;
579 const char *service = NULL;
580 pytalloc_Object *smb;
581 struct smb_private_data *spdata;
582 NTSTATUS status;
584 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zz|OO",
585 discard_const_p(char *, kwnames),
586 &hostname, &service, &py_creds, &py_lp)) {
587 return NULL;
590 smb = (pytalloc_Object *)type->tp_alloc(type, 0);
591 if (smb == NULL) {
592 PyErr_NoMemory();
593 return NULL;
595 smb->talloc_ctx = talloc_new(NULL);
596 if (smb->talloc_ctx == NULL) {
597 PyErr_NoMemory();
598 return NULL;
601 spdata = talloc_zero(smb->talloc_ctx, struct smb_private_data);
602 if (spdata == NULL) {
603 PyErr_NoMemory();
604 Py_DECREF(smb);
605 return NULL;
608 spdata->lp_ctx = lpcfg_from_py_object(smb->talloc_ctx, py_lp);
609 if (spdata->lp_ctx == NULL) {
610 Py_DECREF(smb);
611 return NULL;
613 spdata->creds = PyCredentials_AsCliCredentials(py_creds);
614 spdata->ev_ctx = s4_event_context_init(smb->talloc_ctx);
615 if (spdata->ev_ctx == NULL) {
616 PyErr_NoMemory();
617 Py_DECREF(smb);
618 return NULL;
621 status = do_smb_connect(smb->talloc_ctx, spdata, hostname, service, &spdata->tree);
622 PyErr_NTSTATUS_IS_ERR_RAISE(status);
623 if (spdata->tree == NULL) {
624 Py_DECREF(smb);
625 return NULL;
628 smb->ptr = spdata;
629 return (PyObject *)smb;
632 static PyTypeObject PySMB = {
633 .tp_name = "smb.SMB",
634 .tp_basicsize = sizeof(pytalloc_Object),
635 .tp_new = py_smb_new,
636 .tp_flags = Py_TPFLAGS_DEFAULT,
637 .tp_methods = py_smb_methods,
638 .tp_doc = "SMB(hostname, service[, creds[, lp]]) -> SMB connection object\n",
642 void initsmb(void)
644 PyObject *m;
645 PyTypeObject *talloc_type = pytalloc_GetObjectType();
646 if (talloc_type == NULL) {
647 return;
650 PySMB.tp_base = talloc_type;
652 if (PyType_Ready(&PySMB) < 0) {
653 return;
656 m = Py_InitModule3("smb", NULL, "SMB File I/O support");
657 if (m == NULL) {
658 return;
661 Py_INCREF(&PySMB);
662 PyModule_AddObject(m, "SMB", (PyObject *)&PySMB);
664 #define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyInt_FromLong(val))
666 ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
667 ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
668 ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
669 ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
670 ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
671 ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
672 ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
673 ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
674 ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
675 ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
676 ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
677 ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
678 ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
679 ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
680 ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
681 ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);