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 "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
45 staticforward PyTypeObject PySMB
;
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
;
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(pytalloc_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(pytalloc_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
);
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
;
161 if(!ISDOT(f
->name
) && !ISDOTDOT(f
->name
)) {
162 py_dirlist
= (PyObject
*)state
;
166 PyDict_SetItemString(dict
, "name", PyString_FromString(f
->name
));
168 /* Windows does not always return short_name */
170 PyDict_SetItemString(dict
, "short_name", PyString_FromString(f
->short_name
));
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
};
193 char *user_mask
= NULL
;
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
)) {
204 if (user_mask
== NULL
) {
205 mask
= talloc_asprintf(self
->talloc_ctx
, "%s\\*", base_dir
);
207 mask
= talloc_asprintf(self
->talloc_ctx
, "%s\\%s", base_dir
, user_mask
);
213 if((py_dirlist
= PyList_New(0)) == NULL
) {
218 smbcli_list(spdata
->tree
, mask
, attribute
, py_smb_list_callback
, (void *)py_dirlist
);
228 static PyObject
*py_smb_mkdir(pytalloc_Object
*self
, PyObject
*args
)
232 struct smb_private_data
*spdata
;
234 if (!PyArg_ParseTuple(args
, "s:mkdir", &dirname
)) {
239 status
= smbcli_mkdir(spdata
->tree
, dirname
);
240 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
248 static PyObject
*py_smb_rmdir(pytalloc_Object
*self
, PyObject
*args
)
252 struct smb_private_data
*spdata
;
254 if (!PyArg_ParseTuple(args
, "s:rmdir", &dirname
)) {
259 status
= smbcli_rmdir(spdata
->tree
, dirname
);
260 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
266 * Remove a directory and all its contents
268 static PyObject
*py_smb_deltree(pytalloc_Object
*self
, PyObject
*args
)
272 struct smb_private_data
*spdata
;
274 if (!PyArg_ParseTuple(args
, "s:deltree", &dirname
)) {
279 status
= smbcli_deltree(spdata
->tree
, dirname
);
288 * Check existence of a path
290 static PyObject
*py_smb_chkpath(pytalloc_Object
*self
, PyObject
*args
)
294 struct smb_private_data
*spdata
;
296 if (!PyArg_ParseTuple(args
, "s:chkpath", &path
)) {
301 status
= smbcli_chkpath(spdata
->tree
, path
);
303 if (NT_STATUS_IS_OK(status
)) {
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
)
317 union smb_fileinfo fio
;
318 struct smb_private_data
*spdata
;
319 const char *filename
;
321 int access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
324 if (!PyArg_ParseTuple(args
, "s|Ii:get_acl", &filename
, &sinfo
, &access_mask
)) {
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
;
353 fio
.query_secdesc
.level
= RAW_FILEINFO_SEC_DESC
;
354 fio
.query_secdesc
.in
.file
.fnum
= fnum
;
356 fio
.query_secdesc
.in
.secinfo_flags
= sinfo
;
358 fio
.query_secdesc
.in
.secinfo_flags
= SECINFO_OWNER
|
361 SECINFO_PROTECTED_DACL
|
362 SECINFO_UNPROTECTED_DACL
|
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
)
383 union smb_setfileinfo fio
;
384 struct smb_private_data
*spdata
;
385 const char *filename
;
387 struct security_descriptor
*sd
;
391 if (!PyArg_ParseTuple(args
, "sO|I:get_acl", &filename
, &py_sd
, &sinfo
)) {
397 sd
= pytalloc_get_type(py_sd
, struct security_descriptor
);
399 PyErr_Format(PyExc_TypeError
,
400 "Expected dcerpc.security.descriptor as argument, got %s",
401 talloc_get_name(pytalloc_get_ptr(py_sd
)));
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
;
430 fio
.set_secdesc
.level
= RAW_SFILEINFO_SEC_DESC
;
431 fio
.set_secdesc
.in
.file
.fnum
= fnum
;
433 fio
.set_secdesc
.in
.secinfo_flags
= sinfo
;
435 fio
.set_secdesc
.in
.secinfo_flags
= SECINFO_OWNER
|
438 SECINFO_PROTECTED_DACL
|
439 SECINFO_UNPROTECTED_DACL
|
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
);
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
)
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;
471 if (!PyArg_ParseTuple(args
, "s|iiii:open_file",
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
;
517 if (!PyArg_ParseTuple(args
, "i:close_file", &fnum
)) {
524 * Should check the status ...
526 smbcli_close(spdata
->tree
, fnum
);
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."},
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
;
584 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "zz|OO",
585 discard_const_p(char *, kwnames
),
586 &hostname
, &service
, &py_creds
, &py_lp
)) {
590 smb
= (pytalloc_Object
*)type
->tp_alloc(type
, 0);
595 smb
->talloc_ctx
= talloc_new(NULL
);
596 if (smb
->talloc_ctx
== NULL
) {
601 spdata
= talloc_zero(smb
->talloc_ctx
, struct smb_private_data
);
602 if (spdata
== NULL
) {
608 spdata
->lp_ctx
= lpcfg_from_py_object(smb
->talloc_ctx
, py_lp
);
609 if (spdata
->lp_ctx
== 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
) {
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
) {
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",
645 PyTypeObject
*talloc_type
= pytalloc_GetObjectType();
646 if (talloc_type
== NULL
) {
650 PySMB
.tp_base
= talloc_type
;
652 if (PyType_Ready(&PySMB
) < 0) {
656 m
= Py_InitModule3("smb", NULL
, "SMB File I/O support");
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
);