s3:libsmb fix a potential crash
[metze/samba/wip.git] / source3 / libsmb / pylibsmb.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Samba-internal work in progress Python binding for libsmbclient
4  *
5  * Copyright (C) Volker Lendecke 2012
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <Python.h>
22 #include "includes.h"
23 #include "libsmb/libsmb.h"
24 #include "libcli/security/security.h"
25 #include "system/select.h"
26 #include "source4/libcli/util/pyerrors.h"
27 #include "auth/credentials/pycredentials.h"
28
29 static PyTypeObject *get_pytype(const char *module, const char *type)
30 {
31         PyObject *mod;
32         PyTypeObject *result;
33
34         mod = PyImport_ImportModule(module);
35         if (mod == NULL) {
36                 PyErr_Format(PyExc_RuntimeError,
37                              "Unable to import %s to check type %s",
38                              module, type);
39                 return NULL;
40         }
41         result = (PyTypeObject *)PyObject_GetAttrString(mod, type);
42         Py_DECREF(mod);
43         if (result == NULL) {
44                 PyErr_Format(PyExc_RuntimeError,
45                              "Unable to find type %s in module %s",
46                              module, type);
47                 return NULL;
48         }
49         return result;
50 }
51
52 struct py_cli_thread;
53
54 struct py_cli_state {
55         PyObject_HEAD
56         struct cli_state *cli;
57         struct tevent_context *ev;
58         struct py_cli_thread *thread_state;
59 };
60
61 #if HAVE_PTHREAD
62
63 #include <pthread.h>
64
65 struct py_cli_thread {
66
67         /*
68          * Pipe to make the poll thread wake up in our destructor, so
69          * that we can exit and join the thread.
70          */
71         int shutdown_pipe[2];
72         struct tevent_fd *shutdown_fde;
73         bool do_shutdown;
74         pthread_t id;
75
76         /*
77          * Thread state to release the GIL during the poll(2) syscall
78          */
79         PyThreadState *py_threadstate;
80 };
81
82 static void *py_cli_state_poll_thread(void *private_data)
83 {
84         struct py_cli_state *self = (struct py_cli_state *)private_data;
85         struct py_cli_thread *t = self->thread_state;
86         PyGILState_STATE gstate;
87
88         gstate = PyGILState_Ensure();
89
90         while (!t->do_shutdown) {
91                 int ret;
92                 ret = tevent_loop_once(self->ev);
93                 assert(ret == 0);
94         }
95         PyGILState_Release(gstate);
96         return NULL;
97 }
98
99 static void py_cli_state_trace_callback(enum tevent_trace_point point,
100                                         void *private_data)
101 {
102         struct py_cli_state *self = (struct py_cli_state *)private_data;
103         struct py_cli_thread *t = self->thread_state;
104
105         switch(point) {
106         case TEVENT_TRACE_BEFORE_WAIT:
107                 assert(t->py_threadstate == NULL);
108                 t->py_threadstate = PyEval_SaveThread();
109                 break;
110         case TEVENT_TRACE_AFTER_WAIT:
111                 assert(t->py_threadstate != NULL);
112                 PyEval_RestoreThread(t->py_threadstate);
113                 t->py_threadstate = NULL;
114                 break;
115         default:
116                 break;
117         }
118 }
119
120 static void py_cli_state_shutdown_handler(struct tevent_context *ev,
121                                           struct tevent_fd *fde,
122                                           uint16_t flags,
123                                           void *private_data)
124 {
125         struct py_cli_state *self = (struct py_cli_state *)private_data;
126         struct py_cli_thread *t = self->thread_state;
127
128         if ((flags & TEVENT_FD_READ) == 0) {
129                 return;
130         }
131         TALLOC_FREE(t->shutdown_fde);
132         t->do_shutdown = true;
133 }
134
135 static int py_cli_thread_destructor(struct py_cli_thread *t)
136 {
137         char c = 0;
138         ssize_t written;
139         int ret;
140
141         do {
142                 /*
143                  * This will wake the poll thread from the poll(2)
144                  */
145                 written = write(t->shutdown_pipe[1], &c, 1);
146         } while ((written == -1) && (errno == EINTR));
147
148         /*
149          * Allow the poll thread to do its own cleanup under the GIL
150          */
151         Py_BEGIN_ALLOW_THREADS
152         ret = pthread_join(t->id, NULL);
153         Py_END_ALLOW_THREADS
154         assert(ret == 0);
155
156         if (t->shutdown_pipe[0] != -1) {
157                 close(t->shutdown_pipe[0]);
158                 t->shutdown_pipe[0] = -1;
159         }
160         if (t->shutdown_pipe[1] != -1) {
161                 close(t->shutdown_pipe[1]);
162                 t->shutdown_pipe[1] = -1;
163         }
164         return 0;
165 }
166
167 static bool py_cli_state_setup_ev(struct py_cli_state *self)
168 {
169         struct py_cli_thread *t = NULL;
170         int ret;
171
172         self->ev = tevent_context_init_byname(NULL, "poll_mt");
173         if (self->ev == NULL) {
174                 goto fail;
175         }
176         tevent_set_trace_callback(self->ev, py_cli_state_trace_callback, self);
177
178         self->thread_state = talloc_zero(NULL, struct py_cli_thread);
179         if (self->thread_state == NULL) {
180                 goto fail;
181         }
182         t = self->thread_state;
183
184         ret = pipe(t->shutdown_pipe);
185         if (ret == -1) {
186                 goto fail;
187         }
188         t->shutdown_fde = tevent_add_fd(
189                 self->ev, self->ev, t->shutdown_pipe[0], TEVENT_FD_READ,
190                 py_cli_state_shutdown_handler, self);
191         if (t->shutdown_fde == NULL) {
192                 goto fail;
193         }
194
195         PyEval_InitThreads();
196
197         ret = pthread_create(&t->id, NULL, py_cli_state_poll_thread, self);
198         if (ret != 0) {
199                 goto fail;
200         }
201         talloc_set_destructor(self->thread_state, py_cli_thread_destructor);
202         return true;
203
204 fail:
205         if (t != NULL) {
206                 TALLOC_FREE(t->shutdown_fde);
207
208                 if (t->shutdown_pipe[0] != -1) {
209                         close(t->shutdown_pipe[0]);
210                         t->shutdown_pipe[0] = -1;
211                 }
212                 if (t->shutdown_pipe[1] != -1) {
213                         close(t->shutdown_pipe[1]);
214                         t->shutdown_pipe[1] = -1;
215                 }
216                 TALLOC_FREE(t);
217         }
218
219         TALLOC_FREE(self->thread_state);
220         TALLOC_FREE(self->ev);
221         return false;
222 }
223
224 struct py_tevent_cond {
225         pthread_mutex_t mutex;
226         pthread_cond_t cond;
227         bool is_done;
228 };
229
230 static void py_tevent_signalme(struct tevent_req *req);
231
232 static int py_tevent_req_wait(struct tevent_context *ev,
233                               struct tevent_req *req)
234 {
235         struct py_tevent_cond cond;
236         int ret, result;
237
238         result = pthread_mutex_init(&cond.mutex, NULL);
239         if (result != 0) {
240                 goto fail;
241         }
242         result = pthread_cond_init(&cond.cond, NULL);
243         if (result != 0) {
244                 goto fail_mutex;
245         }
246
247         cond.is_done = false;
248         tevent_req_set_callback(req, py_tevent_signalme, &cond);
249
250         result = pthread_mutex_lock(&cond.mutex);
251         if (result != 0) {
252                 goto fail_cond;
253         }
254
255         while (!cond.is_done) {
256
257                 Py_BEGIN_ALLOW_THREADS
258                 result = pthread_cond_wait(&cond.cond, &cond.mutex);
259                 Py_END_ALLOW_THREADS
260
261                 if (result != 0) {
262                         goto fail_unlock;
263                 }
264         }
265
266 fail_unlock:
267         ret = pthread_mutex_unlock(&cond.mutex);
268         assert(ret == 0);
269 fail_cond:
270         ret = pthread_cond_destroy(&cond.cond);
271         assert(ret == 0);
272 fail_mutex:
273         ret = pthread_mutex_destroy(&cond.mutex);
274         assert(ret == 0);
275 fail:
276         return result;
277 }
278
279 static void py_tevent_signalme(struct tevent_req *req)
280 {
281         struct py_tevent_cond *cond = (struct py_tevent_cond *)
282                 tevent_req_callback_data_void(req);
283         int ret;
284
285         ret = pthread_mutex_lock(&cond->mutex);
286         assert(ret == 0);
287
288         cond->is_done = true;
289
290         ret = pthread_cond_signal(&cond->cond);
291         assert(ret == 0);
292         ret = pthread_mutex_unlock(&cond->mutex);
293         assert(ret == 0);
294 }
295
296 #else
297
298 static bool py_cli_state_setup_ev(struct py_cli_state *self)
299 {
300         self->ev = tevent_context_init(NULL);
301         return (self->ev != NULL);
302 }
303
304 static int py_tevent_req_wait(struct tevent_context *ev,
305                               struct tevent_req *req)
306 {
307         while (tevent_req_is_in_progress(req)) {
308                 int ret;
309
310                 ret = tevent_loop_once(ev);
311                 if (ret != 0) {
312                         return ret;
313                 }
314         }
315         return 0;
316 }
317
318 #endif
319
320 static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args,
321                                   PyObject *kwds)
322 {
323         struct py_cli_state *self;
324
325         self = (struct py_cli_state *)type->tp_alloc(type, 0);
326         if (self == NULL) {
327                 return NULL;
328         }
329         self->cli = NULL;
330         self->ev = NULL;
331         self->thread_state = NULL;
332         return (PyObject *)self;
333 }
334
335 static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
336                              PyObject *kwds)
337 {
338         NTSTATUS status;
339         char *host, *share;
340         PyObject *creds;
341         struct cli_credentials *cli_creds;
342         bool ret;
343
344         static const char *kwlist[] = {
345                 "host", "share", "credentials", NULL
346         };
347
348         PyTypeObject *py_type_Credentials = get_pytype(
349                 "samba.credentials", "Credentials");
350         if (py_type_Credentials == NULL) {
351                 return -1;
352         }
353
354         ret = PyArg_ParseTupleAndKeywords(
355                 args, kwds, "ss|O!", (char **)kwlist,
356                 &host, &share, py_type_Credentials, &creds);
357
358         Py_DECREF(py_type_Credentials);
359
360         if (!ret) {
361                 return -1;
362         }
363
364         if (!py_cli_state_setup_ev(self)) {
365                 return -1;
366         }
367
368         cli_creds = cli_credentials_from_py_object(creds);
369         if (cli_creds == NULL) {
370                 PyErr_SetString(PyExc_TypeError, "Expected credentials");
371                 return -1;
372         }
373
374         status = cli_full_connection(
375                 &self->cli, "myname", host, NULL, 0, share, "?????",
376                 cli_credentials_get_username(cli_creds),
377                 cli_credentials_get_domain(cli_creds),
378                 cli_credentials_get_password(cli_creds),
379                 0, 0);
380         if (!NT_STATUS_IS_OK(status)) {
381                 PyErr_SetNTSTATUS(status);
382                 return -1;
383         }
384         return 0;
385 }
386
387 static void py_cli_state_dealloc(struct py_cli_state *self)
388 {
389         TALLOC_FREE(self->thread_state);
390         TALLOC_FREE(self->ev);
391
392         if (self->cli != NULL) {
393                 cli_shutdown(self->cli);
394                 self->cli = NULL;
395         }
396         self->ob_type->tp_free((PyObject *)self);
397 }
398
399 static bool py_tevent_req_wait_exc(struct tevent_context *ev,
400                                    struct tevent_req *req)
401 {
402         int ret;
403
404         if (req == NULL) {
405                 PyErr_NoMemory();
406                 return false;
407         }
408         ret = py_tevent_req_wait(ev, req);
409         if (ret != 0) {
410                 TALLOC_FREE(req);
411                 errno = ret;
412                 PyErr_SetFromErrno(PyExc_RuntimeError);
413                 return false;
414         }
415         return true;
416 }
417
418 static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
419                                PyObject *kwds)
420 {
421         char *fname;
422         unsigned CreateFlags = 0;
423         unsigned DesiredAccess = FILE_GENERIC_READ;
424         unsigned FileAttributes = 0;
425         unsigned ShareAccess = 0;
426         unsigned CreateDisposition = FILE_OPEN;
427         unsigned CreateOptions = 0;
428         unsigned SecurityFlags = 0;
429         uint16_t fnum;
430         struct tevent_req *req;
431         NTSTATUS status;
432
433         static const char *kwlist[] = {
434                 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
435                 "ShareAccess", "CreateDisposition", "CreateOptions",
436                 "SecurityFlags", NULL };
437
438         if (!PyArg_ParseTupleAndKeywords(
439                     args, kwds, "s|IIIIIII", (char **)kwlist,
440                     &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
441                     &ShareAccess, &CreateDisposition, &CreateOptions,
442                     &SecurityFlags)) {
443                 return NULL;
444         }
445
446         req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
447                                 DesiredAccess, FileAttributes, ShareAccess,
448                                 CreateDisposition, CreateOptions,
449                                 SecurityFlags);
450         if (!py_tevent_req_wait_exc(self->ev, req)) {
451                 return NULL;
452         }
453         status = cli_ntcreate_recv(req, &fnum);
454         TALLOC_FREE(req);
455
456         if (!NT_STATUS_IS_OK(status)) {
457                 PyErr_SetNTSTATUS(status);
458                 return NULL;
459         }
460         return Py_BuildValue("I", (unsigned)fnum);
461 }
462
463 static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
464 {
465         struct tevent_req *req;
466         int fnum;
467         NTSTATUS status;
468
469         if (!PyArg_ParseTuple(args, "i", &fnum)) {
470                 return NULL;
471         }
472
473         req = cli_close_send(NULL, self->ev, self->cli, fnum);
474         if (!py_tevent_req_wait_exc(self->ev, req)) {
475                 return NULL;
476         }
477         status = cli_close_recv(req);
478         TALLOC_FREE(req);
479
480         if (!NT_STATUS_IS_OK(status)) {
481                 PyErr_SetNTSTATUS(status);
482                 return NULL;
483         }
484         Py_INCREF(Py_None);
485         return Py_None;
486 }
487
488 static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
489                               PyObject *kwds)
490 {
491         int fnum;
492         unsigned mode = 0;
493         char *buf;
494         int buflen;
495         unsigned long long offset;
496         struct tevent_req *req;
497         NTSTATUS status;
498         size_t written;
499
500         static const char *kwlist[] = {
501                 "fnum", "buffer", "offset", "mode", NULL };
502
503         if (!PyArg_ParseTupleAndKeywords(
504                     args, kwds, "Is#K|I", (char **)kwlist,
505                     &fnum, &buf, &buflen, &offset, &mode)) {
506                 return NULL;
507         }
508
509         req = cli_write_andx_send(NULL, self->ev, self->cli, fnum, mode,
510                                   (uint8_t *)buf, offset, buflen);
511         if (!py_tevent_req_wait_exc(self->ev, req)) {
512                 return NULL;
513         }
514         status = cli_write_andx_recv(req, &written);
515         TALLOC_FREE(req);
516
517         if (!NT_STATUS_IS_OK(status)) {
518                 PyErr_SetNTSTATUS(status);
519                 return NULL;
520         }
521         return Py_BuildValue("K", (unsigned long long)written);
522 }
523
524 static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
525                              PyObject *kwds)
526 {
527         int fnum;
528         unsigned long long offset;
529         unsigned size;
530         struct tevent_req *req;
531         NTSTATUS status;
532         uint8_t *buf;
533         ssize_t buflen;
534         PyObject *result;
535
536         static const char *kwlist[] = {
537                 "fnum", "offset", "size", NULL };
538
539         if (!PyArg_ParseTupleAndKeywords(
540                     args, kwds, "IKI", (char **)kwlist, &fnum, &offset,
541                     &size)) {
542                 return NULL;
543         }
544
545         req = cli_read_andx_send(NULL, self->ev, self->cli, fnum,
546                                  offset, size);
547         if (!py_tevent_req_wait_exc(self->ev, req)) {
548                 return NULL;
549         }
550         status = cli_read_andx_recv(req, &buflen, &buf);
551
552         if (!NT_STATUS_IS_OK(status)) {
553                 TALLOC_FREE(req);
554                 PyErr_SetNTSTATUS(status);
555                 return NULL;
556         }
557         result = Py_BuildValue("s#", (char *)buf, (int)buflen);
558         TALLOC_FREE(req);
559         return result;
560 }
561
562 static PyObject *py_cli_ftruncate(struct py_cli_state *self, PyObject *args,
563                                   PyObject *kwds)
564 {
565         int fnum;
566         unsigned long long size;
567         struct tevent_req *req;
568         NTSTATUS status;
569
570         static const char *kwlist[] = {
571                 "fnum", "size", NULL };
572
573         if (!PyArg_ParseTupleAndKeywords(
574                     args, kwds, "IK", (char **)kwlist, &fnum, &size)) {
575                 return NULL;
576         }
577
578         req = cli_ftruncate_send(NULL, self->ev, self->cli, fnum, size);
579         if (!py_tevent_req_wait_exc(self->ev, req)) {
580                 return NULL;
581         }
582         status = cli_ftruncate_recv(req);
583         TALLOC_FREE(req);
584
585         if (!NT_STATUS_IS_OK(status)) {
586                 PyErr_SetNTSTATUS(status);
587                 return NULL;
588         }
589         Py_INCREF(Py_None);
590         return Py_None;
591 }
592
593 static PyObject *py_cli_delete_on_close(struct py_cli_state *self,
594                                         PyObject *args,
595                                         PyObject *kwds)
596 {
597         unsigned fnum, flag;
598         struct tevent_req *req;
599         NTSTATUS status;
600
601         static const char *kwlist[] = {
602                 "fnum", "flag", NULL };
603
604         if (!PyArg_ParseTupleAndKeywords(
605                     args, kwds, "II", (char **)kwlist, &fnum, &flag)) {
606                 return NULL;
607         }
608
609         req = cli_nt_delete_on_close_send(NULL, self->ev, self->cli, fnum,
610                                           flag);
611         if (!py_tevent_req_wait_exc(self->ev, req)) {
612                 return NULL;
613         }
614         status = cli_nt_delete_on_close_recv(req);
615         TALLOC_FREE(req);
616
617         if (!NT_STATUS_IS_OK(status)) {
618                 PyErr_SetNTSTATUS(status);
619                 return NULL;
620         }
621         Py_INCREF(Py_None);
622         return Py_None;
623 }
624
625 static PyMethodDef py_cli_state_methods[] = {
626         { "create", (PyCFunction)py_cli_create, METH_VARARGS|METH_KEYWORDS,
627           "Open a file" },
628         { "close", (PyCFunction)py_cli_close, METH_VARARGS,
629           "Close a file handle" },
630         { "write", (PyCFunction)py_cli_write, METH_VARARGS|METH_KEYWORDS,
631           "Write to a file handle" },
632         { "read", (PyCFunction)py_cli_read, METH_VARARGS|METH_KEYWORDS,
633           "Read from a file handle" },
634         { "truncate", (PyCFunction)py_cli_ftruncate,
635           METH_VARARGS|METH_KEYWORDS,
636           "Truncate a file" },
637         { "delete_on_close", (PyCFunction)py_cli_delete_on_close,
638           METH_VARARGS|METH_KEYWORDS,
639           "Set/Reset the delete on close flag" },
640         { NULL, NULL, 0, NULL }
641 };
642
643 static PyTypeObject py_cli_state_type = {
644         PyObject_HEAD_INIT(NULL)
645         .tp_name = "libsmb_samba_internal.Conn",
646         .tp_basicsize = sizeof(struct py_cli_state),
647         .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
648         .tp_doc = "libsmb connection",
649         .tp_new = py_cli_state_new,
650         .tp_init = (initproc)py_cli_state_init,
651         .tp_dealloc = (destructor)py_cli_state_dealloc,
652         .tp_methods = py_cli_state_methods,
653 };
654
655 static PyMethodDef py_libsmb_methods[] = {
656         { NULL },
657 };
658
659 void initlibsmb_samba_internal(void);
660 void initlibsmb_samba_internal(void)
661 {
662         PyObject *m;
663
664         talloc_stackframe();
665
666         m = Py_InitModule3("libsmb_samba_internal", py_libsmb_methods,
667                            "libsmb wrapper");
668
669         if (PyType_Ready(&py_cli_state_type) < 0) {
670                 return;
671         }
672         Py_INCREF(&py_cli_state_type);
673         PyModule_AddObject(m, "Conn", (PyObject *)&py_cli_state_type);
674 }