Fix the PyDoc comments in samba.smb.SMB and add a security_info argument to both...
[Samba/id10ts.git] / source4 / libcli / pysmb.c
blobb4eba1454717b0a91406a397df45d48826687484
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;
57 static void dos_format(char *s)
59 string_replace(s, '/', '\\');
64 * Connect to SMB share using smb_full_connection
66 static NTSTATUS do_smb_connect(TALLOC_CTX *mem_ctx, struct smb_private_data *spdata,
67 const char *hostname, const char *service, struct smbcli_tree **tree)
69 struct smbcli_state *smb_state;
70 NTSTATUS status;
71 struct smbcli_options options;
72 struct smbcli_session_options session_options;
74 *tree = NULL;
76 gensec_init();
78 smb_state = smbcli_state_init(mem_ctx);
80 lpcfg_smbcli_options(spdata->lp_ctx, &options);
81 lpcfg_smbcli_session_options(spdata->lp_ctx, &session_options);
83 status = smbcli_full_connection(mem_ctx, &smb_state, hostname,
84 lpcfg_smb_ports(spdata->lp_ctx),
85 service,
86 NULL,
87 lpcfg_socket_options(spdata->lp_ctx),
88 spdata->creds,
89 lpcfg_resolve_context(spdata->lp_ctx),
90 spdata->ev_ctx,
91 &options,
92 &session_options,
93 lpcfg_gensec_settings(mem_ctx, spdata->lp_ctx));
95 if (NT_STATUS_IS_OK(status)) {
96 *tree = smb_state->tree;
99 return status;
104 * Read SMB file and return the contents of the file as python string
106 static PyObject * py_smb_loadfile(pytalloc_Object *self, PyObject *args)
108 struct smb_composite_loadfile io;
109 const char *filename;
110 NTSTATUS status;
111 struct smb_private_data *spdata;
113 if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
114 return NULL;
117 ZERO_STRUCT(io);
119 io.in.fname = filename;
121 spdata = self->ptr;
122 status = smb_composite_loadfile(spdata->tree, self->talloc_ctx, &io);
123 PyErr_NTSTATUS_IS_ERR_RAISE(status);
125 return Py_BuildValue("s#", io.out.data, io.out.size);
129 * Create a SMB file with given string as the contents
131 static PyObject * py_smb_savefile(pytalloc_Object *self, PyObject *args)
133 struct smb_composite_savefile io;
134 const char *filename;
135 char *data;
136 NTSTATUS status;
137 struct smb_private_data *spdata;
139 if (!PyArg_ParseTuple(args, "ss:savefile", &filename, &data)) {
140 return NULL;
143 io.in.fname = filename;
144 io.in.data = (unsigned char *)data;
145 io.in.size = strlen(data);
147 spdata = self->ptr;
148 status = smb_composite_savefile(spdata->tree, &io);
149 PyErr_NTSTATUS_IS_ERR_RAISE(status);
151 Py_RETURN_NONE;
156 * Callback function to accumulate directory contents in a python list
158 static void py_smb_list_callback(struct clilist_file_info *f, const char *mask, void *state)
160 PyObject *py_dirlist;
161 PyObject *dict;
163 if(!ISDOT(f->name) && !ISDOTDOT(f->name)) {
164 py_dirlist = (PyObject *)state;
166 dict = PyDict_New();
167 if(dict) {
168 PyDict_SetItemString(dict, "name", PyString_FromString(f->name));
170 /* Windows does not always return short_name */
171 if (f->short_name) {
172 PyDict_SetItemString(dict, "short_name", PyString_FromString(f->short_name));
173 } else {
174 PyDict_SetItemString(dict, "short_name", Py_None);
177 PyDict_SetItemString(dict, "size", PyLong_FromUnsignedLongLong(f->size));
178 PyDict_SetItemString(dict, "attrib", PyInt_FromLong(f->attrib));
179 PyDict_SetItemString(dict, "mtime", PyInt_FromLong(f->mtime));
181 PyList_Append(py_dirlist, dict);
188 * List the directory contents for specified directory (Ignore '.' and '..' dirs)
190 static PyObject *py_smb_list(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
192 struct smb_private_data *spdata;
193 PyObject *py_dirlist;
194 const char *kwnames[] = { "directory", "mask", "attribs", NULL };
195 char *base_dir;
196 char *user_mask = NULL;
197 char *mask;
198 uint16_t attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY
199 | FILE_ATTRIBUTE_ARCHIVE;
201 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|sH:list",
202 discard_const_p(char *, kwnames),
203 &base_dir, &user_mask, &attribute)) {
204 return NULL;
207 if (user_mask == NULL) {
208 mask = talloc_asprintf(self->talloc_ctx, "%s\\*", base_dir);
209 } else {
210 mask = talloc_asprintf(self->talloc_ctx, "%s\\%s", base_dir, user_mask);
212 dos_format(mask);
214 spdata = self->ptr;
216 if((py_dirlist = PyList_New(0)) == NULL) {
217 PyErr_NoMemory();
218 return NULL;
221 smbcli_list(spdata->tree, mask, attribute, py_smb_list_callback, (void *)py_dirlist);
223 talloc_free(mask);
225 return py_dirlist;
230 * Create a directory
232 static PyObject *py_smb_mkdir(pytalloc_Object *self, PyObject *args)
234 NTSTATUS status;
235 const char *dirname;
236 struct smb_private_data *spdata;
238 if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
239 return NULL;
242 spdata = self->ptr;
243 status = smbcli_mkdir(spdata->tree, dirname);
244 PyErr_NTSTATUS_IS_ERR_RAISE(status);
246 Py_RETURN_NONE;
251 * Remove a directory
253 static PyObject *py_smb_rmdir(pytalloc_Object *self, PyObject *args)
255 NTSTATUS status;
256 const char *dirname;
257 struct smb_private_data *spdata;
259 if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
260 return NULL;
263 spdata = self->ptr;
264 status = smbcli_rmdir(spdata->tree, dirname);
265 PyErr_NTSTATUS_IS_ERR_RAISE(status);
267 Py_RETURN_NONE;
272 * Check existence of a path
274 static PyObject *py_smb_chkpath(pytalloc_Object *self, PyObject *args)
276 NTSTATUS status;
277 const char *path;
278 struct smb_private_data *spdata;
280 if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
281 return NULL;
284 spdata = self->ptr;
285 status = smbcli_chkpath(spdata->tree, path);
287 if (NT_STATUS_IS_OK(status)) {
288 Py_RETURN_TRUE;
291 Py_RETURN_FALSE;
296 * Read ACL on a given file/directory as a security descriptor object
298 static PyObject *py_smb_getacl(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
300 NTSTATUS status;
301 union smb_open io;
302 union smb_fileinfo fio;
303 struct smb_private_data *spdata;
304 const char *filename;
305 int sinfo = 0;
306 int fnum;
308 if (!PyArg_ParseTuple(args, "s|i:get_acl", &filename, &sinfo)) {
309 return NULL;
312 ZERO_STRUCT(io);
314 spdata = self->ptr;
316 io.generic.level = RAW_OPEN_NTCREATEX;
317 io.ntcreatex.in.root_fid.fnum = 0;
318 io.ntcreatex.in.flags = 0;
319 io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
320 io.ntcreatex.in.create_options = 0;
321 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
322 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
323 NTCREATEX_SHARE_ACCESS_WRITE;
324 io.ntcreatex.in.alloc_size = 0;
325 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
326 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
327 io.ntcreatex.in.security_flags = 0;
328 io.ntcreatex.in.fname = filename;
330 status = smb_raw_open(spdata->tree, self->talloc_ctx, &io);
331 PyErr_NTSTATUS_IS_ERR_RAISE(status);
333 fnum = io.ntcreatex.out.file.fnum;
335 ZERO_STRUCT(fio);
337 fio.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
338 fio.query_secdesc.in.file.fnum = fnum;
339 if (sinfo)
340 fio.query_secdesc.in.secinfo_flags = sinfo;
341 else
342 fio.query_secdesc.in.secinfo_flags = SECINFO_OWNER |
343 SECINFO_GROUP |
344 SECINFO_DACL |
345 SECINFO_PROTECTED_DACL |
346 SECINFO_UNPROTECTED_DACL |
347 SECINFO_SACL |
348 SECINFO_PROTECTED_SACL |
349 SECINFO_UNPROTECTED_SACL;
351 status = smb_raw_query_secdesc(spdata->tree, self->talloc_ctx, &fio);
352 smbcli_close(spdata->tree, fnum);
354 PyErr_NTSTATUS_IS_ERR_RAISE(status);
356 return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
357 self->talloc_ctx, fio.query_secdesc.out.sd);
362 * Set ACL on file/directory using given security descriptor object
364 static PyObject *py_smb_setacl(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
366 NTSTATUS status;
367 union smb_open io;
368 union smb_setfileinfo fio;
369 struct smb_private_data *spdata;
370 const char *filename;
371 PyObject *py_sd;
372 struct security_descriptor *sd;
373 uint32_t sinfo = 0;
374 int fnum;
376 if (!PyArg_ParseTuple(args, "sO|i:get_acl", &filename, &py_sd, &sinfo)) {
377 return NULL;
380 spdata = self->ptr;
382 sd = pytalloc_get_type(py_sd, struct security_descriptor);
383 if (!sd) {
384 PyErr_Format(PyExc_TypeError,
385 "Expected dcerpc.security.descriptor as argument, got %s",
386 talloc_get_name(pytalloc_get_ptr(py_sd)));
387 return NULL;
390 ZERO_STRUCT(io);
392 spdata = self->ptr;
394 io.generic.level = RAW_OPEN_NTCREATEX;
395 io.ntcreatex.in.root_fid.fnum = 0;
396 io.ntcreatex.in.flags = 0;
397 io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
398 io.ntcreatex.in.create_options = 0;
399 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
400 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
401 NTCREATEX_SHARE_ACCESS_WRITE;
402 io.ntcreatex.in.alloc_size = 0;
403 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
404 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
405 io.ntcreatex.in.security_flags = 0;
406 io.ntcreatex.in.fname = filename;
408 status = smb_raw_open(spdata->tree, self->talloc_ctx, &io);
409 PyErr_NTSTATUS_IS_ERR_RAISE(status);
411 fnum = io.ntcreatex.out.file.fnum;
413 ZERO_STRUCT(fio);
415 fio.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
416 fio.set_secdesc.in.file.fnum = fnum;
417 if (sinfo)
418 fio.set_secdesc.in.secinfo_flags = sinfo;
419 else
420 fio.set_secdesc.in.secinfo_flags = SECINFO_OWNER |
421 SECINFO_GROUP |
422 SECINFO_DACL |
423 SECINFO_PROTECTED_DACL |
424 SECINFO_UNPROTECTED_DACL |
425 SECINFO_SACL |
426 SECINFO_PROTECTED_SACL |
427 SECINFO_UNPROTECTED_SACL;
429 fio.set_secdesc.in.sd = sd;
431 status = smb_raw_set_secdesc(spdata->tree, &fio);
432 smbcli_close(spdata->tree, fnum);
434 PyErr_NTSTATUS_IS_ERR_RAISE(status);
436 Py_RETURN_NONE;
440 static PyMethodDef py_smb_methods[] = {
441 { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
442 "loadfile(path) -> file contents as a string\n\n \
443 Read contents of a file." },
444 { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
445 "savefile(path, str) -> None\n\n \
446 Write string str to file." },
447 { "list", (PyCFunction)py_smb_list, METH_VARARGS|METH_KEYWORDS,
448 "list(path) -> directory contents as a dictionary\n\n \
449 List contents of a directory. The keys are, \n \
450 \tname: Long name of the directory item\n \
451 \tshort_name: Short name of the directory item\n \
452 \tsize: File size in bytes\n \
453 \tattrib: Attributes\n \
454 \tmtime: Modification time\n" },
455 { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
456 "mkdir(path) -> None\n\n \
457 Create a directory." },
458 { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
459 "rmdir(path) -> None\n\n \
460 Delete a directory." },
461 { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
462 "chkpath(path) -> True or False\n\n \
463 Return true if path exists, false otherwise." },
464 { "get_acl", (PyCFunction)py_smb_getacl, METH_VARARGS,
465 "get_acl(path[, security_info=0]) -> security_descriptor object\n\n \
466 Get security descriptor for file." },
467 { "set_acl", (PyCFunction)py_smb_setacl, METH_VARARGS,
468 "set_acl(path, security_descriptor[, security_info=0]) -> None\n\n \
469 Set security descriptor for file." },
470 { NULL },
474 static PyObject *py_smb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
476 PyObject *py_creds = Py_None;
477 PyObject *py_lp = Py_None;
478 const char *kwnames[] = { "hostname", "service", "creds", "lp", NULL };
479 const char *hostname = NULL;
480 const char *service = NULL;
481 pytalloc_Object *smb;
482 struct smb_private_data *spdata;
483 NTSTATUS status;
485 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zz|OO",
486 discard_const_p(char *, kwnames),
487 &hostname, &service, &py_creds, &py_lp)) {
488 return NULL;
491 smb = (pytalloc_Object *)type->tp_alloc(type, 0);
492 if (smb == NULL) {
493 PyErr_NoMemory();
494 return NULL;
496 smb->talloc_ctx = talloc_new(NULL);
497 if (smb->talloc_ctx == NULL) {
498 PyErr_NoMemory();
499 return NULL;
502 spdata = talloc_zero(smb->talloc_ctx, struct smb_private_data);
503 if (spdata == NULL) {
504 PyErr_NoMemory();
505 Py_DECREF(smb);
506 return NULL;
509 spdata->lp_ctx = lpcfg_from_py_object(smb->talloc_ctx, py_lp);
510 if (spdata->lp_ctx == NULL) {
511 Py_DECREF(smb);
512 return NULL;
514 spdata->creds = PyCredentials_AsCliCredentials(py_creds);
515 spdata->ev_ctx = s4_event_context_init(smb->talloc_ctx);
516 if (spdata->ev_ctx == NULL) {
517 PyErr_NoMemory();
518 Py_DECREF(smb);
519 return NULL;
522 status = do_smb_connect(smb->talloc_ctx, spdata, hostname, service, &spdata->tree);
523 PyErr_NTSTATUS_IS_ERR_RAISE(status);
524 if (spdata->tree == NULL) {
525 Py_DECREF(smb);
526 return NULL;
529 smb->ptr = spdata;
530 return (PyObject *)smb;
534 static PyTypeObject PySMB = {
535 .tp_name = "smb.SMB",
536 .tp_basicsize = sizeof(pytalloc_Object),
537 .tp_new = py_smb_new,
538 .tp_flags = Py_TPFLAGS_DEFAULT,
539 .tp_methods = py_smb_methods,
540 .tp_doc = "SMB(hostname, service[, creds[, lp]]) -> SMB connection object\n",
544 void initsmb(void)
546 PyObject *m;
547 PyTypeObject *talloc_type = pytalloc_GetObjectType();
548 if (talloc_type == NULL) {
549 return;
552 PySMB.tp_base = talloc_type;
554 if (PyType_Ready(&PySMB) < 0) {
555 return;
558 m = Py_InitModule3("smb", NULL, "SMB File I/O support");
559 if (m == NULL) {
560 return;
563 Py_INCREF(&PySMB);
564 PyModule_AddObject(m, "SMB", (PyObject *)&PySMB);
566 #define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyInt_FromLong(val))
568 ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
569 ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
570 ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
571 ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
572 ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
573 ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
574 ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
575 ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
576 ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
577 ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
578 ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
579 ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
580 ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
581 ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
582 ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
583 ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);