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/>.
21 #include "python/py3compat.h"
25 #include "param/param.h"
26 #include "param/pyparam.h"
27 #include "system/dir.h"
28 #include "system/filesys.h"
29 #include "lib/events/events.h"
30 #include "auth/credentials/credentials.h"
31 #include "auth/credentials/pycredentials.h"
32 #include "auth/gensec/gensec.h"
33 #include "libcli/libcli.h"
34 #include "libcli/raw/libcliraw.h"
35 #include "libcli/raw/raw_proto.h"
36 #include "libcli/resolve/resolve.h"
37 #include "libcli/util/pyerrors.h"
38 #include "libcli/smb_composite/smb_composite.h"
39 #include "libcli/security/security_descriptor.h"
40 #include "librpc/rpc/pyrpc_util.h"
42 static PyTypeObject PySMB
;
46 struct smb_private_data
{
47 struct loadparm_context
*lp_ctx
;
48 struct cli_credentials
*creds
;
49 struct tevent_context
*ev_ctx
;
50 struct smbcli_tree
*tree
;
53 static void dos_format(char *s
)
55 string_replace(s
, '/', '\\');
60 * Connect to SMB share using smb_full_connection
62 static NTSTATUS
do_smb_connect(TALLOC_CTX
*mem_ctx
, struct smb_private_data
*spdata
,
63 const char *hostname
, const char *service
,
64 struct smbcli_options
*options
,
65 struct smbcli_session_options
*session_options
,
66 struct smbcli_tree
**tree
)
68 struct smbcli_state
*smb_state
;
75 smb_state
= smbcli_state_init(mem_ctx
);
77 status
= smbcli_full_connection(mem_ctx
, &smb_state
, hostname
,
78 lpcfg_smb_ports(spdata
->lp_ctx
),
81 lpcfg_socket_options(spdata
->lp_ctx
),
83 lpcfg_resolve_context(spdata
->lp_ctx
),
87 lpcfg_gensec_settings(mem_ctx
, spdata
->lp_ctx
));
89 if (NT_STATUS_IS_OK(status
)) {
90 *tree
= smb_state
->tree
;
98 * Read SMB file and return the contents of the file as python string
100 static PyObject
* py_smb_loadfile(PyObject
*self
, PyObject
*args
)
102 struct smb_composite_loadfile io
;
103 const char *filename
;
105 struct smb_private_data
*spdata
;
107 if (!PyArg_ParseTuple(args
, "s:loadfile", &filename
)) {
113 io
.in
.fname
= filename
;
115 spdata
= pytalloc_get_ptr(self
);
116 status
= smb_composite_loadfile(spdata
->tree
, pytalloc_get_mem_ctx(self
), &io
);
117 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
119 return Py_BuildValue(PYARG_BYTES_LEN
, io
.out
.data
, io
.out
.size
);
123 * Create a SMB file with given string as the contents
125 static PyObject
* py_smb_savefile(PyObject
*self
, PyObject
*args
)
127 struct smb_composite_savefile io
;
128 const char *filename
;
132 struct smb_private_data
*spdata
;
134 if (!PyArg_ParseTuple(args
, "s"PYARG_BYTES_LEN
":savefile", &filename
, &data
, &size
)) {
138 io
.in
.fname
= filename
;
139 io
.in
.data
= (unsigned char *)data
;
142 spdata
= pytalloc_get_ptr(self
);
143 status
= smb_composite_savefile(spdata
->tree
, &io
);
144 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
150 * Callback function to accumulate directory contents in a python list
152 static void py_smb_list_callback(struct clilist_file_info
*f
, const char *mask
, void *state
)
154 PyObject
*py_dirlist
;
157 if(!ISDOT(f
->name
) && !ISDOTDOT(f
->name
)) {
158 py_dirlist
= (PyObject
*)state
;
162 PyDict_SetItemString(dict
, "name", PyStr_FromString(f
->name
));
164 /* Windows does not always return short_name */
166 PyDict_SetItemString(dict
, "short_name", PyStr_FromString(f
->short_name
));
168 PyDict_SetItemString(dict
, "short_name", Py_None
);
171 PyDict_SetItemString(dict
, "size", PyLong_FromUnsignedLongLong(f
->size
));
172 PyDict_SetItemString(dict
, "attrib", PyInt_FromLong(f
->attrib
));
173 PyDict_SetItemString(dict
, "mtime", PyInt_FromLong(f
->mtime
));
175 PyList_Append(py_dirlist
, dict
);
181 * List the directory contents for specified directory (Ignore '.' and '..' dirs)
183 static PyObject
*py_smb_list(PyObject
*self
, PyObject
*args
, PyObject
*kwargs
)
185 struct smb_private_data
*spdata
;
186 PyObject
*py_dirlist
;
187 const char *kwnames
[] = { "directory", "mask", "attribs", NULL
};
189 char *user_mask
= NULL
;
191 uint16_t attribute
= FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_DIRECTORY
192 | FILE_ATTRIBUTE_ARCHIVE
;
194 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "z|sH:list",
195 discard_const_p(char *, kwnames
),
196 &base_dir
, &user_mask
, &attribute
)) {
200 if (user_mask
== NULL
) {
201 mask
= talloc_asprintf(pytalloc_get_mem_ctx(self
), "%s\\*", base_dir
);
203 mask
= talloc_asprintf(pytalloc_get_mem_ctx(self
), "%s\\%s", base_dir
, user_mask
);
207 spdata
= pytalloc_get_ptr(self
);
209 if((py_dirlist
= PyList_New(0)) == NULL
) {
214 smbcli_list(spdata
->tree
, mask
, attribute
, py_smb_list_callback
, (void *)py_dirlist
);
224 static PyObject
*py_smb_mkdir(PyObject
*self
, PyObject
*args
)
228 struct smb_private_data
*spdata
;
230 if (!PyArg_ParseTuple(args
, "s:mkdir", &dirname
)) {
234 spdata
= pytalloc_get_ptr(self
);
235 status
= smbcli_mkdir(spdata
->tree
, dirname
);
236 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
244 static PyObject
*py_smb_rmdir(PyObject
*self
, PyObject
*args
)
248 struct smb_private_data
*spdata
;
250 if (!PyArg_ParseTuple(args
, "s:rmdir", &dirname
)) {
254 spdata
= pytalloc_get_ptr(self
);
255 status
= smbcli_rmdir(spdata
->tree
, dirname
);
256 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
265 static PyObject
*py_smb_unlink(PyObject
*self
, PyObject
*args
)
268 const char *filename
;
269 struct smb_private_data
*spdata
;
271 if (!PyArg_ParseTuple(args
, "s:unlink", &filename
)) {
275 spdata
= pytalloc_get_ptr(self
);
276 status
= smbcli_unlink(spdata
->tree
, filename
);
277 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
283 * Remove a directory and all its contents
285 static PyObject
*py_smb_deltree(PyObject
*self
, PyObject
*args
)
289 struct smb_private_data
*spdata
;
291 if (!PyArg_ParseTuple(args
, "s:deltree", &dirname
)) {
295 spdata
= pytalloc_get_ptr(self
);
296 status
= smbcli_deltree(spdata
->tree
, dirname
);
305 * Check existence of a path
307 static PyObject
*py_smb_chkpath(PyObject
*self
, PyObject
*args
)
311 struct smb_private_data
*spdata
;
313 if (!PyArg_ParseTuple(args
, "s:chkpath", &path
)) {
317 spdata
= pytalloc_get_ptr(self
);
318 status
= smbcli_chkpath(spdata
->tree
, path
);
320 if (NT_STATUS_IS_OK(status
)) {
328 * Read ACL on a given file/directory as a security descriptor object
330 static PyObject
*py_smb_getacl(PyObject
*self
, PyObject
*args
, PyObject
*kwargs
)
334 union smb_fileinfo fio
;
335 struct smb_private_data
*spdata
;
336 const char *filename
;
338 int access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
341 if (!PyArg_ParseTuple(args
, "s|Ii:get_acl", &filename
, &sinfo
, &access_mask
)) {
347 spdata
= pytalloc_get_ptr(self
);
349 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
350 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
351 io
.ntcreatex
.in
.flags
= 0;
352 io
.ntcreatex
.in
.access_mask
= access_mask
;
353 io
.ntcreatex
.in
.create_options
= 0;
354 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
355 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
356 NTCREATEX_SHARE_ACCESS_WRITE
;
357 io
.ntcreatex
.in
.alloc_size
= 0;
358 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
359 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
360 io
.ntcreatex
.in
.security_flags
= 0;
361 io
.ntcreatex
.in
.fname
= filename
;
363 status
= smb_raw_open(spdata
->tree
, pytalloc_get_mem_ctx(self
), &io
);
364 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
366 fnum
= io
.ntcreatex
.out
.file
.fnum
;
370 fio
.query_secdesc
.level
= RAW_FILEINFO_SEC_DESC
;
371 fio
.query_secdesc
.in
.file
.fnum
= fnum
;
373 fio
.query_secdesc
.in
.secinfo_flags
= sinfo
;
375 fio
.query_secdesc
.in
.secinfo_flags
= SECINFO_OWNER
|
378 SECINFO_PROTECTED_DACL
|
379 SECINFO_UNPROTECTED_DACL
|
381 SECINFO_PROTECTED_SACL
|
382 SECINFO_UNPROTECTED_SACL
;
384 status
= smb_raw_query_secdesc(spdata
->tree
, pytalloc_get_mem_ctx(self
), &fio
);
385 smbcli_close(spdata
->tree
, fnum
);
387 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
389 return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
390 pytalloc_get_mem_ctx(self
), fio
.query_secdesc
.out
.sd
);
394 * Set ACL on file/directory using given security descriptor object
396 static PyObject
*py_smb_setacl(PyObject
*self
, PyObject
*args
, PyObject
*kwargs
)
400 union smb_setfileinfo fio
;
401 struct smb_private_data
*spdata
;
402 const char *filename
;
404 struct security_descriptor
*sd
;
408 if (!PyArg_ParseTuple(args
, "sO|I:get_acl", &filename
, &py_sd
, &sinfo
)) {
412 spdata
= pytalloc_get_ptr(self
);
414 sd
= pytalloc_get_type(py_sd
, struct security_descriptor
);
416 PyErr_Format(PyExc_TypeError
,
417 "Expected dcerpc.security.descriptor as argument, got %s",
418 talloc_get_name(pytalloc_get_ptr(py_sd
)));
424 spdata
= pytalloc_get_ptr(self
);
426 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
427 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
428 io
.ntcreatex
.in
.flags
= 0;
429 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
430 io
.ntcreatex
.in
.create_options
= 0;
431 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
432 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
433 NTCREATEX_SHARE_ACCESS_WRITE
;
434 io
.ntcreatex
.in
.alloc_size
= 0;
435 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
436 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
437 io
.ntcreatex
.in
.security_flags
= 0;
438 io
.ntcreatex
.in
.fname
= filename
;
440 status
= smb_raw_open(spdata
->tree
, pytalloc_get_mem_ctx(self
), &io
);
441 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
443 fnum
= io
.ntcreatex
.out
.file
.fnum
;
447 fio
.set_secdesc
.level
= RAW_SFILEINFO_SEC_DESC
;
448 fio
.set_secdesc
.in
.file
.fnum
= fnum
;
450 fio
.set_secdesc
.in
.secinfo_flags
= sinfo
;
452 fio
.set_secdesc
.in
.secinfo_flags
= SECINFO_OWNER
|
455 SECINFO_PROTECTED_DACL
|
456 SECINFO_UNPROTECTED_DACL
|
458 SECINFO_PROTECTED_SACL
|
459 SECINFO_UNPROTECTED_SACL
;
461 fio
.set_secdesc
.in
.sd
= sd
;
463 status
= smb_raw_set_secdesc(spdata
->tree
, &fio
);
464 smbcli_close(spdata
->tree
, fnum
);
466 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
472 * Open the file with the parameters passed in and return an object if OK
474 static PyObject
*py_open_file(PyObject
*self
, PyObject
*args
, PyObject
*kwargs
)
478 struct smb_private_data
*spdata
;
479 const char *filename
;
480 uint32_t access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
481 uint32_t share_access
= NTCREATEX_SHARE_ACCESS_READ
|
482 NTCREATEX_SHARE_ACCESS_WRITE
;
483 uint32_t open_disposition
= NTCREATEX_DISP_OPEN
;
484 uint32_t create_options
= 0;
488 if (!PyArg_ParseTuple(args
, "s|iiii:open_file",
499 spdata
= pytalloc_get_ptr(self
);
501 mem_ctx
= talloc_new(NULL
);
503 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
504 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
505 io
.ntcreatex
.in
.flags
= 0;
506 io
.ntcreatex
.in
.access_mask
= access_mask
;
507 io
.ntcreatex
.in
.create_options
= create_options
;
508 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
509 io
.ntcreatex
.in
.share_access
= share_access
;
510 io
.ntcreatex
.in
.alloc_size
= 0;
511 io
.ntcreatex
.in
.open_disposition
= open_disposition
;
512 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
513 io
.ntcreatex
.in
.security_flags
= 0;
514 io
.ntcreatex
.in
.fname
= filename
;
516 status
= smb_raw_open(spdata
->tree
, mem_ctx
, &io
);
517 talloc_free(mem_ctx
);
519 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
521 fnum
= io
.ntcreatex
.out
.file
.fnum
;
523 return Py_BuildValue("i", fnum
);
527 * Close the file based on the fnum passed in
529 static PyObject
*py_close_file(PyObject
*self
, PyObject
*args
, PyObject
*kwargs
)
531 struct smb_private_data
*spdata
;
534 if (!PyArg_ParseTuple(args
, "i:close_file", &fnum
)) {
538 spdata
= pytalloc_get_ptr(self
);
541 * Should check the status ...
543 smbcli_close(spdata
->tree
, fnum
);
548 static PyMethodDef py_smb_methods
[] = {
549 { "loadfile", py_smb_loadfile
, METH_VARARGS
,
550 "loadfile(path) -> file contents as a "
552 "\n\n Read contents of a file." },
553 { "savefile", py_smb_savefile
, METH_VARARGS
,
554 "savefile(path, str) -> None\n\n Write "
557 { "list", (PyCFunction
)py_smb_list
, METH_VARARGS
|METH_KEYWORDS
,
558 "list(path, access_mask='*', attribs=DEFAULT_ATTRS) -> \
559 directory contents as a dictionary\n \
560 DEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | \
561 FILE_ATTRIBUTE_DIRECTORY | \
562 FILE_ATTRIBUTE_ARCHIVE\n\n \
563 List contents of a directory. The keys are, \n \
564 \tname: Long name of the directory item\n \
565 \tshort_name: Short name of the directory item\n \
566 \tsize: File size in bytes\n \
567 \tattrib: Attributes\n \
568 \tmtime: Modification time\n" },
569 { "mkdir", py_smb_mkdir
, METH_VARARGS
,
570 "mkdir(path) -> None\n\n \
571 Create a directory." },
572 { "rmdir", py_smb_rmdir
, METH_VARARGS
,
573 "rmdir(path) -> None\n\n \
574 Delete a directory." },
575 { "unlink", py_smb_unlink
, METH_VARARGS
,
576 "unlink(path) -> None\n\n \
578 { "deltree", py_smb_deltree
, METH_VARARGS
,
579 "deltree(path) -> None\n\n \
580 Delete a directory and all its contents." },
581 { "chkpath", py_smb_chkpath
, METH_VARARGS
,
582 "chkpath(path) -> True or False\n\n \
583 Return true if path exists, false otherwise." },
584 { "get_acl", (PyCFunction
)py_smb_getacl
, METH_VARARGS
,
585 "get_acl(path[, security_info=0]) -> security_descriptor object\n\n \
586 Get security descriptor for file." },
587 { "set_acl", (PyCFunction
)py_smb_setacl
, METH_VARARGS
,
588 "set_acl(path, security_descriptor[, security_info=0]) -> None\n\n \
589 Set security descriptor for file." },
590 { "open_file", (PyCFunction
)py_open_file
, METH_VARARGS
,
591 "open_file(path, access_mask[, share_access[, open_disposition[, create_options]]] -> fnum\n\n \
592 Open a file. Throws NTSTATUS exceptions on errors." },
593 { "close_file", (PyCFunction
)py_close_file
, METH_VARARGS
,
594 "close_file(fnum) -> None\n\n \
595 Close the file based on fnum."},
599 static PyObject
*py_smb_new(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwargs
)
601 PyObject
*py_creds
= Py_None
;
602 PyObject
*py_lp
= Py_None
;
603 const char *kwnames
[] = { "hostname", "service", "creds", "lp",
604 "ntlmv2_auth", "use_spnego", "sign", NULL
};
605 const char *hostname
= NULL
;
606 const char *service
= NULL
;
608 struct smb_private_data
*spdata
;
610 TALLOC_CTX
*frame
= NULL
;
611 struct smbcli_options options
;
612 struct smbcli_session_options session_options
;
613 uint8_t ntlmv2_auth
= 0xFF;
614 uint8_t use_spnego
= 0xFF;
615 PyObject
*sign
= Py_False
;
617 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "zz|OObbO",
618 discard_const_p(char *, kwnames
),
619 &hostname
, &service
, &py_creds
, &py_lp
,
620 &ntlmv2_auth
, &use_spnego
, &sign
)) {
624 frame
= talloc_stackframe();
626 spdata
= talloc_zero(frame
, struct smb_private_data
);
627 if (spdata
== NULL
) {
633 spdata
->lp_ctx
= lpcfg_from_py_object(spdata
, py_lp
);
634 if (spdata
->lp_ctx
== NULL
) {
635 PyErr_SetString(PyExc_TypeError
, "Expected loadparm context");
640 spdata
->creds
= cli_credentials_from_py_object(py_creds
);
641 if (spdata
->creds
== NULL
) {
642 PyErr_SetString(PyExc_TypeError
, "Expected credentials");
646 spdata
->ev_ctx
= s4_event_context_init(spdata
);
647 if (spdata
->ev_ctx
== NULL
) {
653 lpcfg_smbcli_options(spdata
->lp_ctx
, &options
);
654 lpcfg_smbcli_session_options(spdata
->lp_ctx
, &session_options
);
656 if (ntlmv2_auth
!= 0xFF) {
657 session_options
.ntlmv2_auth
= ntlmv2_auth
;
659 if (use_spnego
!= 0xFF) {
660 options
.use_spnego
= use_spnego
;
662 if (PyObject_IsTrue(sign
)) {
663 options
.signing
= SMB_SIGNING_REQUIRED
;
666 status
= do_smb_connect(spdata
, spdata
, hostname
, service
,
670 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
671 if (spdata
->tree
== NULL
) {
676 smb
= pytalloc_steal(type
, spdata
);
681 static PyTypeObject PySMB
= {
682 .tp_name
= "smb.SMB",
683 .tp_new
= py_smb_new
,
684 .tp_flags
= Py_TPFLAGS_DEFAULT
,
685 .tp_methods
= py_smb_methods
,
686 .tp_doc
= "SMB(hostname, service[, creds[, lp]]) -> SMB connection object\n",
690 static struct PyModuleDef moduledef
= {
691 PyModuleDef_HEAD_INIT
,
693 .m_doc
= "SMB File I/O support",
700 MODULE_INIT_FUNC(smb
)
704 if (pytalloc_BaseObject_PyType_Ready(&PySMB
) < 0) {
708 m
= PyModule_Create(&moduledef
);
714 PyModule_AddObject(m
, "SMB", (PyObject
*)&PySMB
);
716 #define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyInt_FromLong(val))
718 ADD_FLAGS(FILE_ATTRIBUTE_READONLY
);
719 ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN
);
720 ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM
);
721 ADD_FLAGS(FILE_ATTRIBUTE_VOLUME
);
722 ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY
);
723 ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE
);
724 ADD_FLAGS(FILE_ATTRIBUTE_DEVICE
);
725 ADD_FLAGS(FILE_ATTRIBUTE_NORMAL
);
726 ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY
);
727 ADD_FLAGS(FILE_ATTRIBUTE_SPARSE
);
728 ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT
);
729 ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED
);
730 ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE
);
731 ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED
);
732 ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED
);
733 ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK
);