s3:pylibsmb: Add .set_acl API to SMB py bindings
[metze/samba/wip.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 "libcli/smb/smbXcli_base.h"
28 #include "libsmb/libsmb.h"
29 #include "libcli/security/security.h"
30 #include "system/select.h"
31 #include "source4/libcli/util/pyerrors.h"
32 #include "auth/credentials/pycredentials.h"
33 #include "trans2.h"
34 #include "libsmb/clirap.h"
35 #include "librpc/rpc/pyrpc_util.h"
36
37 #define LIST_ATTRIBUTE_MASK \
38         (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN)
39
40 #define SECINFO_DEFAULT_FLAGS \
41         (SECINFO_OWNER | SECINFO_GROUP | \
42          SECINFO_DACL | SECINFO_PROTECTED_DACL | SECINFO_UNPROTECTED_DACL | \
43          SECINFO_SACL | SECINFO_PROTECTED_SACL | SECINFO_UNPROTECTED_SACL)
44
45 static PyTypeObject *get_pytype(const char *module, const char *type)
46 {
47         PyObject *mod;
48         PyTypeObject *result;
49
50         mod = PyImport_ImportModule(module);
51         if (mod == NULL) {
52                 PyErr_Format(PyExc_RuntimeError,
53                              "Unable to import %s to check type %s",
54                              module, type);
55                 return NULL;
56         }
57         result = (PyTypeObject *)PyObject_GetAttrString(mod, type);
58         Py_DECREF(mod);
59         if (result == NULL) {
60                 PyErr_Format(PyExc_RuntimeError,
61                              "Unable to find type %s in module %s",
62                              module, type);
63                 return NULL;
64         }
65         return result;
66 }
67
68 /*
69  * We're using "const char * const *" for keywords,
70  * PyArg_ParseTupleAndKeywords expects a "char **". Confine the
71  * inevitable warnings to just one place.
72  */
73 static int ParseTupleAndKeywords(PyObject *args, PyObject *kw,
74                                  const char *format, const char * const *keywords,
75                                  ...)
76 {
77         char **_keywords = discard_const_p(char *, keywords);
78         va_list a;
79         int ret;
80         va_start(a, keywords);
81         ret = PyArg_VaParseTupleAndKeywords(args, kw, format,
82                                             _keywords, a);
83         va_end(a);
84         return ret;
85 }
86
87 struct py_cli_thread;
88
89 struct py_cli_oplock_break {
90         uint16_t fnum;
91         uint8_t level;
92 };
93
94 struct py_cli_state {
95         PyObject_HEAD
96         struct cli_state *cli;
97         bool is_smb1;
98         struct tevent_context *ev;
99         int (*req_wait_fn)(struct tevent_context *ev,
100                            struct tevent_req *req);
101         struct py_cli_thread *thread_state;
102
103         struct tevent_req *oplock_waiter;
104         struct py_cli_oplock_break *oplock_breaks;
105         struct py_tevent_cond *oplock_cond;
106 };
107
108 #ifdef HAVE_PTHREAD
109
110 #include <pthread.h>
111
112 struct py_cli_thread {
113
114         /*
115          * Pipe to make the poll thread wake up in our destructor, so
116          * that we can exit and join the thread.
117          */
118         int shutdown_pipe[2];
119         struct tevent_fd *shutdown_fde;
120         bool do_shutdown;
121         pthread_t id;
122
123         /*
124          * Thread state to release the GIL during the poll(2) syscall
125          */
126         PyThreadState *py_threadstate;
127 };
128
129 static void *py_cli_state_poll_thread(void *private_data)
130 {
131         struct py_cli_state *self = (struct py_cli_state *)private_data;
132         struct py_cli_thread *t = self->thread_state;
133         PyGILState_STATE gstate;
134
135         gstate = PyGILState_Ensure();
136
137         while (!t->do_shutdown) {
138                 int ret;
139                 ret = tevent_loop_once(self->ev);
140                 assert(ret == 0);
141         }
142         PyGILState_Release(gstate);
143         return NULL;
144 }
145
146 static void py_cli_state_trace_callback(enum tevent_trace_point point,
147                                         void *private_data)
148 {
149         struct py_cli_state *self = (struct py_cli_state *)private_data;
150         struct py_cli_thread *t = self->thread_state;
151
152         switch(point) {
153         case TEVENT_TRACE_BEFORE_WAIT:
154                 assert(t->py_threadstate == NULL);
155                 t->py_threadstate = PyEval_SaveThread();
156                 break;
157         case TEVENT_TRACE_AFTER_WAIT:
158                 assert(t->py_threadstate != NULL);
159                 PyEval_RestoreThread(t->py_threadstate);
160                 t->py_threadstate = NULL;
161                 break;
162         default:
163                 break;
164         }
165 }
166
167 static void py_cli_state_shutdown_handler(struct tevent_context *ev,
168                                           struct tevent_fd *fde,
169                                           uint16_t flags,
170                                           void *private_data)
171 {
172         struct py_cli_state *self = (struct py_cli_state *)private_data;
173         struct py_cli_thread *t = self->thread_state;
174
175         if ((flags & TEVENT_FD_READ) == 0) {
176                 return;
177         }
178         TALLOC_FREE(t->shutdown_fde);
179         t->do_shutdown = true;
180 }
181
182 static int py_cli_thread_destructor(struct py_cli_thread *t)
183 {
184         char c = 0;
185         ssize_t written;
186         int ret;
187
188         do {
189                 /*
190                  * This will wake the poll thread from the poll(2)
191                  */
192                 written = write(t->shutdown_pipe[1], &c, 1);
193         } while ((written == -1) && (errno == EINTR));
194
195         /*
196          * Allow the poll thread to do its own cleanup under the GIL
197          */
198         Py_BEGIN_ALLOW_THREADS
199         ret = pthread_join(t->id, NULL);
200         Py_END_ALLOW_THREADS
201         assert(ret == 0);
202
203         if (t->shutdown_pipe[0] != -1) {
204                 close(t->shutdown_pipe[0]);
205                 t->shutdown_pipe[0] = -1;
206         }
207         if (t->shutdown_pipe[1] != -1) {
208                 close(t->shutdown_pipe[1]);
209                 t->shutdown_pipe[1] = -1;
210         }
211         return 0;
212 }
213
214 static int py_tevent_cond_req_wait(struct tevent_context *ev,
215                                    struct tevent_req *req);
216
217 static bool py_cli_state_setup_mt_ev(struct py_cli_state *self)
218 {
219         struct py_cli_thread *t = NULL;
220         int ret;
221
222         self->ev = tevent_context_init_byname(NULL, "poll_mt");
223         if (self->ev == NULL) {
224                 goto fail;
225         }
226         samba_tevent_set_debug(self->ev, "pylibsmb_tevent_mt");
227         tevent_set_trace_callback(self->ev, py_cli_state_trace_callback, self);
228
229         self->req_wait_fn = py_tevent_cond_req_wait;
230
231         self->thread_state = talloc_zero(NULL, struct py_cli_thread);
232         if (self->thread_state == NULL) {
233                 goto fail;
234         }
235         t = self->thread_state;
236
237         ret = pipe(t->shutdown_pipe);
238         if (ret == -1) {
239                 goto fail;
240         }
241         t->shutdown_fde = tevent_add_fd(
242                 self->ev, self->ev, t->shutdown_pipe[0], TEVENT_FD_READ,
243                 py_cli_state_shutdown_handler, self);
244         if (t->shutdown_fde == NULL) {
245                 goto fail;
246         }
247
248         PyEval_InitThreads();
249
250         ret = pthread_create(&t->id, NULL, py_cli_state_poll_thread, self);
251         if (ret != 0) {
252                 goto fail;
253         }
254         talloc_set_destructor(self->thread_state, py_cli_thread_destructor);
255         return true;
256
257 fail:
258         if (t != NULL) {
259                 TALLOC_FREE(t->shutdown_fde);
260
261                 if (t->shutdown_pipe[0] != -1) {
262                         close(t->shutdown_pipe[0]);
263                         t->shutdown_pipe[0] = -1;
264                 }
265                 if (t->shutdown_pipe[1] != -1) {
266                         close(t->shutdown_pipe[1]);
267                         t->shutdown_pipe[1] = -1;
268                 }
269         }
270
271         TALLOC_FREE(self->thread_state);
272         TALLOC_FREE(self->ev);
273         return false;
274 }
275
276 struct py_tevent_cond {
277         pthread_mutex_t mutex;
278         pthread_cond_t cond;
279         bool is_done;
280 };
281
282 static void py_tevent_signalme(struct tevent_req *req);
283
284 static int py_tevent_cond_wait(struct py_tevent_cond *cond)
285 {
286         int ret, result;
287
288         result = pthread_mutex_init(&cond->mutex, NULL);
289         if (result != 0) {
290                 goto fail;
291         }
292         result = pthread_cond_init(&cond->cond, NULL);
293         if (result != 0) {
294                 goto fail_mutex;
295         }
296
297         result = pthread_mutex_lock(&cond->mutex);
298         if (result != 0) {
299                 goto fail_cond;
300         }
301
302         cond->is_done = false;
303
304         while (!cond->is_done) {
305
306                 Py_BEGIN_ALLOW_THREADS
307                 result = pthread_cond_wait(&cond->cond, &cond->mutex);
308                 Py_END_ALLOW_THREADS
309
310                 if (result != 0) {
311                         goto fail_unlock;
312                 }
313         }
314
315 fail_unlock:
316         ret = pthread_mutex_unlock(&cond->mutex);
317         assert(ret == 0);
318 fail_cond:
319         ret = pthread_cond_destroy(&cond->cond);
320         assert(ret == 0);
321 fail_mutex:
322         ret = pthread_mutex_destroy(&cond->mutex);
323         assert(ret == 0);
324 fail:
325         return result;
326 }
327
328 static int py_tevent_cond_req_wait(struct tevent_context *ev,
329                                    struct tevent_req *req)
330 {
331         struct py_tevent_cond cond;
332         tevent_req_set_callback(req, py_tevent_signalme, &cond);
333         return py_tevent_cond_wait(&cond);
334 }
335
336 static void py_tevent_cond_signal(struct py_tevent_cond *cond)
337 {
338         int ret;
339
340         ret = pthread_mutex_lock(&cond->mutex);
341         assert(ret == 0);
342
343         cond->is_done = true;
344
345         ret = pthread_cond_signal(&cond->cond);
346         assert(ret == 0);
347         ret = pthread_mutex_unlock(&cond->mutex);
348         assert(ret == 0);
349 }
350
351 static void py_tevent_signalme(struct tevent_req *req)
352 {
353         struct py_tevent_cond *cond = (struct py_tevent_cond *)
354                 tevent_req_callback_data_void(req);
355
356         py_tevent_cond_signal(cond);
357 }
358
359 #endif
360
361 static int py_tevent_req_wait(struct tevent_context *ev,
362                               struct tevent_req *req);
363
364 static bool py_cli_state_setup_ev(struct py_cli_state *self)
365 {
366         self->ev = tevent_context_init(NULL);
367         if (self->ev == NULL) {
368                 return false;
369         }
370
371         samba_tevent_set_debug(self->ev, "pylibsmb_tevent");
372
373         self->req_wait_fn = py_tevent_req_wait;
374
375         return true;
376 }
377
378 static int py_tevent_req_wait(struct tevent_context *ev,
379                               struct tevent_req *req)
380 {
381         while (tevent_req_is_in_progress(req)) {
382                 int ret;
383
384                 ret = tevent_loop_once(ev);
385                 if (ret != 0) {
386                         return ret;
387                 }
388         }
389         return 0;
390 }
391
392 static bool py_tevent_req_wait_exc(struct py_cli_state *self,
393                                    struct tevent_req *req)
394 {
395         int ret;
396
397         if (req == NULL) {
398                 PyErr_NoMemory();
399                 return false;
400         }
401         ret = self->req_wait_fn(self->ev, req);
402         if (ret != 0) {
403                 TALLOC_FREE(req);
404                 errno = ret;
405                 PyErr_SetFromErrno(PyExc_RuntimeError);
406                 return false;
407         }
408         return true;
409 }
410
411 static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args,
412                                   PyObject *kwds)
413 {
414         struct py_cli_state *self;
415
416         self = (struct py_cli_state *)type->tp_alloc(type, 0);
417         if (self == NULL) {
418                 return NULL;
419         }
420         self->cli = NULL;
421         self->is_smb1 = false;
422         self->ev = NULL;
423         self->thread_state = NULL;
424         self->oplock_waiter = NULL;
425         self->oplock_cond = NULL;
426         self->oplock_breaks = NULL;
427         return (PyObject *)self;
428 }
429
430 static void py_cli_got_oplock_break(struct tevent_req *req);
431
432 static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
433                              PyObject *kwds)
434 {
435         NTSTATUS status;
436         char *host, *share;
437         PyObject *creds = NULL;
438         struct cli_credentials *cli_creds;
439         PyObject *py_lp = Py_None;
440         PyObject *py_multi_threaded = Py_False;
441         bool multi_threaded = false;
442         PyObject *py_sign = Py_False;
443         bool sign = false;
444         int signing_state = SMB_SIGNING_DEFAULT;
445         PyObject *py_force_smb1 = Py_False;
446         bool force_smb1 = false;
447         struct tevent_req *req;
448         bool ret;
449         int flags = 0;
450
451         static const char *kwlist[] = {
452                 "host", "share", "lp", "creds",
453                 "multi_threaded", "sign", "force_smb1",
454                 NULL
455         };
456
457         PyTypeObject *py_type_Credentials = get_pytype(
458                 "samba.credentials", "Credentials");
459         if (py_type_Credentials == NULL) {
460                 return -1;
461         }
462
463         ret = ParseTupleAndKeywords(
464                 args, kwds, "ssO|O!OOO", kwlist,
465                 &host, &share, &py_lp,
466                 py_type_Credentials, &creds,
467                 &py_multi_threaded,
468                 &py_sign,
469                 &py_force_smb1);
470
471         Py_DECREF(py_type_Credentials);
472
473         if (!ret) {
474                 return -1;
475         }
476
477         multi_threaded = PyObject_IsTrue(py_multi_threaded);
478         sign = PyObject_IsTrue(py_sign);
479         force_smb1 = PyObject_IsTrue(py_force_smb1);
480
481         if (sign) {
482                 signing_state = SMB_SIGNING_REQUIRED;
483         }
484
485         if (force_smb1) {
486                 /*
487                  * As most of the cli_*_send() function
488                  * don't support SMB2 (it's only plugged
489                  * into the sync wrapper functions currently)
490                  * we have a way to force SMB1.
491                  */
492                 flags = CLI_FULL_CONNECTION_FORCE_SMB1;
493         }
494
495         if (multi_threaded) {
496 #ifdef HAVE_PTHREAD
497                 ret = py_cli_state_setup_mt_ev(self);
498                 if (!ret) {
499                         return -1;
500                 }
501 #else
502                 PyErr_SetString(PyExc_RuntimeError,
503                                 "No PTHREAD support available");
504                 return -1;
505 #endif
506                 if (!force_smb1) {
507                         PyErr_SetString(PyExc_RuntimeError,
508                                         "multi_threaded is only possible on "
509                                         "SMB1 connections");
510                         return -1;
511                 }
512         } else {
513                 ret = py_cli_state_setup_ev(self);
514                 if (!ret) {
515                         return -1;
516                 }
517         }
518
519         if (creds == NULL) {
520                 cli_creds = cli_credentials_init_anon(NULL);
521         } else {
522                 cli_creds = PyCredentials_AsCliCredentials(creds);
523         }
524
525         req = cli_full_connection_creds_send(
526                 NULL, self->ev, "myname", host, NULL, 0, share, "?????",
527                 cli_creds, flags, signing_state);
528         if (!py_tevent_req_wait_exc(self, req)) {
529                 return -1;
530         }
531         status = cli_full_connection_creds_recv(req, &self->cli);
532         TALLOC_FREE(req);
533
534         if (!NT_STATUS_IS_OK(status)) {
535                 PyErr_SetNTSTATUS(status);
536                 return -1;
537         }
538
539         if (smbXcli_conn_protocol(self->cli->conn) < PROTOCOL_SMB2_02) {
540                 self->is_smb1 = true;
541         }
542
543         /*
544          * Oplocks require a multi threaded connection
545          */
546         if (self->thread_state == NULL) {
547                 return 0;
548         }
549
550         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
551                 self->ev, self->ev, self->cli);
552         if (self->oplock_waiter == NULL) {
553                 PyErr_NoMemory();
554                 return -1;
555         }
556         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
557                                 self);
558         return 0;
559 }
560
561 static void py_cli_got_oplock_break(struct tevent_req *req)
562 {
563         struct py_cli_state *self = (struct py_cli_state *)
564                 tevent_req_callback_data_void(req);
565         struct py_cli_oplock_break b;
566         struct py_cli_oplock_break *tmp;
567         size_t num_breaks;
568         NTSTATUS status;
569
570         status = cli_smb_oplock_break_waiter_recv(req, &b.fnum, &b.level);
571         TALLOC_FREE(req);
572         self->oplock_waiter = NULL;
573
574         if (!NT_STATUS_IS_OK(status)) {
575                 return;
576         }
577
578         num_breaks = talloc_array_length(self->oplock_breaks);
579         tmp = talloc_realloc(self->ev, self->oplock_breaks,
580                              struct py_cli_oplock_break, num_breaks+1);
581         if (tmp == NULL) {
582                 return;
583         }
584         self->oplock_breaks = tmp;
585         self->oplock_breaks[num_breaks] = b;
586
587         if (self->oplock_cond != NULL) {
588                 py_tevent_cond_signal(self->oplock_cond);
589         }
590
591         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
592                 self->ev, self->ev, self->cli);
593         if (self->oplock_waiter == NULL) {
594                 return;
595         }
596         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
597                                 self);
598 }
599
600 static PyObject *py_cli_get_oplock_break(struct py_cli_state *self,
601                                          PyObject *args)
602 {
603         size_t num_oplock_breaks;
604
605         if (!PyArg_ParseTuple(args, "")) {
606                 return NULL;
607         }
608
609         if (self->thread_state == NULL) {
610                 PyErr_SetString(PyExc_RuntimeError,
611                                 "get_oplock_break() only possible on "
612                                 "a multi_threaded connection");
613                 return NULL;
614         }
615
616         if (self->oplock_cond != NULL) {
617                 errno = EBUSY;
618                 PyErr_SetFromErrno(PyExc_RuntimeError);
619                 return NULL;
620         }
621
622         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
623
624         if (num_oplock_breaks == 0) {
625                 struct py_tevent_cond cond;
626                 int ret;
627
628                 self->oplock_cond = &cond;
629                 ret = py_tevent_cond_wait(&cond);
630                 self->oplock_cond = NULL;
631
632                 if (ret != 0) {
633                         errno = ret;
634                         PyErr_SetFromErrno(PyExc_RuntimeError);
635                         return NULL;
636                 }
637         }
638
639         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
640         if (num_oplock_breaks > 0) {
641                 PyObject *result;
642
643                 result = Py_BuildValue(
644                         "{s:i,s:i}",
645                         "fnum", self->oplock_breaks[0].fnum,
646                         "level", self->oplock_breaks[0].level);
647
648                 memmove(&self->oplock_breaks[0], &self->oplock_breaks[1],
649                         sizeof(self->oplock_breaks[0]) *
650                         (num_oplock_breaks - 1));
651                 self->oplock_breaks = talloc_realloc(
652                         NULL, self->oplock_breaks, struct py_cli_oplock_break,
653                         num_oplock_breaks - 1);
654
655                 return result;
656         }
657         Py_RETURN_NONE;
658 }
659
660 static void py_cli_state_dealloc(struct py_cli_state *self)
661 {
662         TALLOC_FREE(self->thread_state);
663         TALLOC_FREE(self->oplock_waiter);
664         TALLOC_FREE(self->ev);
665
666         if (self->cli != NULL) {
667                 cli_shutdown(self->cli);
668                 self->cli = NULL;
669         }
670         Py_TYPE(self)->tp_free((PyObject *)self);
671 }
672
673 static PyObject *py_cli_settimeout(struct py_cli_state *self, PyObject *args)
674 {
675         unsigned int nmsecs = 0;
676         unsigned int omsecs = 0;
677
678         if (!PyArg_ParseTuple(args, "I", &nmsecs)) {
679                 return NULL;
680         }
681
682         omsecs = cli_set_timeout(self->cli, nmsecs);
683
684         return PyInt_FromLong(omsecs);
685 }
686
687 static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
688                                PyObject *kwds)
689 {
690         char *fname;
691         unsigned CreateFlags = 0;
692         unsigned DesiredAccess = FILE_GENERIC_READ;
693         unsigned FileAttributes = 0;
694         unsigned ShareAccess = 0;
695         unsigned CreateDisposition = FILE_OPEN;
696         unsigned CreateOptions = 0;
697         unsigned ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
698         unsigned SecurityFlags = 0;
699         uint16_t fnum;
700         struct tevent_req *req;
701         NTSTATUS status;
702
703         static const char *kwlist[] = {
704                 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
705                 "ShareAccess", "CreateDisposition", "CreateOptions",
706                 "ImpersonationLevel", "SecurityFlags", NULL };
707
708         if (!ParseTupleAndKeywords(
709                     args, kwds, "s|IIIIIIII", kwlist,
710                     &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
711                     &ShareAccess, &CreateDisposition, &CreateOptions,
712                     &ImpersonationLevel, &SecurityFlags)) {
713                 return NULL;
714         }
715
716         req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
717                                 DesiredAccess, FileAttributes, ShareAccess,
718                                 CreateDisposition, CreateOptions,
719                                 ImpersonationLevel, SecurityFlags);
720         if (!py_tevent_req_wait_exc(self, req)) {
721                 return NULL;
722         }
723         status = cli_ntcreate_recv(req, &fnum, NULL);
724         TALLOC_FREE(req);
725
726         if (!NT_STATUS_IS_OK(status)) {
727                 PyErr_SetNTSTATUS(status);
728                 return NULL;
729         }
730         return Py_BuildValue("I", (unsigned)fnum);
731 }
732
733 static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
734 {
735         struct tevent_req *req;
736         int fnum;
737         NTSTATUS status;
738
739         if (!PyArg_ParseTuple(args, "i", &fnum)) {
740                 return NULL;
741         }
742
743         req = cli_close_send(NULL, self->ev, self->cli, fnum);
744         if (!py_tevent_req_wait_exc(self, req)) {
745                 return NULL;
746         }
747         status = cli_close_recv(req);
748         TALLOC_FREE(req);
749
750         if (!NT_STATUS_IS_OK(status)) {
751                 PyErr_SetNTSTATUS(status);
752                 return NULL;
753         }
754         Py_RETURN_NONE;
755 }
756
757 struct push_state {
758         char *data;
759         off_t nread;
760         off_t total_data;
761 };
762
763 /*
764  * cli_push() helper to write a chunk of data to a remote file
765  */
766 static size_t push_data(uint8_t *buf, size_t n, void *priv)
767 {
768         struct push_state *state = (struct push_state *)priv;
769         char *curr_ptr = NULL;
770         off_t remaining;
771         size_t copied_bytes;
772
773         if (state->nread >= state->total_data) {
774                 return 0;
775         }
776
777         curr_ptr = state->data + state->nread;
778         remaining = state->total_data - state->nread;
779         copied_bytes = MIN(remaining, n);
780
781         memcpy(buf, curr_ptr, copied_bytes);
782         state->nread += copied_bytes;
783         return copied_bytes;
784 }
785
786 /*
787  * Writes a file with the contents specified
788  */
789 static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args,
790                                  PyObject *kwargs)
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                                  PyObject *kwargs)
915 {
916         NTSTATUS status;
917         const char *filename = NULL;
918         struct tevent_req *req = NULL;
919         uint16_t fnum;
920         off_t size;
921         char *buf = NULL;
922         off_t nread = 0;
923         PyObject *result = NULL;
924
925         if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
926                 return NULL;
927         }
928
929         /* get a read file handle */
930         req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
931                                 FILE_READ_DATA, 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         int ret;
1127
1128         /* suppress '.' and '..' in the results we return */
1129         if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
1130                 return NT_STATUS_OK;
1131         }
1132
1133         /*
1134          * Build a dictionary representing the file info.
1135          * Note: Windows does not always return short_name (so it may be None)
1136          */
1137         file = Py_BuildValue("{s:s,s:i,s:s,s:O,s:l}",
1138                              "name", finfo->name,
1139                              "attrib", (int)finfo->mode,
1140                              "short_name", finfo->short_name,
1141                              "size", PyLong_FromUnsignedLongLong(finfo->size),
1142                              "mtime",
1143                              convert_timespec_to_time_t(finfo->mtime_ts));
1144
1145         if (file == NULL) {
1146                 return NT_STATUS_NO_MEMORY;
1147         }
1148
1149         ret = PyList_Append(result, file);
1150         if (ret == -1) {
1151                 return NT_STATUS_INTERNAL_ERROR;
1152         }
1153
1154         return NT_STATUS_OK;
1155 }
1156
1157 static NTSTATUS do_listing(struct py_cli_state *self,
1158                            const char *base_dir, const char *user_mask,
1159                            uint16_t attribute,
1160                            NTSTATUS (*callback_fn)(const char *,
1161                                                    struct file_info *,
1162                                                    const char *, void *),
1163                            void *priv)
1164 {
1165         char *mask = NULL;
1166         unsigned int info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
1167         struct file_info *finfos = NULL;
1168         size_t i;
1169         size_t num_finfos = 0;
1170         NTSTATUS status;
1171
1172         if (user_mask == NULL) {
1173                 mask = talloc_asprintf(NULL, "%s\\*", base_dir);
1174         } else {
1175                 mask = talloc_asprintf(NULL, "%s\\%s", base_dir, user_mask);
1176         }
1177
1178         if (mask == NULL) {
1179                 return NT_STATUS_NO_MEMORY;
1180         }
1181         dos_format(mask);
1182
1183         if (self->is_smb1) {
1184                 struct tevent_req *req = NULL;
1185
1186                 req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
1187                                     info_level);
1188                 if (!py_tevent_req_wait_exc(self, req)) {
1189                         return NT_STATUS_INTERNAL_ERROR;
1190                 }
1191                 status = cli_list_recv(req, NULL, &finfos, &num_finfos);
1192                 TALLOC_FREE(req);
1193         } else {
1194                 status = cli_list(self->cli, mask, attribute, callback_fn,
1195                                   priv);
1196         }
1197         TALLOC_FREE(mask);
1198
1199         if (!NT_STATUS_IS_OK(status)) {
1200                 return status;
1201         }
1202
1203         /* invoke the callback for the async results (SMBv1 connections) */
1204         for (i = 0; i < num_finfos; i++) {
1205                 status = callback_fn(base_dir, &finfos[i], user_mask,
1206                                      priv);
1207                 if (!NT_STATUS_IS_OK(status)) {
1208                         TALLOC_FREE(finfos);
1209                         return status;
1210                 }
1211         }
1212
1213         TALLOC_FREE(finfos);
1214         return status;
1215 }
1216
1217 static PyObject *py_cli_list(struct py_cli_state *self,
1218                              PyObject *args,
1219                              PyObject *kwds)
1220 {
1221         char *base_dir;
1222         char *user_mask = NULL;
1223         unsigned int attribute = LIST_ATTRIBUTE_MASK;
1224         NTSTATUS status;
1225         PyObject *result;
1226         const char *kwlist[] = { "directory", "mask", "attribs", NULL };
1227
1228         if (!ParseTupleAndKeywords(args, kwds, "z|sH:list", kwlist,
1229                                    &base_dir, &user_mask, &attribute)) {
1230                 return NULL;
1231         }
1232
1233         result = Py_BuildValue("[]");
1234         if (result == NULL) {
1235                 return NULL;
1236         }
1237
1238         status = do_listing(self, base_dir, user_mask, attribute,
1239                             list_helper, result);
1240
1241         if (!NT_STATUS_IS_OK(status)) {
1242                 Py_XDECREF(result);
1243                 PyErr_SetNTSTATUS(status);
1244                 return NULL;
1245         }
1246
1247         return result;
1248 }
1249
1250 /*
1251  * Deletes a file
1252  */
1253 static NTSTATUS unlink_file(struct py_cli_state *self, const char *filename)
1254 {
1255         NTSTATUS status;
1256         uint16_t attrs = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
1257
1258         if (self->is_smb1) {
1259                 struct tevent_req *req = NULL;
1260
1261                 req = cli_unlink_send(NULL, self->ev, self->cli, filename,
1262                                       attrs);
1263                 if (!py_tevent_req_wait_exc(self, req)) {
1264                         return NT_STATUS_INTERNAL_ERROR;
1265                 }
1266                 status = cli_unlink_recv(req);
1267                 TALLOC_FREE(req);
1268         } else {
1269                 status = cli_unlink(self->cli, filename, attrs);
1270         }
1271
1272         return status;
1273 }
1274
1275 static PyObject *py_smb_unlink(struct py_cli_state *self, PyObject *args)
1276 {
1277         NTSTATUS status;
1278         const char *filename = NULL;
1279
1280         if (!PyArg_ParseTuple(args, "s:unlink", &filename)) {
1281                 return NULL;
1282         }
1283
1284         status = unlink_file(self, filename);
1285         PyErr_NTSTATUS_NOT_OK_RAISE(status);
1286
1287         Py_RETURN_NONE;
1288 }
1289
1290 /*
1291  * Delete an empty directory
1292  */
1293 static NTSTATUS remove_dir(struct py_cli_state *self, const char *dirname)
1294 {
1295         NTSTATUS status;
1296
1297         if (self->is_smb1) {
1298                 struct tevent_req *req = NULL;
1299
1300                 req = cli_rmdir_send(NULL, self->ev, self->cli, dirname);
1301                 if (!py_tevent_req_wait_exc(self, req)) {
1302                         return NT_STATUS_INTERNAL_ERROR;
1303                 }
1304                 status = cli_rmdir_recv(req);
1305                 TALLOC_FREE(req);
1306         } else {
1307                 status = cli_rmdir(self->cli, dirname);
1308         }
1309         return status;
1310 }
1311
1312 static PyObject *py_smb_rmdir(struct py_cli_state *self, PyObject *args)
1313 {
1314         NTSTATUS status;
1315         const char *dirname;
1316
1317         if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
1318                 return NULL;
1319         }
1320
1321         status = remove_dir(self, dirname);
1322         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1323
1324         Py_RETURN_NONE;
1325 }
1326
1327 /*
1328  * Create a directory
1329  */
1330 static PyObject *py_smb_mkdir(struct py_cli_state *self, PyObject *args)
1331 {
1332         NTSTATUS status;
1333         const char *dirname;
1334
1335         if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
1336                 return NULL;
1337         }
1338
1339         if (self->is_smb1) {
1340                 struct tevent_req *req = NULL;
1341
1342                 req = cli_mkdir_send(NULL, self->ev, self->cli, dirname);
1343                 if (!py_tevent_req_wait_exc(self, req)) {
1344                         return NULL;
1345                 }
1346                 status = cli_mkdir_recv(req);
1347                 TALLOC_FREE(req);
1348         } else {
1349                 status = cli_mkdir(self->cli, dirname);
1350         }
1351         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1352
1353         Py_RETURN_NONE;
1354 }
1355
1356 /*
1357  * Checks existence of a directory
1358  */
1359 static bool check_dir_path(struct py_cli_state *self, const char *path)
1360 {
1361         NTSTATUS status;
1362
1363         if (self->is_smb1) {
1364                 struct tevent_req *req = NULL;
1365
1366                 req = cli_chkpath_send(NULL, self->ev, self->cli, path);
1367                 if (!py_tevent_req_wait_exc(self, req)) {
1368                         return false;
1369                 }
1370                 status = cli_chkpath_recv(req);
1371                 TALLOC_FREE(req);
1372         } else {
1373                 status = cli_chkpath(self->cli, path);
1374         }
1375
1376         return NT_STATUS_IS_OK(status);
1377 }
1378
1379 static PyObject *py_smb_chkpath(struct py_cli_state *self, PyObject *args)
1380 {
1381         const char *path;
1382         bool dir_exists;
1383
1384         if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
1385                 return NULL;
1386         }
1387
1388         dir_exists = check_dir_path(self, path);
1389         return PyBool_FromLong(dir_exists);
1390 }
1391
1392 struct deltree_state {
1393         struct py_cli_state *self;
1394         const char *full_dirpath;
1395 };
1396
1397 static NTSTATUS delete_dir_tree(struct py_cli_state *self,
1398                                 const char *dirpath);
1399
1400 /*
1401  * Deletes a single item in the directory tree. This could be either a file
1402  * or a directory. This function gets invoked as a callback for every item in
1403  * the given directory's listings.
1404  */
1405 static NTSTATUS delete_tree_callback(const char *mntpoint,
1406                                      struct file_info *finfo,
1407                                      const char *mask, void *priv)
1408 {
1409         char *filepath = NULL;
1410         struct deltree_state *state = priv;
1411         NTSTATUS status;
1412
1413         /* skip '.' or '..' directory listings */
1414         if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
1415                 return NT_STATUS_OK;
1416         }
1417
1418         /* get the absolute filepath */
1419         filepath = talloc_asprintf(NULL, "%s\\%s", state->full_dirpath,
1420                                    finfo->name);
1421         if (filepath == NULL) {
1422                 return NT_STATUS_NO_MEMORY;
1423         }
1424
1425         if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) {
1426
1427                 /* recursively delete the sub-directory and its contents */
1428                 status = delete_dir_tree(state->self, filepath);
1429         } else {
1430                 status = unlink_file(state->self, filepath);
1431         }
1432
1433         TALLOC_FREE(filepath);
1434         return status;
1435 }
1436
1437 /*
1438  * Removes a directory and all its contents
1439  */
1440 static NTSTATUS delete_dir_tree(struct py_cli_state *self,
1441                                 const char *filepath)
1442 {
1443         NTSTATUS status;
1444         const char *mask = "*";
1445         struct deltree_state state = { 0 };
1446
1447         /* go through the directory's contents, deleting each item */
1448         state.self = self;
1449         state.full_dirpath = filepath;
1450         status = do_listing(self, filepath, mask, LIST_ATTRIBUTE_MASK,
1451                             delete_tree_callback, &state);
1452
1453         /* remove the directory itself */
1454         if (NT_STATUS_IS_OK(status)) {
1455                 status = remove_dir(self, filepath);
1456         }
1457         return status;
1458 }
1459
1460 static PyObject *py_smb_deltree(struct py_cli_state *self, PyObject *args)
1461 {
1462         NTSTATUS status;
1463         const char *filepath = NULL;
1464         bool dir_exists;
1465
1466         if (!PyArg_ParseTuple(args, "s:deltree", &filepath)) {
1467                 return NULL;
1468         }
1469
1470         /* check whether we're removing a directory or a file */
1471         dir_exists = check_dir_path(self, filepath);
1472
1473         if (dir_exists) {
1474                 status = delete_dir_tree(self, filepath);
1475         } else {
1476                 status = unlink_file(self, filepath);
1477         }
1478
1479         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1480
1481         Py_RETURN_NONE;
1482 }
1483
1484 /*
1485  * Read ACL on a given file/directory as a security descriptor object
1486  */
1487 static PyObject *py_smb_getacl(struct py_cli_state *self, PyObject *args)
1488 {
1489         NTSTATUS status;
1490         const char *filename = NULL;
1491         unsigned int sinfo = SECINFO_DEFAULT_FLAGS;
1492         unsigned int access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
1493         uint16_t fnum;
1494         struct security_descriptor *sd = NULL;
1495
1496         /* there's no async version of cli_query_security_descriptor() */
1497         if (self->thread_state != NULL) {
1498                 PyErr_SetString(PyExc_RuntimeError,
1499                                 "get_acl() is not supported on "
1500                                 "a multi_threaded connection");
1501                 return NULL;
1502         }
1503
1504         if (!PyArg_ParseTuple(args, "s|II:get_acl", &filename, &sinfo,
1505                               &access_mask)) {
1506                 return NULL;
1507         }
1508
1509         /* get a file handle with the desired access */
1510         status = cli_ntcreate(self->cli, filename, 0, access_mask, 0,
1511                               FILE_SHARE_READ|FILE_SHARE_WRITE,
1512                               FILE_OPEN, 0x0, 0x0, &fnum, NULL);
1513         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1514
1515         /* query the security descriptor for this file */
1516         status = cli_query_security_descriptor(self->cli, fnum, sinfo,
1517                                                NULL, &sd);
1518         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1519
1520         /* close the file handle and convert the SD to a python struct */
1521         status = cli_close(self->cli, fnum);
1522         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1523
1524         return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
1525                                     sd, sd);
1526 }
1527
1528 /*
1529  * Set ACL on file/directory using given security descriptor object
1530  */
1531 static PyObject *py_smb_setacl(struct py_cli_state *self, PyObject *args)
1532 {
1533         NTSTATUS status;
1534         char *filename = NULL;
1535         PyObject *py_sd = NULL;
1536         struct security_descriptor *sd = NULL;
1537         unsigned int sinfo = SECINFO_DEFAULT_FLAGS;
1538         uint16_t fnum;
1539
1540         /* there's no async version of cli_set_security_descriptor() */
1541         if (self->thread_state != NULL) {
1542                 PyErr_SetString(PyExc_RuntimeError,
1543                                 "set_acl() is not supported on "
1544                                 "a multi_threaded connection");
1545                 return NULL;
1546         }
1547
1548         if (!PyArg_ParseTuple(args, "sO|I:set_acl", &filename, &py_sd,
1549                               &sinfo)) {
1550                 return NULL;
1551         }
1552
1553         sd = pytalloc_get_type(py_sd, struct security_descriptor);
1554         if (!sd) {
1555                 PyErr_Format(PyExc_TypeError,
1556                         "Expected dcerpc.security.descriptor as argument, got %s",
1557                         talloc_get_name(pytalloc_get_ptr(py_sd)));
1558                 return NULL;
1559         }
1560
1561         status = cli_ntcreate(self->cli, filename, 0,
1562                               SEC_FLAG_MAXIMUM_ALLOWED, 0,
1563                               FILE_SHARE_READ|FILE_SHARE_WRITE,
1564                               FILE_OPEN, 0x0, 0x0, &fnum, NULL);
1565         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1566
1567         status = cli_set_security_descriptor(self->cli, fnum, sinfo, sd);
1568         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1569
1570         status = cli_close(self->cli, fnum);
1571         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1572
1573         Py_RETURN_NONE;
1574 }
1575
1576 static PyMethodDef py_cli_state_methods[] = {
1577         { "settimeout", (PyCFunction)py_cli_settimeout, METH_VARARGS,
1578           "settimeout(new_timeout_msecs) => return old_timeout_msecs" },
1579         { "create", (PyCFunction)py_cli_create, METH_VARARGS|METH_KEYWORDS,
1580           "Open a file" },
1581         { "close", (PyCFunction)py_cli_close, METH_VARARGS,
1582           "Close a file handle" },
1583         { "write", (PyCFunction)py_cli_write, METH_VARARGS|METH_KEYWORDS,
1584           "Write to a file handle" },
1585         { "read", (PyCFunction)py_cli_read, METH_VARARGS|METH_KEYWORDS,
1586           "Read from a file handle" },
1587         { "truncate", (PyCFunction)py_cli_ftruncate,
1588           METH_VARARGS|METH_KEYWORDS,
1589           "Truncate a file" },
1590         { "delete_on_close", (PyCFunction)py_cli_delete_on_close,
1591           METH_VARARGS|METH_KEYWORDS,
1592           "Set/Reset the delete on close flag" },
1593         { "list", (PyCFunction)py_cli_list, METH_VARARGS|METH_KEYWORDS,
1594           "list(directory, mask='*', attribs=DEFAULT_ATTRS) -> "
1595           "directory contents as a dictionary\n"
1596           "\t\tDEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | "
1597           "FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE\n\n"
1598           "\t\tList contents of a directory. The keys are, \n"
1599           "\t\t\tname: Long name of the directory item\n"
1600           "\t\t\tshort_name: Short name of the directory item\n"
1601           "\t\t\tsize: File size in bytes\n"
1602           "\t\t\tattrib: Attributes\n"
1603           "\t\t\tmtime: Modification time\n" },
1604         { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break,
1605           METH_VARARGS, "Wait for an oplock break" },
1606         { "unlink", (PyCFunction)py_smb_unlink,
1607           METH_VARARGS,
1608           "unlink(path) -> None\n\n \t\tDelete a file." },
1609         { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
1610           "mkdir(path) -> None\n\n \t\tCreate a directory." },
1611         { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
1612           "rmdir(path) -> None\n\n \t\tDelete a directory." },
1613         { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
1614           "chkpath(dir_path) -> True or False\n\n"
1615           "\t\tReturn true if directory exists, false otherwise." },
1616         { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
1617           "savefile(path, str) -> None\n\n"
1618           "\t\tWrite " PY_DESC_PY3_BYTES " str to file." },
1619         { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
1620           "loadfile(path) -> file contents as a " PY_DESC_PY3_BYTES
1621           "\n\n\t\tRead contents of a file." },
1622         { "deltree", (PyCFunction)py_smb_deltree, METH_VARARGS,
1623           "deltree(path) -> None\n\n"
1624           "\t\tDelete a directory and all its contents." },
1625         { "get_acl", (PyCFunction)py_smb_getacl, METH_VARARGS,
1626           "get_acl(path[, security_info=0]) -> security_descriptor object\n\n"
1627           "\t\tGet security descriptor for file." },
1628         { "set_acl", (PyCFunction)py_smb_setacl, METH_VARARGS,
1629           "set_acl(path, security_descriptor[, security_info=0]) -> None\n\n"
1630           "\t\tSet security descriptor for file." },
1631         { NULL, NULL, 0, NULL }
1632 };
1633
1634 static PyTypeObject py_cli_state_type = {
1635         PyVarObject_HEAD_INIT(NULL, 0)
1636         .tp_name = "libsmb_samba_internal.Conn",
1637         .tp_basicsize = sizeof(struct py_cli_state),
1638         .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1639         .tp_doc = "libsmb connection",
1640         .tp_new = py_cli_state_new,
1641         .tp_init = (initproc)py_cli_state_init,
1642         .tp_dealloc = (destructor)py_cli_state_dealloc,
1643         .tp_methods = py_cli_state_methods,
1644 };
1645
1646 static PyMethodDef py_libsmb_methods[] = {
1647         { NULL },
1648 };
1649
1650 void initlibsmb_samba_internal(void);
1651
1652 static struct PyModuleDef moduledef = {
1653     PyModuleDef_HEAD_INIT,
1654     .m_name = "libsmb_samba_internal",
1655     .m_doc = "libsmb wrapper",
1656     .m_size = -1,
1657     .m_methods = py_libsmb_methods,
1658 };
1659
1660 MODULE_INIT_FUNC(libsmb_samba_internal)
1661 {
1662         PyObject *m = NULL;
1663
1664         talloc_stackframe();
1665
1666         m = PyModule_Create(&moduledef);
1667         if (m == NULL) {
1668                 return m;
1669         }
1670         if (PyType_Ready(&py_cli_state_type) < 0) {
1671                 return NULL;
1672         }
1673         Py_INCREF(&py_cli_state_type);
1674         PyModule_AddObject(m, "Conn", (PyObject *)&py_cli_state_type);
1675
1676 #define ADD_FLAGS(val)  PyModule_AddObject(m, #val, PyInt_FromLong(val))
1677
1678         ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
1679         ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
1680         ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
1681         ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
1682         ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
1683         ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
1684         ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
1685         ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
1686         ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
1687         ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
1688         ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
1689         ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
1690         ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
1691         ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
1692         ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
1693         ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
1694
1695         return m;
1696 }