s4-libcli: Added python interface for file I/O using SMB
[metze/samba/wip.git] / source4 / libcli / pysmb.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Amitay Isaacs 2011
5
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.
10
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.
15
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/>.
18 */
19
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 "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"
39
40 #ifndef Py_RETURN_NONE
41 #define Py_RETURN_NONE  return Py_INCREF(Py_None), Py_None
42 #endif
43
44 staticforward PyTypeObject PySMB;
45
46 void initsmb(void);
47
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;
53 };
54
55
56 static void dos_format(char *s)
57 {
58         string_replace(s, '/', '\\');
59 }
60
61
62 static struct smbcli_tree *do_smb_connect(TALLOC_CTX *mem_ctx, struct smb_private_data *spdata,
63                         const char *hostname, const char *service)
64 {
65         struct smb_composite_connect io;
66         NTSTATUS status;
67
68         gensec_init();
69
70         io.in.dest_host = hostname;
71
72         io.in.dest_ports = lpcfg_smb_ports(spdata->lp_ctx);
73         io.in.socket_options = lpcfg_socket_options(spdata->lp_ctx);
74         io.in.gensec_settings = lpcfg_gensec_settings(mem_ctx, spdata->lp_ctx);
75
76         io.in.called_name = lpcfg_netbios_name(spdata->lp_ctx);
77         io.in.workgroup = lpcfg_workgroup(spdata->lp_ctx);
78
79         lpcfg_smbcli_options(spdata->lp_ctx, &io.in.options);
80         lpcfg_smbcli_session_options(spdata->lp_ctx, &io.in.session_options);
81
82         io.in.service = service;
83         io.in.service_type = NULL;
84
85         io.in.credentials = spdata->creds;
86         io.in.fallback_to_anonymous = false;
87
88         status = smb_composite_connect(&io, mem_ctx,
89                                         lpcfg_resolve_context(spdata->lp_ctx),
90                                         spdata->ev_ctx);
91         PyErr_NTSTATUS_IS_ERR_RAISE(status);
92
93         return io.out.tree;
94 }
95
96
97 static PyObject * py_smb_loadfile(py_talloc_Object *self, PyObject *args)
98 {
99         struct smb_composite_loadfile io;
100         const char *filename;
101         NTSTATUS status;
102         struct smb_private_data *spdata;
103
104         if (!PyArg_ParseTuple(args, "s", &filename)) {
105                 return NULL;
106         }
107
108         io.in.fname = filename;
109
110         spdata = self->ptr;
111         status = smb_composite_loadfile(spdata->tree, self->talloc_ctx, &io);
112         PyErr_NTSTATUS_IS_ERR_RAISE(status);
113
114         return Py_BuildValue("s#", io.out.data, io.out.size);
115 }
116
117
118 static PyObject * py_smb_savefile(py_talloc_Object *self, PyObject *args)
119 {
120         struct smb_composite_savefile io;
121         const char *filename;
122         char *data;
123         NTSTATUS status;
124         struct smb_private_data *spdata;
125
126         if (!PyArg_ParseTuple(args, "ss", &filename, &data)) {
127                 return NULL;
128         }
129
130         io.in.fname = filename;
131         io.in.data = (unsigned char *)data;
132         io.in.size = strlen(data);
133
134         spdata = self->ptr;
135         status = smb_composite_savefile(spdata->tree, &io);
136         PyErr_NTSTATUS_IS_ERR_RAISE(status);
137
138         Py_RETURN_NONE;
139 }
140
141 static void py_smb_list_callback(struct clilist_file_info *f, const char *mask, void *state)
142 {
143         PyObject *py_dirlist;
144         PyObject *dict;
145
146         if(!ISDOT(f->name) && !ISDOTDOT(f->name)) {
147                 py_dirlist = (PyObject *)state;
148
149                 dict = PyDict_New();
150                 if(dict) {
151                         PyDict_SetItemString(dict, "name", PyString_FromString(f->name));
152                         PyDict_SetItemString(dict, "short_name", PyString_FromString(f->short_name));
153                         PyDict_SetItemString(dict, "size", PyLong_FromUnsignedLongLong(f->size));
154                         PyDict_SetItemString(dict, "attrib", PyInt_FromLong(f->attrib));
155                         PyDict_SetItemString(dict, "mtime", PyInt_FromLong(f->mtime));
156
157                         PyList_Append(py_dirlist, dict);
158                 }
159         }
160 }
161
162
163 static PyObject *py_smb_list(py_talloc_Object *self, PyObject *args, PyObject *kwargs)
164 {
165         struct smb_private_data *spdata;
166         PyObject *py_dirlist;
167         const char *kwnames[] = { "directory", "mask", "attribs", NULL };
168         char *base_dir;
169         char *user_mask = NULL;
170         uint16_t attribute = 0;
171         char *mask;
172
173         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|sH",
174                                         discard_const_p(char *, kwnames),
175                                         &base_dir, &user_mask, &attribute)) {
176                 return NULL;
177         }
178
179         if (attribute == 0) {
180                 attribute = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
181                                 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY |
182                                 FILE_ATTRIBUTE_ARCHIVE;
183         }
184
185         if (user_mask == NULL) {
186                 mask = talloc_asprintf(self->talloc_ctx, "%s\\*", base_dir);
187         } else {
188                 mask = talloc_asprintf(self->talloc_ctx, "%s\\%s", base_dir, user_mask);
189         }
190         dos_format(mask);
191
192         spdata = self->ptr;
193
194         if((py_dirlist = PyList_New(0)) == NULL) {
195                 PyErr_NoMemory();
196                 return NULL;
197         }
198
199         smbcli_list(spdata->tree, mask, attribute, py_smb_list_callback, (void *)py_dirlist);
200
201         talloc_free(mask);
202
203         return py_dirlist;
204 }
205
206 static PyObject *py_smb_getacl(py_talloc_Object *self, PyObject *args, PyObject *kwargs)
207 {
208         NTSTATUS status;
209         union smb_fileinfo io;
210         struct smb_private_data *spdata;
211         const char *filename;
212
213         if (!PyArg_ParseTuple(args, "s", &filename)) {
214                 return NULL;
215         }
216
217         spdata = self->ptr;
218
219         io.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
220         io.query_secdesc.in.file.path = filename;
221         io.query_secdesc.in.secinfo_flags = 0;
222
223         status = smb_raw_query_secdesc(spdata->tree, self->talloc_ctx, &io);
224         PyErr_NTSTATUS_IS_ERR_RAISE(status);
225
226         return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
227                                 self->talloc_ctx, io.query_secdesc.out.sd);
228 }
229
230
231 static PyObject *py_smb_setacl(py_talloc_Object *self, PyObject *args, PyObject *kwargs)
232 {
233         NTSTATUS status;
234         union smb_setfileinfo io;
235         struct smb_private_data *spdata;
236         const char *filename;
237         PyObject *py_sd;
238         struct security_descriptor *sd;
239
240         if (!PyArg_ParseTuple(args, "sO", &filename, &py_sd)) {
241                 return NULL;
242         }
243
244         spdata = self->ptr;
245
246         sd = py_talloc_get_type(py_sd, struct security_descriptor);
247         if (!sd) {
248                 PyErr_Format(PyExc_TypeError,
249                                 "Expected dcerpc.security.descriptor for security_descriptor argument, got %s", talloc_get_name(py_talloc_get_ptr(py_sd)));
250                 return NULL;
251         }
252
253         io.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
254         io.set_secdesc.in.file.path = filename;
255         io.set_secdesc.in.secinfo_flags = 0;
256         io.set_secdesc.in.sd = sd;
257
258         status = smb_raw_set_secdesc(spdata->tree, &io);
259         PyErr_NTSTATUS_IS_ERR_RAISE(status);
260
261         Py_RETURN_NONE;
262 }
263
264
265 static PyMethodDef py_smb_methods[] = {
266         { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
267                 "Read contents of a file" },
268         { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
269                 "Write contents to a file" },
270         { "list", (PyCFunction)py_smb_list, METH_VARARGS|METH_KEYWORDS,
271                 "List contents of a directory" },
272         { "get_acl", (PyCFunction)py_smb_getacl, METH_VARARGS,
273                 "Get security descriptor for a file" },
274         { "set_acl", (PyCFunction)py_smb_setacl, METH_VARARGS,
275                 "Set security descriptor for a file" },
276         { NULL },
277 };
278
279
280 static PyObject *py_smb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
281 {
282         PyObject *py_creds = Py_None;
283         PyObject *py_lp = Py_None;
284         const char *kwnames[] = { "hostname", "service", "creds", "lp", NULL };
285         const char *hostname = NULL;
286         const char *service = NULL;
287         py_talloc_Object *smb;
288         struct smb_private_data *spdata;
289
290         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zz|OO",
291                                         discard_const_p(char *, kwnames),
292                                         &hostname, &service, &py_creds, &py_lp)) {
293                 return NULL;
294         }
295
296         smb = (py_talloc_Object *)type->tp_alloc(type, 0);
297         if (smb == NULL) {
298                 PyErr_NoMemory();
299                 return NULL;
300         }
301         smb->talloc_ctx = talloc_new(NULL);
302         if (smb->talloc_ctx == NULL) {
303                 PyErr_NoMemory();
304                 return NULL;
305         }
306
307         spdata = talloc_zero(smb->talloc_ctx, struct smb_private_data);
308         if (spdata == NULL) {
309                 PyErr_NoMemory();
310                 Py_DECREF(smb);
311                 return NULL;
312         }
313
314         spdata->lp_ctx = lpcfg_from_py_object(smb->talloc_ctx, py_lp);
315         if (spdata->lp_ctx == NULL) {
316                 Py_DECREF(smb);
317                 return NULL;
318         }
319         spdata->creds = PyCredentials_AsCliCredentials(py_creds);
320         spdata->ev_ctx = s4_event_context_init(smb->talloc_ctx);
321         if (spdata->ev_ctx == NULL) {
322                 PyErr_NoMemory();
323                 Py_DECREF(smb);
324                 return NULL;
325         }
326
327         spdata->tree = do_smb_connect(smb->talloc_ctx, spdata, hostname, service);
328         if (spdata->tree == NULL) {
329                 Py_DECREF(smb);
330                 return NULL;
331         }
332
333         smb->ptr = spdata;
334         return (PyObject *)smb;
335 }
336
337
338 static PyTypeObject PySMB = {
339         .tp_name = "smb.SMB",
340         .tp_basicsize = sizeof(py_talloc_Object),
341         .tp_new = py_smb_new,
342         .tp_flags = Py_TPFLAGS_DEFAULT,
343         .tp_methods = py_smb_methods,
344         .tp_doc = "Create a SMB connection to <hostname> using <service>",
345 };
346
347 void initsmb(void)
348 {
349         PyObject *m;
350         PyTypeObject *talloc_type = PyTalloc_GetObjectType();
351         if (talloc_type == NULL) {
352                 return;
353         }
354
355         PySMB.tp_base = talloc_type;
356
357         if (PyType_Ready(&PySMB) < 0) {
358                 return;
359         }
360
361         m = Py_InitModule3("smb", NULL, "SMB File I/O support");
362         if (m == NULL) {
363             return;
364         }
365
366         Py_INCREF(&PySMB);
367         PyModule_AddObject(m, "SMB", (PyObject *)&PySMB);
368
369 #define ADD_FLAGS(val)  PyModule_AddObject(m, #val, PyInt_FromLong(val))
370
371         ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
372         ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
373         ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
374         ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
375         ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
376         ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
377         ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
378         ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
379         ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
380         ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
381         ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
382         ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
383         ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
384         ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
385         ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
386         ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
387 }