pysmbd: Change to keyword based arguments
[amitay/samba.git] / source3 / smbd / pysmbd.c
1 /*
2    Unix SMB/CIFS implementation.
3    Set NT and POSIX ACLs and other VFS operations from Python
4
5    Copyrigyt (C) Andrew Bartlett 2012
6    Copyright (C) Jeremy Allison 1994-2009.
7    Copyright (C) Andreas Gruenbacher 2002.
8    Copyright (C) Simo Sorce <idra@samba.org> 2009.
9    Copyright (C) Simo Sorce 2002
10    Copyright (C) Eric Lorimer 2002
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "smbd/smbd.h"
28 #include <Python.h>
29 #include "libcli/util/pyerrors.h"
30 #include "librpc/rpc/pyrpc_util.h"
31 #include <pytalloc.h>
32 #include "system/filesys.h"
33
34 extern const struct generic_mapping file_generic_mapping;
35
36 #undef  DBGC_CLASS
37 #define DBGC_CLASS DBGC_ACLS
38
39 static int conn_free_wrapper(connection_struct *conn)
40 {
41         conn_free(conn);
42         return 0;
43 };
44
45 static connection_struct *get_conn(TALLOC_CTX *mem_ctx, const char *service)
46 {
47         connection_struct *conn;
48         TALLOC_CTX *frame = talloc_stackframe();
49         if (!posix_locking_init(false)) {
50                 PyErr_NoMemory();
51                 TALLOC_FREE(frame);
52                 return NULL;
53         }
54
55         if (service) {
56                 NTSTATUS status;
57                 int snum = lp_servicenumber(service);
58                 if (snum == -1) {
59                         TALLOC_FREE(frame);
60                         PyErr_SetString(PyExc_RuntimeError, "unknown service");
61                         return NULL;
62                 }
63                 status = create_conn_struct(mem_ctx, NULL, NULL, &conn, snum, "/",
64                                             NULL);
65                 PyErr_NTSTATUS_IS_ERR_RAISE(status);
66         } else {
67                 conn = talloc_zero(mem_ctx, connection_struct);
68                 if (conn == NULL) {
69                         DEBUG(0, ("talloc failed\n"));
70                         TALLOC_FREE(frame);
71                         PyErr_NoMemory();
72                         return NULL;
73                 }
74
75                 if (!(conn->params = talloc(conn, struct share_params))) {
76                         TALLOC_FREE(frame);
77                         DEBUG(0,("get_conn: talloc() failed!\n"));
78                         PyErr_NoMemory();
79                         return NULL;
80                 }
81                 conn->params->service = -1;
82
83                 set_conn_connectpath(conn, "/");
84
85                 smbd_vfs_init(conn);
86         }
87         TALLOC_FREE(frame);
88         conn->read_only = false;
89         talloc_set_destructor(conn, conn_free_wrapper);
90         return conn;
91 }
92
93 static NTSTATUS set_sys_acl_conn(const char *fname,
94                                  SMB_ACL_TYPE_T acltype,
95                                  SMB_ACL_T theacl, connection_struct *conn)
96 {
97         NTSTATUS status = NT_STATUS_OK;
98         int ret;
99         mode_t saved_umask;
100
101         TALLOC_CTX *frame = talloc_stackframe();
102
103         /* we want total control over the permissions on created files,
104            so set our umask to 0 */
105         saved_umask = umask(0);
106
107         ret = SMB_VFS_SYS_ACL_SET_FILE( conn, fname, acltype, theacl);
108         if (ret != 0) {
109                 status = map_nt_error_from_unix_common(ret);
110                 DEBUG(0,("set_sys_acl_conn: SMB_VFS_SYS_ACL_SET_FILE "
111                          "returned zero.\n"));
112         }
113
114         umask(saved_umask);
115
116         TALLOC_FREE(frame);
117         return status;
118 }
119
120 static NTSTATUS set_nt_acl_conn(const char *fname,
121                                 uint32 security_info_sent, const struct security_descriptor *sd,
122                                 connection_struct *conn)
123 {
124         TALLOC_CTX *frame = talloc_stackframe();
125         NTSTATUS status = NT_STATUS_OK;
126         files_struct *fsp;
127         struct smb_filename *smb_fname = NULL;
128         int flags, ret;
129         mode_t saved_umask;
130
131         fsp = talloc_zero(frame, struct files_struct);
132         if (fsp == NULL) {
133                 TALLOC_FREE(frame);
134                 return NT_STATUS_NO_MEMORY;
135         }
136         fsp->fh = talloc(fsp, struct fd_handle);
137         if (fsp->fh == NULL) {
138                 TALLOC_FREE(frame);
139                 return NT_STATUS_NO_MEMORY;
140         }
141         fsp->conn = conn;
142
143         /* we want total control over the permissions on created files,
144            so set our umask to 0 */
145         saved_umask = umask(0);
146
147         status = create_synthetic_smb_fname_split(fsp, fname, NULL,
148                                                   &smb_fname);
149         if (!NT_STATUS_IS_OK(status)) {
150                 TALLOC_FREE(frame);
151                 umask(saved_umask);
152                 return status;
153         }
154
155         fsp->fsp_name = smb_fname;
156
157 #ifdef O_DIRECTORY
158         flags = O_RDONLY|O_DIRECTORY;
159 #else
160         /* POSIX allows us to open a directory with O_RDONLY. */
161         flags = O_RDONLY;
162 #endif
163
164         fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, O_RDWR, 00400);
165         if (fsp->fh->fd == -1 && errno == EISDIR) {
166                 fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, 00400);
167         }
168         if (fsp->fh->fd == -1) {
169                 printf("open: error=%d (%s)\n", errno, strerror(errno));
170                 TALLOC_FREE(frame);
171                 umask(saved_umask);
172                 return NT_STATUS_UNSUCCESSFUL;
173         }
174
175         ret = SMB_VFS_FSTAT(fsp, &smb_fname->st);
176         if (ret == -1) {
177                 /* If we have an fd, this stat should succeed. */
178                 DEBUG(0,("Error doing fstat on open file %s "
179                         "(%s)\n",
180                         smb_fname_str_dbg(smb_fname),
181                         strerror(errno) ));
182                 TALLOC_FREE(frame);
183                 umask(saved_umask);
184                 return map_nt_error_from_unix(errno);
185         }
186
187         fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
188         fsp->vuid = UID_FIELD_INVALID;
189         fsp->file_pid = 0;
190         fsp->can_lock = True;
191         fsp->can_read = True;
192         fsp->can_write = True;
193         fsp->print_file = NULL;
194         fsp->modified = False;
195         fsp->sent_oplock_break = NO_BREAK_SENT;
196         fsp->is_directory = S_ISDIR(smb_fname->st.st_ex_mode);
197
198         status = SMB_VFS_FSET_NT_ACL( fsp, security_info_sent, sd);
199         if (!NT_STATUS_IS_OK(status)) {
200                 DEBUG(0,("set_nt_acl_no_snum: fset_nt_acl returned %s.\n", nt_errstr(status)));
201         }
202
203         SMB_VFS_CLOSE(fsp);
204
205         conn_free(conn);
206         TALLOC_FREE(frame);
207
208         umask(saved_umask);
209         return status;
210 }
211
212 static NTSTATUS get_nt_acl_conn(TALLOC_CTX *mem_ctx,
213                                 const char *fname,
214                                 connection_struct *conn,
215                                 uint32 security_info_wanted,
216                                 struct security_descriptor **sd)
217 {
218         TALLOC_CTX *frame = talloc_stackframe();
219         NTSTATUS status = SMB_VFS_GET_NT_ACL( conn, fname, security_info_wanted,
220                                      mem_ctx, sd);
221         if (!NT_STATUS_IS_OK(status)) {
222                 DEBUG(0,("get_nt_acl_conn: get_nt_acl returned %s.\n", nt_errstr(status)));
223         }
224
225         TALLOC_FREE(frame);
226
227         return status;
228 }
229
230
231 static SMB_ACL_T make_simple_acl(gid_t gid, mode_t chmod_mode)
232 {
233         TALLOC_CTX *frame = talloc_stackframe();
234
235         mode_t mode = SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE;
236
237         mode_t mode_user = (chmod_mode & 0700) >> 6;
238         mode_t mode_group = (chmod_mode & 070) >> 3;
239         mode_t mode_other = chmod_mode &  07;
240         SMB_ACL_ENTRY_T entry;
241         SMB_ACL_T acl = sys_acl_init(frame);
242
243         if (!acl) {
244                 return NULL;
245         }
246
247         if (sys_acl_create_entry(&acl, &entry) != 0) {
248                 TALLOC_FREE(frame);
249                 return NULL;
250         }
251
252         if (sys_acl_set_tag_type(entry, SMB_ACL_USER_OBJ) != 0) {
253                 TALLOC_FREE(frame);
254                 return NULL;
255         }
256
257         if (sys_acl_set_permset(entry, &mode_user) != 0) {
258                 TALLOC_FREE(frame);
259                 return NULL;
260         }
261
262         if (sys_acl_create_entry(&acl, &entry) != 0) {
263                 TALLOC_FREE(frame);
264                 return NULL;
265         }
266
267         if (sys_acl_set_tag_type(entry, SMB_ACL_GROUP_OBJ) != 0) {
268                 TALLOC_FREE(frame);
269                 return NULL;
270         }
271
272         if (sys_acl_set_permset(entry, &mode_group) != 0) {
273                 TALLOC_FREE(frame);
274                 return NULL;
275         }
276
277         if (sys_acl_create_entry(&acl, &entry) != 0) {
278                 TALLOC_FREE(frame);
279                 return NULL;
280         }
281
282         if (sys_acl_set_tag_type(entry, SMB_ACL_OTHER) != 0) {
283                 TALLOC_FREE(frame);
284                 return NULL;
285         }
286
287         if (sys_acl_set_permset(entry, &mode_other) != 0) {
288                 TALLOC_FREE(frame);
289                 return NULL;
290         }
291
292         if (gid != -1) {
293                 if (sys_acl_create_entry(&acl, &entry) != 0) {
294                         TALLOC_FREE(frame);
295                         return NULL;
296                 }
297
298                 if (sys_acl_set_tag_type(entry, SMB_ACL_GROUP) != 0) {
299                         TALLOC_FREE(frame);
300                         return NULL;
301                 }
302
303                 if (sys_acl_set_qualifier(entry, &gid) != 0) {
304                         TALLOC_FREE(frame);
305                         return NULL;
306                 }
307
308                 if (sys_acl_set_permset(entry, &mode_group) != 0) {
309                         TALLOC_FREE(frame);
310                         return NULL;
311                 }
312         }
313
314         if (sys_acl_create_entry(&acl, &entry) != 0) {
315                 TALLOC_FREE(frame);
316                 return NULL;
317         }
318
319         if (sys_acl_set_tag_type(entry, SMB_ACL_MASK) != 0) {
320                 TALLOC_FREE(frame);
321                 return NULL;
322         }
323
324         if (sys_acl_set_permset(entry, &mode) != 0) {
325                 TALLOC_FREE(frame);
326                 return NULL;
327         }
328         return acl;
329 }
330
331 /*
332   set a simple ACL on a file, as a test
333  */
334 static PyObject *py_smbd_set_simple_acl(PyObject *self, PyObject *args, PyObject *kwargs)
335 {
336         const char * const kwnames[] = { "fname", "mode", "gid", "service", NULL };
337         NTSTATUS status;
338         char *fname, *service = NULL;
339         int mode, gid = -1;
340         SMB_ACL_T acl;
341         TALLOC_CTX *frame;
342         connection_struct *conn;
343
344         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "si|iz",
345                                          discard_const_p(char *, kwnames),
346                                          &fname, &mode, &gid, &service))
347                 return NULL;
348
349         acl = make_simple_acl(gid, mode);
350
351         frame = talloc_stackframe();
352
353         conn = get_conn(frame, service);
354         if (!conn) {
355                 return NULL;
356         }
357
358         status = set_sys_acl_conn(fname, SMB_ACL_TYPE_ACCESS, acl, conn);
359         TALLOC_FREE(acl);
360
361         TALLOC_FREE(frame);
362
363         PyErr_NTSTATUS_IS_ERR_RAISE(status);
364
365         Py_RETURN_NONE;
366 }
367
368 /*
369   chown a file
370  */
371 static PyObject *py_smbd_chown(PyObject *self, PyObject *args, PyObject *kwargs)
372 {
373         const char * const kwnames[] = { "fname", "uid", "gid", "service", NULL };
374         connection_struct *conn;
375         NTSTATUS status = NT_STATUS_OK;
376         int ret;
377
378         char *fname, *service = NULL;
379         int uid, gid;
380         TALLOC_CTX *frame;
381         mode_t saved_umask;
382
383         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sii|z",
384                                          discard_const_p(char *, kwnames),
385                                          &fname, &uid, &gid, &service))
386                 return NULL;
387
388         frame = talloc_stackframe();
389
390         conn = get_conn(frame, service);
391         if (!conn) {
392                 return NULL;
393         }
394
395         /* we want total control over the permissions on created files,
396            so set our umask to 0 */
397         saved_umask = umask(0);
398
399         ret = SMB_VFS_CHOWN( conn, fname, uid, gid);
400         if (ret != 0) {
401                 status = map_nt_error_from_unix_common(errno);
402                 DEBUG(0,("chown returned failure: %s\n", strerror(errno)));
403         }
404
405         umask(saved_umask);
406
407         TALLOC_FREE(frame);
408
409         PyErr_NTSTATUS_IS_ERR_RAISE(status);
410
411         Py_RETURN_NONE;
412 }
413
414 /*
415   chown a file
416  */
417 static PyObject *py_smbd_unlink(PyObject *self, PyObject *args, PyObject *kwargs)
418 {
419         const char * const kwnames[] = { "fname", "service", NULL };
420         connection_struct *conn;
421         NTSTATUS status = NT_STATUS_OK;
422         int ret;
423         struct smb_filename *smb_fname = NULL;
424         char *fname, *service = NULL;
425         TALLOC_CTX *frame;
426
427         frame = talloc_stackframe();
428
429         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|z",
430                                          discard_const_p(char *, kwnames),
431                                          &fname, &service)) {
432                 TALLOC_FREE(frame);
433                 return NULL;
434         }
435
436         conn = get_conn(frame, service);
437         if (!conn) {
438                 TALLOC_FREE(frame);
439                 return NULL;
440         }
441
442         status = create_synthetic_smb_fname_split(frame, fname, NULL,
443                                                   &smb_fname);
444         if (!NT_STATUS_IS_OK(status)) {
445                 TALLOC_FREE(frame);
446                 PyErr_NTSTATUS_IS_ERR_RAISE(status);
447         }
448
449         ret = SMB_VFS_UNLINK(conn, smb_fname);
450         if (ret != 0) {
451                 status = map_nt_error_from_unix_common(errno);
452                 DEBUG(0,("unlink returned failure: %s\n", strerror(errno)));
453         }
454
455         TALLOC_FREE(frame);
456
457         PyErr_NTSTATUS_IS_ERR_RAISE(status);
458
459         Py_RETURN_NONE;
460 }
461
462 /*
463   check if we have ACL support
464  */
465 static PyObject *py_smbd_have_posix_acls(PyObject *self)
466 {
467 #ifdef HAVE_POSIX_ACLS
468         return PyBool_FromLong(true);
469 #else
470         return PyBool_FromLong(false);
471 #endif
472 }
473
474 /*
475   set the NT ACL on a file
476  */
477 static PyObject *py_smbd_set_nt_acl(PyObject *self, PyObject *args, PyObject *kwargs)
478 {
479         const char * const kwnames[] = { "fname", "security_info_sent", "sd", "service", NULL };
480         NTSTATUS status;
481         char *fname, *service = NULL;
482         int security_info_sent;
483         PyObject *py_sd;
484         struct security_descriptor *sd;
485         connection_struct *conn;
486         TALLOC_CTX *frame;
487
488         frame = talloc_stackframe();
489
490         if (!PyArg_ParseTupleAndKeywords(args, kwargs,
491                                          "siO|z", discard_const_p(char *, kwnames),
492                                          &fname, &security_info_sent, &py_sd, &service)) {
493                 TALLOC_FREE(frame);
494                 return NULL;
495         }
496
497         if (!py_check_dcerpc_type(py_sd, "samba.dcerpc.security", "descriptor")) {
498                 TALLOC_FREE(frame);
499                 return NULL;
500         }
501
502         conn = get_conn(frame, service);
503         if (!conn) {
504                 TALLOC_FREE(frame);
505                 return NULL;
506         }
507
508         sd = pytalloc_get_type(py_sd, struct security_descriptor);
509
510         status = set_nt_acl_conn(fname, security_info_sent, sd, conn);
511         TALLOC_FREE(frame);
512         PyErr_NTSTATUS_IS_ERR_RAISE(status);
513
514         Py_RETURN_NONE;
515 }
516
517 /*
518   Return the NT ACL on a file
519  */
520 static PyObject *py_smbd_get_nt_acl(PyObject *self, PyObject *args, PyObject *kwargs)
521 {
522         const char * const kwnames[] = { "fname", "security_info_wanted", "service", NULL };
523         char *fname, *service = NULL;
524         int security_info_wanted;
525         PyObject *py_sd;
526         struct security_descriptor *sd;
527         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
528         connection_struct *conn;
529         NTSTATUS status;
530
531         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "si|z", discard_const_p(char *, kwnames),
532                                          &fname, &security_info_wanted, &service)) {
533                 TALLOC_FREE(tmp_ctx);
534                 return NULL;
535         }
536
537         conn = get_conn(tmp_ctx, service);
538         if (!conn) {
539                 TALLOC_FREE(tmp_ctx);
540                 return NULL;
541         }
542
543         status = get_nt_acl_conn(tmp_ctx, fname, conn, security_info_wanted, &sd);
544         PyErr_NTSTATUS_IS_ERR_RAISE(status);
545
546         py_sd = py_return_ndr_struct("samba.dcerpc.security", "descriptor", sd, sd);
547
548         TALLOC_FREE(tmp_ctx);
549
550         return py_sd;
551 }
552
553 /*
554   set the posix (or similar) ACL on a file
555  */
556 static PyObject *py_smbd_set_sys_acl(PyObject *self, PyObject *args, PyObject *kwargs)
557 {
558         const char * const kwnames[] = { "fname", "acl_type", "acl", "service", NULL };
559         TALLOC_CTX *frame = talloc_stackframe();
560         NTSTATUS status;
561         char *fname, *service = NULL;
562         PyObject *py_acl;
563         struct smb_acl_t *acl;
564         int acl_type;
565         connection_struct *conn;
566
567         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO|z",
568                                          discard_const_p(char *, kwnames),
569                                          &fname, &acl_type, &py_acl, &service)) {
570                 TALLOC_FREE(frame);
571                 return NULL;
572         }
573
574         if (!py_check_dcerpc_type(py_acl, "samba.dcerpc.smb_acl", "t")) {
575                 TALLOC_FREE(frame);
576                 return NULL;
577         }
578
579         conn = get_conn(frame, service);
580         if (!conn) {
581                 TALLOC_FREE(frame);
582                 return NULL;
583         }
584
585         acl = pytalloc_get_type(py_acl, struct smb_acl_t);
586
587         status = set_sys_acl_conn(fname, acl_type, acl, conn);
588         PyErr_NTSTATUS_IS_ERR_RAISE(status);
589
590         TALLOC_FREE(frame);
591         Py_RETURN_NONE;
592 }
593
594 /*
595   Return the posix (or similar) ACL on a file
596  */
597 static PyObject *py_smbd_get_sys_acl(PyObject *self, PyObject *args, PyObject *kwargs)
598 {
599         const char * const kwnames[] = { "fname", "acl_type", "service", NULL };
600         char *fname;
601         PyObject *py_acl;
602         struct smb_acl_t *acl;
603         int acl_type;
604         TALLOC_CTX *frame = talloc_stackframe();
605         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
606         connection_struct *conn;
607         NTSTATUS status = NT_STATUS_OK;
608         char *service = NULL;
609         if (!tmp_ctx) {
610                 PyErr_NoMemory();
611                 return NULL;
612         }
613
614         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "si|z",
615                                          discard_const_p(char *, kwnames),
616                                          &fname, &acl_type, &service)) {
617                 TALLOC_FREE(frame);
618                 TALLOC_FREE(tmp_ctx);
619                 return NULL;
620         }
621
622         conn = get_conn(frame, service);
623         if (!conn) {
624                 TALLOC_FREE(frame);
625                 TALLOC_FREE(tmp_ctx);
626                 return NULL;
627         }
628
629         acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, acl_type, tmp_ctx);
630         if (!acl) {
631                 TALLOC_FREE(frame);
632                 TALLOC_FREE(tmp_ctx);
633                 status = map_nt_error_from_unix_common(errno);
634                 DEBUG(0,("sys_acl_get_file returned NULL: %s\n", strerror(errno)));
635                 PyErr_NTSTATUS_IS_ERR_RAISE(status);
636         }
637
638         py_acl = py_return_ndr_struct("samba.dcerpc.smb_acl", "t", acl, acl);
639
640         TALLOC_FREE(frame);
641         TALLOC_FREE(tmp_ctx);
642
643         return py_acl;
644 }
645
646 static PyMethodDef py_smbd_methods[] = {
647         { "have_posix_acls",
648                 (PyCFunction)py_smbd_have_posix_acls, METH_NOARGS,
649                 NULL },
650         { "set_simple_acl",
651                 (PyCFunction)py_smbd_set_simple_acl, METH_VARARGS|METH_KEYWORDS,
652                 NULL },
653         { "set_nt_acl",
654                 (PyCFunction)py_smbd_set_nt_acl, METH_VARARGS|METH_KEYWORDS,
655                 NULL },
656         { "get_nt_acl",
657                 (PyCFunction)py_smbd_get_nt_acl, METH_VARARGS|METH_KEYWORDS,
658                 NULL },
659         { "get_sys_acl",
660                 (PyCFunction)py_smbd_get_sys_acl, METH_VARARGS|METH_KEYWORDS,
661                 NULL },
662         { "set_sys_acl",
663                 (PyCFunction)py_smbd_set_sys_acl, METH_VARARGS|METH_KEYWORDS,
664                 NULL },
665         { "chown",
666                 (PyCFunction)py_smbd_chown, METH_VARARGS|METH_KEYWORDS,
667                 NULL },
668         { "unlink",
669                 (PyCFunction)py_smbd_unlink, METH_VARARGS|METH_KEYWORDS,
670                 NULL },
671         { NULL }
672 };
673
674 void initsmbd(void);
675 void initsmbd(void)
676 {
677         PyObject *m;
678
679         m = Py_InitModule3("smbd", py_smbd_methods,
680                            "Python bindings for the smbd file server.");
681         if (m == NULL)
682                 return;
683
684 }