426c7b28b646bf407461f6be5e1d58b18dfa9b49
[bbaumbach/samba-autobuild/.git] / source3 / libsmb / pylibsmb.c
1 /*
2  * Unix SMB/CIFS implementation.
3  *
4  * SMB client Python bindings used internally by Samba (for things like
5  * samba-tool). These Python bindings may change without warning, and so
6  * should not be used outside of the Samba codebase.
7  *
8  * Copyright (C) Volker Lendecke 2012
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include <Python.h>
25 #include "includes.h"
26 #include "python/py3compat.h"
27 #include "python/modules.h"
28 #include "libcli/smb/smbXcli_base.h"
29 #include "libsmb/libsmb.h"
30 #include "libcli/security/security.h"
31 #include "system/select.h"
32 #include "source4/libcli/util/pyerrors.h"
33 #include "auth/credentials/pycredentials.h"
34 #include "trans2.h"
35 #include "libsmb/clirap.h"
36 #include "librpc/rpc/pyrpc_util.h"
37
38 #define LIST_ATTRIBUTE_MASK \
39         (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN)
40
41 #define SECINFO_DEFAULT_FLAGS \
42         (SECINFO_OWNER | SECINFO_GROUP | \
43          SECINFO_DACL | SECINFO_PROTECTED_DACL | SECINFO_UNPROTECTED_DACL | \
44          SECINFO_SACL | SECINFO_PROTECTED_SACL | SECINFO_UNPROTECTED_SACL)
45
46 static PyTypeObject *get_pytype(const char *module, const char *type)
47 {
48         PyObject *mod;
49         PyTypeObject *result;
50
51         mod = PyImport_ImportModule(module);
52         if (mod == NULL) {
53                 PyErr_Format(PyExc_RuntimeError,
54                              "Unable to import %s to check type %s",
55                              module, type);
56                 return NULL;
57         }
58         result = (PyTypeObject *)PyObject_GetAttrString(mod, type);
59         Py_DECREF(mod);
60         if (result == NULL) {
61                 PyErr_Format(PyExc_RuntimeError,
62                              "Unable to find type %s in module %s",
63                              module, type);
64                 return NULL;
65         }
66         return result;
67 }
68
69 /*
70  * We're using "const char * const *" for keywords,
71  * PyArg_ParseTupleAndKeywords expects a "char **". Confine the
72  * inevitable warnings to just one place.
73  */
74 static int ParseTupleAndKeywords(PyObject *args, PyObject *kw,
75                                  const char *format, const char * const *keywords,
76                                  ...)
77 {
78         char **_keywords = discard_const_p(char *, keywords);
79         va_list a;
80         int ret;
81         va_start(a, keywords);
82         ret = PyArg_VaParseTupleAndKeywords(args, kw, format,
83                                             _keywords, a);
84         va_end(a);
85         return ret;
86 }
87
88 struct py_cli_thread;
89
90 struct py_cli_oplock_break {
91         uint16_t fnum;
92         uint8_t level;
93 };
94
95 struct py_cli_state {
96         PyObject_HEAD
97         struct cli_state *cli;
98         bool is_smb1;
99         struct tevent_context *ev;
100         int (*req_wait_fn)(struct tevent_context *ev,
101                            struct tevent_req *req);
102         struct py_cli_thread *thread_state;
103
104         struct tevent_req *oplock_waiter;
105         struct py_cli_oplock_break *oplock_breaks;
106         struct py_tevent_cond *oplock_cond;
107 };
108
109 #ifdef HAVE_PTHREAD
110
111 #include <pthread.h>
112
113 struct py_cli_thread {
114
115         /*
116          * Pipe to make the poll thread wake up in our destructor, so
117          * that we can exit and join the thread.
118          */
119         int shutdown_pipe[2];
120         struct tevent_fd *shutdown_fde;
121         bool do_shutdown;
122         pthread_t id;
123
124         /*
125          * Thread state to release the GIL during the poll(2) syscall
126          */
127         PyThreadState *py_threadstate;
128 };
129
130 static void *py_cli_state_poll_thread(void *private_data)
131 {
132         struct py_cli_state *self = (struct py_cli_state *)private_data;
133         struct py_cli_thread *t = self->thread_state;
134         PyGILState_STATE gstate;
135
136         gstate = PyGILState_Ensure();
137
138         while (!t->do_shutdown) {
139                 int ret;
140                 ret = tevent_loop_once(self->ev);
141                 assert(ret == 0);
142         }
143         PyGILState_Release(gstate);
144         return NULL;
145 }
146
147 static void py_cli_state_trace_callback(enum tevent_trace_point point,
148                                         void *private_data)
149 {
150         struct py_cli_state *self = (struct py_cli_state *)private_data;
151         struct py_cli_thread *t = self->thread_state;
152
153         switch(point) {
154         case TEVENT_TRACE_BEFORE_WAIT:
155                 assert(t->py_threadstate == NULL);
156                 t->py_threadstate = PyEval_SaveThread();
157                 break;
158         case TEVENT_TRACE_AFTER_WAIT:
159                 assert(t->py_threadstate != NULL);
160                 PyEval_RestoreThread(t->py_threadstate);
161                 t->py_threadstate = NULL;
162                 break;
163         default:
164                 break;
165         }
166 }
167
168 static void py_cli_state_shutdown_handler(struct tevent_context *ev,
169                                           struct tevent_fd *fde,
170                                           uint16_t flags,
171                                           void *private_data)
172 {
173         struct py_cli_state *self = (struct py_cli_state *)private_data;
174         struct py_cli_thread *t = self->thread_state;
175
176         if ((flags & TEVENT_FD_READ) == 0) {
177                 return;
178         }
179         TALLOC_FREE(t->shutdown_fde);
180         t->do_shutdown = true;
181 }
182
183 static int py_cli_thread_destructor(struct py_cli_thread *t)
184 {
185         char c = 0;
186         ssize_t written;
187         int ret;
188
189         do {
190                 /*
191                  * This will wake the poll thread from the poll(2)
192                  */
193                 written = write(t->shutdown_pipe[1], &c, 1);
194         } while ((written == -1) && (errno == EINTR));
195
196         /*
197          * Allow the poll thread to do its own cleanup under the GIL
198          */
199         Py_BEGIN_ALLOW_THREADS
200         ret = pthread_join(t->id, NULL);
201         Py_END_ALLOW_THREADS
202         assert(ret == 0);
203
204         if (t->shutdown_pipe[0] != -1) {
205                 close(t->shutdown_pipe[0]);
206                 t->shutdown_pipe[0] = -1;
207         }
208         if (t->shutdown_pipe[1] != -1) {
209                 close(t->shutdown_pipe[1]);
210                 t->shutdown_pipe[1] = -1;
211         }
212         return 0;
213 }
214
215 static int py_tevent_cond_req_wait(struct tevent_context *ev,
216                                    struct tevent_req *req);
217
218 static bool py_cli_state_setup_mt_ev(struct py_cli_state *self)
219 {
220         struct py_cli_thread *t = NULL;
221         int ret;
222
223         self->ev = tevent_context_init_byname(NULL, "poll_mt");
224         if (self->ev == NULL) {
225                 goto fail;
226         }
227         samba_tevent_set_debug(self->ev, "pylibsmb_tevent_mt");
228         tevent_set_trace_callback(self->ev, py_cli_state_trace_callback, self);
229
230         self->req_wait_fn = py_tevent_cond_req_wait;
231
232         self->thread_state = talloc_zero(NULL, struct py_cli_thread);
233         if (self->thread_state == NULL) {
234                 goto fail;
235         }
236         t = self->thread_state;
237
238         ret = pipe(t->shutdown_pipe);
239         if (ret == -1) {
240                 goto fail;
241         }
242         t->shutdown_fde = tevent_add_fd(
243                 self->ev, self->ev, t->shutdown_pipe[0], TEVENT_FD_READ,
244                 py_cli_state_shutdown_handler, self);
245         if (t->shutdown_fde == NULL) {
246                 goto fail;
247         }
248
249         PyEval_InitThreads();
250
251         ret = pthread_create(&t->id, NULL, py_cli_state_poll_thread, self);
252         if (ret != 0) {
253                 goto fail;
254         }
255         talloc_set_destructor(self->thread_state, py_cli_thread_destructor);
256         return true;
257
258 fail:
259         if (t != NULL) {
260                 TALLOC_FREE(t->shutdown_fde);
261
262                 if (t->shutdown_pipe[0] != -1) {
263                         close(t->shutdown_pipe[0]);
264                         t->shutdown_pipe[0] = -1;
265                 }
266                 if (t->shutdown_pipe[1] != -1) {
267                         close(t->shutdown_pipe[1]);
268                         t->shutdown_pipe[1] = -1;
269                 }
270         }
271
272         TALLOC_FREE(self->thread_state);
273         TALLOC_FREE(self->ev);
274         return false;
275 }
276
277 struct py_tevent_cond {
278         pthread_mutex_t mutex;
279         pthread_cond_t cond;
280         bool is_done;
281 };
282
283 static void py_tevent_signalme(struct tevent_req *req);
284
285 static int py_tevent_cond_wait(struct py_tevent_cond *cond)
286 {
287         int ret, result;
288
289         result = pthread_mutex_init(&cond->mutex, NULL);
290         if (result != 0) {
291                 goto fail;
292         }
293         result = pthread_cond_init(&cond->cond, NULL);
294         if (result != 0) {
295                 goto fail_mutex;
296         }
297
298         result = pthread_mutex_lock(&cond->mutex);
299         if (result != 0) {
300                 goto fail_cond;
301         }
302
303         cond->is_done = false;
304
305         while (!cond->is_done) {
306
307                 Py_BEGIN_ALLOW_THREADS
308                 result = pthread_cond_wait(&cond->cond, &cond->mutex);
309                 Py_END_ALLOW_THREADS
310
311                 if (result != 0) {
312                         goto fail_unlock;
313                 }
314         }
315
316 fail_unlock:
317         ret = pthread_mutex_unlock(&cond->mutex);
318         assert(ret == 0);
319 fail_cond:
320         ret = pthread_cond_destroy(&cond->cond);
321         assert(ret == 0);
322 fail_mutex:
323         ret = pthread_mutex_destroy(&cond->mutex);
324         assert(ret == 0);
325 fail:
326         return result;
327 }
328
329 static int py_tevent_cond_req_wait(struct tevent_context *ev,
330                                    struct tevent_req *req)
331 {
332         struct py_tevent_cond cond;
333         tevent_req_set_callback(req, py_tevent_signalme, &cond);
334         return py_tevent_cond_wait(&cond);
335 }
336
337 static void py_tevent_cond_signal(struct py_tevent_cond *cond)
338 {
339         int ret;
340
341         ret = pthread_mutex_lock(&cond->mutex);
342         assert(ret == 0);
343
344         cond->is_done = true;
345
346         ret = pthread_cond_signal(&cond->cond);
347         assert(ret == 0);
348         ret = pthread_mutex_unlock(&cond->mutex);
349         assert(ret == 0);
350 }
351
352 static void py_tevent_signalme(struct tevent_req *req)
353 {
354         struct py_tevent_cond *cond = (struct py_tevent_cond *)
355                 tevent_req_callback_data_void(req);
356
357         py_tevent_cond_signal(cond);
358 }
359
360 #endif
361
362 static int py_tevent_req_wait(struct tevent_context *ev,
363                               struct tevent_req *req);
364
365 static bool py_cli_state_setup_ev(struct py_cli_state *self)
366 {
367         self->ev = tevent_context_init(NULL);
368         if (self->ev == NULL) {
369                 return false;
370         }
371
372         samba_tevent_set_debug(self->ev, "pylibsmb_tevent");
373
374         self->req_wait_fn = py_tevent_req_wait;
375
376         return true;
377 }
378
379 static int py_tevent_req_wait(struct tevent_context *ev,
380                               struct tevent_req *req)
381 {
382         while (tevent_req_is_in_progress(req)) {
383                 int ret;
384
385                 ret = tevent_loop_once(ev);
386                 if (ret != 0) {
387                         return ret;
388                 }
389         }
390         return 0;
391 }
392
393 static bool py_tevent_req_wait_exc(struct py_cli_state *self,
394                                    struct tevent_req *req)
395 {
396         int ret;
397
398         if (req == NULL) {
399                 PyErr_NoMemory();
400                 return false;
401         }
402         ret = self->req_wait_fn(self->ev, req);
403         if (ret != 0) {
404                 TALLOC_FREE(req);
405                 errno = ret;
406                 PyErr_SetFromErrno(PyExc_RuntimeError);
407                 return false;
408         }
409         return true;
410 }
411
412 static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args,
413                                   PyObject *kwds)
414 {
415         struct py_cli_state *self;
416
417         self = (struct py_cli_state *)type->tp_alloc(type, 0);
418         if (self == NULL) {
419                 return NULL;
420         }
421         self->cli = NULL;
422         self->is_smb1 = false;
423         self->ev = NULL;
424         self->thread_state = NULL;
425         self->oplock_waiter = NULL;
426         self->oplock_cond = NULL;
427         self->oplock_breaks = NULL;
428         return (PyObject *)self;
429 }
430
431 static void py_cli_got_oplock_break(struct tevent_req *req);
432
433 static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
434                              PyObject *kwds)
435 {
436         NTSTATUS status;
437         char *host, *share;
438         PyObject *creds = NULL;
439         struct cli_credentials *cli_creds;
440         PyObject *py_lp = Py_None;
441         PyObject *py_multi_threaded = Py_False;
442         bool multi_threaded = false;
443         PyObject *py_sign = Py_False;
444         bool sign = false;
445         int signing_state = SMB_SIGNING_DEFAULT;
446         PyObject *py_force_smb1 = Py_False;
447         bool force_smb1 = false;
448         struct tevent_req *req;
449         bool ret;
450         int flags = 0;
451
452         static const char *kwlist[] = {
453                 "host", "share", "lp", "creds",
454                 "multi_threaded", "sign", "force_smb1",
455                 NULL
456         };
457
458         PyTypeObject *py_type_Credentials = get_pytype(
459                 "samba.credentials", "Credentials");
460         if (py_type_Credentials == NULL) {
461                 return -1;
462         }
463
464         ret = ParseTupleAndKeywords(
465                 args, kwds, "ssO|O!OOO", kwlist,
466                 &host, &share, &py_lp,
467                 py_type_Credentials, &creds,
468                 &py_multi_threaded,
469                 &py_sign,
470                 &py_force_smb1);
471
472         Py_DECREF(py_type_Credentials);
473
474         if (!ret) {
475                 return -1;
476         }
477
478         multi_threaded = PyObject_IsTrue(py_multi_threaded);
479         sign = PyObject_IsTrue(py_sign);
480         force_smb1 = PyObject_IsTrue(py_force_smb1);
481
482         if (sign) {
483                 signing_state = SMB_SIGNING_REQUIRED;
484         }
485
486         if (force_smb1) {
487                 /*
488                  * As most of the cli_*_send() function
489                  * don't support SMB2 (it's only plugged
490                  * into the sync wrapper functions currently)
491                  * we have a way to force SMB1.
492                  */
493                 flags = CLI_FULL_CONNECTION_FORCE_SMB1;
494         }
495
496         if (multi_threaded) {
497 #ifdef HAVE_PTHREAD
498                 ret = py_cli_state_setup_mt_ev(self);
499                 if (!ret) {
500                         return -1;
501                 }
502 #else
503                 PyErr_SetString(PyExc_RuntimeError,
504                                 "No PTHREAD support available");
505                 return -1;
506 #endif
507                 if (!force_smb1) {
508                         PyErr_SetString(PyExc_RuntimeError,
509                                         "multi_threaded is only possible on "
510                                         "SMB1 connections");
511                         return -1;
512                 }
513         } else {
514                 ret = py_cli_state_setup_ev(self);
515                 if (!ret) {
516                         return -1;
517                 }
518         }
519
520         if (creds == NULL) {
521                 cli_creds = cli_credentials_init_anon(NULL);
522         } else {
523                 cli_creds = PyCredentials_AsCliCredentials(creds);
524         }
525
526         req = cli_full_connection_creds_send(
527                 NULL, self->ev, "myname", host, NULL, 0, share, "?????",
528                 cli_creds, flags, signing_state);
529         if (!py_tevent_req_wait_exc(self, req)) {
530                 return -1;
531         }
532         status = cli_full_connection_creds_recv(req, &self->cli);
533         TALLOC_FREE(req);
534
535         if (!NT_STATUS_IS_OK(status)) {
536                 PyErr_SetNTSTATUS(status);
537                 return -1;
538         }
539
540         if (smbXcli_conn_protocol(self->cli->conn) < PROTOCOL_SMB2_02) {
541                 self->is_smb1 = true;
542         }
543
544         /*
545          * Oplocks require a multi threaded connection
546          */
547         if (self->thread_state == NULL) {
548                 return 0;
549         }
550
551         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
552                 self->ev, self->ev, self->cli);
553         if (self->oplock_waiter == NULL) {
554                 PyErr_NoMemory();
555                 return -1;
556         }
557         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
558                                 self);
559         return 0;
560 }
561
562 static void py_cli_got_oplock_break(struct tevent_req *req)
563 {
564         struct py_cli_state *self = (struct py_cli_state *)
565                 tevent_req_callback_data_void(req);
566         struct py_cli_oplock_break b;
567         struct py_cli_oplock_break *tmp;
568         size_t num_breaks;
569         NTSTATUS status;
570
571         status = cli_smb_oplock_break_waiter_recv(req, &b.fnum, &b.level);
572         TALLOC_FREE(req);
573         self->oplock_waiter = NULL;
574
575         if (!NT_STATUS_IS_OK(status)) {
576                 return;
577         }
578
579         num_breaks = talloc_array_length(self->oplock_breaks);
580         tmp = talloc_realloc(self->ev, self->oplock_breaks,
581                              struct py_cli_oplock_break, num_breaks+1);
582         if (tmp == NULL) {
583                 return;
584         }
585         self->oplock_breaks = tmp;
586         self->oplock_breaks[num_breaks] = b;
587
588         if (self->oplock_cond != NULL) {
589                 py_tevent_cond_signal(self->oplock_cond);
590         }
591
592         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
593                 self->ev, self->ev, self->cli);
594         if (self->oplock_waiter == NULL) {
595                 return;
596         }
597         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
598                                 self);
599 }
600
601 static PyObject *py_cli_get_oplock_break(struct py_cli_state *self,
602                                          PyObject *args)
603 {
604         size_t num_oplock_breaks;
605
606         if (!PyArg_ParseTuple(args, "")) {
607                 return NULL;
608         }
609
610         if (self->thread_state == NULL) {
611                 PyErr_SetString(PyExc_RuntimeError,
612                                 "get_oplock_break() only possible on "
613                                 "a multi_threaded connection");
614                 return NULL;
615         }
616
617         if (self->oplock_cond != NULL) {
618                 errno = EBUSY;
619                 PyErr_SetFromErrno(PyExc_RuntimeError);
620                 return NULL;
621         }
622
623         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
624
625         if (num_oplock_breaks == 0) {
626                 struct py_tevent_cond cond;
627                 int ret;
628
629                 self->oplock_cond = &cond;
630                 ret = py_tevent_cond_wait(&cond);
631                 self->oplock_cond = NULL;
632
633                 if (ret != 0) {
634                         errno = ret;
635                         PyErr_SetFromErrno(PyExc_RuntimeError);
636                         return NULL;
637                 }
638         }
639
640         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
641         if (num_oplock_breaks > 0) {
642                 PyObject *result;
643
644                 result = Py_BuildValue(
645                         "{s:i,s:i}",
646                         "fnum", self->oplock_breaks[0].fnum,
647                         "level", self->oplock_breaks[0].level);
648
649                 memmove(&self->oplock_breaks[0], &self->oplock_breaks[1],
650                         sizeof(self->oplock_breaks[0]) *
651                         (num_oplock_breaks - 1));
652                 self->oplock_breaks = talloc_realloc(
653                         NULL, self->oplock_breaks, struct py_cli_oplock_break,
654                         num_oplock_breaks - 1);
655
656                 return result;
657         }
658         Py_RETURN_NONE;
659 }
660
661 static void py_cli_state_dealloc(struct py_cli_state *self)
662 {
663         TALLOC_FREE(self->thread_state);
664         TALLOC_FREE(self->oplock_waiter);
665         TALLOC_FREE(self->ev);
666
667         if (self->cli != NULL) {
668                 cli_shutdown(self->cli);
669                 self->cli = NULL;
670         }
671         Py_TYPE(self)->tp_free((PyObject *)self);
672 }
673
674 static PyObject *py_cli_settimeout(struct py_cli_state *self, PyObject *args)
675 {
676         unsigned int nmsecs = 0;
677         unsigned int omsecs = 0;
678
679         if (!PyArg_ParseTuple(args, "I", &nmsecs)) {
680                 return NULL;
681         }
682
683         omsecs = cli_set_timeout(self->cli, nmsecs);
684
685         return PyLong_FromLong(omsecs);
686 }
687
688 static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
689                                PyObject *kwds)
690 {
691         char *fname;
692         unsigned CreateFlags = 0;
693         unsigned DesiredAccess = FILE_GENERIC_READ;
694         unsigned FileAttributes = 0;
695         unsigned ShareAccess = 0;
696         unsigned CreateDisposition = FILE_OPEN;
697         unsigned CreateOptions = 0;
698         unsigned ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
699         unsigned SecurityFlags = 0;
700         uint16_t fnum;
701         struct tevent_req *req;
702         NTSTATUS status;
703
704         static const char *kwlist[] = {
705                 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
706                 "ShareAccess", "CreateDisposition", "CreateOptions",
707                 "ImpersonationLevel", "SecurityFlags", NULL };
708
709         if (!ParseTupleAndKeywords(
710                     args, kwds, "s|IIIIIIII", kwlist,
711                     &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
712                     &ShareAccess, &CreateDisposition, &CreateOptions,
713                     &ImpersonationLevel, &SecurityFlags)) {
714                 return NULL;
715         }
716
717         req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
718                                 DesiredAccess, FileAttributes, ShareAccess,
719                                 CreateDisposition, CreateOptions,
720                                 ImpersonationLevel, SecurityFlags);
721         if (!py_tevent_req_wait_exc(self, req)) {
722                 return NULL;
723         }
724         status = cli_ntcreate_recv(req, &fnum, NULL);
725         TALLOC_FREE(req);
726
727         if (!NT_STATUS_IS_OK(status)) {
728                 PyErr_SetNTSTATUS(status);
729                 return NULL;
730         }
731         return Py_BuildValue("I", (unsigned)fnum);
732 }
733
734 static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
735 {
736         struct tevent_req *req;
737         int fnum;
738         NTSTATUS status;
739
740         if (!PyArg_ParseTuple(args, "i", &fnum)) {
741                 return NULL;
742         }
743
744         req = cli_close_send(NULL, self->ev, self->cli, fnum);
745         if (!py_tevent_req_wait_exc(self, req)) {
746                 return NULL;
747         }
748         status = cli_close_recv(req);
749         TALLOC_FREE(req);
750
751         if (!NT_STATUS_IS_OK(status)) {
752                 PyErr_SetNTSTATUS(status);
753                 return NULL;
754         }
755         Py_RETURN_NONE;
756 }
757
758 struct push_state {
759         char *data;
760         off_t nread;
761         off_t total_data;
762 };
763
764 /*
765  * cli_push() helper to write a chunk of data to a remote file
766  */
767 static size_t push_data(uint8_t *buf, size_t n, void *priv)
768 {
769         struct push_state *state = (struct push_state *)priv;
770         char *curr_ptr = NULL;
771         off_t remaining;
772         size_t copied_bytes;
773
774         if (state->nread >= state->total_data) {
775                 return 0;
776         }
777
778         curr_ptr = state->data + state->nread;
779         remaining = state->total_data - state->nread;
780         copied_bytes = MIN(remaining, n);
781
782         memcpy(buf, curr_ptr, copied_bytes);
783         state->nread += copied_bytes;
784         return copied_bytes;
785 }
786
787 /*
788  * Writes a file with the contents specified
789  */
790 static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args)
791 {
792         uint16_t fnum;
793         const char *filename = NULL;
794         char *data = NULL;
795         Py_ssize_t size = 0;
796         NTSTATUS status;
797         struct tevent_req *req = NULL;
798         struct push_state state;
799
800         if (!PyArg_ParseTuple(args, "s"PYARG_BYTES_LEN":savefile", &filename,
801                               &data, &size)) {
802                 return NULL;
803         }
804
805         /* create a new file handle for writing to */
806         req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
807                                 FILE_WRITE_DATA, FILE_ATTRIBUTE_NORMAL,
808                                 FILE_SHARE_READ|FILE_SHARE_WRITE,
809                                 FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE,
810                                 SMB2_IMPERSONATION_IMPERSONATION, 0);
811         if (!py_tevent_req_wait_exc(self, req)) {
812                 return NULL;
813         }
814         status = cli_ntcreate_recv(req, &fnum, NULL);
815         TALLOC_FREE(req);
816         PyErr_NTSTATUS_IS_ERR_RAISE(status);
817
818         /* write the new file contents */
819         state.data = data;
820         state.nread = 0;
821         state.total_data = size;
822
823         req = cli_push_send(NULL, self->ev, self->cli, fnum, 0, 0, 0,
824                             push_data, &state);
825         if (!py_tevent_req_wait_exc(self, req)) {
826                 return NULL;
827         }
828         status = cli_push_recv(req);
829         TALLOC_FREE(req);
830         PyErr_NTSTATUS_IS_ERR_RAISE(status);
831
832         /* close the file handle */
833         req = cli_close_send(NULL, self->ev, self->cli, fnum);
834         if (!py_tevent_req_wait_exc(self, req)) {
835                 return NULL;
836         }
837         status = cli_close_recv(req);
838         PyErr_NTSTATUS_IS_ERR_RAISE(status);
839
840         Py_RETURN_NONE;
841 }
842
843 static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
844                               PyObject *kwds)
845 {
846         int fnum;
847         unsigned mode = 0;
848         char *buf;
849         Py_ssize_t buflen;
850         unsigned long long offset;
851         struct tevent_req *req;
852         NTSTATUS status;
853         size_t written;
854
855         static const char *kwlist[] = {
856                 "fnum", "buffer", "offset", "mode", NULL };
857
858         if (!ParseTupleAndKeywords(
859                     args, kwds, "i" PYARG_BYTES_LEN "K|I", kwlist,
860                     &fnum, &buf, &buflen, &offset, &mode)) {
861                 return NULL;
862         }
863
864         req = cli_write_send(NULL, self->ev, self->cli, fnum, mode,
865                              (uint8_t *)buf, offset, buflen);
866         if (!py_tevent_req_wait_exc(self, req)) {
867                 return NULL;
868         }
869         status = cli_write_recv(req, &written);
870         TALLOC_FREE(req);
871
872         if (!NT_STATUS_IS_OK(status)) {
873                 PyErr_SetNTSTATUS(status);
874                 return NULL;
875         }
876         return Py_BuildValue("K", (unsigned long long)written);
877 }
878
879 /*
880  * Returns the size of the given file
881  */
882 static NTSTATUS py_smb_filesize(struct py_cli_state *self, uint16_t fnum,
883                                 off_t *size)
884 {
885         NTSTATUS status;
886
887         if (self->is_smb1) {
888                 uint8_t *rdata = NULL;
889                 struct tevent_req *req = NULL;
890
891                 req = cli_qfileinfo_send(NULL, self->ev, self->cli, fnum,
892                                          SMB_QUERY_FILE_ALL_INFO, 68,
893                                          CLI_BUFFER_SIZE);
894                 if (!py_tevent_req_wait_exc(self, req)) {
895                         return NT_STATUS_INTERNAL_ERROR;
896                 }
897                 status = cli_qfileinfo_recv(req, NULL, NULL, &rdata, NULL);
898                 if (NT_STATUS_IS_OK(status)) {
899                         *size = IVAL2_TO_SMB_BIG_UINT(rdata, 48);
900                 }
901                 TALLOC_FREE(req);
902                 TALLOC_FREE(rdata);
903         } else {
904                 status = cli_qfileinfo_basic(self->cli, fnum, NULL, size,
905                                              NULL, NULL, NULL, NULL, NULL);
906         }
907         return status;
908 }
909
910 /*
911  * Loads the specified file's contents and returns it
912  */
913 static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args)
914 {
915         NTSTATUS status;
916         const char *filename = NULL;
917         struct tevent_req *req = NULL;
918         uint16_t fnum;
919         off_t size;
920         char *buf = NULL;
921         off_t nread = 0;
922         PyObject *result = NULL;
923
924         if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
925                 return NULL;
926         }
927
928         /* get a read file handle */
929         req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
930                                 FILE_READ_DATA | FILE_READ_ATTRIBUTES,
931                                 FILE_ATTRIBUTE_NORMAL,
932                                 FILE_SHARE_READ, FILE_OPEN, 0,
933                                 SMB2_IMPERSONATION_IMPERSONATION, 0);
934         if (!py_tevent_req_wait_exc(self, req)) {
935                 return NULL;
936         }
937         status = cli_ntcreate_recv(req, &fnum, NULL);
938         TALLOC_FREE(req);
939         PyErr_NTSTATUS_IS_ERR_RAISE(status);
940
941         /* get a buffer to hold the file contents */
942         status = py_smb_filesize(self, fnum, &size);
943         PyErr_NTSTATUS_IS_ERR_RAISE(status);
944
945         result = PyBytes_FromStringAndSize(NULL, size);
946         if (result == NULL) {
947                 return NULL;
948         }
949
950         /* read the file contents */
951         buf = PyBytes_AS_STRING(result);
952         req = cli_pull_send(NULL, self->ev, self->cli, fnum, 0, size,
953                             size, cli_read_sink, &buf);
954         if (!py_tevent_req_wait_exc(self, req)) {
955                 Py_XDECREF(result);
956                 return NULL;
957         }
958         status = cli_pull_recv(req, &nread);
959         TALLOC_FREE(req);
960         if (!NT_STATUS_IS_OK(status)) {
961                 Py_XDECREF(result);
962                 PyErr_SetNTSTATUS(status);
963                 return NULL;
964         }
965
966         /* close the file handle */
967         req = cli_close_send(NULL, self->ev, self->cli, fnum);
968         if (!py_tevent_req_wait_exc(self, req)) {
969                 Py_XDECREF(result);
970                 return NULL;
971         }
972         status = cli_close_recv(req);
973         TALLOC_FREE(req);
974         if (!NT_STATUS_IS_OK(status)) {
975                 Py_XDECREF(result);
976                 PyErr_SetNTSTATUS(status);
977                 return NULL;
978         }
979
980         /* sanity-check we read the expected number of bytes */
981         if (nread > size) {
982                 Py_XDECREF(result);
983                 PyErr_Format(PyExc_IOError,
984                              "read invalid - got %zu requested %zu",
985                              nread, size);
986                 return NULL;
987         }
988
989         if (nread < size) {
990                 if (_PyBytes_Resize(&result, nread) < 0) {
991                         return NULL;
992                 }
993         }
994
995         return result;
996 }
997
998 static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
999                              PyObject *kwds)
1000 {
1001         int fnum;
1002         unsigned long long offset;
1003         unsigned size;
1004         struct tevent_req *req;
1005         NTSTATUS status;
1006         char *buf;
1007         size_t received;
1008         PyObject *result;
1009
1010         static const char *kwlist[] = {
1011                 "fnum", "offset", "size", NULL };
1012
1013         if (!ParseTupleAndKeywords(
1014                     args, kwds, "iKI", kwlist, &fnum, &offset,
1015                     &size)) {
1016                 return NULL;
1017         }
1018
1019         result = PyBytes_FromStringAndSize(NULL, size);
1020         if (result == NULL) {
1021                 return NULL;
1022         }
1023         buf = PyBytes_AS_STRING(result);
1024
1025         req = cli_read_send(NULL, self->ev, self->cli, fnum,
1026                             buf, offset, size);
1027         if (!py_tevent_req_wait_exc(self, req)) {
1028                 Py_XDECREF(result);
1029                 return NULL;
1030         }
1031         status = cli_read_recv(req, &received);
1032         TALLOC_FREE(req);
1033
1034         if (!NT_STATUS_IS_OK(status)) {
1035                 Py_XDECREF(result);
1036                 PyErr_SetNTSTATUS(status);
1037                 return NULL;
1038         }
1039
1040         if (received > size) {
1041                 Py_XDECREF(result);
1042                 PyErr_Format(PyExc_IOError,
1043                              "read invalid - got %zu requested %u",
1044                              received, size);
1045                 return NULL;
1046         }
1047
1048         if (received < size) {
1049                 if (_PyBytes_Resize(&result, received) < 0) {
1050                         return NULL;
1051                 }
1052         }
1053
1054         return result;
1055 }
1056
1057 static PyObject *py_cli_ftruncate(struct py_cli_state *self, PyObject *args,
1058                                   PyObject *kwds)
1059 {
1060         int fnum;
1061         unsigned long long size;
1062         struct tevent_req *req;
1063         NTSTATUS status;
1064
1065         static const char *kwlist[] = {
1066                 "fnum", "size", NULL };
1067
1068         if (!ParseTupleAndKeywords(
1069                     args, kwds, "IK", kwlist, &fnum, &size)) {
1070                 return NULL;
1071         }
1072
1073         req = cli_ftruncate_send(NULL, self->ev, self->cli, fnum, size);
1074         if (!py_tevent_req_wait_exc(self, req)) {
1075                 return NULL;
1076         }
1077         status = cli_ftruncate_recv(req);
1078         TALLOC_FREE(req);
1079
1080         if (!NT_STATUS_IS_OK(status)) {
1081                 PyErr_SetNTSTATUS(status);
1082                 return NULL;
1083         }
1084         Py_RETURN_NONE;
1085 }
1086
1087 static PyObject *py_cli_delete_on_close(struct py_cli_state *self,
1088                                         PyObject *args,
1089                                         PyObject *kwds)
1090 {
1091         unsigned fnum, flag;
1092         struct tevent_req *req;
1093         NTSTATUS status;
1094
1095         static const char *kwlist[] = {
1096                 "fnum", "flag", NULL };
1097
1098         if (!ParseTupleAndKeywords(
1099                     args, kwds, "II", kwlist, &fnum, &flag)) {
1100                 return NULL;
1101         }
1102
1103         req = cli_nt_delete_on_close_send(NULL, self->ev, self->cli, fnum,
1104                                           flag);
1105         if (!py_tevent_req_wait_exc(self, req)) {
1106                 return NULL;
1107         }
1108         status = cli_nt_delete_on_close_recv(req);
1109         TALLOC_FREE(req);
1110
1111         if (!NT_STATUS_IS_OK(status)) {
1112                 PyErr_SetNTSTATUS(status);
1113                 return NULL;
1114         }
1115         Py_RETURN_NONE;
1116 }
1117
1118 /*
1119  * Helper to add directory listing entries to an overall Python list
1120  */
1121 static NTSTATUS list_helper(const char *mntpoint, struct file_info *finfo,
1122                             const char *mask, void *state)
1123 {
1124         PyObject *result = (PyObject *)state;
1125         PyObject *file = NULL;
1126         PyObject *size = NULL;
1127         int ret;
1128
1129         /* suppress '.' and '..' in the results we return */
1130         if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
1131                 return NT_STATUS_OK;
1132         }
1133         size = PyLong_FromUnsignedLongLong(finfo->size);
1134         /*
1135          * Build a dictionary representing the file info.
1136          * Note: Windows does not always return short_name (so it may be None)
1137          */
1138         file = Py_BuildValue("{s:s,s:i,s:s,s:O,s:l}",
1139                              "name", finfo->name,
1140                              "attrib", (int)finfo->mode,
1141                              "short_name", finfo->short_name,
1142                              "size", size,
1143                              "mtime",
1144                              convert_timespec_to_time_t(finfo->mtime_ts));
1145
1146         Py_CLEAR(size);
1147
1148         if (file == NULL) {
1149                 return NT_STATUS_NO_MEMORY;
1150         }
1151
1152         ret = PyList_Append(result, file);
1153         Py_CLEAR(file);
1154         if (ret == -1) {
1155                 return NT_STATUS_INTERNAL_ERROR;
1156         }
1157
1158         return NT_STATUS_OK;
1159 }
1160
1161 static NTSTATUS do_listing(struct py_cli_state *self,
1162                            const char *base_dir, const char *user_mask,
1163                            uint16_t attribute,
1164                            NTSTATUS (*callback_fn)(const char *,
1165                                                    struct file_info *,
1166                                                    const char *, void *),
1167                            void *priv)
1168 {
1169         char *mask = NULL;
1170         unsigned int info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
1171         struct file_info *finfos = NULL;
1172         size_t i;
1173         size_t num_finfos = 0;
1174         NTSTATUS status;
1175
1176         if (user_mask == NULL) {
1177                 mask = talloc_asprintf(NULL, "%s\\*", base_dir);
1178         } else {
1179                 mask = talloc_asprintf(NULL, "%s\\%s", base_dir, user_mask);
1180         }
1181
1182         if (mask == NULL) {
1183                 return NT_STATUS_NO_MEMORY;
1184         }
1185         dos_format(mask);
1186
1187         if (self->is_smb1) {
1188                 struct tevent_req *req = NULL;
1189
1190                 req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
1191                                     info_level);
1192                 if (!py_tevent_req_wait_exc(self, req)) {
1193                         return NT_STATUS_INTERNAL_ERROR;
1194                 }
1195                 status = cli_list_recv(req, NULL, &finfos, &num_finfos);
1196                 TALLOC_FREE(req);
1197         } else {
1198                 status = cli_list(self->cli, mask, attribute, callback_fn,
1199                                   priv);
1200         }
1201         TALLOC_FREE(mask);
1202
1203         if (!NT_STATUS_IS_OK(status)) {
1204                 return status;
1205         }
1206
1207         /* invoke the callback for the async results (SMBv1 connections) */
1208         for (i = 0; i < num_finfos; i++) {
1209                 status = callback_fn(base_dir, &finfos[i], user_mask,
1210                                      priv);
1211                 if (!NT_STATUS_IS_OK(status)) {
1212                         TALLOC_FREE(finfos);
1213                         return status;
1214                 }
1215         }
1216
1217         TALLOC_FREE(finfos);
1218         return status;
1219 }
1220
1221 static PyObject *py_cli_list(struct py_cli_state *self,
1222                              PyObject *args,
1223                              PyObject *kwds)
1224 {
1225         char *base_dir;
1226         char *user_mask = NULL;
1227         unsigned int attribute = LIST_ATTRIBUTE_MASK;
1228         NTSTATUS status;
1229         PyObject *result = NULL;
1230         const char *kwlist[] = { "directory", "mask", "attribs", NULL };
1231
1232         if (!ParseTupleAndKeywords(args, kwds, "z|sI:list", kwlist,
1233                                    &base_dir, &user_mask, &attribute)) {
1234                 return NULL;
1235         }
1236
1237         result = Py_BuildValue("[]");
1238         if (result == NULL) {
1239                 return NULL;
1240         }
1241
1242         status = do_listing(self, base_dir, user_mask, attribute,
1243                             list_helper, result);
1244
1245         if (!NT_STATUS_IS_OK(status)) {
1246                 Py_XDECREF(result);
1247                 PyErr_SetNTSTATUS(status);
1248                 return NULL;
1249         }
1250
1251         return result;
1252 }
1253
1254 /*
1255  * Deletes a file
1256  */
1257 static NTSTATUS unlink_file(struct py_cli_state *self, const char *filename)
1258 {
1259         NTSTATUS status;
1260         uint16_t attrs = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
1261         struct tevent_req *req = NULL;
1262
1263         req = cli_unlink_send(
1264                 NULL, self->ev, self->cli, filename, attrs);
1265         if (!py_tevent_req_wait_exc(self, req)) {
1266                 return NT_STATUS_INTERNAL_ERROR;
1267         }
1268         status = cli_unlink_recv(req);
1269         TALLOC_FREE(req);
1270
1271         return status;
1272 }
1273
1274 static PyObject *py_smb_unlink(struct py_cli_state *self, PyObject *args)
1275 {
1276         NTSTATUS status;
1277         const char *filename = NULL;
1278
1279         if (!PyArg_ParseTuple(args, "s:unlink", &filename)) {
1280                 return NULL;
1281         }
1282
1283         status = unlink_file(self, filename);
1284         PyErr_NTSTATUS_NOT_OK_RAISE(status);
1285
1286         Py_RETURN_NONE;
1287 }
1288
1289 /*
1290  * Delete an empty directory
1291  */
1292 static NTSTATUS remove_dir(struct py_cli_state *self, const char *dirname)
1293 {
1294         NTSTATUS status;
1295         struct tevent_req *req = NULL;
1296
1297         req = cli_rmdir_send(NULL, self->ev, self->cli, dirname);
1298         if (!py_tevent_req_wait_exc(self, req)) {
1299                 return NT_STATUS_INTERNAL_ERROR;
1300         }
1301         status = cli_rmdir_recv(req);
1302         TALLOC_FREE(req);
1303
1304         return status;
1305 }
1306
1307 static PyObject *py_smb_rmdir(struct py_cli_state *self, PyObject *args)
1308 {
1309         NTSTATUS status;
1310         const char *dirname = NULL;
1311
1312         if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
1313                 return NULL;
1314         }
1315
1316         status = remove_dir(self, dirname);
1317         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1318
1319         Py_RETURN_NONE;
1320 }
1321
1322 /*
1323  * Create a directory
1324  */
1325 static PyObject *py_smb_mkdir(struct py_cli_state *self, PyObject *args)
1326 {
1327         NTSTATUS status;
1328         const char *dirname = NULL;
1329         struct tevent_req *req = NULL;
1330
1331         if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
1332                 return NULL;
1333         }
1334
1335         req = cli_mkdir_send(NULL, self->ev, self->cli, dirname);
1336         if (!py_tevent_req_wait_exc(self, req)) {
1337                 return NULL;
1338         }
1339         status = cli_mkdir_recv(req);
1340         TALLOC_FREE(req);
1341         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1342
1343         Py_RETURN_NONE;
1344 }
1345
1346 /*
1347  * Checks existence of a directory
1348  */
1349 static bool check_dir_path(struct py_cli_state *self, const char *path)
1350 {
1351         NTSTATUS status;
1352         struct tevent_req *req = NULL;
1353
1354         req = cli_chkpath_send(NULL, self->ev, self->cli, path);
1355         if (!py_tevent_req_wait_exc(self, req)) {
1356                 return false;
1357         }
1358         status = cli_chkpath_recv(req);
1359         TALLOC_FREE(req);
1360
1361         return NT_STATUS_IS_OK(status);
1362 }
1363
1364 static PyObject *py_smb_chkpath(struct py_cli_state *self, PyObject *args)
1365 {
1366         const char *path = NULL;
1367         bool dir_exists;
1368
1369         if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
1370                 return NULL;
1371         }
1372
1373         dir_exists = check_dir_path(self, path);
1374         return PyBool_FromLong(dir_exists);
1375 }
1376
1377 struct deltree_state {
1378         struct py_cli_state *self;
1379         const char *full_dirpath;
1380 };
1381
1382 static NTSTATUS delete_dir_tree(struct py_cli_state *self,
1383                                 const char *dirpath);
1384
1385 /*
1386  * Deletes a single item in the directory tree. This could be either a file
1387  * or a directory. This function gets invoked as a callback for every item in
1388  * the given directory's listings.
1389  */
1390 static NTSTATUS delete_tree_callback(const char *mntpoint,
1391                                      struct file_info *finfo,
1392                                      const char *mask, void *priv)
1393 {
1394         char *filepath = NULL;
1395         struct deltree_state *state = priv;
1396         NTSTATUS status;
1397
1398         /* skip '.' or '..' directory listings */
1399         if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
1400                 return NT_STATUS_OK;
1401         }
1402
1403         /* get the absolute filepath */
1404         filepath = talloc_asprintf(NULL, "%s\\%s", state->full_dirpath,
1405                                    finfo->name);
1406         if (filepath == NULL) {
1407                 return NT_STATUS_NO_MEMORY;
1408         }
1409
1410         if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) {
1411
1412                 /* recursively delete the sub-directory and its contents */
1413                 status = delete_dir_tree(state->self, filepath);
1414         } else {
1415                 status = unlink_file(state->self, filepath);
1416         }
1417
1418         TALLOC_FREE(filepath);
1419         return status;
1420 }
1421
1422 /*
1423  * Removes a directory and all its contents
1424  */
1425 static NTSTATUS delete_dir_tree(struct py_cli_state *self,
1426                                 const char *filepath)
1427 {
1428         NTSTATUS status;
1429         const char *mask = "*";
1430         struct deltree_state state = { 0 };
1431
1432         /* go through the directory's contents, deleting each item */
1433         state.self = self;
1434         state.full_dirpath = filepath;
1435         status = do_listing(self, filepath, mask, LIST_ATTRIBUTE_MASK,
1436                             delete_tree_callback, &state);
1437
1438         /* remove the directory itself */
1439         if (NT_STATUS_IS_OK(status)) {
1440                 status = remove_dir(self, filepath);
1441         }
1442         return status;
1443 }
1444
1445 static PyObject *py_smb_deltree(struct py_cli_state *self, PyObject *args)
1446 {
1447         NTSTATUS status;
1448         const char *filepath = NULL;
1449         bool dir_exists;
1450
1451         if (!PyArg_ParseTuple(args, "s:deltree", &filepath)) {
1452                 return NULL;
1453         }
1454
1455         /* check whether we're removing a directory or a file */
1456         dir_exists = check_dir_path(self, filepath);
1457
1458         if (dir_exists) {
1459                 status = delete_dir_tree(self, filepath);
1460         } else {
1461                 status = unlink_file(self, filepath);
1462         }
1463
1464         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1465
1466         Py_RETURN_NONE;
1467 }
1468
1469 /*
1470  * Read ACL on a given file/directory as a security descriptor object
1471  */
1472 static PyObject *py_smb_getacl(struct py_cli_state *self, PyObject *args)
1473 {
1474         NTSTATUS status;
1475         const char *filename = NULL;
1476         unsigned int sinfo = SECINFO_DEFAULT_FLAGS;
1477         unsigned int access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
1478         uint16_t fnum;
1479         struct security_descriptor *sd = NULL;
1480
1481         /* there's no async version of cli_query_security_descriptor() */
1482         if (self->thread_state != NULL) {
1483                 PyErr_SetString(PyExc_RuntimeError,
1484                                 "get_acl() is not supported on "
1485                                 "a multi_threaded connection");
1486                 return NULL;
1487         }
1488
1489         if (!PyArg_ParseTuple(args, "s|II:get_acl", &filename, &sinfo,
1490                               &access_mask)) {
1491                 return NULL;
1492         }
1493
1494         /* get a file handle with the desired access */
1495         status = cli_ntcreate(self->cli, filename, 0, access_mask, 0,
1496                               FILE_SHARE_READ|FILE_SHARE_WRITE,
1497                               FILE_OPEN, 0x0, 0x0, &fnum, NULL);
1498         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1499
1500         /* query the security descriptor for this file */
1501         status = cli_query_security_descriptor(self->cli, fnum, sinfo,
1502                                                NULL, &sd);
1503         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1504
1505         /* close the file handle and convert the SD to a python struct */
1506         status = cli_close(self->cli, fnum);
1507         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1508
1509         return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
1510                                     sd, sd);
1511 }
1512
1513 /*
1514  * Set ACL on file/directory using given security descriptor object
1515  */
1516 static PyObject *py_smb_setacl(struct py_cli_state *self, PyObject *args)
1517 {
1518         NTSTATUS status;
1519         char *filename = NULL;
1520         PyObject *py_sd = NULL;
1521         struct security_descriptor *sd = NULL;
1522         unsigned int sinfo = SECINFO_DEFAULT_FLAGS;
1523         uint16_t fnum;
1524
1525         /* there's no async version of cli_set_security_descriptor() */
1526         if (self->thread_state != NULL) {
1527                 PyErr_SetString(PyExc_RuntimeError,
1528                                 "set_acl() is not supported on "
1529                                 "a multi_threaded connection");
1530                 return NULL;
1531         }
1532
1533         if (!PyArg_ParseTuple(args, "sO|I:set_acl", &filename, &py_sd,
1534                               &sinfo)) {
1535                 return NULL;
1536         }
1537
1538         sd = pytalloc_get_type(py_sd, struct security_descriptor);
1539         if (!sd) {
1540                 PyErr_Format(PyExc_TypeError,
1541                         "Expected dcerpc.security.descriptor as argument, got %s",
1542                         pytalloc_get_name(py_sd));
1543                 return NULL;
1544         }
1545
1546         status = cli_ntcreate(self->cli, filename, 0,
1547                               SEC_FLAG_MAXIMUM_ALLOWED, 0,
1548                               FILE_SHARE_READ|FILE_SHARE_WRITE,
1549                               FILE_OPEN, 0x0, 0x0, &fnum, NULL);
1550         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1551
1552         status = cli_set_security_descriptor(self->cli, fnum, sinfo, sd);
1553         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1554
1555         status = cli_close(self->cli, fnum);
1556         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1557
1558         Py_RETURN_NONE;
1559 }
1560
1561 static PyMethodDef py_cli_state_methods[] = {
1562         { "settimeout", (PyCFunction)py_cli_settimeout, METH_VARARGS,
1563           "settimeout(new_timeout_msecs) => return old_timeout_msecs" },
1564         { "create", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_create),
1565                 METH_VARARGS|METH_KEYWORDS,
1566           "Open a file" },
1567         { "close", (PyCFunction)py_cli_close, METH_VARARGS,
1568           "Close a file handle" },
1569         { "write", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_write),
1570                 METH_VARARGS|METH_KEYWORDS,
1571           "Write to a file handle" },
1572         { "read", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_read),
1573                 METH_VARARGS|METH_KEYWORDS,
1574           "Read from a file handle" },
1575         { "truncate", PY_DISCARD_FUNC_SIG(PyCFunction,
1576                         py_cli_ftruncate),
1577           METH_VARARGS|METH_KEYWORDS,
1578           "Truncate a file" },
1579         { "delete_on_close", PY_DISCARD_FUNC_SIG(PyCFunction,
1580                                          py_cli_delete_on_close),
1581           METH_VARARGS|METH_KEYWORDS,
1582           "Set/Reset the delete on close flag" },
1583         { "list", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_list),
1584                 METH_VARARGS|METH_KEYWORDS,
1585           "list(directory, mask='*', attribs=DEFAULT_ATTRS) -> "
1586           "directory contents as a dictionary\n"
1587           "\t\tDEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | "
1588           "FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE\n\n"
1589           "\t\tList contents of a directory. The keys are, \n"
1590           "\t\t\tname: Long name of the directory item\n"
1591           "\t\t\tshort_name: Short name of the directory item\n"
1592           "\t\t\tsize: File size in bytes\n"
1593           "\t\t\tattrib: Attributes\n"
1594           "\t\t\tmtime: Modification time\n" },
1595         { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break,
1596           METH_VARARGS, "Wait for an oplock break" },
1597         { "unlink", (PyCFunction)py_smb_unlink,
1598           METH_VARARGS,
1599           "unlink(path) -> None\n\n \t\tDelete a file." },
1600         { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
1601           "mkdir(path) -> None\n\n \t\tCreate a directory." },
1602         { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
1603           "rmdir(path) -> None\n\n \t\tDelete a directory." },
1604         { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
1605           "chkpath(dir_path) -> True or False\n\n"
1606           "\t\tReturn true if directory exists, false otherwise." },
1607         { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
1608           "savefile(path, str) -> None\n\n"
1609           "\t\tWrite " PY_DESC_PY3_BYTES " str to file." },
1610         { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
1611           "loadfile(path) -> file contents as a " PY_DESC_PY3_BYTES
1612           "\n\n\t\tRead contents of a file." },
1613         { "deltree", (PyCFunction)py_smb_deltree, METH_VARARGS,
1614           "deltree(path) -> None\n\n"
1615           "\t\tDelete a directory and all its contents." },
1616         { "get_acl", (PyCFunction)py_smb_getacl, METH_VARARGS,
1617           "get_acl(path[, security_info=0]) -> security_descriptor object\n\n"
1618           "\t\tGet security descriptor for file." },
1619         { "set_acl", (PyCFunction)py_smb_setacl, METH_VARARGS,
1620           "set_acl(path, security_descriptor[, security_info=0]) -> None\n\n"
1621           "\t\tSet security descriptor for file." },
1622         { NULL, NULL, 0, NULL }
1623 };
1624
1625 static PyTypeObject py_cli_state_type = {
1626         PyVarObject_HEAD_INIT(NULL, 0)
1627         .tp_name = "libsmb_samba_internal.Conn",
1628         .tp_basicsize = sizeof(struct py_cli_state),
1629         .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1630         .tp_doc = "libsmb connection",
1631         .tp_new = py_cli_state_new,
1632         .tp_init = (initproc)py_cli_state_init,
1633         .tp_dealloc = (destructor)py_cli_state_dealloc,
1634         .tp_methods = py_cli_state_methods,
1635 };
1636
1637 static PyMethodDef py_libsmb_methods[] = {
1638         {0},
1639 };
1640
1641 void initlibsmb_samba_internal(void);
1642
1643 static struct PyModuleDef moduledef = {
1644     PyModuleDef_HEAD_INIT,
1645     .m_name = "libsmb_samba_internal",
1646     .m_doc = "libsmb wrapper",
1647     .m_size = -1,
1648     .m_methods = py_libsmb_methods,
1649 };
1650
1651 MODULE_INIT_FUNC(libsmb_samba_internal)
1652 {
1653         PyObject *m = NULL;
1654
1655         talloc_stackframe();
1656
1657         m = PyModule_Create(&moduledef);
1658         if (m == NULL) {
1659                 return m;
1660         }
1661         if (PyType_Ready(&py_cli_state_type) < 0) {
1662                 return NULL;
1663         }
1664         Py_INCREF(&py_cli_state_type);
1665         PyModule_AddObject(m, "Conn", (PyObject *)&py_cli_state_type);
1666
1667 #define ADD_FLAGS(val)  PyModule_AddObject(m, #val, PyLong_FromLong(val))
1668
1669         ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
1670         ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
1671         ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
1672         ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
1673         ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
1674         ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
1675         ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
1676         ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
1677         ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
1678         ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
1679         ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
1680         ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
1681         ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
1682         ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
1683         ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
1684         ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
1685
1686         return m;
1687 }