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