s4-libcli: Check if short_name is not null, before converting to python string
[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 /*
63  * Connect to SMB share using smb_full_connection
64  */
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)
67 {
68         struct smbcli_state *smb_state;
69         NTSTATUS status;
70         struct smbcli_options options;
71         struct smbcli_session_options session_options;
72
73         *tree = NULL;
74
75         gensec_init();
76
77         smb_state = smbcli_state_init(mem_ctx);
78
79         lpcfg_smbcli_options(spdata->lp_ctx, &options);
80         lpcfg_smbcli_session_options(spdata->lp_ctx, &session_options);
81
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));
93
94         if (NT_STATUS_IS_OK(status)) {
95                 *tree = smb_state->tree;
96         }
97
98         return status;
99 }
100
101
102 /*
103  * Read SMB file and return the contents of the file as python string
104  */
105 static PyObject * py_smb_loadfile(py_talloc_Object *self, PyObject *args)
106 {
107         struct smb_composite_loadfile io;
108         const char *filename;
109         NTSTATUS status;
110         struct smb_private_data *spdata;
111
112         if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
113                 return NULL;
114         }
115
116         ZERO_STRUCT(io);
117
118         io.in.fname = filename;
119
120         spdata = self->ptr;
121         status = smb_composite_loadfile(spdata->tree, self->talloc_ctx, &io);
122         PyErr_NTSTATUS_IS_ERR_RAISE(status);
123
124         return Py_BuildValue("s#", io.out.data, io.out.size);
125 }
126
127 /*
128  * Create a SMB file with given string as the contents
129  */
130 static PyObject * py_smb_savefile(py_talloc_Object *self, PyObject *args)
131 {
132         struct smb_composite_savefile io;
133         const char *filename;
134         char *data;
135         NTSTATUS status;
136         struct smb_private_data *spdata;
137
138         if (!PyArg_ParseTuple(args, "ss:savefile", &filename, &data)) {
139                 return NULL;
140         }
141
142         io.in.fname = filename;
143         io.in.data = (unsigned char *)data;
144         io.in.size = strlen(data);
145
146         spdata = self->ptr;
147         status = smb_composite_savefile(spdata->tree, &io);
148         PyErr_NTSTATUS_IS_ERR_RAISE(status);
149
150         Py_RETURN_NONE;
151 }
152
153
154 /*
155  * Callback function to accumulate directory contents in a python list
156  */
157 static void py_smb_list_callback(struct clilist_file_info *f, const char *mask, void *state)
158 {
159         PyObject *py_dirlist;
160         PyObject *dict;
161
162         if(!ISDOT(f->name) && !ISDOTDOT(f->name)) {
163                 py_dirlist = (PyObject *)state;
164
165                 dict = PyDict_New();
166                 if(dict) {
167                         PyDict_SetItemString(dict, "name", PyString_FromString(f->name));
168                         
169                         /* Windows does not always return short_name */
170                         if (f->short_name) {
171                                 PyDict_SetItemString(dict, "short_name", PyString_FromString(f->short_name));
172                         } else {
173                                 PyDict_SetItemString(dict, "short_name", Py_None);
174                         }
175
176                         PyDict_SetItemString(dict, "size", PyLong_FromUnsignedLongLong(f->size));
177                         PyDict_SetItemString(dict, "attrib", PyInt_FromLong(f->attrib));
178                         PyDict_SetItemString(dict, "mtime", PyInt_FromLong(f->mtime));
179
180                         PyList_Append(py_dirlist, dict);
181                 }
182         }
183 }
184
185
186 /*
187  * List the directory contents for specified directory (Ignore '.' and '..' dirs)
188  */
189 static PyObject *py_smb_list(py_talloc_Object *self, PyObject *args, PyObject *kwargs)
190 {
191         struct smb_private_data *spdata;
192         PyObject *py_dirlist;
193         const char *kwnames[] = { "directory", "mask", "attribs", NULL };
194         char *base_dir;
195         char *user_mask = NULL;
196         char *mask;
197         uint16_t attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY
198                                 | FILE_ATTRIBUTE_ARCHIVE;
199
200         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|sH:list",
201                                         discard_const_p(char *, kwnames),
202                                         &base_dir, &user_mask, &attribute)) {
203                 return NULL;
204         }
205
206         if (user_mask == NULL) {
207                 mask = talloc_asprintf(self->talloc_ctx, "%s\\*", base_dir);
208         } else {
209                 mask = talloc_asprintf(self->talloc_ctx, "%s\\%s", base_dir, user_mask);
210         }
211         dos_format(mask);
212
213         spdata = self->ptr;
214
215         if((py_dirlist = PyList_New(0)) == NULL) {
216                 PyErr_NoMemory();
217                 return NULL;
218         }
219
220         smbcli_list(spdata->tree, mask, attribute, py_smb_list_callback, (void *)py_dirlist);
221
222         talloc_free(mask);
223
224         return py_dirlist;
225 }
226
227
228 /*
229  * Create a directory
230  */
231 static PyObject *py_smb_mkdir(py_talloc_Object *self, PyObject *args)
232 {
233         NTSTATUS status;
234         const char *dirname;
235         struct smb_private_data *spdata;
236
237         if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
238                 return NULL;
239         }
240
241         spdata = self->ptr;     
242         status = smbcli_mkdir(spdata->tree, dirname);
243         PyErr_NTSTATUS_IS_ERR_RAISE(status);
244
245         Py_RETURN_NONE;
246 }
247
248
249 /*
250  * Remove a directory
251  */
252 static PyObject *py_smb_rmdir(py_talloc_Object *self, PyObject *args)
253 {
254         NTSTATUS status;
255         const char *dirname;
256         struct smb_private_data *spdata;
257
258         if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
259                 return NULL;
260         }
261
262         spdata = self->ptr;     
263         status = smbcli_rmdir(spdata->tree, dirname);
264         PyErr_NTSTATUS_IS_ERR_RAISE(status);
265
266         Py_RETURN_NONE;
267 }
268
269
270 /*
271  * Check existence of a path
272  */
273 static PyObject *py_smb_chkpath(py_talloc_Object *self, PyObject *args)
274 {
275         NTSTATUS status;
276         const char *path;
277         struct smb_private_data *spdata;
278
279         if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
280                 return NULL;
281         }
282
283         spdata = self->ptr;     
284         status = smbcli_chkpath(spdata->tree, path);
285
286         if (NT_STATUS_IS_OK(status)) {
287                 Py_RETURN_TRUE;
288         }
289
290         Py_RETURN_FALSE;
291 }
292
293
294 /*
295  * Read ACL on a given file/directory as a security descriptor object
296  */
297 static PyObject *py_smb_getacl(py_talloc_Object *self, PyObject *args, PyObject *kwargs)
298 {
299         NTSTATUS status;
300         union smb_fileinfo io;
301         struct smb_private_data *spdata;
302         const char *filename;
303
304         if (!PyArg_ParseTuple(args, "s:get_acl", &filename)) {
305                 return NULL;
306         }
307
308         ZERO_STRUCT(io);
309
310         io.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
311         io.query_secdesc.in.file.path = filename;
312         io.query_secdesc.in.secinfo_flags = 0;
313
314         spdata = self->ptr;
315
316         status = smb_raw_query_secdesc(spdata->tree, self->talloc_ctx, &io);
317         PyErr_NTSTATUS_IS_ERR_RAISE(status);
318
319         return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
320                                 self->talloc_ctx, io.query_secdesc.out.sd);
321 }
322
323
324 /*
325  * Set ACL on file/directory using given security descriptor object
326  */
327 static PyObject *py_smb_setacl(py_talloc_Object *self, PyObject *args, PyObject *kwargs)
328 {
329         NTSTATUS status;
330         union smb_setfileinfo io;
331         struct smb_private_data *spdata;
332         const char *filename;
333         PyObject *py_sd;
334         struct security_descriptor *sd;
335
336         if (!PyArg_ParseTuple(args, "sO:set_acl", &filename, &py_sd)) {
337                 return NULL;
338         }
339
340         spdata = self->ptr;
341
342         sd = py_talloc_get_type(py_sd, struct security_descriptor);
343         if (!sd) {
344                 PyErr_Format(PyExc_TypeError,
345                                 "Expected dcerpc.security.descriptor for security_descriptor argument, got %s", talloc_get_name(py_talloc_get_ptr(py_sd)));
346                 return NULL;
347         }
348
349         ZERO_STRUCT(io);
350
351         io.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
352         io.set_secdesc.in.file.path = filename;
353         io.set_secdesc.in.secinfo_flags = 0;
354         io.set_secdesc.in.sd = sd;
355
356         status = smb_raw_set_secdesc(spdata->tree, &io);
357         PyErr_NTSTATUS_IS_ERR_RAISE(status);
358
359         Py_RETURN_NONE;
360 }
361
362
363 static PyMethodDef py_smb_methods[] = {
364         { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
365                 "loadfile(path) -> file contents as a string\n\n \
366                 Read contents of a file." },
367         { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
368                 "savefile(path, str) -> None\n\n \
369                 Write string str to file." },
370         { "list", (PyCFunction)py_smb_list, METH_VARARGS|METH_KEYWORDS,
371                 "list(path) -> directory contents as a dictionary\n\n \
372                 List contents of a directory. The keys are, \n \
373                 \tname: Long name of the directory item\n \
374                 \tshort_name: Short name of the directory item\n \
375                 \tsize: File size in bytes\n \
376                 \tattrib: Attributes\n \
377                 \tmtime: Modification time\n" },
378         { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
379                 "mkdir(path) -> None\n\n \
380                 Create a directory." },
381         { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
382                 "rmdir(path) -> None\n\n \
383                 Delete a directory." },
384         { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
385                 "chkpath(path) -> True or False\n\n \
386                 Return true if path exists, false otherwise." },
387         { "get_acl", (PyCFunction)py_smb_getacl, METH_VARARGS,
388                 "get_acl(path) -> security_descriptor object\n\n \
389                 Get security descriptor for file." },
390         { "set_acl", (PyCFunction)py_smb_setacl, METH_VARARGS,
391                 "set_acl(path, security_descriptor) -> None\n\n \
392                 Set security descriptor for file." },
393         { NULL },
394 };
395
396
397 static PyObject *py_smb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
398 {
399         PyObject *py_creds = Py_None;
400         PyObject *py_lp = Py_None;
401         const char *kwnames[] = { "hostname", "service", "creds", "lp", NULL };
402         const char *hostname = NULL;
403         const char *service = NULL;
404         py_talloc_Object *smb;
405         struct smb_private_data *spdata;
406         NTSTATUS status;
407
408         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zz|OO",
409                                         discard_const_p(char *, kwnames),
410                                         &hostname, &service, &py_creds, &py_lp)) {
411                 return NULL;
412         }
413
414         smb = (py_talloc_Object *)type->tp_alloc(type, 0);
415         if (smb == NULL) {
416                 PyErr_NoMemory();
417                 return NULL;
418         }
419         smb->talloc_ctx = talloc_new(NULL);
420         if (smb->talloc_ctx == NULL) {
421                 PyErr_NoMemory();
422                 return NULL;
423         }
424
425         spdata = talloc_zero(smb->talloc_ctx, struct smb_private_data);
426         if (spdata == NULL) {
427                 PyErr_NoMemory();
428                 Py_DECREF(smb);
429                 return NULL;
430         }
431
432         spdata->lp_ctx = lpcfg_from_py_object(smb->talloc_ctx, py_lp);
433         if (spdata->lp_ctx == NULL) {
434                 Py_DECREF(smb);
435                 return NULL;
436         }
437         spdata->creds = PyCredentials_AsCliCredentials(py_creds);
438         spdata->ev_ctx = s4_event_context_init(smb->talloc_ctx);
439         if (spdata->ev_ctx == NULL) {
440                 PyErr_NoMemory();
441                 Py_DECREF(smb);
442                 return NULL;
443         }
444
445         status = do_smb_connect(smb->talloc_ctx, spdata, hostname, service, &spdata->tree);
446         PyErr_NTSTATUS_IS_ERR_RAISE(status);
447         if (spdata->tree == NULL) {
448                 Py_DECREF(smb);
449                 return NULL;
450         }
451
452         smb->ptr = spdata;
453         return (PyObject *)smb;
454 }
455
456
457 static PyTypeObject PySMB = {
458         .tp_name = "smb.SMB",
459         .tp_basicsize = sizeof(py_talloc_Object),
460         .tp_new = py_smb_new,
461         .tp_flags = Py_TPFLAGS_DEFAULT,
462         .tp_methods = py_smb_methods,
463         .tp_doc = "SMB(hostname, service[, lp[, creds]]) -> SMB connection object\n",
464
465 };
466
467 void initsmb(void)
468 {
469         PyObject *m;
470         PyTypeObject *talloc_type = PyTalloc_GetObjectType();
471         if (talloc_type == NULL) {
472                 return;
473         }
474
475         PySMB.tp_base = talloc_type;
476
477         if (PyType_Ready(&PySMB) < 0) {
478                 return;
479         }
480
481         m = Py_InitModule3("smb", NULL, "SMB File I/O support");
482         if (m == NULL) {
483             return;
484         }
485
486         Py_INCREF(&PySMB);
487         PyModule_AddObject(m, "SMB", (PyObject *)&PySMB);
488
489 #define ADD_FLAGS(val)  PyModule_AddObject(m, #val, PyInt_FromLong(val))
490
491         ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
492         ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
493         ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
494         ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
495         ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
496         ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
497         ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
498         ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
499         ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
500         ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
501         ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
502         ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
503         ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
504         ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
505         ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
506         ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
507 }